Skip to Content
Architecture · Acquisition

The meaning layer, held apart from the graph.

What it is for

The graphGraphA model made of units and links between them. In NovusLens it records teams, services and their dependencies. records the organisation’s structure and what has been measured about it. Policies, runbooks and board papers hold the other half: what a thing is, why it matters and what has been decided about it. The meaning layer keeps that second half as dated, attributed entries, each limited to the people entitled to read it. Questions answered by 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. draw on these entries and cite their source documents.

How it works

Structure and document statements

The graph is the main record of structure and measurement. The 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. holds document statements, with each entry linked to 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. identifiers in the graph. Each durable fact belongs to exactly one canon. Sealed personnel data enters neither, as recorded in the package docstring of conway_meaning.

Because the same fact can be written in both places, the two can drift apart. The sweeps described below exist to notice that drift. The graph is the main record for structure, so the sweeps never rewrite the graph to match a note.

The graph and the meaning canon, the deterministic sweep that checks their keys, and the on-demand deep sweep that checks their prose. The meaning canon is a fictional demonstration organisation's data.

What an entry holds

Each row of meaning_entries (migrationMigrationAn ordered change to a database's tables or rules. Applied migration files are recorded so the same file is not run again. 0019_meaning_layer) holds:

  • identity and 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.: entry_id, org, page (a grouping path such as org/board or cells/grid-ops), cell_ids;
  • content: title, body_md, and a free-form attributes object;
  • visibility: visibility, a JSONJSONJavaScript Object Notation: a text format for data, using named fields, lists and values. Configuration files and service messages use it. object of the form {"roles": [...], "cells": [...]};
  • provenanceProvenanceThe record of where a value came from, who stated it and when. Every value keeps this record.: ingested_by, ingested_role, ingested_at, the artifact_id of the source document and a locator;
  • 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.: retracted_at, retracted_by, retract_reason;
  • a simulated flag, set for demonstration data.

The source document is also written to evidence_artifacts with kind meaning-doc, so the citation resolves to the passage it came from. The entry id is a hashHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records. of the org, page, title and the body’s own hash. A changed body therefore produces a new entry id, not an edit to the old one.

Visibility derived from the ingester’s position

derive_visibility sets the default visibility from the person who ingestsIngestRead supplied records or documents and add their information to NovusLens, after the required checks. the entry:

  • cxo, board and operator give roles: [cxo, board] and no cells, so a leadership document is leadership-only by default.
  • infra adds the infra role to the leadership roles.
  • A bu-owner gets the named cells plus their management chain, walked upward through parent_cell_id (bounded at 32 steps). Leadership always sees upward. Where the chain is thin, the default is narrower, not wider.

can_view decides every read, per entry, in code. The operator sees everything. Anyone else sees an entry only when their role appears in roles or their scope cell appears in cells. Absent and invisible give the same answer, so a viewer cannot probe for entries outside their scope. The default can be overridden at ingest. Widening it later is a separate act, set_visibility, which only the ingester or a steward may perform, and which needs a recorded reason.

Retract, do not erase

retract_entry requires a reason. It sets retracted_at, retracted_by and retract_reason, and the row stays. Every read excludes retracted rows. Re-ingesting the same bytes after a retraction does not bring the entry back. The new ingest receives a fresh entry id, so the retraction stands.

When a source document is purgedPurgeRemoving a stored document’s contents while keeping a record that it was removed and why. Conclusions that used it must be checked again., retract_entries_citing retracts every entry that cites it, with the reason source purged. An entry whose citation no longer resolves does not stand.

Two link fields look similar and do different things.

Field (route)Flag (./lens connectors document)Effect
supersedes_entry_ids--supersedesReplaces. Each predecessor is retracted with the reason superseded by <new id>. The new entry carries supersedes and each predecessor carries superseded_by. The new entry and the retractions commit together or not at all.
meaning_entry_ids--based-onRests on. The entries are stored as meaning_dependencies in attributes. Nothing is retracted.

The distinction matters when checking support. An entry whose supporting links cannot be resolved does not count as supporting evidence.

The deterministic sweep

sweep_meaning_graph needs no model. For every live entry it compares cell_ids with the cells in the graph. An entry keyed to a cell that does not exist raises a findingFindingA difference in the records that needs a decision. It includes its sources and can describe a slow handoff, disputed fact or missing owner. of kind meaning-graph-incoherence, with the id meaning-incoherence-<org>-<entry id>. Running the sweep again creates no duplicate finding.

The finding clears itself when the condition no longer holds: every key now resolves, or the entry was retracted. The sweep then resolves its own finding, owned by coherence-sweep, with the reason recorded. This is the one case where the system resolves a finding without a person. The reason is a re-checked fact, not a judgement.

The sweep runs in three places: on each read-model refresh (the meaning-sweep step in operations.py), after a document is ingested, and after a retraction.

The on-demand deep sweep

POST /steward/deep-sweep runs deep_sweep. It is available to cxo and operator only, is audited, and is never on the refresh path because it takes minutes on a local modelLanguage modelSoftware that produces text from instructions and supplied context. NovusLens checks its answers against the records retrieved for the question..

The sweep reads the graph once at its start, so structure that lands mid-run does not change the comparisons. It then takes the 30 newest live entries and makes two kinds of comparison:

  • each entry’s prose against the facts of each of its cells (external or internal, 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., granted authority, and up to 12 dependencyDependencyA link where one unit needs something from another. A dependency is not a reporting line: it shows where work relies on something else. edges);
  • pairs of entries that share a cell or a page, up to 20 pairs.

The model is set by CONWAY_SWEEP_MODEL, falling back to CONWAY_COMPASS_MODEL, and defaults to qwen3:30b. A judge call proposes direct contradictions only. An omission is not a contradiction. If the judge’s reply is not the required JSON, the call is retried once. A malformed item beside valid proposals is counted as discarded and marks the comparison as partial.

Each proposal then gets two independently framed checks:

  1. Can both statements hold true at once for the same subject, scope and time?
  2. Do the two statements assert different facts about the same thing?

The proposal is a confirmed contradiction only when both answers agree that the statements conflict. It is dismissed only when both agree that they are compatible. Any other outcome, including disagreement between the two, is kept as UNRESOLVED. An UNRESOLVED proposal is never read as absence of a contradiction.

Confirmed and unresolved proposals raise findings of kind meaning-contradiction, with the id meaning-contradiction-<org>-<digest>. An UNRESOLVED subject begins with that word, so a person can see at once that it has not been judged. If the judge cannot answer after its retry, the comparison is counted as unevaluated and the report’s status reads partial or failed. A clean-looking report is never produced from a comparison that was not evaluated.

For a leadership-only entry, restricted details are removed from the finding. It names the entry id and the graph side, and never the restricted title or prose.

Humans resolve

The system proposes and surfaces. People resolve and commit. A meaning-contradiction finding is a judgement, so the sweep never clears it. A person retracts the entry, re-keys it or supersedes it. A sweep that raises nothing is not proof of consistency.

Interfaces

InterfaceWhat it does
./lens connectors documentIngests a Markdown or plain-text document in the running Compose reasoning service. Takes --viewer, --title, --file, and repeatable --cell, --attribute KEY=VALUE, --supersedes and --based-on. --page and --visibility (JSON) are optional.
POST /meaning/ingestThe same path as the command. Requires a writer. Refuses cells outside the viewer’s scope with 403. A predecessor that cannot be superseded gets one 409.
POST /meaning/retractRetracts an entry with a reason, then runs the sweep so a cleared finding closes at once.
POST /steward/deep-sweepThe on-demand deep sweepDeep sweepAn on-demand review that uses AI models to look for contradictions between written entries and the organisation model, or between written entries.. cxo or operator only.
conway_meaning functionsderive_visibility, can_view, ingest_entry, query_entries, read_entry, set_visibility, supersede_entries, retract_entry, retract_entries_citing, sweep_meaning_graph, deep_sweep.
apps/meaning/fixtures/load_meridian_docs.pyLoads five documents 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. demonstration organisation. Needs the reasoning service on port 8500 and the viewers it names.

Invariants and failure behaviour

  • Reads are refused by default. Retracted entries are invisible to every reader, and the row keeps who, when and why.
  • Ingest and supersession commit together, or not at all.
  • An entry’s source document is kept as evidence, so its citation resolves.
  • Forgetting an organisation deletes its meaning_entries rows. The canonical logCanonical logThe permanent history of changes to an organisation’s model. New entries are added at the end. Earlier entries stay when the database is rebuilt. keeps the commits, as the forget module states.
  • A set_visibility change is refused unless it carries a reason and comes from the ingester or a steward.
  • Demonstration data is marked simulated. The Meridian Grid documents are fictional.
Last updated