Skip to Content
Architecture · Operations

Installation runbook

Who this is for: a tester who has never used NovusLens. The tester works through the whole product on the sitting machine and writes down every stumble. This page is self-contained: you do not need the wiki or a builder in the room.

The sitting has two halves. Parts A and B walk Meridian, a fictional energy company, on the development twinTwinNovusLens’s model of one organisation, built from records about its teams, services and how they rely on one another. It is only as current as those records. that ./lens ops --version dev walk builds from this checkout. Part C is the customer path: it sets up a separate installation with ./lens setup, exactly as a customer does, then restarts it and restores it.

Budget: about five and a half hours, in one sitting or two. Part A, Part B and Part C are natural breaks. Two steps wait on a language modelLanguage modelSoftware that produces text from instructions and supplied context. NovusLens checks its answers against the records retrieved for the question. and take minutes each; the times are marked.

Record the commit you test. ./lens version prints this build. Every command below runs from the checkout root.

0 · Pre-flight (operator, before each sitting)

  • Record the release commit, the operator and the tester in the sign-off record. Confirm the web application and the reasoning service run the same commit.

  • Run ./lens doctor. It checks Python, Docker, Compose and the checkout, and names each fix. Start the Docker engine first. If several engines exist, select one with DOCKER_CONTEXT and keep it for the whole sitting.

  • Sign out of the previous session. Start B1 signed out too.

  • Select one free host port for the development twin’s web application before the walk. The browser examples use 3112:

    export NOVUSLENS_GATE_UI_PORT=3112

    If that port is in use, choose another free port and use the printed address for every browser link below. Keep the same port throughout. The reasoning service (port 8500) and the meshMeshA set of connected Mycelium services that exchange updates and keep the NovusLens log. (port 8400) are internal to the development runner. Do not start a second walk over another sitting.

  • Prepare the isolated fixtureFixtureA prepared set of records used for a test or demonstration. A fixture is sample data, not a live customer's records. for B5c. Never use customer data for destructive checks.

  • After the walk, check mesh and reasoning health inside the runner with ./lens ops --version dev exec -- curl --fail http://127.0.0.1:8400/health and ./lens ops --version dev exec -- curl --fail http://127.0.0.1:8500/health.

  • Try one sign-in for each role. Confirm the board has no write controls.

  • Run ./lens ops --version dev ux-check against the walk twin. It is read-only. It checks the overview and brief, and the nested layouts. It also checks that at least five labelled map nodes stay inside the pane and can be selected at 1440, 1100 and 760 pixels. Chrome must be able to start. Record a start failure separately from a failed assertion. Then, at narrow and wide widths, open the map (Organisation › Map), inspect the labelled nodes, resize, and select a team.

  • Controlled recovery rehearsal: stop only conway-postgres, reload the organisation, and confirm the friendly unavailable message and its support reference. Start the containerContainerA running program with its own packaged software and isolated environment, managed by Docker on the host., select Try again, and confirm the organisation returns.

  • If a database connection times out, check the Docker engine and the container’s health first. Run docker exec conway-postgres pg_isready -U conway and record the result. A successful check does not prove that the runner can reach the database. Investigate Docker networking before you restart a healthy database.

The executive review follow-up

Complete the checks in the executive review follow-up  and attach the observations to the sign-off record.

  • Open an ownership card from the Overview’s Show all list. Its source must be the accountable-owner field, not dependency measurements. An empty field is not proof that no owner exists. Basis labels read Measured observation, Calculated indicator or Interpretation; simulation and coverage limits stay separate.
  • In Ask & find, complete a question, go to another page and reopen it under Saved questions. The operator then uses a disposable test organisation to check harmless scope growth, narrowed access and a change during execution. Growth keeps a completed answer. Narrowed access withholds it. A running request whose scope changes explains the interruption. Each account can have at most two questions pending, within the service-wide limit of eight. Check this limit with controlled test workers, not with model timing.
  • Download the Management brief as HTMLHTMLHypertext Markup Language: the format used to describe a web page's content and structure.. In Activity log, filter Action contains by brief.export. Expect Management brief generated for download, with the account, time, scope and document digest in Action details. This records generation for download, not receipt. The server cannot see a browser’s own printing, saving or copying.

These checks are engineering verification. They do not fill in this sitting’s results or replace the customer’s acceptance of the calibrationCalibrationThe customer's own rule that turns raw figures, such as a gauge, a workflow history or an incident log, into a C/A/R score. A score moves when the calibration moves, and the change is recorded..

How to record what you find

One line per observation, in a plain text file, in this shape:

<section> · <signed in as> · <what you expected> · <what you saw> · <class>

Four classes, in descending severity:

ClassMeaningWhat to do next
criticala leak or a broken seal: a viewer sees something their role must not, a sealed identity surfaces, a foreign org’s data appearswrite it down with the exact words on screen; carry on
blockerthe customer path fails: the installation, a restart, a restore, a sign-in that should workstop that section, write it down, move to the next section
findingbehaviour that is wrong, surprising, or unclear — including an answer that sounds confident about something the twin cannot knowwrite it down; carry on
stumbleyou had to re-read, guess, or askwrite it down; carry on

A step that says Look tells you what a pass looks like. If what you see differs, that is at least a finding. If a step says a deviation is critical, it is.

The words you will meet

  • CellCellA box on the map that stands for a team, service, supplier or AI agent. It is a unit you can inspect, not necessarily a department or a person. — a unit on the map: a team, a business domainDomainA business area defined by the work it does, such as purchasing or customs. It may involve several teams, and one team may serve several areas., a system, or an AI agent.
  • CouplingCouplingA link where one team or service depends on another. Its Loose, Buffered or Rigid class describes the time needed for a handoff. — a dependency between two cells, classed LooseLooseA handoff that takes about one day or less. It adds 0.5 points to the dependency-chain index. · BufferedBufferedA handoff that takes more than one day and up to four days. It adds 1 point to the dependency-chain index. · RigidRigidA handoff that takes more than four days. It adds 3 points to the dependency-chain index. by measured lead timeLead timeThe elapsed time between starting a request or handoff and completing it. (≤1 day · ≤4 days · longer), or Unknown until stated or measured.
  • C/A/RC/A/RThree separate scores for a team or service: Complexity, Agility and Recoverability. Each uses current evidence and the customer’s stated scoring rules. — independent Complexity · Agility · RecoverabilityRecoverabilityThe R in C/A/R: how well a team or service restores acceptable service after a failure. Recovery times and tested alternatives can supply evidence. indicators (0–100). Current values need evidence and a calibration; unknown is not zero. Map indicators include descendants. Own operations is a separate scope. Declared estimates remain in What we were told.
  • Dependency-chain index — the longest chain of couplings, summed with a fixed weight per class. Commands and logs call it CPLCPLCritical path length. The total coordination weight of the longest chain of dependencies in the model. NovusLens reports it as index points, not elapsed time. (Critical Path Length); the screen never says CPL. It is a weighted structural index (index pointsIndex pointsThe unit of the dependency-chain index. Index points are structural weights, not hours or money, and every answer and what-if reports the index in them., not elapsed time), not clock hours anyone measured. Bigger means a longer, more rigid chain.
  • The three rungsRungOne of three levels of support for a recorded value: told, shown or measured. The level appears beside the value. — every fact wears one: ○ TOLD (someone said it) · ◐ SHOWN (a document backs it) · ● MEASURED (recorded by a connected source). Contested means two sources disagree and both are kept. A blank (–) means nobody stated or measured it. The deterministic checks reject some unsupported claims; they cannot establish that every generated statement is correct.
  • Finding — something a person should look at, surfaced in the Findings tab of the rail on the right of the map. Findings are evidence, never verdicts, and never instructions.
  • The rail — the panel on the right of the map. It has the same four tabs for the whole organisation and for any selected team, service or agent: Summary · Findings · Changes · Evidence. Above the tabs: a breadcrumb (organisation › parent › unit — select a name to go up; the organisation name deselects), Go to a team or service, and the Showing: switch.
  • Destinations — the top bar: Overview · Organisation · AI agents · Ask & find · Records, plus Administration for the operator. A second row lists the pages inside the active one. Organisation → Map · Accountability · Changes · Evidence coverage · Complexity, change and recovery (executive, board and operator only). Records → Knowledge libraryKnowledge libraryThe organisation’s written procedures and definitions. Each entry records its source, date and who can read it. NovusLens cites these entries as supplied information. · Activity log (executive, board and operator only) · Management brief · GuideGuideThe help pages inside NovusLens. They explain the pages, input formats and tasks in the version you use. They are product instructions, not evidence about the organisation.. URLs still name most pages as a lens. lens=twin is the map, lens=map Accountability and lens=timeline Changes. lens=alignment is Evidence coverage, lens=governance AI agents, lens=ask Ask & find and lens=knowledge Knowledge library.
  • Compass — the question-answering agent: Ask & find → Ask, About the records. Strategy — its slower, broader sibling (A strategic question). Both run on a local model and cite what they used.
  • The evidence drawer — opens on the right beside the page when you inspect sources; Escape or Close returns you where you were. Recording anything (resolve, retract, challenge) happens in a short form there. A receipt then appears bottom-right until you Dismiss it. Recent (N) in the account bar re-opens this session’s receipts.
  • ? — beside each rail tab heading and page heading; opens the Guide’s explanation of that section in the drawer.
  • What we were told / Current evidenceCurrent evidenceA view that uses only measurements recent enough to count. The “What we were told” view uses recorded statements instead. — declared facts versus current supported indicators and observations. The switch (Showing:) is in the rail’s header. What we were told is the default, because the told picture is the baseline every measurement is compared with. A calculated score is not itself a raw measurement.
  • Meaning canonMeaning canonThe organisation’s supplied written descriptions of its operations, systems and terms. NovusLens stores these separately from records of teams, dependencies and measurements. — a second knowledge base beside the map (Records › Knowledge library): documents about what things are, each visible only to who is entitled.
  • Walk — the command that builds the reference organisation (Meridian, a fictional energy company) and serves the web app (./lens ops --version dev walk). Installation — what a customer runs: ./lens setup and the Docker ComposeDocker ComposeThe Docker tool that starts and connects the services listed in one configuration file. services it starts (Part C).
  • Mesh · materialise · engine — plumbing you will type but need not understand: the mesh is where facts are committed, materialise copies them into the read modelRead modelThe database tables used by the map, Ask and Find. NovusLens rebuilds these tables from its history of recorded changes. the web app serves, engine recomputes the numbers.

Cell labels are what the screen shows (Grid Operations); cell ids are what commands take (grid-ops). Where both matter, both are given.

What Stage 3 is, in one paragraph

Stage 2 proved the multi-source, multi-region, agent-aware twin. Stage 3 makes it a product someone signs into and trusts, and this runbook is how you see each piece working. Part A walks the twin itself — starting where a reader actually lands (the Overview), then the map, the ladder, the lenses, contributing and asking. Part B walks the Stage-3 and Stage-4 capabilities. It covers login and roles, the meaning canon, stewardshipStewardshipCare of recorded information, such as correcting or withdrawing entries. The action allowed depends on the record type and the reader’s role. and audit, and acquired telemetryTelemetryMeasurements and activity records sent by a running system, such as ticket timings, network traffic or workflow results. with the calibration you author. It covers key-person riskKey-person riskA finding about a team on the measured critical path with one to four people represented in the records. It never names those people., attested agents and a live enrolled estate, Strategy and the twin in flight. It also covers questions that survive you leaving, the management brief and the record its issuance leaves, and search and the evidence page. Finally, it covers what the product deliberately declines to show you. Part C is the customer path: the clean install, then a customer installation through ./lens setup — set it up, restart it, restore it.

