Soul found B1's rule sound and its transition unsafe. This makes the rule's
old inputs impossible to reintroduce a stuck state from.
- The control store's legacy lift owns the transition. A pre-B1 continuity
abort that issued an activation lifts with topology_reconciliation Pending,
so the one resolution demotes its own activation. A record already terminal
under the old rule is reopened at Starting, since a terminal record cannot
owe work. History lifts records as written. Before this, one resident
record of that shape failed validation and ControlSnapshot::read refused the
whole store, so Idunn could not boot.
- Supervision owns the Expected-only precondition. When its pre-restart
demotion fails it mints no continuity and writes next_restart_at_unix_millis
on the generation's ContinuityBackoff (attempts and window untouched), so it
neither restarts over an undemotable projection nor retries every tick.
- Boot reconciles once: a projected activation that exactly matches the
activation a failed continuity in history (or a resident terminal record)
issued is demoted. Nothing is adopted; an activation no admitted generation,
live transaction or failed issuer accounts for is reported and left alone.
New read-only CultCacheTopologyDriver::projected_activation.
- Unreadable history.cc fails closed. The continuity restart ceiling and the
consumed-command check no longer read an empty history; they stop, and the
fault is reported once while it lasts (ReportOnce). The undecodable-record
report is a pure function, pinned.
- docs/deployment-authority.md no longer says the projection never drifts.
Tests: legacy fixtures written by the 9001b58 encoder for the three abort
shapes (a second throwaway generator, see the fixtures README), driven through
the Engine; the seeded-transaction helper now takes a command kind and an
incumbent, and a Deploy carries a frozen-source receipt and a signed brake
release, so Deploy commit, Deploy pre- and post-fence aborts and a continuity
abort over an incumbent run through the real phase machine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
One migration for the route-continuity foundation. No decision reads the new
state.
- ReadinessEvidence (OdinCorrelated | RouteProof) replaces the bare Odin Ready
receipt on both records. The class a target must collect is derived from its
Expected (ReadinessClass::of): a shared-infrastructure odin.verse-rendezvous
dependency means Odin-correlated, none means route-proof. It is not stored.
RouteProof evidence is refused for a target that declares Odin.
- Generation carries route supervision state (challenge times, failures,
degraded marker, actuation window) and continuity backoff. A new generation
inherits the actuation window and backoff and starts unobserved and
undegraded. route_repair_started_at stays readable.
- Transaction carries the current phase deadline (stamped by transition from
the plan's frozen durations) and lease adoption evidence.
- FailedAfterFencing names its recovery: RestartAdmitted, RestoreIncumbent
(the default for older records) or OperatorRequired{reason}.
- Every v2 generation lifts as Odin-correlated with its receipts unchanged;
v2 and v3 transactions lift through LegacyDeploymentTransaction. The boot
migration rewrites both, preserving stored_at, and history reads lift the
same way. The Legacy structs die when no older record can remain.
Fixtures under tests/fixtures/idunn-control-legacy are the bytes the 9001b58
encoder wrote for records built through its own constructors.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>