Skip to Content
ArchitectureConnectors and meaningAcquirers and the corrector
Architecture · Acquisition

Measure what the source says, and refuse what it does not.

An acquirerAcquirerA program that reads a customer's operational records, calculates quantities from them and records the results in NovusLens. reads one of the customer’s own systems, computes a quantity from the raw records, and writes it to 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. as observed evidence. The correctorCorrectorThe NovusLens component that compares measurements with recorded statements and adds observations and differences to the log. then sets that evidence against the account the organisation has told NovusLens, and records any difference as 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.. Neither step changes the source system.

Each step either carries on with what the records show or refuses with its counts. Nothing is filled from a neighbouring source.

What it is for

Every figure in the twin depends on its source. Examples include dependencyDependencyA link where one unit needs something from another. A dependency is not a reporting line: it shows where work relies on something else. lead timesLead timeThe elapsed time between starting a request or handoff and completing it. and 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. 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. (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.) scores. Acquisition is what lets a figure say which system measured it, over which population, and when. A figure that cannot be measured is shown as unknown rather than filled from a neighbouring source.

How it works

Acquisition commands

./lens connectors acquire --org <org> --kind <kind> runs one acquisition pass. Each kind has its own collector and its own refusals.

--kindSource and inputsWhat it measuresWhat it refuses or counts
servicenow, jiraTicket APIAPIApplication programming interface: a defined way for one program to request data or actions from another., --url, optional --token, --mappingEdge lead time: the mean of resolved minus opened, per mapped ticket group, over resolved tickets only.Open tickets are skipped and counted. Unmapped groups are counted, not guessed onto a capabilityCapabilityWhat a team can provide or an agent can do. NovusLens records the claim and the evidence that supports it.. A partial listing writes nothing.
prometheusPrometheus-shaped query API, --urlCell C/A/R from series the organisation already exports, scaled by its own linear terms.A cell missing any calibrated series is refused. A series returning more than one sample is refused as ambiguous. Unmapped jobs are reported.
githubWorkflow-runs API in the GitHub shape, --urlQuantities computed from raw runs: pipelines, change lead days, change failure rate and recovery hours.In-progress runs are skipped. A failure with no later success in its own workflow and branch cannot yield a recovery time. A partial repository listing is refused with its counts.
pagerdutyIncident API in the PagerDuty shape, --urlMean hours from created to resolved for each mapped service (pd_mttr_hours).Unresolved incidents are skipped. A service with no resolved incident has no recovery figure. Standalone, it refuses most triples, because incidents measure nothing about Complexity or Agility.
compositeRepeatable --source KIND=URL, where KIND is prometheus, github or pagerdutyOne 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. drawing each quantity from the source that measures it. See composition.As for each contributing source, and the whole-or-nothing rule applies to the combined triple.
network-flowFlow-log exports: --format hubble or vpc-flow-v2, one or more --flow-log FILETraffic leaving each mapped cell, grouped by destination, protocol and whether the network forwarded or dropped it.Unparseable lines are counted with their line number. Unmapped or ambiguous sources are not attributed. Coverage gaps such as NODATA, SKIPDATA and Hubble lost_events are counted, never read as quiet.

The network-flow kind is a flow record of what the network forwarded or dropped. It is never a gatewayGatewayA service that accepts requests and passes them into another system. The Mycelium gateway gives NovusLens access to the mesh. decision, and every entry carries observation_point: "network". No real Container Network InterfaceCNIContainer Network Interface: the standard used by software that connects containers to a network. (CNI) or virtual private cloudVPCVirtual private cloud: a private network defined inside a cloud provider's infrastructure. (VPC) source has been connected. Exports are supplied files, so this path needs no paging.

Simulated sources declare themselves in the payloadPayloadThe data carried inside a message, apart from the surrounding fields that identify and date it., and the flag propagates into every entry. A composed entry is simulated if any contributing source is simulated, so mixed data never carries a real label.

The customer-authored mapping

Each acquirer takes a --mapping JSONJSONJavaScript Object Notation: a text format for data, using named fields, lists and values. Configuration files and service messages use it. file that the customer writes. It connects names in the source system to cells or capabilities in the twin. For ticket acquisition the joins are ticket groups to capabilities. For the cell kinds the joins are job, repo or service per cell, and an axes block gives each axis its calibration.

