Twelve gates, one transcript per run.
What it is for
./lens ops is the development driver at the repository root. It copies the image-owned pinnedPinA recorded choice of one exact dependency version. It prevents an update from changing that dependency without a deliberate decision. sidecarSidecarA separate supporting process that runs beside the application. Here it runs Mycelium and exposes the gateway that NovusLens calls. when requested and starts a fresh containerContainerA running program with its own packaged software and isolated environment, managed by Docker on the host. meshMeshA set of connected Mycelium services that exchange updates and keep the NovusLens log.. Its acceptance checks provide evidence for release decisions. Those twelve checks are called gates. They are NovusLens’s own release checks on its own code. They say nothing about a customer’s agents. This stack is separate from a persistent customer installation.
How it works
./lens ops starts the Docker controller. The controller invokes Bash command bodies in the selected runner, which it removes when the command ends unless services remain; each subcommand is a function. Gates needing a mesh call start_mesh. It frees ports 8400–8402, 57010–57012 and 8500. UI cleanup checks the command and worktree before stopping a server. It refuses a Docker port forwarder or another worktree’s UI; choose a free gate UI port instead. Each gate starts with an empty log. The mesh is three sidecar nodes with gatewaysGatewayA service that accepts requests and passes them into another system. The Mycelium gateway gives NovusLens access to the mesh. on 8400, 8401 and 8402 and gossipGossipA way for connected services to pass updates to one another until the updates spread through the group. on 57010 to 57012. There is no persistence: a fresh log per gate is by design.
pin_check runs on every mesh start. It warns if the image sidecar hashHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records. differs from config/release-manifest.json. A difference means testing an unrecorded build.
PostgresPostgresThe database software that stores the tables read by the NovusLens screens, along with document bodies, access records and protected personnel records. comes from ./lens docker -- compose -f deploy/dev/postgres.compose.yml up -d and listens on 5433 with container conway-postgres. The reasoning service uses 8500. The runner chooses a free loopbackLoopbackA network address that refers to the same host, such as 127.0.0.1. Other hosts cannot reach a service through that address. UI port from 3112. NOVUSLENS_GATE_UI_PORT can select another port before runner startup. The browser tests use that address too.
Subcommands
| Command | What it does |
|---|---|
./lens ops --version dev gates [group] | Runs a named group and writes a transcript. Default all runs twelve gates. Groups: all, mesh, customer, browser, llm, pr. |
./lens ops --version dev gates --list | Lists groups and their prerequisites. |
./lens ops --version dev gates <group> --members | Prints the selected group’s gate names. |
./lens ops --version dev test [path …] | Runs all tests or the supplied test paths, with the test prerequisites and audit checks. |
./lens ops --version dev test-mesh | Runs the subset marked as needing a live mesh gateway. |
./lens ops --version dev smoke, test, steel, fresh, ask, federation, agents, domains, e2e, kit, boundary, durable | One gate each. |
./lens ops --version dev sidecar | Copies /opt/novuslens/three_node_demo from the pinned shared base into the development checkout’s .sidecar/. It does not compile a host checkout. |
./lens ops --version dev mesh | Boots the local three-node mesh in the background and leaves it up. Process identifiers (PIDs) belong to /run/novuslens-ops inside the runner. |
./lens ops --version dev estate | Starts a simulated customer agent estateAgent estateThe collection of AI agents an organisation operates, together with the systems and providers they use. on its own two-node mesh (gateways 8410 and 8411, gossip 57020 and 57021), never peered with the 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.’s mesh. Its boundary feedBoundary 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. is marked as simulated. |
./lens ops --version dev walk | Not a gate. Builds the full fictional demonstration twin in one run and leaves it up for browsing. Needs a configured model endpointEndpointA particular address on a service that accepts a defined kind of request.; the local default uses Ollama. |
./lens ops --version dev answers [args] | Not one of the twelve. The answer-consistency evaluation: equivalent questions must give the same supported conclusions, qualifications and sources. It reads config/answer-eval.json, runs scripts/answer_eval.py and writes a JSONJSONJavaScript Object Notation: a text format for data, using named fields, lists and values. Configuration files and service messages use it. result under docs/gates/answers/. |
./lens ops --version dev ux-check | Runs the executive-view test (pnpm --filter ui test:executive) and apps/ui/scripts/check-overlaps.mjs against the running interface. |
./lens ops --version dev down | Stops the mesh, and any reasoning or UI server and 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. left running. |
An unknown command prints the usage line and exits with status 64.
Image refresh and teardown
Each invocation refreshes latest and nightly once for each required image: ops and its MyceliumMyceliumThe application infrastructure underneath NovusLens. Its connected services exchange updates and keep the ordered structural log. base.
A fixed release reuses cached images and pulls missing images only.
Both pulls share one temporary registry login when a token is needed.
A failed pull stops preparation and reports the failed image.
down uses the cached controller without pulling or building images, so teardown works offline.
Acquire the simulated estate
After walk, start the separate simulated estate in the same selected runner:
./lens ops --version dev estate
./lens ops --version dev exec -- uv run python -m conway_connectors --org meridian boundary --gateway 127.0.0.1:8410 --enrollment /tmp/conway-estate-enrollment.jsonUse the version printed by walk or estate in place of dev when running another version.
The enrollment file and loopback gateway belong to that runner.
Both estate gateways must become healthy within 30 seconds. A refusal reports TCP and UDP sockets on the configured estate ports, their process owners and the network namespaceNamespaceA naming area that keeps one set of records apart from another. NovusLens uses the conway/ prefix so its keys do not occupy Mycelium's reserved names. identifiers. Inspection failures are reported explicitly. A configured UDP override is included; dynamic port allocation is marked as unknown. These diagnostics leave the refusal, time limit and cleanup of owned processes intact. An address-in-use error alone does not identify which socket or process caused it.
The command records simulated evidence for the fictional Meridian GridMeridian GridThe fictional demonstration organisation used by the demonstration walk and its sample data. Nothing about Meridian Grid describes a real company. twin.
Stop the runner with ./lens ops --version dev down.
Developers can call ./ops --version dev <command> directly from a checkout.
The twelve gates
Order is the order ./lens ops --version dev gates runs them.
| Gate | What it checks |
|---|---|
smoke | The gate runs two storage checks, each on a fresh mesh. They test stored dependencies and disagreements, their recorded resolution, and updates from measurements. The gate also checks Prometheus /metrics. |
test | Unit and integration tests. Starts Postgres, applies migrationsMigrationAn ordered change to a database's tables or rules. Applied migration files are recorded so the same file is not run again., seeds development viewers and starts a fresh mesh. Requires opa and cedar for policy-export checks. Checks software packages for security problems, checks config/guide.json, runs pytest over apps and packages, and refuses unaccounted skips. |
steel | The first two demonstration acts. Act 1 checks 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) at 3.0 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. through the fictional Risk team. It also checks the rendered figure in headless Chrome. Act 2 streams simulated telemetryTelemetryMeasurements and activity records sent by a running system, such as ticket timings, network traffic or workflow results. and runs correction. It checks 4.5 index points through the fictional Data Governance team and nine measured facts. |
fresh | Builds a fictional organisation from interview, infrastructure files, personnel records, operations report and business-domain list, without a hand-written graphGraphA model made of units and links between them. In NovusLens it records teams, services and their dependencies.. Browser checks cover drill-down and evidence upgrades with citations. Needs a configured model endpoint; the local default uses Ollama. |
ask | Checks tool registration and access through an external MCPMCPModel Context Protocol: a standard way for an AI application to discover and call named tools supplied by another program. client. Tests ownership queries, group questions and deep-sweep decisions on the configured model. Challenges the verdict filter and checks that answers from AskAskThe tab that composes an answer to a question in your own words, from the records, with each statement pointing to its sources. Ask answers only as far as your role can see. cite supporting records. Needs a configured model endpoint; the local default uses Ollama. |
federation | Checks regional separation and shared summaries. A signed cross-region fact passes; a changed copy is refused. Builds the Group view in Postgres and checks agent-card discovery. Needs Postgres only. |
agents | Checks mapped agents, declared and measured evidence, and refusal of changed signed documents. Covers control gaps, fleet incidentsFleet 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., remitsRemitA written list of an agent’s allowed actions, resources and destinations for information. The list states whether it is complete. and the simulated estate’s boundary feed. |
domains | Checks the declared business-domain list, its 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. mappings and team-to-domain differences. FindingsFindingA difference in the records that needs a decision. It includes its sources and can describe a slow handoff, disputed fact or missing owner. are evidence for review. Needs Postgres and mesh; no model. |
e2e | Playwright tests in system Chrome cover cell chat, regional group summaries and the AI agents page. They use a fixed-response test model and prepared sample records. |
kit | Refuses organisation-specific tokens in product code. Starts an empty database and mesh. Runs the engagement kit through product interfaces for a fictional organisation other than Meridian Grid. |
boundary | Runs a throwaway Compose installation with its own project, port and database. Checks session identity, service tokenService tokenA secret value sent with requests between NovusLens services. The receiving service refuses requests with a missing or wrong value., read-only board access, and access refusals distinct from outages. Refuses development secrets. A network UI bind needs a declared TLSTLSTransport Layer Security: the technology that encrypts a connection and helps check the identity of the service at the other end. proxyProxyA service that receives a request and forwards it to another service. The NovusLens web proxy adds the signed-in identity and service token.. The mesh gateway stays unpublished and requires a 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., with no NovusLens tools exposed. |
durable | Runs administration inside the Compose installation and refuses missing or development configuration. Checks log survival through restart, backup, wipe and restore. Refuses malformed archives without changes. Checks projectionProjectionThe regular pass that brings the read model up to date with the canonical log. It runs every 30 seconds by default. recovery after process termination and database outage. |
The skip gate
The test gate treats a skipped test as a failure unless it is on a short allowlist. scripts/pytest_skip_gate.py reads pytest’s -rs output. Each allowed skip must match both a test-path suffix and a reason prefix. Examples include tests needing the full fresh organisation or more git history. It also counts expected failures. Reported skip lines must add up to pytest’s skipped total, so missing results cannot pass.
The supply-chain audit
Before the suite, the test gate runs pnpm audit --prod in apps/ui and pip-audit over the locked Python set, then python -m common_py.audit_gate against config/audit-triage.json. An audit that did not complete fails the gate, and so does any advisoryAdvisoryA published report of a security problem in a software package. not recorded there. See the shared Python page and pins.
Runner and CI ownership
Working-tree application gates explicitly select --version dev. local uses frozen local source; registry modes run packaged source.
Run routine development commands directly in the task worktree.
The controller holds the shared Postgres lock for each invocation. A second checkout does not isolate the database.
CIContinuous integrationAutomated checks run when code changes, usually including building the code and running tests. uses Blacksmith Linux runners. Reusable gate lanes retain their configured prerequisites and cached Docker builds.
Pull requests run selected checks and gate lanes, plus fast Docker and installer checks, without full image builds or publication.
.github/workflows/latest.yml runs quick Python and UI checks, builds all six images on native Linux amd64 and arm64 workers, and publishes main pushes as latest.
This main-push path does not rehearse the installation.
The nightly workflow builds and rehearses both architectures before publishing nightly images and moving the Git tag nightly.
GitHub release: published also builds and rehearses all six images before publishing immutable 0.x.y tags, accepting an optional v prefix.
A tag push alone does not publish. The container workflow has no manual or reusable workflow trigger.
Release publication does not move latest.
Main-push builds stage temporary architecture tags before publishing a verified two-platform index.
Nightly and release publication promotes the images retained after rehearsal.
Cleanup preserves fixed release tags, and a mismatched immutable tag is refused.
Publication does not establish that an unrun acceptance gate passed.
Transcripts
./lens ops --version dev gates writes docs/gates/<date>-<first twelve characters of the commit>.md. It records the head commit, start time and working-tree state, listing uncommitted files. A table gives each gate’s PASS or FAIL(rc=…) result and duration. Per-gate logs go to /tmp/novuslens-gate-<gate>.log. The transcript is committed in the next commit, by construction, so a verifier ties a pass to a commit. ./lens ops --version dev gates returns non-zero if any gate failed, but it runs all twelve regardless.
The live group-routing check logs each case as it starts and completes, so earlier results survive a later request failure. It uses escaped case identifiers and bounded model metadata. It omits questions, free-text model reasons and private exception text. A request error names the case, run, exception class and HTTPHTTPHypertext Transfer Protocol: the rules a browser or program uses to send a request to a web service and receive its response. status, then fails the check. The CLICLICommand-line interface: commands you type into a terminal to operate software. reports safe fields without an exception-local dump. A failed request is not itself evidence of a wrong classification; the model’s completed choices still face the same expected-set checks. These diagnostics add no retries.
Owned process cleanup rechecks an invalid process handle only when the PID is positive. It sends no signal if a fresh inspection confirms that process has exited. A live or reused identity, inspection failure or another error still refuses cleanup. This adds no retries or fallback signals.
Isolated sidecar persistence check
The Gates workflow also accepts lane=sidecar-persistence. It builds the pinned
sidecar from the selected source on native amd64 and arm64 runners. Each job
checks its host and image architecture and records a separate evidence artifact.
It tests public HTTP persistence without a full installation or model calls. One check appends a record, confirms its exact anchor,
sends SIGTERM and checks that anchor after restart. A second forces an owned
WAL-open failure and requires HTTP 503 without an HLCHybrid logical clockA number attached to a log entry to order it against other entries. It combines clock time with a counter, so entries can be ordered even when clocks do not agree exactly. success.
These checks record the source, pin, patch and binaryBinaryA program file built to run on a particular kind of machine. A program built for one processor may not run on another. identity with exclusive evidence paths. They do not replace customer durability or all twelve release checks. A passing substrateSubstrateThe shared Mycelium software beneath NovusLens. It stores records and lets NovusLens components exchange them. check alone does not establish full installation recovery.
Optional manual inference profile
Default CI, nightly and labelled runs retain free-only inferenceInferenceThe work a language model does to produce an answer from its instructions and input.. A separately approved manual dispatch can select inference-profile=gpt-oss-20b-darkbloom-fp8-capped with version=dev for real LLM gates. Both preflight and Gates refuse other versions before invoking the driver. No-LLM steps clear the mode, model and key.
The profile fixes the model/provider and maximum per-unit prices at the shared HTTP boundary, including preflight. It forbids fallback, preserves stricter caller limits and privacy restrictions, and refuses conflicting or fee-bearing options. Free-only mode still rejects paid models. Publishing the dormant option is not permission to select it; no paid schedule is enabled. The same real semantic and twelve-gate checks remain required.
Named manual inference and account declaration
The default CI route remains openrouter/free. The xing4-phala-zero manual profile pins the named Xing model to Phala.
It requires parameter support, disables provider fallback and caps prompt,
completion, request and image provider prices at zero. It extends CONWAY_LLM_FREE_ONLY=1 only for that exact reviewed
profile; arbitrary free suffixes and paid alternatives are refused.
Both named profiles require account-plugins-off: true on an explicit manual
version=dev dispatch. This operator declaration means the CI key’s account
plugins are off and cannot be forced on by account policy. It is not an automatic
account check and must be reassessed if account settings change. Missing or false
confirmation refuses before an ops command; the shared HTTP guard also requires
CONWAY_LLM_ACCOUNT_PLUGINS_OFF=1. OpenRouter documents account plugin defaults and forced overrides .
Provider price caps do not cap account add-on fees or total spend. Paid-profile permission remains a separate decision.
The inference-compatibility component accepts only the confirmed named-zero
profile. It makes one real public BuildAgent.chat request with the existing static group
schema, low effort and 8,192-token limit. It makes no inference preflight call,
formatting nudge or retry. Its shape result is protocol evidence,
not model semantic qualification or an application-gate pass. The ordinary live
routing matrix and all twelve application gates remain required.