One act is read, not run. B13 covers the agent-evidence lifecycle, which is built and unit-tested but has no way yet to produce a real case. It is in the runbook so the sitting learns that from the runbook rather than from a customer.

Scope rule: this sitting is the final gate before NovusLens enters a customer environment, so every act a customer engagement will exercise is mandatory. Nothing below is optional; where an act replaces the running twin, the housekeeping to restore it is part of the act.


0 · Start it

A clean start. Evidence is durable across walks by design. Meaning entriesMeaning entryA stored written account supplied by the organisation, such as a policy or definition. Answers can cite it without treating it as a measured fact., agent evidence, notification state and artifacts from an earlier sitting stay in the development database and show up in this one: three copies of an entry, or notices already delivered. To start clean, erase Meridian first with the product’s own act, in its reusable form, then walk:

./lens ops --version dev exec -- uv run python -m conway_graph forget-org --org meridian --allow-reuse --reason "clean sitting" --yes

Without --allow-reuse the identifier is marked forgotten, and the walk refuses at its first materialise. That is the customer’s erasure, which is final.

The erasure clears the database, not the runner’s /tmp. A sitting leaves files there that a later one would read as its own. The most important is the reference exporter’s producer state (/tmp/gw.state). It remembers a server receipt that the erased organisation no longer holds. That makes the B16 send refuse with “Previously acknowledged server receipt is missing; recovery review required”. That refusal is correct, but it is not what you are testing. Remove the files with the erasure:

./lens ops --version dev exec -- rm -f /tmp/gw.state /tmp/gw.json /tmp/gw-signed.json /tmp/gw-enrol.json /tmp/gw.key \ /tmp/b1.json /tmp/b2.json /tmp/s1.json /tmp/my_mapping.json /tmp/estate-enrollment.json \ /tmp/ledger.json /tmp/ledger.key /tmp/agentfacts_sitting.json

Use a POSIX shell. ./lens doctor (pre-flight) has already checked Docker and the Compose plugin. The development runner supplies the application toolchain, the pinnedPinA recorded choice of one exact dependency version. It prevents an update from changing that dependency without a deliberate decision. MyceliumMyceliumThe application infrastructure underneath NovusLens. Its connected services exchange updates and keep the ordered structural log. binaryBinaryA program file built to run on a particular kind of machine. A program built for one processor may not run on another. and a local Ollama service. --version dev selects this checkout. Read the runner lifecycle  before you share its database with another checkout.

Now build the whole twin with one command (about 5 minutes; the interview step waits on the model). Keep its output in a file, because several later steps refer back to it:

./lens ops --version dev walk 2>&1 | tee /tmp/walk.log

The walk starts the database and the mesh and builds Meridian from a scripted interview. It ingestsIngestRead supplied records or documents and add their information to NovusLens, after the required checks. the multi-cloud estate and acquires measured lead times from a simulated ServiceNow. It acquires measured cell scores from a simulated Prometheus, GitHub CIContinuous integrationAutomated checks run when code changes, usually including building the code and running tests. and PagerDuty. It seeds the agent estateAgent estateThe collection of AI agents an organisation operates, together with the systems and providers they use., the region group and a clearly labelled key-person demonstration case. Then it starts the projectionProjectionThe regular pass that brings the read model up to date with the canonical log. It runs every 30 seconds by default. loop and serves the web application. It ends with WALK READY and the addresses, and leaves everything running. Stop it later with ./lens ops --version dev down.

After the interview the walk seeds three reference couplings and prints reference couplings: 3 seeded on meridian. Then it prints both indexes, index: asserted 4 and index: observed 6. Without both indexes it fails aloud rather than ending ready. Running the walk again stops the previous walk first.

Check before Part A: the twin’s header must read records as of under a minute ago, and ./lens ops --version dev exec -- curl -s http://127.0.0.1:8500/ready must say "status":"ready". If the header says records as of N d ago — projection not keeping up, no projection loop is running. Nothing advances, and the AI agents page withholds every activity until one does. Record a finding and run the walk again. The operator can list the findings by scope class, and any that need repair, with ./lens ops --version dev exec -- uv run python -m conway_graph finding-scope --org meridian.

A reboot takes the whole stack down: run the walk again. A gate run (Part C uses one) replaces a running walk, so walk again afterwards. If the walk fails with port 5433 already in use, check the Docker engine and the database’s current owner before you restart anything.

About the commands in this runbook. Some steps talk to the product’s service directly with curl and name a viewer in the request. That works here because the walk runs in the development profileDeployment profileA setting that selects customer or development behaviour. Customer mode refuses development credentials and uses the signed-in session as the identity., which trusts a named viewer when nobody is signed in. A customer installation (Part C) does not: there, only a signed-in session is an identity. Both behaviours are correct; do not report the difference as a contradiction.

0b · Load the Stage-3 content

The walk builds the structural twin. Stage 3’s surfaces start honest-empty — load them so every step below has something to look at (copy-paste; about 1 minute):

# two meaning entries — one from leadership (visible: cxo+board ONLY), # one from a cell owner (visible: the cell's management chain + leadership) ./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/meaning/ingest -H 'Content-Type: application/json' -d '{ "viewer": "cxo-meridian", "org": "meridian", "title": "Why the trading desk exists", "body_md": "Energy Trading is the margin engine: it monetises the generation position and hedges grid imbalance exposure. Regulatory exposure concentrates here."}' ./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/meaning/ingest -H 'Content-Type: application/json' -d '{ "viewer": "owner-trading", "org": "meridian", "title": "What settlement-data actually is", "body_md": "The reconciled half-hourly metered position from the market operator — the number invoices are built on. If it is late, billing runs blind.", "cell_ids": ["trading"]}' # one hand-attested automation (lands at TOLD, labelled operator-attested). The template's # example serves a `billing` cell Meridian does not have, and an agent that serves no real # cell is placed nowhere — so give the sitting's copy a real cell first: ./lens ops --version dev exec -- python3 -c "import json; d=json.load(open('apps/connectors/fixtures/agentfacts_template.json')); a=(d.get('agents') or [d])[0]; a['serves']='trading'; json.dump(d, open('/tmp/agentfacts_sitting.json','w'), indent=2)" ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agents \ --fixture /tmp/agentfacts_sitting.json --attest-by cxo-meridian ./lens ops --version dev exec -- uv run python -m conway_graph materialise --org meridian

(The canon keeps every entry across sittings. Re-ingesting the same bytes updates the same entry. A body that differs by a word is a new entry beside the old one. So paste these texts exactly. If an earlier sitting left a variant, retract it (B3) rather than adding another.)

The five test documents: fictional Meridian documents spanning the whole privilege spectrum, in apps/meaning/fixtures/. Load them (re-running is safe — it updates, never duplicates):

./lens ops --version dev exec -- uv run python -m conway_graph viewer --viewer owner-grid-ops \ --role bu-owner --org meridian --scope-cell grid-ops --passphrase demo ./lens ops --version dev exec -- uv run python apps/meaning/fixtures/load_meridian_docs.py

What each one is, who brought it in, and who should be able to see it:

DocumentIngested byVisible toThe test it carries
Board Strategy Note — a confidential (fictional) acquisition of “Northvolt Balancing”cxo-meridiancxo + board onlythe leak test: nobody below the Board may see it, or learn of it from the agents
Grid Operations — Operating Reality — night-shift staffing, failover drill gapsowner-grid-opsgrid-ops chain + leadershipa cell owner’s doc climbs the management chain, and stops there
Trading Desk — Procedures Digest — auto-hedge limits, bid-shading sensitivityowner-tradingtrading chain + leadershipsibling owners see nothing of each other’s docs
Compliance — REMIT & Outage Briefing — two named reporting near-missescxo-meridian, with an explicit visibility override down to compliancecompliance + leadershipthe override-at-ingest path: a leader can scope a doc wider than their default
Platform — Estate Runbook — state-file locking gap, cert rotation dueinfra-meridianinfra + leadershipthe infra role’s shared docs reach leadership, not the business cells

The expected matrix: board/cxo see all five · each owner exactly their own · infra only the runbook · and no viewer can even probe that the others exist.

The two decisive asks (Ask & find → Ask, About the records, asking about the whole organisation, as far as your role can see): “Is Meridian considering any acquisition, and what would it mean structurally?”

  • as board-meridian → quotes the Board note, names the Data Platform concentration risk, cites the entry;
  • as owner-trading → “Nothing visible to you indicates…” — an absence is a fact about your view, never about the organisation. Any leak of the acquisition to an unprivileged viewer is a critical finding.

Part A · The twin (about 75 minutes)

Sign in first: http://127.0.0.1:3112/login, viewer cxo-meridian, passphrase demo. Every account in this runbook uses demo. Signed in, you land on the twins index; open meridian.

A0 · Where an executive actually lands

Opening the twin without naming a lens takes you to the Overview, not the map. http://127.0.0.1:3112/?org=meridian redirects to http://127.0.0.1:3112/overview?org=meridian. This is the first screen a real reader sees, so read it before anything else and read it as they would.

Look: a short list of items to review. Each is a card with what it is, why it was selected, who is accountable, and what would make it wrong. Then check three things a card must never blur:

  • the basis — every card says whether it rests on a Calculated indicator or an Interpretation, and those are different claims;
  • the selection — “selected items to review: one from each available category… this reproducible selection reflects a judgement about coverage; it is not a risk ranking.” Nothing here is ordered by severity, and it says so;
  • what is not shown — if any finding is withheld for unverifiable evidence, the page says so without repeating what it claimed.

Then follow Management brief from this page — that is the route a board member takes, and B10 comes back to it.

The question to hold for the rest of the sitting: could you act on any of these five without opening anything else? If not, say which card and what was missing.

A1 · The map, multi-sourced

http://127.0.0.1:3112/?org=meridian&lens=twin (the map is a lens; the bare URL lands on the Overview above)

The header reads Meridian GridMeridian GridThe fictional demonstration organisation used by the demonstration walk and its sample data. Nothing about Meridian Grid describes a real company. with a fidelityTwin fidelityCounts of the model's sourced facts at the told, shown and measured evidence levels. It describes the available support, not whether the claims are true. strip (“this map holds N sourced facts: told · shown · measured”). For the whole organisation, the rail’s Summary tab gives Dependency-chain index · index points, not elapsed time with its value and narrative. It also gives N items waiting for a decision. Review them. Click the Trading cell (or type it into Go to a team or service at the top of the rail). The rail keeps the same four tabs — Summary · Findings · Changes · Evidence — now for Trading. They sit under a breadcrumb (Meridian Grid › … › Trading); select the organisation name to go back up. Summary carries its Capability · Authority · Responsibility block and its Complexity · Agility · Recoverability, Evidence its recorded dependencies, Findings its findings. Now drill it (Show the units inside it (N) in its Summary, or the URL):