A calibration term names either a series (an exported Prometheus series) or a quantity (a value an acquirer derives). The score is base plus the sum of per_unit times each value, clamped to 0 to 100 and rounded. The loader refuses a calibration with no terms, no finite base, or a zero or non-finite per_unit. A constant cannot stand for a measurement. NovusLens applies the calibration as declared and never tunes it. A change to it is an edit to 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., which is visible in that file’s history.

The mapping can declare window_days for ticket acquisition. Without it, the population is every resolved ticket in the instance’s history, and the record says so.

Paging and incomplete collections

Listings are read page by page through one shared walker in paging.py. It keeps a page budget of 50 pages, removes duplicate rows using their identifiers, and stops if a source repeats a page. A listing is complete when the source reports its end or the advertised total has arrived. Without an advertised total, a short final page ends it. It is never complete because the budget ran out or the rows repeated.

Every listing returns a record of fetched, advertised, pages and complete. The acquirers act on that record:

  • The ticket acquirer writes nothing when the listing is partial, and its report says how many tickets were retrieved and how many were advertised.
  • The CIContinuous integrationAutomated checks run when code changes, usually including building the code and running tests. and incident acquirers refuse any repository or service whose listing is incomplete. The refusal carries the counts. A partial page is never scored.
  • The GitHub-shaped listing is refused after its first page when the advertised total is beyond the budget, because the outcome is already decided.
  • Incomplete repository and service lists are labelled incomplete. Unmapped items are never presented as a complete inventory.

Composition

composite runs each contributing source’s collector and merges the quantities per cell, using each source’s own join. The mapping stays one file with one calibration. Each term names the series or quantity of the source that measures it, so the organisation decides which source supplies each measure. A self-exported gauge can be superseded by naming the system of record in a term, and the gauge then has no route into the score.

By default all three scores must be available before any is written. A quantity that no given source supplies refuses the cell, and the missing names appear in the report. With partial_axes: true in the calibration, each measurable axis is written and the rest stay unknown. Unknown is shown as unavailable and is never written as zero.

ProvenanceProvenanceThe record of where a value came from, who stated it and when. Every value keeps this record. cites every contributor, for example prometheus:…+pagerduty:… (simulated source).

The corrector

correct runs one comparison pass (corrector.run_correction). It compares the asserted account, which is rebuilt from the commit log, with the measured account in the telemetryTelemetryMeasurements and activity records sent by a running system, such as ticket timings, network traffic or workflow results. namespacesNamespaceA 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..

  1. Read. Edge and cell feeds are read through the telemetry scanners. A feed older than three times its declared refresh_interval_ms is discarded. A feed that never declared its freshness is treated as stale and reported as a warning.
  2. Attribute. A feed attaches to an asserted edge only on an exact or subset name match. If several asserted edges share a name, the feed must name its consumer and provider. Anything else is an unmatched finding, not a guess.
  3. Fence. The pass takes the distributed lockDistributed lockA lock that coordinates programs running in separate processes or on separate machines, so only one holder is recognised at a time. conway-correct-<org> with a 30-second time to liveTTLTime to live: the period after which a temporary record or lock is no longer treated as current. (TTL), but only to obtain a fencing tokenFencing tokenA number that identifies a turn to update shared data. NovusLens rejects an earlier correction’s writes after a newer correction is applied.. The lock is released before the writes start, so a long pass cannot hold it past its TTL. The token travels on every observed write from the pass.
  4. Write. Each measured edge carries the token. observed_at_ms uses the feed’s written_at_ms, or the pass time if absent. valid_until_ms adds CONWAY_OBSERVATION_TTL_DAYS days (default 30) to that time. Cell and axis observations are written the same way. Flow observations follow the same pattern.
  5. Find. A difference between the stated and measured handoffHandoffWork or information passed from one team or service to another. Its measured or stated completion time determines the dependency class. becomes an asserted-observed-delta finding, with the identifier delta-<org>-<consumer>~<provider>~<capability>. A measurement against an asserted value of Unknown is a first-measurement finding instead. A feed that matches nothing becomes an unmatched-telemetry finding. The asserted account is kept beside the measurement. Findings close when a later pass no longer reproduces the condition.
  6. Record. Each correction also writes an authoritative record through consistent_set. Nothing reads that record back, so it demonstrates linearisabilityLinearisabilityAccepted operations behave as if they happened one at a time in a shared order. That order respects operations that already finished. and nothing more. A write-once conflict is reported as a warning and does not stop the pass.
