Bitspark constellation
accepted source ↗

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's process.v0 is "the canonical fact-driven reconcile pattern"; arche-ops the 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) and arche-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 admissionmay 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 (Moderun | 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 with OutcomeUnknown and 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 companion deploy.v0 profile draft), the saga / OutcomeUnknown mechanics, and arche-ops are 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.

The Bitspark constellation — how the systems are built and relate.

GitHub