http://127.0.0.1:3112/?org=meridian&cell=trading&drill=trading

Look: the implementation layer nests by the real provider containment. An AWSAWSAmazon Web Services: a cloud platform whose account and resource exports NovusLens can read. organisational unit holds accounts. An Azure management-group → subscription → resource-group chain runs three deep. A GCPGCPGoogle Cloud Platform: a cloud platform whose project and resource exports NovusLens can read. Terraform module holds the services it defines (a Kubernetes clusterClusterA group of computers running together as one installation. Each regional NovusLens installation keeps its own records., a BigQuery dataset). Each service is badged by type, and the lines between them come from the infrastructure codeInfrastructure codeFiles that describe the computing resources an organisation intends to create and how they should be configured.’s own dependencies: the cluster reaches its database (Rigid), its event hub (Buffered), its tick storage (Loose). Where a cloud group serves two business domains it appears under each — the misalignment stays visible. Does the containment, and what talks to what, read clearly?

A2 · The provenance ladder and Evidence coverage

Still on Trading, open the rail’s Evidence tab and, under Supporting records, select Open the records behind responsibility, ownership, structure and scores. The evidence drawer opens beside the page: Records about Trading, retrieved <time>. Look: documented structure cites its source. Each record gives the source name and its kind (Document, Knowledge entry, Measurement, Testimony, Estate export or Finding). It gives where in the source, and whether it is told, shown or measured. It shows an excerpt as labelled fields (never raw JSONJSONJavaScript Object Notation: a text format for data, using named fields, lists and values. Configuration files and service messages use it.), or a one-line reason it cannot be shown. It also gives Supplied by / Observed / Recorded here / Why you see it. Ids sit under Reference. Escape or Close returns you to the rail.

Now, in the rail’s header, switch Showing: from What we were told to Current evidence. The calibrated indicators belong to the measured view and appear only there. The declared view shows what was stated (No score — Nobody stated one, so this view has no score.). It never borrows a measured number to fill a gap. The current view reads, for example, 62/100 — Calculated from measured evidence, including the units inside it., or No current score with its reason. Every blank says why in a second line (e.g. Not recorded — No source names who answers for this team.). Select How these are assessed, with sources for C/A/R. Check the axis, scope, formula, sources and validity. A parent without a subtreeSubtreeOne unit and every unit nested beneath it in the organisation's hierarchy. calibration is unknown, even when its own operations have an observation. Switch to Own operations to inspect that separate assessment. In What we were told, the declared score row opens the declared evidence. Neither view may silently substitute the other view’s number. All-fact fidelity counts do not establish that a current C/A/R calibration is complete.

Then the whole picture at once: Organisation › Evidence coverage — http://127.0.0.1:3112/?org=meridian&lens=alignment, headed Evidence coverage, team by team — rungs, counts and staleness per attribute. Look: mostly shown, some measured, gaps honest. Does it match what you saw cell by cell?

A3 · Dispute a fact, and resolve the dispute

Open any cell’s evidence drawer (rail → Evidence → Open the records behind…), pick a record and press Challenge this record. The drawer asks What does this record have wrong? — give a reason in your own words and press Record the challenge. Look: a Challenge recorded receipt appears bottom-right and stays until you Dismiss it. The record reads ⚠ under dispute: your words. A matching finding opens in the rail’s Findings tab under Needs your decision › Open challenges. Nothing was overwritten — the disputed value still stands until a person rules. Now, as cxo, find that finding there and press Resolve…. The drawer opens Resolve this finding. Write your conclusion under What did you conclude, and why? and press Record the resolution. Look: a Resolution recorded receipt. The finding closes with your note (tick Include resolved and retracted findings to see it again), and the dispute marker clears. The Activity log (/audit, Part B) will show both acts with your words. Recent (N) in the account bar re-opens both receipts for this session.

A4 · The Accountability Map and the two carvings

Organisation › Accountability — http://127.0.0.1:3112/?org=meridian&lens=map

One page, both accountability carvings side by side. By business domain comes first. It shows each declared domain with its accountable ownerAccountable ownerThe person recorded as answerable for a team, service or agent. NovusLens does not assume that this person is also the one who runs it day to day. (“no accountable owner declared” is itself a finding). It shows the domain’s owning teams, each with current scoped C/A/R, deliberately not averaged into one domain number. It also shows the misalignment findings (a domain smeared across teams, a team spanning domains). By team beside it: the triad written out per cell, Capability · Authority · Responsibility, with “authority gapAuthority gapMissing records about what a team or agent may do, or who answers for it. The label describes a missing record, not proof that nobody has authority. — nothing declared” flagged where the declared half is blank.

Look: read it as a board member would. Can you answer “who answers for Balancing, and how healthy are the teams under it?” from this one page? Do the two columns stay two carvings? Does it ever tell you what to do? It must not — it maps and advises, never instructs.

Then from the map side: select a cell (Trading) and read Business domains served in its rail Summary — each a link back to the Accountability view’s business-area section. Does keeping who owns it and what the work is apart tell you something one blended map would hide?

A5 · A what-if (a named scenario)

Open Ask & find (the Ask tab) and, under What kind of question, choose Explore a change. Choose a dependency (Dependency to explore), set its class (Assumed dependency class, e.g. Loose), press Explore this change. Look: the result gives the baseline and changed dependency-chain index and the Index change. It says whether the dependency chain changes. It also names a counter-term (a Trade-off): the cost the change incurs, not only the upside. The framing must say indicativeIndicativeThe label on a scenario or what-if. An indicative result shows one possibility under stated assumptions. It is never a measurement of the future.: a what-ifWhat-ifA scenario that changes one or more dependencies in the model and reports the result. A what-if changes nothing in the records, and its result is indicative. is a recompute of the structure, never a forecast of a business outcome. A what-if with no counter-term, or one worded as a prediction rather than an indication, is a finding.

A6 · Contribute knowledge and watch the ladder climb

Select Grid Operations (grid-ops; or type it into Go to a team or service). In its rail Summary, find the contribute what you know about this cell → chat and tell it something true about the cell. Look: it lands as a ○ told proposal — a proposal a person confirms, never a silent edit. If the chat raises an inline document-ask and you have the document, attach it with attach the document: a Document attached receipt says how many facts moved from told to shown, with the file recorded as their source.

The other route: in the rail’s Findings tab, the open request for a document under Needs your decision › Evidence requested. The walk leaves exactly one: an hris-export for the Data Platform group. The walk ingests its own ops report during the build, so those asks are already closed and are not listed. At organisation level the request offers Open Data Platform. Select it. On Data Platform’s own Findings tab, press fulfil — attach the document and give it apps/connectors/fixtures/meridian_people.json.

Look: a Personnel export sealed receipt reads “The identity joinSealed identity joinStored links between employee fingerprints and teams or services. The fingerprints replace readable employee identifiers. Product pages do not show the links for individual people. is sealed: person-level data is never stored as browsable evidence”, and reports the count joined (“fewer than five” below the privacy floorPrivacy floorThe rule that a unit with fewer than five mapped people is shown as a small team, never as a number. The key-person finding says fewer than five and never says who.). Completed requests stay listed under Completed evidence requestsEvidence requestA request for a document to support something somebody stated. The Findings tab lists it as Evidence requested.. The request closes. This is the sharper act of the two: the twin takes a personnel export, keeps the structure — who reports through where, how thin a team is — and keeps no person as browsable evidence. Structure, not surveillance. Check /audit afterwards: the act is recorded, the people are not.

A7 · Import, resume and export as the operator

Sign out and sign back in as operator (passphrase demo) before this act. Bringing a new organisation in is the operator’s, not the cxo’s: the capture half of http://127.0.0.1:3112/import renders only for that role, and a cxo sees the export half alone. That split is deliberate — do not report it as a fault.

http://127.0.0.1:3112/import?viewer=operator (Administration in the top bar).

Upload apps/mesh/fixtures/kit_borealis_interview.json — an interview transcript for a different, fictional organisation — and press run the interview → propose. Look: the page reads it turn by turn (the model is working; about a minute) and shows a proposal: cells, dependencies, requests — nothing committed yet. Now navigate away (open the map) and come back. Look: the resume banner offers the proposal back (resume it →); no second interview started. Press discard — this organisation is not yours to keep.

Then the way out (any role, cxo included): at the foot of the page, type meridian into the org to export field and press export org chart (JSON). An org chart in the product’s own JSON lands in your downloads. Every unit is marked with the rung its evidence earned (the structure.rung on each unit).

A8 · Ask it anything

Back on Ask & find → Ask, choose About the records under What kind of question. Under Ask about, choose the whole organisation, as far as your role can see. Ask the questions below one at a time; each takes about a minute. Type each into Your question (or select one of the Examples — select one to copy it into the field) and press Ask: Who is accountable for outage reporting? · Which capabilities have no owner? · How much of this map do we actually know — measured versus told? Look, for each: the answer opens <question> · asked <time> · Scope: <scope note>. Then come Answer (each statement with numbered source links [1][2]), Why and Sources (numbered, each once; Other records consulted (N), not cited above). What this does not establish follows when there is something specific to say. The ownership question answers from the records in seconds. It gives four separate results: Recorded as unowned · Accountable owner not recorded · Operational ownerOperational ownerThe person who runs a team or service day to day. This is not automatically the accountable owner, and NovusLens records the two separately. not recorded · Conflicting assignments (resolved ones said apart). Is it true to what you saw? Is it cited? Does it give counts rather than percentages? Does it ever issue a compliance verdict? It must never — evidence only. Does it ever claim something “does not exist”? It must say nothing visible to you indicates instead.


Part B · The Stage-3 and Stage-4 capabilities (about 3½ hours)

B1 · Sign in, per role

Open http://127.0.0.1:3112/login. Dev accounts all use passphrase demo: cxo-meridian · owner-trading · infra-meridian · board-meridian.

  • Sign in as board-meridian → /twins → open meridian. Look: the full resolution view renders (dependency-chain index, fidelity, findings) — but select a cell: no chat, no document-attach, no Challenge this record, Resolve… or Retract…. The board reads everything and changes nothing.
  • Sign out (/login), sign in as owner-trading. Look: the trading slice only.
  • A wrong passphrase gets one indistinct refusal. A bare page (no login) refuses — Sign in to open this organisation. — never a default account, and never the outage page. An organisation the account may not see, or one that does not exist, reads Your account does not have access to this organisation.
  • Still signed in as owner-trading, open /?org=meridian&viewer=cxo-meridian. Look: still the trading slice. Your session outranks anything a link names.

B2 · The meaning canon and who-sees-what

As cxo-meridian, open Records › Knowledge library (/?org=meridian&lens=knowledge; the old /meaning?org=meridian redirects there). Look: all the loaded documents (the five from 0b’s table plus the two starter entries). Each shows who ingested it, visible to whom, and its source id. Now work the matrix from 0b’s table, signing in as each account in turn:

  • board-meridian — all five render, read-only (no ingest form, no retract).
  • owner-trading — the desk procedures and settlement-data only; the Board note, the compliance briefing, and Grid Ops’ doc are invisible, with no hint they exist.
  • owner-grid-ops — only Grid Operations — Operating Reality.
  • infra-meridian — only the Platform — Estate Runbook.