The six steps of a pass. The lock is released before the writes, and the materialiser is where a stale token is refused.

The protection against a stale holder is the token and the materialiserMaterialiserThe component that reads recorded changes in order and updates the database. It records its place so it can continue after a restart.’s check. In apps/graph/src/conway_graph/materialiser.py, an observed write whose token is below the correction epochCorrection epochThe highest correction token already applied for one organisation. An observation with a lower token belongs to an older correction pass and is refused. already applied for the organisation is refused with StaleCorrectionError. It is quarantinedQuarantineA holding area for a log entry that could not be applied. The entry keeps its error and waits for a retry or an operator. and surfaced, and the newer correction stands. The stored correction token advances to the highest token seen. Writes that carry no token are admitted, which the code reserves for tests and seeds.

The lock taken for a token, the observed writes carrying it, and the materialiser refusing a stale holder's late write.

Estate drift

drift compares what the infrastructure codeInfrastructure codeFiles that describe the computing resources an organisation intends to create and how they should be configured. declares with what the provider’s control plane reports, and surfaces the difference as findings. The declared tier is Terraform state. The actual tier is the control-plane export. Both are kept, and no verdict is issued.

The join is strict. A declared resource is matched on its recorded deployed name (terraform:resource_name), converted to the standard identifier format, within the same provider and domain. Names are never matched fuzzily. A declared resource with no deployed name is counted as unjoinable and is never reported as drift. Three finding kinds are raised:

  • estate-drift-absent-resource: declared in code, not reported by the control plane.
  • estate-drift-attribute: both tiers report the resource, and its region differs.
  • estate-drift-undeclared-resource: the control plane places the resource in a containerContainerA running program with its own packaged software and isolated environment, managed by Docker on the host. that names a managed-by tool, and that tool’s state does not describe it. A resource with no managing tool is unmanaged, not drift.

Each finding carries the coverage counts, so a clean sweep is not read as “no drift” when nothing was comparable. Without --estate SOURCE=FIXTURE, the sweep re-reads the newest artifact of each kind from the evidence store.

Interfaces

Entry pointRole
acquire --kind …, --mapping, --token, --refresh-ms (default 30000)One acquisition pass, per kind above.
acquire --kind composite --source KIND=URLComposed pass across several sources.
acquire --kind network-flow --format … --flow-log FILEFlow-export pass.
correctOne comparison pass. Prints corrections, confirmations, telemetry warnings and the fencing token used.
drift [--estate SOURCE=FIXTURE]Declared-against-actual estate sweep.
boundary --gateway HOST:PORT [--enrollment FILE]Read-only pull of an agent estateAgent estateThe collection of AI agents an organisation operates, together with the systems and providers they use.’s 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.. Covered on the agent evidence sources page.
CONWAY_OBSERVATION_TTL_DAYSObservation windowObservation windowThe period in which a measurement counts as current. The default is 30 days. After it passes, the measurement remains as history and is counted as expired. for corrected evidence. Default 30.
NOVUSLENS_MESH_HTTP_ADDR, NOVUSLENS_MESH_HTTP_BASEDefault twin host and port (127.0.0.1 and 8400).

Invariants and failure behaviour

  • Read-only against the customer. An acquirer only reads the source. A failed or partial read writes nothing for the affected 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..
  • Use measurements, never fill gaps with estimates. An unmapped item, an open ticket, an unresolved incident or an in-progress run is counted and reported. It is never estimated onto a cell.
  • Whole or nothing. A C/A/R triple is written in full or not at all, unless the calibration sets partial_axes.
  • A combined record keeps simulated labels. Any simulated contributor makes the entry simulated.
  • Old measurements stop counting as current. Evidence that stops being refreshed goes stale after three refresh intervals, and an observation expires after its window. The row stays and remains historically measured.
  • Both accounts are kept. A delta is a finding for a person to resolve. Neither the asserted nor the measured account is overwritten.
  • Stale holders are refused. A late write under an older fencing token is quarantined at the materialiser.
Last updated