In short

Ten contracts carry data between layers, and repos may depend on nothing else: PlantState, Evidence, Prescription, Action, ValueRecord, AutonomyPolicy, Lot and genealogy, WriteRequest, ClosureState and Envelope. The L3 to L4 hop is Evidence and the L4 to L5 hop is Prescription; today they run on Finding 1.2.0 and the card proposal. Every message carries the Envelope header with its episode_id. A WriteRequest needs both a Prescription and a granted AutonomyPolicy. All ten are drafted as 0.x and move to 1.0.0 on first production use, and each section lists the fields and what exists in the code today.

Ten contracts carry the stack (D2). They are drafted in SE beside contracts 0.16.0 as 0.x, marked experimental; the first plant that uses one in production bumps it to 1.0.0 (section 5.11). Every message travels inside the Envelope header (section 5.10). Where an existing schema covers part of a contract, the section says so.

Section ContractProduced byConsumed byFirst needed for
5.1PlantStateLayer 3 estimators, Plant Box twinLayer 4, writer, safety filterFast loop in shadow
5.2EvidenceLayer 3Layer 4, agents, ledgerSlow loop
5.3PrescriptionLayer 4 (KR)L5 cards, safety filterSlow loop
5.4ActionL5, writerLedgerSlow loop
5.5ValueRecordL5 verificationCustomer, registry, autonomySlow loop
5.6AutonomyPolicyA person at the plant, through L5Safety filter, writerFast loop in shadow
5.7Lot and genealogyL2 linkersLayer 3, ledgerPlants that track lots
5.8WriteRequestLayer 4 or a personSafety filter, writerFast loop in shadow
5.9ClosureStateL5 card machineL6, analyticsSlow loop
5.10EnvelopeEvery producerEvery consumerDay one
Contract flow

State and evidence

same-condition wins

Lot and genealogy

PlantState

Evidence

Prescription

WriteRequest

AutonomyPolicy

ClosureState (cards)

Action

ValueRecord

How to read it.

  1. All ten travel inside the Envelope (section 5.10), which is not drawn.
  2. A WriteRequest needs both a Prescription and a granted AutonomyPolicy.
  3. ValueRecords feed back into the policy: autonomy is earned from same-condition wins.

Build now: Evidence, Prescription, ClosureState. Later: PlantState, WriteRequest, AutonomyPolicy for the fast loop.

View Mermaid source
flowchart TB
    %% house-style: contract-flow
    subgraph inputs["State and evidence"]
        direction LR
        LOT["Lot and genealogy"]
        PS["PlantState"]
        EVI["Evidence"]
    end
    LOT --> EVI
    PS --> EVI
    EVI --> RX["Prescription"]
    RX --> WR["WriteRequest"]
    POL{{"AutonomyPolicy"}} --> WR
    RX --> CS["ClosureState (cards)"]
    WR --> ACT["Action"]
    CS --> ACT
    ACT --> VR["ValueRecord"]
    EVI --> VR
    VR -. "same-condition wins" .-> POL

    classDef govc fill:#fff4d6,stroke:#c99a2e,color:#000
    classDef agentc fill:#e8f0ff,stroke:#5b7bd5,color:#000
    classDef loopc fill:#eef7ee,stroke:#4f9a4f,color:#000
    class POL govc

5.1 PlantState#

The estimated state of an asset at a time, with uncertainty. Produced by layer 3 (twin runtime or estimators), consumed by layer 4 and the writer.

FieldTypeNote
plant_state_id, asset_id, tsids, timestamp
operating_stateenum: running, stopped, changeover, restart, unknownFrom the fast-loop docs; writes allowed only in running
state_vectormap name → {mean, std, unit}Examples: thermal batch T_furnace, T_load; exchanger R_f, dR_f; filler mu_head_1..n
covariance_refoptional blob refFull covariance when needed
model_ref{model_id, version, params_version}Ties to registry
data_quality{inputs_ok, gated_readings, last_good_ts}From C03
coverage_labelenum: calibrated, under-covering, unknownFrom replay coverage (for example, a nominal 90% interval that covers well under 90% of outcomes on replay is under-covering). Uncalibrated states cannot clear write gates (C44).
evidence_labelper state_vector element: Measured, Estimated, Assumed, UnknownPer element (recommended inside D13; it costs one field). Assumed or Unknown elements block action proposals through the router's abstain (C45)
episode_ididTrace key across layers

Exists today: nothing. twin_state table is DOCS.

5.2 Evidence#