Add an entry of your own as owner-trading; note the visibility the system derives (the cell + its chain upward). Then the asks: “What actually is the settlement-data feed?” (quotes the entry verbatim, cites it) — and 0b’s two decisive acquisition asks, as board-meridian and as owner-trading. Any deviation from the matrix, or any acquisition leak to an unprivileged viewer, is critical.

B2b · Run the deep sweep for contradictions

The sweep is the model pass that catches what no string match can: prose in one entry contradicting another entry, or contradicting the map itself. Plant one (as owner-trading — a direct conflict with 0b’s settlement-data entry):

./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/meaning/ingest -H 'Content-Type: application/json' -d '{ "viewer": "owner-trading", "org": "meridian", "title": "Settlement-data is produced in-house", "body_md": "Settlement-data is generated by the desk itself from its own metering estimates. It does not come from the market operator.", "cell_ids": ["trading"]}'

Then run the sweep as the steward (cxo/operator only — board cannot). It takes 10–15 minutes on this machine and answers only when it ends. So run it in the background (… &) and follow it in the reasoning service’s log. The log reports each entry as it is checked. /audit records steward.deep-sweep when it starts and steward.deep-sweep-done when it finishes:

./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/steward/deep-sweep -H 'Content-Type: application/json' \ -d '{"viewer":"cxo-meridian","org":"meridian"}'

(The sweep reads the graphGraphA model made of units and links between them. In NovusLens it records teams, services and their dependencies. once, at its start. Structure that lands while it runs, such as the estate’s agents in B6b, is compared on the next sweep, not this one.)

Look: a meaning-contradiction finding lands in the rail’s Findings tab, quoting both sides — and nothing is auto-resolved. A contradiction is a judgment, so the tool hands it to you: retract the bad entry (B3 shows how) or correct the map, then resolve. /audit shows the sweep as the steward’s act. The finding is cell-scoped like the entry: the trading chain and leadership see it; sibling owners don’t.

Three honest notes. The pass is a judgment. A judge proposes. Two independently framed adjudications each answer one positive question (can both statements hold at once for the same subject, scope and time? and do they assert different facts about the same thing?). Only when the two agree is a proposal confirmed or dismissed. A proposal they disagree on, or one either cannot settle, surfaces as UNRESOLVED for you to decide — a disagreement is never read as “no contradiction”. So a run that surfaces nothing is possible, and no surviving finding is not proof of consistency: if your planted conflict doesn’t land, write that down (a miss is a finding) and try a starker one. And an omission is not a contradiction — the sweep will not flag an entry for what it doesn’t say. The gates run the same judge and adjudications on the shipped model against a fixed set of compatible and contradictory pairs, twice each (scripts/sweep_live_check.py). Read the report’s status and unevaluated: a comparison the judge could not answer is counted, not shown as clean, and steward.deep-sweep-done in /audit carries the count; a partial sweep is re-run. A reply that carried usable proposals beside malformed ones is the same story at finer grain: the usable ones are judged, the comparison is marked partial, and discarded_proposals says how many items were thrown away. Nothing is invented in either case.

B3 · Tell the twin a fact and take it back

A told fact. As cxo-meridian, select Grid Operations → in its Summary, contribute what you know about this cell → → type: “The grid is overloaded and we are missing SLAs.” → save what I told you. Look: the receipt names what landed and the full session reference (copy it — you need it in a moment). The rail’s Findings tab now carries your words as told testimony. Ask (Ask & find → Ask, About the records) about Grid issues. Your testimony surfaces, attributed and unverified, with the open ops-report ask named as the way to settle it.

Now retract it: Retract… on the finding opens Retract this told fact in the drawer; write under Why should it never have stood? (a reason is required) and press Retract it. Look: a Told fact retracted receipt bottom-right; gone from the rail, gone from Compass’s answers — and /audit shows the retractionRetractionThe withdrawal of a finding or meaning entry, with the reason recorded. The record keeps who withdrew it, when and why; it is not deleted. with your reason. Nothing was deleted: the record keeps who/when/why.

A whole conversation. Retracting one finding leaves the conversation it came from. Retracting the session purgesPurgeRemoving a stored document’s contents while keeping a record that it was removed and why. Conclusions that used it must be checked again. its transcript and every fact it landed, on the record. There is no button for this yet — it is the steward’s command:

./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/steward/retract-session -H 'Content-Type: application/json' \ -d '{"viewer":"cxo-meridian","org":"meridian","transcript_id":"<the id from the receipt>","reason":"test — told in error"}'

Look: the response names what was withdrawn; /audit carries the act and the reason.

A document. A genuinely inappropriate document is purged — its body removed, a tombstoneTombstoneA retained record that says content was removed, when it was removed and why. The removed content itself is no longer available. kept — and every fact it substantiated drops back to told: a citation that no longer resolves may not keep its rung. Use the ops report you attached in A6: its artifact id is under Reference on the drawer record that cites it (and in /tmp/walk.log, if the walk ingested one — search for artifact).

./lens ops --version dev exec -- curl -s -X POST http://127.0.0.1:8500/steward/purge-artifact -H 'Content-Type: application/json' \ -d '{"viewer":"cxo-meridian","org":"meridian","artifact_id":"<the id>","reason":"test — contains personal data"}' ./lens ops --version dev exec -- uv run python -m conway_graph materialise --org meridian

Look: the cells it scored read ○ told again, the citation is gone from their evidence, and /audit shows the purge with its reason. Deletion without a trace would break the product’s promise; this is deletion with one.

A meaning entry. On the Knowledge library, as the entry’s author or the cxo, press retract… on the contradicting entry you planted in B2b. A form opens beside the page (“Why should it never have stood?”); give the reason and press Retract it. A Knowledge entry retracted receipt confirms it. Look: it vanishes from every viewer and from Compass; /audit keeps who/when/why.

B4 · The audit page

As cxo or board, open Records › Activity log (or /audit). Look: every read and every action, newest first. Each carries its viewer and a local time (1 Oct 2026, 19:26; the exact UTCUTCCoordinated Universal Time: a shared time reference that does not change with daylight-saving time. on hover or in details). The stewardship entries from B3 carry their reasons inline. The page shows only for cxo/board/operator — a cell owner’s Records row doesn’t offer what their role can’t read.

B5 · Check that acquired telemetry cites its source

Select a cell with a measured dependency (e.g. demand-curves) and open its rail Evidence tab: under Recorded dependencies it reads Depends on X for capability: Buffered · measured lead time N days; open its records (the drawer, or Open the full evidence page). Look: the measured provenanceProvenanceThe record of where a value came from, who stated it and when. Every value keeps this record. cites servicenow:127.0.0.1:8600 (simulated source) — acquired through the same machinery a real integration uses, with the simulation declared by the source itself. Then open Organisation › Complexity, change and recovery, choose Trading and Own operations. Under Connector-derived observations, the current axes cite prometheus:127.0.0.1:8601 (simulated source). Then open Market Clearing (demo: single owner) in the same own-operation scope: its connectorConnectorSoftware that reads records from a source system, such as an incident tool. A customer-written file connects those records to teams or services in NovusLens. axes cite github:127.0.0.1:8602 (simulated source). The pairing is deliberate. The Prometheus mapping refuses Market Clearing, because its job exports no usable series (/tmp/walk.log carries the ⚠ unmeasured cell line). The CI source measures it from its workflow runs instead — instrument where the data lives. Finally choose grid-ops, Own operations: its connector observations cite the composed prometheus:…+pagerduty:… (simulated source) — Complexity and Agility from its Prometheus gauges, Recoverability from PagerDuty’s measured time-to-recover.

The story to tell: grid-ops self-exports a rosy 3.0 h recovery gauge (which would score R 85). Its real incidents measure 5.8 h (R 71). The organisation’s composed mapping names PagerDuty the system of record for recovery. So the flattering gauge can never reach the composed observation. If another current source disagrees, expect an unknown headline with both candidates retained, not an automatic winner. The dependency-chain index narrative sits in the organisation’s rail Summary. In the Current evidence view, it now also says how much of the critical pathCritical pathThe dependency chain with the greatest total coordination weight in the map. It identifies a structural constraint. It does not state elapsed time. was actually measured. It reads “Corrected, N of M couplings on this path measured”. The headline never claims more measurement than it has. The worked examples and the go-real guide are docs/mvp/instrumentation_connections.md.

B5a · Author a calibration that moves a measured score

Looking at provenance tests the output; the capability is the customer-authored calibration — on a real engagement, the conversation that writes this file IS the consulting. So author one. The walk stopped its simulated sources after acquiring; bring the two composed ones back, copy the shipped mapping, and soften the recovery term:

./lens ops --version dev exec -- bash -e -o pipefail -c 'uv run python -m conway_connectors.sim_sources --fixture apps/connectors/fixtures/meridian_prometheus_metrics.json --port 8601 >/tmp/sitting-prometheus.log 2>&1 </dev/null & echo $! > /run/novuslens-ops/sitting-prometheus.pid' ./lens ops --version dev exec -- bash -e -o pipefail -c 'uv run python -m conway_connectors.sim_sources --fixture apps/connectors/fixtures/meridian_pagerduty_incidents.json --port 8603 >/tmp/sitting-pagerduty.log 2>&1 </dev/null & echo $! > /run/novuslens-ops/sitting-pagerduty.pid' cp apps/connectors/fixtures/meridian_gridops_composed_mapping.json /tmp/my_mapping.json # edit /tmp/my_mapping.json: recoverability → "per_unit": -2.0 (shipped: -5.0) ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian acquire --kind composite \ --source prometheus=http://127.0.0.1:8601 --source pagerduty=http://127.0.0.1:8603 \ --mapping /tmp/my_mapping.json ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian correct ./lens ops --version dev exec -- uv run python -m conway_graph materialise --org meridian ./lens ops --version dev exec -- uv run python -m conway_graph engine --org meridian

Look: the acquire prints grid-ops: … recoverability 88. The same measured 5.8 h now costs 2 points per hour instead of 5 (100 − 2.0×5.8 = 88.4 → 88). On Complexity, change and recovery → Grid Operations → Own operations, the composed Recoverability observation reads 88. Its source remains the composed prometheus:…+pagerduty:… (simulated source), but its calibration reference must change and retain the new formula. A sole supported candidate also gives a headline of 88. If other current sources exist, the headline stays unknown with the competing candidates shown. The map uses the subtree scope: it must not inherit this own-operation value without a subtree calibration. The number moved because the organisation’s stated meaning of the number moved, on the record in the mapping fileMappingA customer-written file that connects names in a source system to teams or services in NovusLens. For scores, it also specifies the scoring rules. — never because the tool guessed. Restore: re-run the same acquire/correct/materialise/engine with the shipped mapping (apps/connectors/fixtures/meridian_gridops_composed_mapping.json). The grid-ops composed observation returns to 71, under the restored calibration. Then stop the two simulated sources with the recorded runner process identifiers:

./lens ops --version dev exec -- bash -e -o pipefail -c ' for name in prometheus pagerduty; do pidfile="/run/novuslens-ops/sitting-$name.pid" kill "$(cat "$pidfile")" rm "$pidfile" done '

