ADR 0031 — the runtime control plane is a fact-derived judgment–realization loop: fact-executed before fact-authored, durable before autonomous
- Status: accepted
- Date: 2026-06-20
- Refines: 0026 (the runtime-plane desired-state registry — this closes the deferred desired-vs-observed loop §4 on-ramps to, introduces a two-axis decomposition — intent authority vs actuation authority — of which 0026's §6 promotion is the intent-authority axis, and splits intent from the effective specification the deployed version derives to)
- Builds on: 0030 (the lens/realizer seam — "decide once, providers realize" — this gives that seam its operational, multi-step form), 0029 (the integration runtime), 0016 (deployment is substrate-managed;
arche-runner'sprocess.v0is "the canonical fact-driven reconcile pattern";arche-opsthe deferred asserter), 0011 (collect-never-invent; the generated observed mirror's determinism) - Relates: 0010 (atlas coordinates, it does not admit — preserved), 0008 / 0006 (the channels/BOM the effective specification resolves from), 0027 (component-release identity + wire epochs + the Gate-C admission verdict the loop computes), theory/0003 (the realization candidate theory — "declaration → artifact → desired → effect → observation" — this loop is its operational instance)
- Grounded in: research-docs/0003 + external-expert advice (
research-docs/0003-…advice.md)
The runtime control plane (
pharos) becomes fact-driven along two separable axes — intent authority (git-authored → natively fact-authored) and actuation authority (observe → admit → block → actuate) — which need not move together (fact-execution comes first). A genuine fact-driven loop can close while git stays the reviewed authoring surface — so the rule is fact-executed before fact-authored, durable before autonomous. The loop is one named, constellation-wide shape — the fact-derived judgment–realization loop (record → lens derives the verdict/plan → durable coordinator sequences → pure realizer effects → observer attests → record), the operational form of 0030's seam, instanced in part by kosmos (the lens/judgment arc) andarche-runner(the reconcile/realizer arc). pharos is the controller / admission / custody / sequencing / audit plane — not "just another realizer." A trusted intent bridge reconciles pharos's GitHub-API pivot with substrate grounding (they are orthogonal couplings); intent is split from the effective specification the deployed version resolves to (refining 0026); observation is a freshness-qualified claim (success ≠ convergence; fresh read-back is); and atlas authors the git baseline + owns the shared contracts, taking no prod-governance authority. This ratifies the frame and vocabulary, not a wire format.
Context
0026 gave the runtime plane a desired-state home
(constellation/runtime.json, hand-authored, atlas-gated) and an observed mirror
(constellation/state.json, generated), but explicitly deferred two things: the desired-vs-observed
gate (§4) and the substrate-native dogfood end-state — env facts in per-environment record
spaces, with an arche-ops asserter (§6). Meanwhile the actuator that exists — pharos's in-process
deploy (pharos ADR 0005
— GHCR pull + Model-B secret injection + a missing-secrets drift gate; the repo is private) — is
fact-informed but
operator-triggered: it reads its desired state from git over the GitHub API and actuates when a
human (or an agent on the same API) pulls the trigger. Nothing observes drift and re-converges; a
partial failure (VM up, DNS set, docker compose up fails) leaves a half-built environment with no
durable, queryable statement of what is actually running where.
0016 already names the
target shape — "desired state lives in stele facts; realizers and controllers reconcile and assert
observations back" — and points at arche-runner's process.v0 watch/reconcile loop as the
canonical pattern. 0030 fixed the
constitutional seam: a theory judgment is decided once by a fact-derived lens; a provider is a
pure realizer. What was missing is the operational model for a control plane whose actuation
is heavy, slow, externally-held, and only weakly idempotent — a whole-service deploy is not
docker run — and a principled answer to whether grounding desired/observed in the substrate
contradicts pharos's deliberate pivot (read atlas's data over the GitHub API; never depend on
atlas's CLI or clone repos at runtime).
research-docs/0003 posed exactly this, and an external-domain expert's response supplies a coherent
migration architecture. This ADR ratifies its cross-cutting frame; the per-system mechanics are
downstream (pharos / stele / arche-ops). The loop inherits arche-runner's shape (level-triggered,
transition-based, fail-closed) but, because deploy actuation is heavy/slow/weakly-idempotent,
deliberately departs from its cheap-actuation defaults (auto-delete, blind retry, one reconcile
interval) — the departures Decision 7 names. Scope is staging-first (mirroring
0026 §5): ratifying the frame does not authorize
building the whole apparatus at once, and production mechanics (native prod authoring, approval facts,
destructive action classes) stay out of scope for ratification now, gated on
0026's ownership criterion.
Decision
1. Two separable migration axes; fact-executed before fact-authored, durable before autonomous. Intent authority (who authors desired state: git, projected into facts → environment operators authoring facts natively) and actuation authority (observe → admit → block → actuate) are separable: neither transition forces the other to happen at the same time. They are independent in requirement but deliberately ordered in practice — the loop becomes genuinely fact-driven (desired facts on the record, a reconciler closing against them) while git remains the reviewed authoring interface, and native authoring follows only later. Requiring both at once is the trap; sequencing them — fact-executed first — is the design.
2. The named pattern: the fact-derived judgment–realization loop (the lens–realizer loop).
One constellation-wide shape, the operational form of
0030's seam, already instanced in part by
kosmos (the lens/judgment arc) and arche-runner (the reconcile/realizer arc, process.v0 — a
fact-driven occupant, not itself a theory-deciding lens):
record → lens → judgment / verdict / plan → durable coordinator → pure realizer → observer/attestor → record
- lens — reads a pinned record projection, validates form, derives the authorization and other theory verdicts, emits a deterministic plan. Holds no provider credentials and performs no effects.
- durable coordinator — owns sequencing, single-flight per target, pins the input bundle, tracks attempts/retries/deadlines/pause/supersession, hands scoped secret material to effectors, and does not re-decide the theory verdict.
- realizer / effector — knows a provider API; implements idempotency, lookup, and read-back; reports physical impossibility/exhaustion/eventual-consistency/external handles; renders no theory judgment (it does not decide whether the actor may act).
- observer / attestor — reads reality through a declared method and asserts evidence with provenance; may share code/process with the effector at first but produces a separate assertion.
This is exactly 0030's litmus — "may this happen?" → the lens; "can I correctly/safely do it now?" → the provider — given multiple steps and durability. The cross-cutting invariant is the role separation: the lens holds no credentials and performs no effects; the realizer holds credentials and renders no theory verdict. pharos is the controller / admission / custody / sequencing / audit plane — not "just another realizer." How pharos enforces that separation internally — one binary with enforced module-import directions, or split services at a real boundary (distinct credential scope, independent operator ownership, untrusted plugin, separate scaling/failure domain, or genuinely independent attestation) — is pharos-owned (its local ADR), not legislated here.
Which lens decides deployment admission (vs 0030).
0030 routes every theory judgment about a
provider's surface to one fact lens (the substrate for its own facts; the integration runtime —
kosmos — for a provider's surface). Deployment admission — may this (system, env) roll to this
effective spec now? (wire-compatibility, required secrets present, approval, action-class) — is a
distinct judgment domain, not a provider-surface authorization. pharos is the designated fact
lens for that domain, and decides it once; it does not re-check a verdict another lens
already made. The provider-surface authorization a deployment's effector itself needs at effect time
(e.g. writing content into a provider) remains that provider's lens's judgment (kosmos), evaluated
separately. Two judgments, two lenses, each decided once — 0030's rule is one decision site per
judgment, and pharos is not a second check. (The admission verdict is itself recorded as a fact —
§6 / the companion profile's …admitted predicate — so "which judgment authorized this operation" is
re-checkable.)
3. The intent bridge — the GitHub-API pivot and substrate grounding are orthogonal couplings. Distinguish four couplings rather than conflating them:
| coupling | posture |
|---|---|
| executable/code coupling to the atlas CLI | avoid (pharos's standing pivot) |
| data-contract coupling to atlas-authored files | intentional |
| authority coupling — who may assert intent | explicit, per (system, env) |
| availability coupling to GitHub / the record | minimize, make failure visible |
A trusted importer identity (e.g. runtime-intent-github) continuously fetches the relevant atlas +
member files over the GitHub API, pins every input to immutable commit/blob identifiers, runs the
native validation, and asserts the resulting canonical desired facts plus an import-status fact
on failure (the concrete identity name and implementation are pharos's). Its signature attests "this
trusted bridge fetched and validated these bytes at these immutable git coordinates" — not that
the human git author personally signed the substrate assertion; that distinction is preserved. The
pivot removes a dependency on atlas's implementation; it never prohibited consuming atlas's data,
nor does it require git to remain authoritative for every class of runtime fact (how a non-atlas
production operator owns the realize decision — e.g. an approval fact — is recorded open below).
4. One active intent-authority epoch per (system, env); no dual-master. Every desired fact
carries an authority epoch; the fold considers only facts from the active epoch and its authorized
source. A safe cutover sequence (one ordering, not the only one) is: git-authoritative direct-execution
(today) → git-authoritative shadow projection (facts written and compared, not used) →
git-authoritative fact-execution (the reconciler consumes the imported facts; git still writes) →
native-authoritative for staging (native facts become the sole writer; git becomes a generated
projection) → native per production environment (only where ownership justifies it). During the
shadow stages, compare git and facts as a migration invariant; never merge by latest-timestamp, and
never build bidirectional sync. Dual representation is fine; dual authority is not. (Composing
one canonical effective spec from separately-owned inputs — a git baseline, operator approval/pause
facts, vault revision tokens, the release-plane resolution — is not dual-mastering; only two
sources claiming the same field is.)
5. Reconcile the environment, not the deploy — and split intent from the effective
specification (refines 0026). The long-lived target
is (system, env); a deployment is a bounded, durable, resumable attempt to reconcile it. Do not
overload "generation": keep four distinct identities —
- intent generation — the author's ordered revision of policy-level intent;
- effective-specification digest — the exact resolved thing being realized (e.g. intent + resolved channel/release + artifact digest + member deploy-descriptor digest + public env config + validation/profile versions + opaque vault-revision tokens). Its grammar is profile-owned and cross-lane-recomputable (so conformance vectors can check byte-identity — §8); resolving the inputs that feed it is pharos's;
- operation/attempt id — one execution attempt against an effective spec;
- observed-specification digest — the spec fresh read-back evidence actually demonstrates.
This refines 0026: the deployed version stays derived
from the channel, but channel is intent; when the release behind a channel moves, the effective
desired state changes with no intent change, so it must enqueue a reconcile, and the resolved
release must be pinned into the plan bundle (never re-resolved mid-operation — ETags are cache
validators, not source revisions; use commit/blob identities). A secret rotation likewise yields a new
effective revision without editing the registry.
6. Observation is a claim about reality, in three vocabularies, freshness-qualified. A valid
signature establishes "this authorized identity asserted this" — never that the cloud/DNS/service
actually has the property. So: operation facts (what was attempted) · evidence facts (what an
observer read, tagged with an observer profile and an evidence class — roughly ordered
self-reported → provider-read → effector-read-back → external-probe → cross-checked, from which the lens
derives whether evidence suffices for a given verdict rather than ranking by a numeric score) ·
derived conditions/verdicts. Represent
observed state as per-aspect conditions (True/False/Unknown, each carrying the
observed-spec digest + an evidence reference), not one aggregate deploy.ok phase. Freshness is a
derived judgment with an explicit asOf + policy version — ambient wall-clock time is an external
input, never hidden inside an ostensibly pure fold; a verdict must be re-runnable from (record position, asOf, policy version). A successful actuation is not convergence; fresh read-back is.
Observation is transition-based (assert on semantic change, spec change, handle/evidence change, or
a freshness-lease renewal) — high-rate telemetry stays in metrics, large provider snapshots go to a
content-addressed store asserted by digest, so the fact log never becomes a time-series database.
7. Heavy actuation is durable and conservative — the cross-cutting invariants, with the mechanics
pharos-owned. A whole-service deploy is heavy, slow, externally-held, and only weakly idempotent, so
pharos must provide a durable, resumable, ambiguity-safe actuation model; its mechanics — the
resumable saga that persists before/after each uncertain external create, an explicit ambiguous-outcome
state that stops rather than blind-retries (rediscover-by-name, adopt iff exactly one match), and
the external-bindings store (logical facet → physical object id) — are pharos-owned. The invariants
this ADR fixes are: a successful actuation is not convergence (only fresh read-back is); the
disappearance of a desired entry or fact is never a teardown instruction — absence is inert,
and removal is only ever an explicit retire intent (Mode ∈ run | stop | retire); cloud
orphan-collection defaults to retain-and-report, not the cheap automatic-delete a process-reconciler
can afford; and any deletion requires explicit ownership + policy + approval (a
terminating/finalizer-style protocol for heavy infrastructure). Observation cadence is separate from
remediation cadence, and remediation is gated per action class (observe-only / plan-only /
require-approval / non-destructive / all) — which is precisely the observe → admit → block → actuate
staging, so the control plane never gains an action class before its model is proven.
8. atlas authors the current git baseline and owns the shared contracts; it takes no prod-governance
authority, and the live record is authoritative for accepted claims, intent, judgments, and history
(native-authoritative environments move intent authorship off git; physical reality stays off-record —
the record holds signed observations of it; preserves 0010 /
0026). atlas owns the truth matrix — which producer
is authoritative for which class of fact, now vs later — and the conformance discipline: version
the JSON and fact profiles, fail closed on unknown versions, record which profile/validator version
accepted each input, and keep shared conformance vectors. atlas offers the vectors and expects
each implementation (pharos today) to validate against them, so CI can show atlas and pharos
accept/reject the same vectors. That kills semantic skew between the two without importing the atlas
CLI — sharing contracts, not implementations, preserves the pivot. The committed state.json stays deterministic: a generated-from-record report pinned to a
record position + asOf, explicitly a report/CI artifact, never consumed by the live controller.
Physical reality never moves onto the record — only signed, freshness-qualified observations of it
do.
9. Recovery is explicit break-glass. Recovery of the fact substrate itself must be possible from a pinned last-known-good input bundle + deliberate operator authorization. A silent fallback from facts to git is forbidden — it would silently change authority semantics; the fallback must be a documented, deliberate break-glass operation.
10. Ownership split and the promotion criterion. This ADR holds (cross-cutting, no single-repo
owner): the loop pattern, the two-axis model + authority epochs, the intent bridge + truth matrix, the
effective-spec refinement, the observation principles, the conformance discipline, the determinism
posture. The mechanics are expected to land in the owning repos' own ADRs, which this ADR does
not pre-empt: pharos — the durable saga, the ambiguous-outcome machine, the external-bindings store,
observation/remediation cadences, anti-thrash controls, the package structure, the effectors/observers;
stele / arche-ops (when it lands — arche-ops is still greenfield,
0016 §6) — env-scoped
spaces and the asserter. The home of the deploy.v0.* fact profiles is left open below
(atlas-hosted shared semantics vs co-owned with stele). Promote a given environment's authoring from
git to native facts on 0026's criterion — when
ownership demands it (a non-atlas operator, PR latency impeding operations, delegated env-scoped
authority, or operational intent needing to change independently of dev-plane config) and only once
the native authoring path itself provides validation, conflict detection / compare-and-swap, explicit
retirement, audit, a usable review/diff UX, and a recovery story — never merely because signed facts
are conceptually cleaner. A hand-asserted fact with no maintained authoring workflow is hand-rotting
config with a signature.
Consequences
- The loop closes without moving authoring off git. The near-term staging path — a durable operation saga + per-aspect observation facts + the intent bridge, all behind operator-triggered actuation, then automatic non-destructive remediation under the action-class gate — is a concrete sequence that needs none of the deferred substrate pieces to start, yet lands on them.
- A latent bug class is named and closed. Splitting intent from the effective specification makes "a moving channel head changed what should run" and "a rotated secret changed the effective revision" first-class reconcile triggers rather than silent drift, and makes "what is actually running here, against which resolved spec, observed how freshly" a same-store query.
- The pivot, the vault, and the 0030 seam are preserved and extended, not contradicted. The controller/effector split keeps "decide once" honest; the four-coupling distinction shows substrate grounding was never what the pivot forbade.
- Cost. A real fact-profile + conformance-vector surface to maintain across atlas and pharos;
per-
(system, env)epoch bookkeeping; a durable operation store withOutcomeUnknownand external bindings; and the standing discipline to keep the lens free of credentials and effects. These are the price of a re-checkable, recoverable control plane and are deliberately staged. - This is a frame, not a wire format. It ratifies a vocabulary and a direction. The concrete
deploy.v0.*profile bodies (the companiondeploy.v0profile draft), the saga /OutcomeUnknownmechanics, andarche-opsare downstream work; this ADR is the cross-cutting contract they implement against. - Lineage. Refines 0026 (closes its deferred loop;
splits intent from effective spec; decomposes its §6 promotion into the intent-authority axis of a
new two-axis model); gives
0030's seam its operational, durable form;
realizes the substrate-managed-deployment intent of
0016 on
arche-runner's reconcile pattern; preserves 0010 / 0011 (atlas coordinates and authors a deterministic mirror; it does not admit). Mints no new layer and takes no prod authority.
Open questions (recorded, not decided)
- Profile home. Whether the
deploy.v0.*fact profiles (the companion draft) are hosted in atlas (as shared family semantics, the way atlas hosts other cross-repo vocabulary) or co-owned with stele. - ADR granularity downstream. Whether the per-system mechanics land as one pharos ADR set plus one stele env-space ADR, or a finer split.
- Clock semantics. If freshness ever needs more than an explicit
asOf+ policy version, a signed time source or a bounded-skew policy — introduced deliberately, never as hidden ambient time. - Approval facts for production. Whether a
deployment.approved(Target, EffectiveSpecDigest, Approver, Reason)fact becomes the standing mechanism by which a non-atlas production operator owns the decision to realize a revision while git still supplies the baseline.