A typed claim with its support. Replaces the energy-only FindingAs-built L3 detector output admitted to L4 (finding.json 1.2.0) as the unit of analytical output.

FieldNote
evidence_id, kind (detection, estimate, attribution, test_result, document)
subject (asset, lot, part, order)Lot and part keys are new
claim (structured: metric, direction, size, window)Metric cites a semantic-metric version (C08)
support (method, sample size, effect estimate with interval, p or posterior, assumptions list)For causal claims, list assumptions explicitly
tier (measured, confirmed, modeled, unknown)Derived from the evidence labels by the mapping below (D13). The master document does not define tiers; SE owns this enum
value_vector ({energy_kwh, energy_inr, scrap_kg, scrap_inr, throughput_units, quality_risk}) with rangesReplaces the required kWh and INR pair
model_ref, data_window, episode_id

Exists today: finding.json (1.2.0, energy-centric) and finding-2.0.0.json (direction only).

Tier mapping (accepted under D13, 7 Oct 2026). The package uses two vocabularies. The contract tiers (measured, confirmed, modeled, unknown) are the provenance of a claim. The evidence labels (Measured, Estimated, Assumed, Unknown, plus calibrated or uncalibrated) are the status of each quantity inside it. A claim takes the weakest label among its load-bearing quantities, and only a passed check raises it to confirmed.

Load-bearing quantities in the claimHighest contract tierNote
All MeasuredmeasuredDirect meter, check-weigher or lab result
Measured or calibrated Estimated, plus a passed check (counterfactual method, or a named plant person)confirmedEstimated quantities must have passed C44
Any Estimated without a check, or any AssumedmodeledAssumed quantities are named in support.assumptions; uncalibrated Estimated cannot clear autonomy gates
Any UnknownunknownShown only with the gap named; no action gated on it

confirmed is not "verified". Only a counterfactual method that passed its checks (D5) may be called verified (section 1.1). A confirmation by a named plant person is recorded and shown as that person's confirmation, never as savings.

5.3 Prescription#

What to do, why, by whom, and how it will be checked.

FieldNote
prescription_id, episode_id, evidence_refsEvery number traces to an Evidence record
what, why, who, when, impact, mv_planExist in prescription.json today
action_class (message, experiment, setpoint_change, schedule_change, maintenance)New. schedule_change and maintenance are advice only: a named person accepts a near-term sequence before any write-back, and Stamped never authorises maintenance
options (hold, step down, step up), each with predicted outcome and intervalNew; hold is always an option
required_autonomy_level (AL0–AL5)New; compared with the AutonomyPolicy in force
constraints_checked (list of envelope, state and FMEA checks with result)New; filled by the safety filter
expires_atNew; an expired prescription cannot become a WriteRequest

Exists today: prescription.json with the first two rows.

5.4 Action#

What actually happened. One record per executed action, whether a person did it or the writer did.

FieldNote
action_id, prescription_id, write_request_id (writes only), episode_id
actor (person id, or writer id with policy grant id)
kind (message_ack, manual_change, confirmed_write, standing_write, experiment_step)
For writes: tag, old_value, requested_value, applied_value, readback_value, readback_ts, limits_applied {min, max, step, rate}, stop_condition, restored_value_on_stopFields missing from action-intent.json
model_ref, plant_state_refWhat the system believed when acting
outcome_windowWhen to judge it
hash_prev, hash_selfHash chain for write_log

Exists today: closure/action-intent.json (leftover, missing the write fields; retired in favour of WriteRequestPlant Box write path request (direction; closure/action-intent.json retired), section 5.8). L5 card transitions record people's actions on cards.

5.5 ValueRecord#

The ledger row. One per action or per bundle, per KPI.

FieldNote
value_record_id, action_ids, kpi (energy, scrap, throughput, quality, gas)Multi-KPI
baseline_method (IPMVP option, ITS, DiD, synthetic control, switchback) and baseline_model_ref
normalisers (output, mix, shift, ambient)As in L5 packs
estimate with interval, units, inr_at_price_versionPrice table versioned
tier (measured, confirmed, modeled, unknown)"Verified" only for counterfactual M&V that passed its model-fit checks
status (pending, ops_confirmed, signed_off, disputed, superseded)Lifecycle only; how strong the claim is lives in tier. Migration: L2 verified becomes signed_off where a sign-off exists, otherwise ops_confirmed; contract modeled becomes tier = modeled with status = pending (D2)
customer_signoffWho signed, when