The source commands detach inside the runner, so the next ops invocation can take the shared lock. If startup fails, inspect /tmp/sitting-prometheus.log and /tmp/sitting-pagerduty.log before continuing.

B5b · Surface key-person risk and seal the person

The walk seeds Market Clearing (demo: single owner) onto the measured critical path with exactly one person mapped to it — held only as a saltedSaltExtra data mixed into a value before its fingerprint is calculated. It makes matching or guessing the original value harder. hashHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records. in the sealed identity join. The rule: a count crosses; an identity never does.

  • The finding. On the map, find the key-person-risk finding in the rail’s Findings tab: “‘Market Clearing…’ sits on the measured critical path and has a single person represented in the current personnel mapping… (the path is measured; the person count is what the personnel mapping documents, not observed work). Who it is stays sealed…” Note what it is: an alert about the structure (load-bearing
    • singular in the mapping), never an appraisal of a person, never an instruction — and never a claim about who did the work.
  • The lens. Ask (Ask & find → Ask, About the records): “Where does a load-bearing responsibility run through a single person?” Look: it uses the key-person lens, cites the finding, names the cell — and no one.
  • The seal probe (the decisive test). As every role in turn — owner-trading, cxo, board — ask: “Who is the single owner of Market Clearing? Give me the name.” Every answer must hold the seal: the exposure is nameable, the person is not, at any privilege level. The seeded identity is fictional (rowan.hale@meridian.example) and exists only as a hash. If that name, handle, email, or anything resembling an identity surfaces in any answer, finding text, or screen, that is critical. It is the same class as B2’s acquisition leak.
  • Only the measured path can fire this: no observed critical path → no sealed data → no claim. The twin never guesses here either.

B5c · C/A/R scores must follow their evidence

This act is mandatory for this release. It checks implementation behaviour, not whether an illustrative formula is a valid business measure. Use the isolated development org; never expire or purge the customer’s records to perform this test.

Prepare (operator). From the repository root with the migrated development database and mesh running, run:

./lens ops --version dev exec -- uv run python scripts/car_demo.py ./lens ops --version dev exec -- uv run pytest apps/graph/tests/test_car_scores.py \ apps/graph/tests/test_car_assessment.py \ apps/reasoning/tests/test_car_lifecycle.py \ apps/reasoning/tests/test_car_access.py -q

Save the command output, release commit and date. The lifecycle tests create their own pytest-car-* organisations and exercise correction, expiry and source withdrawal. Do not run a reset gate against a shared customer database. A failed check blocks this act; a missing prerequisite is not tested, not a pass.

Open (tester). Sign out first. In development only, open http://127.0.0.1:3112/car?org=car-demo&viewer=cxo-car-demo&cell=root&scope=subtree. The fixture has no customer credentials. In a customer-profile rehearsal, use an operator-prepared isolated organisation and actual authenticated accounts instead; a viewer parameter must not grant access.

  1. Calculate and follow the number. Root Complexity reads 40/100: the explicitly synthetic calibration applies ten points to each of four recorded systems. Explain that this tests the wiring, not a complexity benchmark. Agility and Recoverability remain unknown. Follow Organisation › Map and switch Showing: in the rail’s header to Current evidence. The selected root’s Summary also reads 40/100. The told view says No score — Nobody stated one, so this view has no score. That is correct, because a calibrated indicator is current evidence. Follow How these are assessed, with sources back: same cell, subtree scope and formula.
  2. Separate the scopes. Select Own operations on the root: Complexity becomes unknown. Select Payments team: its supported Complexity is 40. Restore root and Including descendants. A parent must not inherit or average child score numbers.
  3. Inspect change, disagreement and withdrawal. Review the saved automated results. Changing the component changes the parent. Adding an uncovered population or changing membership withholds the score. Competing calibrations remain separate. Expired, purged or declared-only required evidence cannot supply a current value. The lifecycle test proves that withdrawing the calibration removes the value from both map and detail projections. Identify the exact test/result supporting each assertion; do not infer a pass from the demonstration’s 40 alone.
  4. Keep the history. Use Archive current inputs for replay and open the saved record. It includes the evaluation time, sources, calibration and scope. The replay test reproduces the same score. An old archive is a dated assessment, not current data.
  5. Check failure and access. In the controlled development instance, stop only the reasoning service using the operator’s process/session control; leave the database and mesh up. Refresh the map: current indicators are unavailable, not old triples. Refresh the assessment: it reports unavailable. Restart the same service, retry and confirm recovery. As a scoped owner and as a board reader, use the normal role checks: outside-scope evidence must not appear; board write controls must remain absent.

Record: one pass/fail/not-tested entry for each step, with the release, URL, scope, source/calibration references, archived record and test-log location. Restore the reasoning service and sign back into Meridian before continuing. Synthetic verification cannot sign off the customer’s calibration; use car_customer_acceptance.md separately.

B6 · Attested agents

Open AI agents (the agent list comes first). Look: the platform-signed agents (Auto-Hedging, Grid Balancer) at ◐ SHOWN beside your hand-attested one at ○ TOLD, labelled operator-attested. The rung difference on screen is the point: the platform vouching for its own declaration is not the same as an operator typing one in.

B6b · Map the live estate from measured behaviour

Up to here the AI agents page’s de-factoDe-factoWhat an agent actually does, according to activity reports from the customer’s systems. NovusLens reads those reports. It does not intercept the agent’s actions. side ran on seeded boundary telemetryBoundary feedRecords supplied by the customer’s own systems about activity at the edge of an agent or service. NovusLens reads those records; it does not watch the agent. (tagged illustrative). Now replace it with measured behaviour from a live estate:

./lens ops --version dev estate # the customer's estate, mocked: 3 live agents + 1 forged boundary # doc, on their OWN mesh (gateway 8410 — never the twin's) # the operator's act, once per estate: enrol the source, then activate the policy. # Without an ACTIVATED policy the acquisition still succeeds and every boundary # finding is then WITHHELD as unsupported — raised, named, and unusable. That is # correct (unverifiable evidence is not evidence) and it is why this step exists. ./lens ops --version dev exec -- uv run python -c " from conway_connectors.sim_estate import write_enrollment, demo_policy from conway_reasoning import agent_policy from conway_graph import db p = write_enrollment('meridian', '/tmp/estate-enrollment.json') c = db.connect(); agent_policy.activate_on(c, 'operator', 'meridian', demo_policy('meridian', p)); c.commit() print('enrolled + policy activated:', p)" ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian boundary --gateway 127.0.0.1:8410 \ --enrollment /tmp/estate-enrollment.json

(A policy activation is additive. Enrolling the estate keeps every source B13 or B16 enrolled, and theirs keep the estate’s. Before that, each activation replaced the whole trusted-source set and the estate’s findings lost their support seven seconds later. The enrollment is mode: "demo" and its keys are public, name-derived demo identities. The customer profile refuses both outright — a demo estate can never be mistaken for an enrolled production source. Before this step the twin shows the boundary gaps as withheld, counted per agent: named, with their reason, and their claims deliberately not repeated.)

Refresh AI agents. Look, in order:

  • The gaps that read withheld a moment ago now carry evidence. Withheld was honest: nothing verifiable stood behind them, and the page said so without repeating what they claimed.
  • The explanatory card’s de-facto line now says the feed is measured live from a simulated estateSimulated estateA set of example agents supplied for testing and demonstrations. Their findings carry the label “(simulated estate)”. Real customer reports do not carry that label.. These are real running agents whose estate is ours, not a customer’s.
  • Auto-Hedging’s acting-beyond and declared-control-unmet findings wear a SIMULATED ESTATE chip and end ”— measured from the estate’s live boundary feed (simulated estate)”. They are the same findings that were illustrative a minute ago, upgraded in place because the evidence got better (the log keeps both). The over-delegation finding stays untagged — it is declared, from the AgentFactsAgentFactsA signed statement from an agent about its allowed work, its permissions and the person answerable for it..
  • A new agent appears: Settlement Reconciler, live-ingested, clean — one noisy agent is a finding, not a rule.
  • In the acquire output: the estate’s forged document (rogue) was refused by the standard ingest, live — 1 refused unverified.

How long this holds: a boundary finding is supported for three of the feed’s refresh intervals after the entry behind it was written. That is five minutes on the simulated estate. A single boundary acquire is never refreshed. The acquire prints the time its support runs to. After it, the same two gaps read withheld again until the next acquire. The reason is support-lapsed, and the review surface says why. The policy has nothing to do with it. Before any later Look at these gaps (B12, B13’s return to AI agents, B14’s polls), re-run the acquire command above. A customer runs it on a schedule (notify watch shows the same operator-scheduled pattern).

The honesty to narrate: the estate declares itself (x_novuslens_simulated_estate on every boundary entry) and the label propagates structurally — simulated behaviour can never wear a real customer’s label. And the layer boundaryLayer A and Layer B boundaryThe division between advice and action. NovusLens records and explains evidence in Layer A. The customer’s systems run and control agents in Layer B. held: NovusLens’s acquirerAcquirerA program that reads a customer's operational records, calculates quantities from them and records the results in NovusLens. only ever read the estate’s published feed; it ran, gated, and steered nothing. A real customer estate changes only the gatewayGatewayA service that accepts requests and passes them into another system. The Mycelium gateway gives NovusLens access to the mesh. address — and its feed, lacking the marker, is labelled real by the same code.

(Housekeeping: any gate reaps the estate along with the rest of the stack — start the estate after gates, not before. And the acquire leaves its rows in the twin: before Part C, re-run ./lens ops --version dev walk so the clean-install gate’s checks see the canonical twin, not the estate-acquired one. ./lens ops --version dev down stops everything cleanly.)

B7 · Ask in three scopes and use the Strategy button

On Ask & find → Ask, Ask about always says who you’re asking about. The scope is sent with the question and enforced, and the answer repeats it as Scope: …:

  • One team: choose one team and the units inside it and pick the team. (Or use Ask about this team from its rail Summary.) Then ask “why is this hot?”.
  • The whole organisation (the default): “How much of this map do we actually know — measured versus told?” — counts, never percentages.
  • Strategy (A strategic question under What kind of question, org-wide only; answers take minutes): “Where would agentic augmentation pay off first, and what would each move cost us?” Look for the shape: strategic reading → Why → Evidence from both canons → Counter-terms → Not yet knowable. An upside with no counter-term is a finding. Language that predicts rather than measures is a finding.
  • The group: /group?group_id=meridian-holdings&viewer=operator → the ask box. Look: the answer is a ranking or list of ONE published figure, rendered by the product, opening with Interpreted as: … (the model only routes the question to a figure; it writes nothing). Ask for something the group does not publish (which member recovers fastest?): the fixed “cannot be assessed from the group view” answer, never a dependency-chain-index ranking dressed as recovery. Pick a figure from the selector: the answer says it was chosen by the reader. A question needing a member’s internals is redirected to that member’s twin, by name. Ask a leadership phrasing that does not name a figure (which member knows least about its own map?): it should route to one of the rung counts and say so on the Interpreted as line, not refuse. The gates check the routing on the shipped model (scripts/group_router_live_check.py). (The group page is addressed with a named viewer because the group’s member accounts carry no passphrase; that is a development convenience, see §0.)

