Measurement and findings, as the evidence stands.
What it is for
NovusLens reports 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. (Complexity, Agility and 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.) for teams and services. These measures describe structure, ability to change and ability to restore service. Every reading names the evidence it rests on. The same modules raise findingsFindingA difference in the records that needs a decision. It includes its sources and can describe a slow handoff, disputed fact or missing owner., which stay on screen only while their support still holds, and they assemble the records an assurance reviewer opens first. NovusLens reports what is found and on what evidence. It does not give an opinion on whether any of it is acceptable.
How it works
Readings and their evidence (car.py)
car.read builds the assessment. With a root 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. it assesses that unit. The scopeScopeThe work, data, activity or access that a rule, statement or calculation covers. For access, it names the records an account can see. For team measures, “Own operations” covers the selected unit. “Including descendants” adds the units beneath it. chooses its own operations or includes the units beneath it. With no root it returns the visible records for every non-external cell, and refuses more than 500 cells. A read runs in a repeatable-readRepeatable readA database mode that lets a group of reads use the same saved picture, even if another program changes the records while those reads run. transactionTransactionA group of database changes that is saved together. If the group fails, its changes are not saved., is limited to the cells the viewer can see, and is written to the audit log. The stored set of C/A/R evidence records supplies the inputs. car.publish and car.capture write these records. The cells come from the cells table, the axis observations from car_axis_observations, and the scores from the assessment model in conway_graph (model.assess and model.scores).
Two checks run before scores are shown. A record whose cited artifactArtifactA stored source file or evidence body that a record can cite. Keeping the exact content lets a reader check what supported the record. can no longer be resolved is left out and counted as unavailable_evidence_records. An axis observation past its valid_until_ms is counted as expired. If a subtreeSubtreeOne unit and every unit nested beneath it in the organisation's hierarchy.’s full scope is not visible to the viewer, each axis returns unknown rather than a partial score.
Four writes sit beside the read:
car.publishtakes a set of C/A/R evidence records from an operator. It checks structure and limits size to 500,000 bytes. Cells must match the current ownership snapshot. Changed evidence since export is refused, usingexpected_artifact_id. Atoldrecord needs no citation. Ashownormeasuredrecord must cite an org-scopedartifact:<id>, and ameasuredrecord must cite an observation-type artifact. Synthetic evidence is accepted only for thecar-demoorganisation and never under the customer profileDeployment profileA setting that selects customer or development behaviour. Customer mode refuses development credentials and uses the signed-in session as the identity.. The bundle is stored, committed as acar_assessment_evidencecommit, and materialised, all under a per-organisation advisory lockAdvisory lockA database lock that cooperating programs take to prevent conflicting work at the same time. They must explicitly use the lock..car.capturerecords one dependencyDependencyA link where one unit needs something from another. A dependency is not a reporting line: it shows where work relies on something else. that a unit has on another for a named capabilityCapabilityWhat a team can provide or an agent can do. NovusLens records the claim and the evidence that supports it.. The role ismandatory,optionalorunknown, and the review interval is 1 to 365 days. The record sits at the toldToldEvidence that somebody stated the value. It is the weakest level, and it is always attributed to its speaker. rungRungOne of three levels of support for a recorded value: told, shown or measured. The level appears beside the value.. It refuses to replace a dependency that already has shown or measured support.car.retain_findingsopens onecar-reviewfinding for each finding in a read. Each keeps the assessment version, snapshot and date, so it can be rechecked before anyone disposes of it.car.archivestores the inputs of a read as acar-assessment-replayartifact.car.replay_recordreturns that artifact later.
Assurance bundles (assurance.py)
assurance.bundle issues a disclosure for one scope and one period. It carries the findings, the withheld findings, the provenanceProvenanceThe record of where a value came from, who stated it and when. Every value keeps this record., the artifacts, the policy revisions and the recorded actions. Each section has a count and a SHA-256SHA-256A method that turns content into a fixed-length fingerprint. Comparing fingerprints helps check whether content changed. digestHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records., and the whole bundle has its own digest. A coverageCoverageWhat the available records cover, and what is missing. A count such as “3 of 5 dependencies measured” names the measured portion and the gap. block (coverage) states what was not covered. The bundle is recorded as issued, with its hash, and says in its own words that it carries no opinion.
Two period modes are built: current-at-issue, which shows findings as they stand at issue, and opened-in-period, which shows findings first recorded inside the period. Two named modes, active-during-period and reconstructed-at-period-end, are refused with the reason stated. The route answers 422.
A withheld finding appears by identity and reason, never by its claim. assurance.sample draws a reproducible sample from defined groups from a bundle, using a stated seed. It labels the draw a coverage judgement rather than a risk ranking. assurance.changes compares two issued bundles by identity. assurance.accept records an acceptor’s decision on a bundle. It matches the exact bundle hash against issue records. A later bundle cannot inherit that decision. The decision is kept separate from current support. sample and changes are reached through the 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. command line (./lens connectors assurance), not through a route.
Catalogues (catalogue.py)
A catalogue is the customer’s reviewed mapping from business activities to the operations, resources and destinations that count as each activity. catalogue.revision writes a draft, numbered one above the highest existing revision. catalogue.approve approves a draft and records the approver. Only a cxo or an operator can approve. Any other writer gets approved: false with a reason. catalogue.current returns the highest approved revision of each catalogue.
mapping_status tells a caller how an observation’s mapping stands against the revision in force: current, unchanged, superseded, unknown-activity or unregistered. warnings_for lists labels in a draft declaration that no approved catalogue names. That is advice on the draft, not a refusal. NovusLens does not infer a mapping. It records the customer’s.
Findings and their support (finding_support.py)
Some findings are tracked. These are meaning-contradiction, control-gap-overdelegation, fleet-incident, remit-drift, and the boundary kinds from conway_graph. finding_support.current returns the tracked findings whose support still evaluates as it did when they were raised.
- A meaning finding stays supported while the 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. it cites still check out and any graphGraphA model made of units and links between them. In NovusLens it records teams, services and their dependencies. revision it captured still matches.
- A derived finding (
fleet-incidentorremit-drift) stays supported while at least one of its member records still matches its revision.derived_subjectthen rewrites the headline to say how many members are currently supported and why the rest lapsed. - A finding that is not supported is withheld, not dropped.
filter_payloadkeeps it in the payloadPayloadThe data carried inside a message, apart from the surrounding fields that identify and date it. with a reason:inputs-not-recorded,support-lapsedorsupport-not-current.
finding_support.review reports the same status for each open finding without repeating its claim. Above 1000 tracked findings, current returns nothing supported and review returns limit-exceeded, so no partial answer is given.
meaning_support supplies the references these checks depend on. capture_meaning confirms that each cited meaning entry is readable and current, and records a digest of its content. checker checks those references again later. capture links a composed answer to the current revision of each agent case it cites, and refuses while a case is still updating.
Evidence behind a team or a finding (evidence_view.py)
evidence_view.evidence_view answers what stands behind one team (a cell_id) or one finding (a finding_id), and exactly one of the two must be given. It reads the team’s provenance and its measured dependencies through tools.retrieve_evidence, so the view cannot show more than the viewer may see. Each item gives a source, a locator, an excerpt, attribution, the rung it reached and the rung attainable, its times, and any open challengeChallengeA recorded dispute of a value. Both the original and the disputed version are kept, and the challenge is listed under Findings..
Excerpts follow the file format. JSONJSONJavaScript Object Notation: a text format for data, using named fields, lists and values. Configuration files and service messages use it. becomes labelled lines about the unit. CSV shows the matching rows, up to 50. Markdown and text show the matching sections, up to 6000 characters. Other formats say that no excerpt adapter exists yet. Personnel exports never open. A purgedPurgeRemoving a stored document’s contents while keeping a record that it was removed and why. Conclusions that used it must be checked again. document shows a tombstoneTombstoneA retained record that says content was removed, when it was removed and why. The removed content itself is no longer available. with its date and reason. For a team outside the viewer’s view, a team request is refused, while a finding request lists the team as withheld.
Capture sessions (capture.py)
A capture session is a live conversation in which a contributor tells 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. about structure. The session lives in memory. Apart from an attached document, nothing reaches the twin until the session is committed. There are two modes. interview covers the whole organisation and only a cxo may open it. cell-chat covers one cell and must name a cell inside the contributor’s visibility.
capture.start seeds the session from the existing twin, so the conversation annotates what is already there. The first question comes from cell_gaps. Gaps include missing ownership or authorityAuthorityWhat a team or agent is allowed to decide or do. For an agent, this comes from a named person or system that grants permission., attributes below their attainable evidence rung, and dependencies whose coordination needs are unmeasured.
Each turn goes to CaptureSession.answer. BuildAgent.take_turn receives the utterance and current question. It extracts proposed record changes and applies them to the session’s agent held in memory. diff_claims then checks each structural claim against the stored citations and gives it one of four verdicts:
documented: the mapMapThe view of teams, services and their dependencies for the selected part of the organisation. Details appear beside the map. already holds the claim, backed by a document, and the reply cites that document.testimony: the map holds the claim at the told rung only.new: the map does not hold the claim, so it is kept as the contributor’s account.conflict: a document shows something different. The reply names the document, and the contributor’s account is held beside it, not over it.
Undocumented claims are batched into one question about documents. The scripted gap questions wait until no conflict or document question is open.
/capture/attach takes a document mid-conversation and ingestsIngestRead supplied records or documents and add their information to NovusLens, after the required checks. it against the twin through operations.attach_document. Its docstring says substantiated attributes move from told to shown. /capture/commit calls session.proposal(), which in cell-chat mode drops and reports any contribution outside the contributor’s scope, and then calls operations.commit_org. The session ends. If nothing landed, the response says so. /capture/discard ends the session and records the discard in the audit log. Nothing had landed, so nothing is undone.
The language model in capture (build_agent.py)
BuildAgent.take_turn calls chat, which posts an OpenAI-style chat request to CONWAY_LLM_ENDPOINT. The default endpointEndpointA particular address on a service that accepts a defined kind of request. is http://localhost:11434/v1/chat/completions. The model is taken from CONWAY_BUILD_MODEL, or CONWAY_LLM_MODEL if that is unset, and defaults to gpt-oss:20b. The request uses model temperatureModel temperatureA setting that controls how much a language model varies its choice of words. Lower values usually produce less varied answers. 0.1 and a 240-second timeout. On OpenRouter, a JSON request omits temperature when no provider of the model accepts it with the other requested parameters. Reasoning effort is low, or medium for a session seeded from an existing twin. The code does not name the provider behind the endpoint. It uses whatever endpoint it is given.
A reply is unusable if it is not one JSON object, or if it has neither a paraphrase nor any proposed change. An unusable reply is repeated with a nudge, up to two calls in all. Capture keeps to two because the UI proxy gives up at 300 s. If every reply is unusable, the turn fails closed. The reply asks for clarification, nothing is recorded, and the pending question stays on the table. If the model cannot be reached, no operations are applied. /capture/answer returns 503 and says the reasoning model is not answering. The model proposes structure. NovusLens applies it to the session only, and the commit call is the confirmation.
Interfaces
The routes below are the ones this page covers. The full list is on the HTTP endpoints page, and the module overview is on the reasoning overview.
| Route | Function | Who may call it |
|---|---|---|
GET /car/scores | car.read, all cells | Any known viewer, limited to their visible cells |
GET /car/assessment | car.read, one cell and scope | As above |
POST /car/evidence | car.publish | Operator |
GET /car/evidence-template | car.evidence_template | Operator |
POST /car/capture | car.capture | cxo or operator |
POST /car/retain-findings | car.retain_findings | cxo or operator |
POST /car/archive | car.archive | cxo, board or operator |
GET /car/replay | car.replay_record | cxo, board or operator |
GET /catalogues | catalogue.catalogues | Any known viewer |
POST /catalogues/revision | catalogue.revision | A writer |
POST /catalogues/approve | catalogue.approve | A writer; only cxo or operator approves |
GET /findings/current-generated | finding_support.current | Any known viewer, limited to their visible cells |
GET /findings/support-review | finding_support.review | As above |
POST /evidence/view | evidence_view.evidence_view | Any known viewer; refused outside scope |
POST /capture/start | capture.start | cxo for interview; a writer for cell-chat within their visibility |
POST /capture/answer | CaptureSession.answer | The contributor who opened the session |
POST /capture/attach | operations.attach_document | As above |
POST /capture/discard | capture.end | As above |
POST /capture/commit | operations.commit_org | As above |
POST /assurance/bundle | assurance.bundle | Operator or cxo |
POST /assurance/accept | assurance.accept | Operator or cxo |
GET /assurance/acceptances | assurance.acceptances | Operator or cxo |
The module uses conway_graph for PostgresPostgresThe database software that stores the tables read by the NovusLens screens, along with document bodies, access records and protected personnel records. and TwinWriter for twin commits. It stores evidence artifacts and audits reads and writes. Capture turns call the language modelLanguage modelSoftware that produces text from instructions and supplied context. NovusLens checks its answers against the records retrieved for the question. endpoint described above.
Invariants and failure behaviour
- Reads are scoped. Readings, findings and evidence are limited to what the viewer can see. A subtree whose full scope is hidden shows
unknown, not a partial score. - Writes are audited and ordered. Publishing and capture take a per-organisation advisory lock. The writes in these modules record an audit entry.
- A stale export is refused.
car.publishrefuses if the stored evidence has changed since the template was exported. - Findings are withheld, not dropped. A finding whose support has lapsed stays in the payload with a reason, and the page does not repeat its claim.
- Bundles carry no opinion. Acceptance is recorded against one bundle hash and held apart from support.
- A capture changes the twin only at commit or at an attached document.
commit_orgapplies the same discipline as elsewhere: told never overwrites shown, and a conflict is kept as a recorded dispute for a person to resolve. Out-of-scope contributions are dropped and reported. A commit that lands nothing says so. - Sessions are not durable. Live capture sessions live in one process’s memory, and a restart loses them. The artifacts already committed are kept. There are at most 64 live sessions. When the registry is full the new session gets 429. Only sessions idle for more than two hours can be evicted to make room.
- Failures are answered in words. A missing or foreign session returns 404 or 403. A refused period mode returns 422. A model endpoint that cannot be reached returns 503, and so does an unreadable database or storage layer. For C/A/R reads, storage errors return 503 with the message that no new assessment is available.