Exists today: ledger.mv_ledger (L2, energy-only, keyed by prescription), closure/ledger-entry.json (energy-only, different enum), L5 verification packs.

5.6 AutonomyPolicy#

The grant of authority.

FieldNote
policy_id, asset_id, action_class, level (AL0 to AL5)
operating_envelope (tags, min, max, step, rate, allowed operating states)Read by the safety filter (D15) and the writer
preconditions (min same-condition wins recorded in the ledger, coverage ≥ target, model version pinned, data quality ok)The L5 gate's min_verified_same_condition field implements the first item
stop_conditions (operator touch, heartbeat lost, state change, coverage drop)
granted_by (person, role, at the plant: yes or no), granted_at, expires_at, revoked_atSwitching on needs a person at the plant
fmea_refsOne hazard row per tag (fast-loop docs)

Exists today: the card-transition gate in L5. No contract.

5.7 Lot and genealogy#

Which units, parts and lots passed through which assets, and how sure the link is. Stored as relational and event tables in L2 (D1).

FieldNote
lot_id, part_id (optional), order_id (optional), material_refKeys used by Evidence subject
event_id, asset_id, start_ts, end_ts, event_kind (cycle, load, test, stop, changeover)Event graph (Option C)
link {from, to, link_method (scan, time_window, head_index, manual), link_confidence 0–1, link_status (suggested, approved, rejected)}Cognite annotation pattern (E29); low-confidence links never gate an action
source_ref, episode_idWhere the link came from

Exists today: L2 context records (migration 014) and topology, stops and changeovers (016). No lot or link tables.

5.8 WriteRequest#

A request to change one set point, following the NE 178 Verification of Request pattern at pattern level (D17). The writer accepts nothing else.

FieldNote
write_request_id, prescription_id, policy_id, episode_id, idempotency_keyDuplicate keys are ignored
tag, requested_value, unit, requested_by (person or standing policy)OPC UA node only (D10)
status (requested, authenticated, authorised, verified, mapped, accepted, applied, read_back, rejected, expired, stopped)VoR flow; each step writes a timestamp and reason
rejection_reason (envelope, operating_state, coverage, stale_model, operator_touch, expired, clock_skew)Status feedback without plant internals
expires_atAL2 confirmation window; standing grants use the policy expiry

Exists today: nothing. closure/action-intent.json is retired.

5.9 ClosureState#

The card lifecycle the L5 machine already runs, published so L6 and analytics stop guessing. Values are the code values in stamped_l5_domain/cards/states.py.

StateWho sees itTerminal
open, assigned, in_progressCustomerNo
closed_verifiedCustomerYes (can take a regressed self-transition)
closed_no_change, closed_unverified, rejectedCustomerYes
deferred_expired with sub-state deferred or expiredCustomerOnly when expired
blocked_disputedCustomerNo
pending_stamped_reviewStaff onlyNo
withheld_by_staffStaff onlyYes

closed_verified is a code name: it means the L5 verification pack saw the change in telemetry, which is ops_confirmed in ledger terms, not counterfactual verification. The contract keeps the code value and documents this; a rename waits for 1.0.0. The 8-state description in older docs is retired.

5.10 Envelope#

The header every contract travels in.

FieldNote
contract, contract_versionFor example PlantState, 0.3.0
tenant_id, plant_idRLS keys (section 12)
message_id, idempotency_key, source_seqDedupe and replay (section 7.3)
produced_at, source_ts, received_atClock handling (section 7.3)
producer {repo, component, version}Traceability
episode_idTrace key across layers

5.11 Versioning and compatibility#

  • Semantic versions per contract. While 0.x, a minor bump may break; consumers pin the minor version.
  • From 1.0.0: additive fields are minor; removing or renaming a field, or changing an enum value, is major and needs a migration note and one release where both versions are accepted.
  • Unknown fields are ignored by consumers; missing required fields are rejected at the seam, never defaulted.
  • SE CI validates every schema and its examples (the existing contract-check workflow).
Page history: last 5 changes
  1. 2026-10-07 docs(research): retire stale research to archive/research-2026-10 with a register ab84821
  2. 2026-10-07 docs(technical): rewrite fast-loop/; all architecture diagrams in house style 7330f47
  3. 2026-10-07 docs(technical): archive archify; add SYSTEM_VIEWS.md house diagrams; check_docs --min 1e190b6
  4. 2026-10-07 docs(technical): carry product sections; rewrite README and pointers ee1e818
  5. 2026-10-07 docs(technical): split decision board into DECISIONS.md b4db9d4

Diagram

100%

Search the architecture