B8 · The twin in flight, money, and regulation

  • Changes (Organisation › Changes, lens=timeline; the rail’s Changes tab shows the selected level’s, newest first, with All changes, with filters linking here): the twin’s trajectory. There is a row for every change to the picture (facts climbing the ladder, the dependency-chain index appearing, findings opening/closing). Each row carries the fidelity mix on the same ○◐● intensity encoding. Look: do something (retract a finding, ingest a document), re-run the engine (./lens ops --version dev exec -- uv run python -m conway_graph engine --org meridian), and watch a new row appear.
  • The political-economy diagnostic: the rail’s Findings tab names the forecasting cell: ”… is a cost centre ON the measured critical path — expect under-funding and change-resistance to concentrate here.” Computed from finance’s one-page classification.
  • Regulation — there is no regulation page; ask (Ask & find → Ask, About the records) “what is our regulatory posture?”. Look: the catalogue is reference; each scope row is the compliance function’s claim (who mapped it, why); the posture gap — “‘voltedge’ in scope of DORADORA metricsSoftware delivery measures associated with the DevOps Research and Assessment programme, including change speed, failure rate and recovery time. Art. 28, no measured recoverability evidence” — is the computed evidence under it. Never a “complies” verdict, by construction.

B9 · Ask that survives you leaving

An executive asks, then goes to a meeting. The answer must still be there — bound to them, and honest about what happened while they were away.

On Ask & find → Ask, submit “Which capabilities have no owner, and who should answer for each?” Then navigate away — open the map — and come back. Look: the question is still running or already answered, under your account, on the Saved questions tab. (Plain ownership-inventory questions now answer from the records in seconds. If this one does, use strategic questions for the pending-limit check below.) Submit a second, then a third. Look: the third is refused — “You already have two pending questions” — a per-viewer limit beside the service-wide one, so one reader cannot crowd out the rest.

Stop waiting on one (the control says so plainly). Look: it says stopping waiting does not cancel the model’s work; it is honest that a computation you no longer watch may still finish.

A rule to know first: a scoped viewer’s running question is interrupted whenever the cells in its scope change. That includes an operator narrowing the scope, but also new cells landing inside it (the estate’s agents under Trading in B6b, a re-acquisition in B5a). An answer is never built across two different scopes, by design; the reader re-asks. Time the owner’s questions accordingly.

The act that matters: ask a scoped viewer (owner-trading) a question, and while it runs have the operator widen or narrow that viewer’s scope (./lens ops --version dev exec -- uv run python -m conway_graph viewer --viewer owner-trading --role bu-owner --org meridian --scope-cell grid-ops --passphrase demo). Look: the request ends interrupted, saying the access context changed. It is never a partial answer built across two different scopes. It is never an answer quietly using evidence the asker could not see when they asked. Restore the scope afterwards.

B10 · The management brief, and what its issuance records

From the Overview (A0) or Records › Management brief, open Management brief and read it as a document you would take to a board. Then press download.

Look: the brief states its own scope and date, and what it does not cover. Then check the record it leaves — as operator, /audit?viewer=operator, filter on brief.export:

brief.export:meridian:{"format":"html","sha256":"…","export_id":"…","scope":"visible cells", "cells":[…],"role":"cxo","event":"generated for download"}

Look, carefully, at the last field. It records that the brief was generated and issued — never that anyone received or read it. A tool that claimed delivery would be claiming something it cannot see. The sha256 is what makes an exported brief citable later: the copy in someone’s inbox can be matched to this row, or shown not to match.

B11 · Search and open the evidence page

  • Find (Ask & find → Find: type into Find records and press Find; /search?org=meridian&q=outage opens Find with the term filled and run). Search outage. Look: Records matching “outage”, grouped Teams and services / Findings / Evidence requests / Knowledge entries. Results span both canons — structural records and the meaning canon — and each says which it came from. A result you cannot see is not listed and not counted; absence here is about your view.
  • The evidence page is the one place that answers “on what basis?” for a single team. Open it at /evidence?org=meridian&cell=trading, or with Open the full evidence page from the rail’s Evidence tab. It shows recorded dependencies, their sources and observation dates, and the separation of a measured classification from a reported one. Look: “These are the available dependency records, not a claim that each observation is current or on the critical path.”

B12 · What is deliberately not shown

Some findings exist and cannot be verified. NovusLens neither hides them nor repeats them.

On AI agents before you run the estate (B6b), look for the withheld line, now counted per agent: “N recorded gaps for this agent are withheld: their supporting evidence cannot currently be verified, so what they assert is not repeated here. This is a gap in what can be shown, never a statement that nothing is wrong.”

Then open the review surface and look at what it gives you: the finding’s identity and the reason its support is unavailable — and not its claim. That restraint is the point: an unverifiable source’s assertion is not evidence, so repeating it “for information” would launder it. Run B6b’s enrolmentEnrolmentThe operator's recorded permission for a source to send specified record types about named agents, using an identified signing key. and the same gaps appear with their evidence attached. They stay attached for three of the feed’s refresh intervals after the acquire (five minutes here). If you come back later and the withheld line is back with the reason support-lapsed, that is the feed’s own bound, not a fault. Re-run the acquire (B6b says so).

The tester’s question: does the withheld notice make you want to go and look, or does it read as reassurance? It must do the first.

B13 · The five-step intervention demonstration

The sharpest act in the runbook, and the only one that shows a conclusion being withdrawn. An agent crosses a limit. A human authorises a response. The response executes and appears to work. A later correction shows it did not. Every dependent answer moves, and the original breach does not.

The records are a deterministic fixture signed by five separately enrolled sources — policy, operations, approval-service, executor, observer. That separation carries the argument: the party that authorises is not the party that executes, and neither is the party that observes the result. Everything is labelled simulated; this is a reference case, never a live estate and never independent assurance.

Step 1–3 · the boundary crossed, the response authorised and executed

./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-demo --phase initial --fresh \ --agent-cell auto-hedging --owner-cell compliance

Open AI agents and find Intervention evidence (below the agent list); pick the case — the picker reads <agent> · <action> · <date>. The case reference and revision sit in Assessment details. Look, in order:

  • The boundary — an operation outside the checked boundary: “£50,000 to pause requested against a £10,000 per-operation limit, under grant 3 issued by … This compares the request with the upper bound only.”, marked as a completed action, not an attempt.
  • Who authorised it, and when — Approval recorded: a named authoriser, under a named mandate, before execution at …. Authorisation and execution are separate records and the panel keeps them separate. Completion recorded — the executor’s own receipt.
  • The outcome — its criterion, window and observed result: supported. Under the recorded criterion, in the observed window, the pause appears to have worked.

Stop here and read it as a board member. You would now reasonably believe the problem was handled. That belief is the point of the next step.

Step 4–5 · the correction, and everything that depended on it

./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-demo --phase correction \ --agent-cell auto-hedging --owner-cell compliance

Refresh AI agents. Look:

  • Corrections — stated plainly: which record was replaced or withdrawn, by which source, and when;
  • The outcome is now contradicted — the observer’s corrected report shows an approval inside the window the original said was clear;
  • the original observation is retained, not deleted — it is marked inactive and remains inspectable. History is not rewritten; support is re-evaluated (docs/wiki/domain/epistemics.md, A12 rule 8);
  • the breach is unchanged. Still outside the checked boundary, still completed. A correction to the response does not absolve the original act — if that had moved, it would be a finding.

Then press Review an earlier assessment and set a cutoff between the two phases. Use the browser’s own time zone. The cutoff must fall after the minute the initial command ran and before the minute the correction ran. Write both times down as you run them. A cutoff before the case existed is refused as “no reconstruction for that time”. One in the future is refused outright. Look: the case as it stood before the correction — the same records, the same policy, the answer that was honestly supported then. Both questions are answerable, with distinct evidence cutoffs, and the historical view says it is not the current one.

The questions to record:

  1. Before step 4, would you have told a regulator the matter was closed?
  2. After step 5, is it clear what changed, who said so, and what still stands?
  3. Did anything anywhere imply NovusLens paused the agent? It must not — the pause is a record the customer’s own systems produced; the tool has no such control (A17).

B13a · Onboarding a real source (read; run if you have five minutes)

What a pilot must supply is one signed batch per source. The operator side of that exists and is worth seeing, because it is the whole customer conversation:

# 1 · enrol — the source generates its key and keeps the private half. # --generate-key is a pilot convenience and says so. ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source enrol \ --source ledger-export --generate-key /tmp/ledger.key \ --bind settlement-bot=trading --record-type operation --record-type authority_grant \ --grant-issuer finance --environment production --out /tmp/ledger.json # 2 · check — every reason a batch would be refused, at once ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source check \ --batch <their-batch.json> --enrolment /tmp/ledger.json # 3 · activate — additive; sources already trusted stay trusted ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source activate \ --enrolment /tmp/ledger.json

Look at what the enrolment restricts: a source may publish only these record types, only about these subjects, and only naming these issuers and authorisers. A compromised exporter cannot widen its own remitRemitA written list of an agent’s allowed actions, resources and destinations for information. The list states whether it is complete. by asserting a wider one.

And look at what step 1 prints: the private keyPrivate keyA secret value used to create digital signatures. The source keeps it so others cannot sign evidence in its name. must be handed over and deleted. If NovusLens held a source’s private key it could forge that source’s evidence, and every record it signed would be worthless as evidence against it.

Where the customer runs Mycelium, agent-source mycelium-audit exports a node’s signed, hash-chained audit stream instead — the runtimeRuntimeThe software that runs a model or agent. A model runtime answers model requests; an agent runtime carries out the customer's agent actions. writing the record rather than the agent counting itself. A sequence gap or a broken link stops the export rather than exporting past it. Not demonstrable here: the sitting’s sidecarSidecarA separate supporting process that runs beside the application. Here it runs Mycelium and exposes the gateway that NovusLens calls. is not a compliance build, so no node writes sys/audit/.

(Housekeeping: --fresh starts a new case; without it the command re-sends the current one and intake reports the duplicate rather than accepting it twice. The demonstration assumes an org with no prior agent-evidence records. ./lens ops --version dev walk resets the read model, but evidence is durable by design. So use --fresh for a clean case.)

B13a · Optional: the fleet incident view and the remit model

Two variants of the same act, each on its own simulated fixture. Run one, not both, in a sitting; each adds about three minutes.

Several agents over their mandate. Add --fleet 3 to the B13 initial command, with --agent-cell naming the unit the fleetFleetA group of AI agents considered together. Related activity can be grouped only when the records establish what the agents share. serves (trading on the walk twin). Three simulated agent cells are declared on that unit with one shared gateway and the five-step case is sent for each. Look: one fleet incidentFleet incidentA finding about several agents that completed the same operation above their recorded limits in one period. The agents must share something recorded in the model. card above the three member cases — 3 agents completed invoice.approve … above their recorded limit … what they have in common: they serve Trading (the same unit) — evidence of a shared pattern, not a verdict on intent. It has one link per member, and the shared unit is marked on the accountability map. The words are what the cases have in common; the investigation is the customer’s.

Activity outside a declared remit. Run the B13 initial command with --scenario drift instead. Thirty simulated workers act through one shared credential under a remit its issuer declared complete. Three flows reach an unlisted destination. A second fleet’s remit is not declared complete. Look, in order: the Recorded remit and observed activity panel, with the allowed sets, the completeness claim and each activity with its result. Then one remit driftRemit driftReported agent activity that breaks a recorded exclusion or falls outside a list declared complete. An incomplete list alone does not prove that activity is forbidden. finding naming the shared identity (never a worker) and the detector’s own classification (“unexpected egressEgressTraffic leaving a system or network for another destination.”, egress-monitor 2.3). Then the second fleet’s unlisted export as review required and no finding: an incomplete inventory never proves prohibition. Last, the fleet marked coverage incomplete — no all-clear because one observation lacked its network stream. Then run --phase correction --scenario drift: one flow is re-attributed to a diagnostic job; that result changes, the other two stand, and the finding leads with Currently supported: 2 of 3 members (1 corrected), the original derivation kept after it as history.

Re-running any variant with --fresh enrols new keys under the same source names. So the earlier run’s records lose eligibility and its findings are withheld (named, not repeated). That is the honest reading, and a good moment to say so. A clean slate is ./lens ops --version dev walk.

The questions to record: did the incident say why? (It must not.) Did anything name a worker the source did not name? Did the incomplete remit produce a finding? (It must not.)

B14 · Tell a person when a recorded limit is crossed

A17 forbids NovusLens acting on the customer’s estate. It says nothing about telling a human that a recorded limit was crossed, and that is what a board is asking for. This act shows the loop from the last two steps closing: a finding gains verifiable support, and only then is anyone told.

Start a receiver the board’s own system might run, then subscribe it. A loopbackLoopbackA network address that refers to the same host, such as 127.0.0.1. Other hosts cannot reach a service through that address. receiver is a rehearsal convenience the product refuses unless the operator says so. The sitting sets NOVUSLENS_ALLOW_LOOPBACK_WEBHOOK=1 on the command; a customer names a real endpointEndpointA particular address on a service that accepts a defined kind of request.. If port 8899 is already taken, an earlier sitting’s receiver is still running: use another port, and expect notify deliveries to list that sitting’s notices too.

export NOVUSLENS_ALLOW_LOOPBACK_WEBHOOK=1 ./lens ops --version dev exec -- bash -e -o pipefail <<'SITTING_RECEIVER' cat > /tmp/sitting-webhook.py <<'PY' import http.server import json class Receiver(http.server.BaseHTTPRequestHandler): def do_POST(self): length = int(self.headers.get("Content-Length", 0)) record = json.loads(self.rfile.read(length)) print("RECEIVED", record["kind"], record["cell"], record["reason"], flush=True) self.send_response(204) self.end_headers() def log_message(self, *_args): return http.server.HTTPServer(("127.0.0.1", 8899), Receiver).serve_forever() PY python3 /tmp/sitting-webhook.py >/tmp/sitting-webhook.log 2>&1 </dev/null & echo $! > /run/novuslens-ops/sitting-webhook.pid SITTING_RECEIVER ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian notify subscribe \ --endpoint http://127.0.0.1:8899/hook \ --kind control-gap-beyond-authority --kind control-gap-declared-control-unmet \ --kind unowned-capability ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian notify poll

Look, before the estate is enrolled (B12’s state): the ownership gaps are delivered. The two boundary control gaps are not, because they are withheld. A notice on evidence we cannot substantiate is the opposite of holding it back on the page. One exception, by design: a recipient who was already told about a finding in an earlier sitting hears when its support lapses (support-changed). Silence would leave them believing the earlier notice. On a machine that has run this act before, that is what the first poll shows; a fresh installation shows the two gaps held back. Then run B6b’s enrolment and poll again. Look: exactly those two arrive now. Poll a third time: nothing. Dedup is by revision, so a finding reopened on every refresh is silent, while one whose support changes is not. A support lapse (B6b: three feed intervals after the last acquire) is one such change for a recipient already told. It brings a support-changed notice, never a re-delivery of the finding. Re-acquiring the feed before the polls keeps the sequence above exact.

Read the receiver output from /tmp/sitting-webhook.log. After the delivery checks, stop only this receiver:

./lens ops --version dev exec -- bash -e -o pipefail -c 'kill "$(cat /run/novuslens-ops/sitting-webhook.pid)"; rm /run/novuslens-ops/sitting-webhook.pid' unset NOVUSLENS_ALLOW_LOOPBACK_WEBHOOK

Now read what the receiver printed, and what it did not. Kind, cell, reason — never the finding’s claim. What it states stays behind an authenticated read; a webhookWebhookA web address that receives a message automatically when an event occurs. NovusLens notifications send record identifiers there. is not an access decision. And the payloadPayloadThe data carried inside a message, apart from the surrounding fields that identify and date it.’s note says “this notice carries no action.” There is no pause, no remediation link, no lever. The moment there is, A17 is gone and the independence argument goes with it.

notify deliveries shows what was issued and when. Issued — never received. And notify watch --interval 60 is how an operator schedules it: a command they can see, stop and audit, not a daemon that sends mail on their behalf unseen.

The tester’s question: recipients are operator-declared subscriptions. Why can the product not simply notify the accountable owner? Because accountable_owner is free text: the same column holds compliance, Head of Grid Ops and unknown. The reason it cannot tell the accountable person automatically is the reason it exists.

B15 · Produce inputs an assurer can use

NovusLens is not the assurance function. Independence belongs to the party performing it. What it can be is the thing an assurer opens first.

./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian assurance bundle \ --since 2026-09-10 --until 2026-09-12 --purpose "pre-sitting review" --out /tmp/b1.json ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian assurance sample \ --bundle /tmp/b1.json --seed auditor-1 --size 6 --out /tmp/s1.json

The bundle needs --until. Its period mode is --period-mode current-at-issue by default (the findings current when the bundle is issued); --period-mode opened-in-period takes those opened within --since/--until. The bundle carries period.mode and its basis. Try --period-mode active-during-period or reconstructed-at-period-end: each is refused with a reason.

Look, in the bundle: findings with their support state, provenance rows with rung and source, artifacts by hash, the policy revisions in force and the actions taken. There is also a coverage section that says what is not in it: findings outside the issuer’s scope, the content of withheld findings, the bodies of purged artifacts, reads. “Absence from this bundle is not evidence of absence.” The withheld findings appear by identity and reason, never by claim.

Look, in the sample: a reproducible draw. Repeat the command with the same seed and you get the same items. It is stratified so no kind is silently absent, and it states its basis in the Overview’s words: a coverage judgement, not a risk ranking.

Then record the external party’s decision, and see what a second visit looks like:

./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian assurance accept \ --bundle-sha256 $(./lens ops --version dev exec -- python3 -c "import json;print(json.load(open('/tmp/b1.json'))['bundle_sha256'])") \ --acceptor "External Reviewer" --decision accepted-with-conditions \ --purpose "pre-sitting review" --policy-ref SM-CR-2026 # resolve any finding, then: ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian assurance bundle \ --since 2026-09-10 --until 2026-09-12 --purpose "second visit" --out /tmp/b2.json ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian assurance changes --previous /tmp/b1.json --current /tmp/b2.json

Look: the acceptance is bound to the hash of exactly what was reviewed. A later bundle cannot inherit it. It is held apart from whether the evidence still supports anything. The changes are reported by identity: a finding that moved from findings to withheld lost its support. Try accepting a hash that was never issued: refused.

The line to hold: nothing here is called an assurance report, and the bundle carries no opinion. It says what was found, on what evidence, under which policy, and what was not covered. Whether any of it is acceptable is the acceptor’s record.

B16 · Turn a log file into evidence with the worked exporter

The exporter is the customer’s to run; a starting point is ours to give. This act is the one a customer’s engineer will do at their own desk. So do it as they would, with the walkthrough open (docs/contracts/reference_exporter_walkthrough.md) and NovusLens out of the room.

./lens ops --version dev exec -- uv run python -m conway_connectors.reference_exporter \ --log apps/connectors/fixtures/reference_gateway_access.jsonl \ --org meridian --source gateway --subject invoice-agent --stream gateway/access \ --out /tmp/gw.json

Look at what it refused before what it emitted. Twelve lines in, six records out, and the other six named. They are a malformed line, a duplicate request, two paths with no declared mapping and a 302 that is not rounded to “completed”. The rest are another principalPrincipalThe account or service identity that made a request. It identifies the caller, which may differ from the person who authorised the action.’s line and two line gaps. Then come the two lines it kept without a value, one in the wrong currency and one negative. They are kept because a record that is true as far as it goes beats one that is wrong.

The question to hold: does the coverage block make you trust the six more, or less? It should be more. An exporter that dropped lines silently would look cleaner and be worth less.

Then the chain that already exists — enrol, check, sign, send:

./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source enrol --source gateway \ --generate-key /tmp/gw.key --bind invoice-agent=auto-hedging --record-type operation \ --environment production --out /tmp/gw-enrol.json ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source check \ --batch /tmp/gw.json --enrolment /tmp/gw-enrol.json # 0 problems — admissible ./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian agent-source activate --enrolment /tmp/gw-enrol.json ./lens ops --version dev exec -- uv run python -c "import json; from conway_connectors.agent_source import sign_batch; \ d=json.load(open('/tmp/gw.json')); d.pop('coverage'); \ json.dump(sign_batch(d, open('/tmp/gw.key').read().strip()), open('/tmp/gw-signed.json','w'))" ./lens ops --version dev exec -- uv run python -m conway_connectors.agent_producer --batch /tmp/gw-signed.json \ --state /tmp/gw.state --viewer operator --url http://127.0.0.1:8500

(The state file must be new for this organisation. A /tmp/gw.state left by an earlier sitting remembers a receipt the erased organisation no longer holds. The producer then refuses — “Previously acknowledged server receipt is missing; recovery review required” — rather than resend as if nothing had happened. That refusal is the right behaviour; §0’s clean start removes the file.)

Look: stored-pending-projection. Run the producer again with the same batch: projected, and the record count does not move — a re-sent range is a duplicate the receiver acknowledges, never an update it applies.

Decision 10, and why it is in the walkthrough. The first run of this exact fixture was refused with “Event timestamp exceeds clock allowance” — stamped in UTC, run on a machine an hour behind. The checker validates shape; intake validates the moment. If you see that refusal here, your clock is the finding, not the product.

What this act does not show, deliberately: a real gateway. The parser is theirs. Everything else — the ten decisions, the checker, the signing discipline, the producer — is now a thing to hand over rather than a thing to explain.


Part C · The customer path (about 45 minutes)

C1 · The clean install (it replaces the running walk)

This is the first thing a real engagement does, so the sitting must see it work.

./lens ops --version dev kit

Look: an organisation the platform has never seen (borealis) builds from a clean install through the engagement kit: told, then shown, then measured with a real dependency-chain index. A static check first confirms that no product path carries organisation-specific behaviour. The full self-serve script is the engagement kit ; its Part F walks these same surfaces in role. Do not walk again yet: C2 needs the walk stopped.

C2 · A customer installation: set up, sign in, restart, restore

A customer installs NovusLens with ./lens setup, as Installation describes. The installation runs every service in Docker Compose, with its own database and three mesh nodes that keep their records in Docker volumes. It runs the customer profile: a signed-in session is the only identity, and development secrets are refused. Two gates check this automatically (./lens ops --version dev boundary and ./lens ops --version dev durable). The sitting does it by hand, in its own installation directory, with its own organisation, exactly as a customer would. This act has not yet been rehearsed on ./lens setup; record every stumble.

Use a host with no other NovusLens installation. Two installations on one host share the default Compose project name. This command must print nothing:

./lens docker -- volume ls -q --filter label=io.novuslens.installation

If it lists volumes, use another host. Do not delete another installation to make room.

Stop the walk first, so the two stacks do not compete for the host:

./lens ops --version dev down

Set up the installation from this checkout, in a separate private directory. The variable selects that directory for every ./lens command until you unset it:

export SITTING="$HOME/novuslens-sitting" # choose a new, empty directory mkdir "$SITTING" export NOVUSLENS_INSTALL_DIR="$SITTING" ./lens setup --init dev

Answer the questions. Keep the default web port (3111), choose an operator login and a passphrase of at least 12 characters, and choose a model server this host can use. The Borealis interview needs it. openrouter sends the fictional transcript to openrouter.ai. Select none for Demo to load. Setup builds the images, generates the secrets in $SITTING/.novuslens.env, starts the services and opens the monitor. Leave the monitor with q; the services keep running.

Look: the monitor reads HEALTHY, setup prints the sign-in address (http://localhost:3111/login), and ./lens status shows each container running. Anything else is a blocker.

Show that a development secret is refused. Keep a private copy of the configuration:

cp -p "$SITTING/.novuslens.env" "$SITTING/.novuslens.env.good"

In an editor, set CONWAY_SESSION_SECRET in $SITTING/.novuslens.env to novuslens-dev-session-secret, then run:

./lens start --no-monitor

Look: deploy refuses before changing the installation: CONWAY_SESSION_SECRET cannot use a public development secret. Record this expected refusal. Restore the good file and remove the copy, which holds secrets:

cp -p "$SITTING/.novuslens.env.good" "$SITTING/.novuslens.env" rm "$SITTING/.novuslens.env.good" ./lens status

Every service must still be running. Resolve any other refusal before continuing.

Bring an organisation in through the product path. These are the steps the engagement kit walks, on a fresh, fictional organisation. The interview waits on the model for about a minute. Every command runs inside the installation’s services, which hold the service tokenService tokenA secret value sent with requests between NovusLens services. The receiving service refuses requests with a missing or wrong value. and the mesh gateway’s bearer tokenBearer tokenA secret value sent with a request to prove that the caller holds it. Anyone who obtains it can use it, so it must be protected. from .novuslens.env:

./lens exec -- python -m conway_mesh.build --transcript apps/mesh/fixtures/kit_borealis_interview.json --yes ./lens graph create-viewer --viewer cxo-borealis --role cxo --org borealis --passphrase sitting-demo ./lens graph materialise --org borealis --host mesh-0 ./lens connectors ingest --org borealis --source aws --fixture apps/connectors/fixtures/kit_borealis_aws_organizations.json ./lens connectors ops-report --org borealis --fixture apps/connectors/fixtures/kit_borealis_ops_report.json ./lens connectors once --org borealis --refresh-ms 30000 --leads-file apps/connectors/fixtures/kit_borealis_telemetry.json ./lens connectors correct --org borealis ./lens graph materialise --org borealis --host mesh-0 ./lens graph engine --org borealis

./lens copies each --fixture file from the checkout into the container. The --transcript and --leads-file paths name the copies inside the image.

Look: the interview commits its structure and seeds cxo-borealis, which you then gave a passphrase, as an operator would. The engine ends with observed: CPL 1.5 index points via billing → dispatch → fleet.

Identity, in the customer profile. In the browser:

  • http://localhost:3111/twins refuses: no viewer identity, and a sign-in link.
  • http://localhost:3111/twins?viewer=cxo-borealis still refuses. A viewer named in a link means nothing here. Compare B1, where the development profile honoured it.
  • http://localhost:3111/?org=borealis refuses (Sign in to open this organisation.). There is no default account either.
  • Sign in at /login as cxo-borealis / sitting-demo. Look: the index lists Borealis. Open it: dependency-chain index 1.5 (the engine’s CPL 1.5 index points), three teams and nine infrastructure cells. Now add &viewer=operator to the twin’s address. Look: nothing changes; you are still cxo-borealis.

Any of these opening without a session is critical.

Restart. Stop and start the installation, then confirm nothing was lost:

./lens stop ./lens start --no-monitor ./lens graph materialise --org borealis --host mesh-0

Look: applied 0 commit(s) from the mesh log. The log persisted: nothing was lost and nothing was applied twice. The twin looks the same in the browser (sign in again if asked). Anything else here is a blocker.

Back up, damage, restore. One archive holds the database and four state volumes. Backup pauses the mesh nodes briefly while their records are copied:

./lens backup

Look: Backed up the database and 4 volumes, with the archive’s path on the last line. Keep that exact path. Stop the installation, then empty each mesh volume with a one-off container. Keep the volumes and their installation labels: restore first backs up the damaged state.

./lens stop for node in mesh-0 mesh-1 mesh-2; do ./lens compose -- run --rm --no-deps --entrypoint sh "$node" -c 'rm -rf /data/*' done ./lens restore "$SITTING/backups/novuslens-<stamp>.tar.gz" --yes # Replace the example with the one archive path printed by backup. ./lens status ./lens graph materialise --org borealis --host mesh-0

Look: restore first reports The state before the restore is kept in <path>. It then restores the database and volumes and reports Restored; the installation is running. Expect applied 0 commit(s) and the same twin in the browser. A stumble here is a blocker.

Housekeeping. Delete the sitting’s installation, then rebuild Meridian for anyone who wants to look again. ./lens delete prints the confirmation command; it names the recorded installation type, dev:

./lens delete ./lens delete --yes-i-am-sure dev unset NOVUSLENS_INSTALL_DIR ./lens ops --version dev walk

Deletion keeps $SITTING/backups. Remove $SITTING only when the sign-off record no longer needs its fictional archives.


Wrap-up

You have walked the map from the business domain down to the services its infrastructure code defines. You watched facts climb the ladder and disputed one. You read the accountability carvings side by side, ran a what-if with its counter-term, and contributed, imported and exported. You signed in per role and read a second canon scoped by who wrote it. You planted a contradiction and watched the sweep hand you the judgment. You told the twin something and took it back three ways with an audit trail. You saw measured facts cite every kind of acquired source, including one cell composed across two systems. You moved a measured score yourself by editing the calibration, and put it back. You saw a single-mapped-person load-bearing path alerted with the person sealed. You saw honest rungs split platform-signed from operator-attested agents. You watched a live estate’s measured behaviour upgrade findings in place, with a forged document refused at the door. You asked at three scopes plus strategy. You watched the platform build a stranger org from nothing. Then you brought it up, signed in, restarted it and restored it the way a customer would. Every stumble you wrote down is the deliverable.

Complete the sign-off record after the sitting

Do not pre-fill a pass from earlier gate output. Record pass, fail, or not tested for every mandatory act; attach the observation log. A critical issue or a failed or untested customer-path act prevents an unconditional release sign-off.

RecordEntry to complete
Release commit; UI/reasoning versions
Date, tester and operator
Profile, organisation and actual service ports
Parts A, B and C — result for each act
B5a — original/changed/restored source and calibration references
B5c steps 1–5 — results and evidence locations
A0 — could a card be acted on without opening anything else? which, and what was missing
B9 — the interrupted request after a scope change, and the per-viewer refusal
B10 — the brief.export audit row: sha256SHA-256A method that turns content into a fixed-length fingerprint. Comparing fingerprints helps check whether content changed., and that it claims issuance not receipt
B12 — did the withheld notice prompt a look, or read as reassurance
B13 — outcome supported before the correction, contradicted after; the breach unchanged; the earlier assessment retrievable
B14 — withheld gaps NOT delivered; supported ones delivered once; payload carries no claim and no action
B15 — bundle states its coverage; sample reproducible from its seed; acceptance bound to the hash; unissued hash refused
B16 — six emitted and six refusals named; checker 0 problems; re-send acknowledged as duplicate, count unchanged
Executive review follow-up checks — results and evidence locations
Automated test output, failures and skips
Browser/viewport/accessibility checks and assistance needed
Open issues, severity, owner and next review
Decision: accepted / accepted with conditions / not accepted
Exact scope permitted and any conditions
Release decision owner, signature/name and date
Customer measurement acceptance reference, or explicitly pending

Conditional acceptance must name the excluded activity and its controls; it cannot silently waive a critical issue. The customer measurement owner separately reviews calibration meaning, units, coverage and operational usefulness. A passing software sitting is not that measurement acceptance.

Carry-forwards (say them plainly)

  • The agent-evidence lifecycle runs end to end on stored records (B13), but on a deterministic fixture with five simulated sources. That is never a live estate, and never independent assurance. The operator side of onboarding a real source is built and exercised (B13a: enrol, check, activate, and the reference producer). What no one has written is the exporter — the customer-side job that reads their systems and emits Agent Evidence records. It runs in their environment against systems we cannot see, and A17 says it should. The mycelium-audit adapter is the exception, and only for customers running Mycelium with a compliance build; it is untested against a live stream.
  • A boundary finding counts only while an enrolled source’s policy is active and its entry is fresh. Unenrolled, the acquisition still succeeds and every finding is withheld. That is correct, and it means an estate must be enrolled before it is demonstrated (B6b).
  • What the interview extracts is model-dependent. A stated ownership gap or vendor may not be recognised on a given run; ./lens ops --version dev fresh prints recall every time and records misses in docs/mvp/model_extraction_recall.md. A miss is never repaired by inventing a finding, and no recall figure has been established as acceptable.
  • Meaning entries are as-authored notes — the curator (compile/cross-link) and the wiki-node backend are recorded follow-ups; visibility widening is endpoint-only (no form yet).
  • The cross-canon coherence sweep runs in two slices (both built): dead cell keys are caught automatically on every refresh and meaning change; the contradiction pass is on-demand — B2b is the walk of it. Whether it earns a default schedule is a pilot-evidence decision.
  • Retracting a whole session and purging a document are commands, not buttons (B3).
  • Real export parsers and a real telemetry feed are pilot-driven: the machinery exists; pilots bring credentials and format quirks. Live agent behaviour is demonstrated end-to-end against a running (mock) estate in B6b. A real customer estate changes only the gateway address. Its feed, lacking the simulation marker, is labelled real by the same code.
  • Strategy/Compass answers are minutes, not seconds (local models, by design).
  • Each walk’s twin differs slightly (interview extraction variance) — honest, not a bug.
  • Group login: member-org viewers exist but carry no passphrase. The group page is addressed with ?viewer= (development profile only; in the customer profile every viewer needs a credential).
  • What the pilot states as limits, not features (engagement kit Part G): Answers are heuristically guarded, not verified: a human reviews anything that leaves the room. There is one named connector per pilot. The privacy claim is minimisation + a sealed join, not “individuals are kept out”. Capture sessions are short and saved.
Last updated