A continuity past its post-fencing deadline is left to the operator and retries
forward on a capped backoff; a stateless deployment aborts to its incumbent; a
stateful deployment is left to the operator (ruling post-fence-forward-retry).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
require_detail judges bytes, truncate cut chars, so a long multibyte build
error made the abort write invalid and wedged the record in Sealing.
bounded_detail keeps head and tail within 2048 bytes, drops NUL, and
never returns empty. Also bounds gate-wait notes, cancel notes, and the
continuity deferral reason at their writers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ResumeBackoff stores the ceiling it was armed with and the skip check reads it.
resume_ceiling is the continuity cap (300 s) for a record that leaves_to_operator
and the short cap otherwise, for every such record: a forward-retrying
continuity, a stateful deploy, a wedged abort, a drain. A step that returns Ok
without moving a record left to the operator is backed off like a failed
attempt, so a silent candidate is challenged once per backoff, not every poll.
Nothing in TargetSupervision is touched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PhaseEnd::RetryForward: phase_end answers it for every continuity at or past
Fencing, because aborting restores the body its candidate already runs (ruling
c2c-retry-decided-by-record). The resolver leaves it to the operator
(operator-required incident, report_once) and the phase step keeps running.
leaves_to_operator covers it. DeploymentTransaction::expirable is the one
spelling the expire CLI, the spent-request retirement and the resolver's
requested flag read: a continuity past Fencing cannot be expired. Deploy keeps
abort-to-incumbent and the stateful operator-required arm.
Tests: the PhaseEnd table, the Routing deadline forward retry and its incident
episode, the stateful deadline, expiry refusal and spent-request retirement,
and a deployment still aborting to its incumbent. Abort-mechanics tests begin
their aborts directly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pre-c2c Idunn aborted a continuity at its phase deadline, which stops the
only running copy of the admitted body. At boot, a continuity abort that has
not stopped its running candidate (candidate_cleanup Pending, lease_withdrawal
not Complete, workload observed, issued lease exact on disk) is withdrawn and
the record retimed, the exact inverse of begin_post_fencing_abort. Negative
tests carry the live shapes of tx-4eaed669 (Routing, adopted lease) and
tx-039b91e9 (AwaitingReady, granted lease); the lift's refusals are pinned per
predicate input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Negative tests (a)(b)(c) are one marked harness that ages a deadline only when
one exists and ends on the waiter's own record, so at the base they fail at the
waiter assertion (still waiting), not at setup. (c) now runs the Odin gate-wait
it names: a stop-then-start continuity on a systemd unit observes and waits,
where the host-actuator step of a world with no actuator errs at once.
New: a stop-first owner's wedged pre-fence abort pages and ends its waiter; a
waiter's own wedged pre-fence abort pages beside a standing owner; a stateful
deploy's incident is silent under an early expiry, one per episode, and closes
while the record goes on; an unparsable drain plan is bounded; a transaction
gone from both live set and history stays a fault.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deadline_for_current_activity's drain arm returned None when parsed_inputs
failed (the running binary re-parses and re-admits the frozen binding), which
left the drain with no deadline, unpaged, and disarmed every waiter behind it.
The retire time is now the promotion plus the drain when the binding parses,
and otherwise now; the frozen committing duration from it bounds the drain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Survivors of cargo mutants --in-diff on 9ea6c71..126d710: the OperatorRequired
clause of leaves_to_operator (now a table beside phase_end) and the close
predicate (a vanished subject closes while another transaction is still left).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IncidentCondition::OperatorRequired (subject: transaction id) opens in
leave_to_operator, only for a record that leaves_to_operator, and closes as
recovered in close_spent_operator_incidents once no transaction in the
snapshot is left to the operator. Incident writes are never progress.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait_for_owner disarms a waiter Warming deadline (the owner bounds the wait)
and ends the waiter through its own pre-fencing abort when the owner leaves to
the operator (owner-left-to-operator); it never aborts the owner. The resolver
times Warming for every transaction, lifts a deadline onto a record that needs
one, skips an owned waiter, and leaves an abort or drain past its deadline to
the operator (report only; the incident follows). Rulings c2b-waiter-ends-on-
operator-owner, c2b-warming-deadline, c2b-abort-and-drain-deadlines.
Negative tests recorded failing at 9ea6c71.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WARMING_DEADLINE_SECONDS and PRE_FENCING_ABORT_DEADLINE_SECONDS are Idunn
constants (no PhaseDeadlines field: plan ids stay). DEADLINE_PHASES is the one
spelling of the deadline range. deadline_for_current_activity derives the
duration of a phase, an abort from its beginning, or a drain to its retire;
retime is the only non-test writer. leaves_to_operator is derived from it.
Validation asks for an activity, not a post-fencing phase. Records written by
this cut are forward-only (ruling c2b-forward-only-records).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Negative test for ghostlight-abort-lease-before-stop: the candidate holds the
lease lock until stopped. Recorded failing at this commit; the fix follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host-native runner has no network profile, so the host-actuator recipe
drops the external input the systemd recipe fetches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add an engine-level stop-then-start fixture (route-less host-actuator plan)
and a test that two such transactions never both enter Starting, in either
order; it kills enter taking the CAS only at Fencing and takes_target_at
ignoring the rollout. Discharges follow-up stop-first-handover-fixture.
- Pin supervision's skip of an owned target: a fenced generation is not
refreshed from Odin, and a stop-first owner's stopped incumbent is not
restarted.
- The rebase test now carries a route preflight, so keeping it dies.
- The step that ends a transaction archives it inside its write; the progress
read now treats a terminal archived record as progress, not a fault.
- enter's doc no longer claims every phase move goes through it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
candidate_ports_in_use is the one reading of which ports admitted, live and
draining transactions hold. Tests pin it, select_candidate_port's routed and
unrouted answers, and the three conditions under which a transaction is not
rebased.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ControlSnapshot::on_target is the one place a transaction is filed under a
target; target_owner, the supervision gates and the handover read it.
Tests: ownership is per target and names only a holder; a draining target is
not restarted until its cleanup ends; the admitted Expected is repaired
beside a building deployment; a live continuity alone holds back a second
mint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Freeze refuses only a second live Deploy of a target, so a profile command
freezes over crash-looping targets. Supervision defers to an owner, a drain
or its own live continuity, never to a deployment that has not taken the
target, and the admitted Expected repair runs beside one. Cut 1's interim
test (no continuity beside a pre-fencing deployment) goes with the state it
asserted.
Fold-in c1-sustained-reset-test-reach: the lower edge to the millisecond,
the newest attempt (not the oldest) deciding the reset, and the mid-wait
crossing recorded in continuity_backoff's doc and pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
settle_before_handover runs after the two abort dispatches and before any
pre-handover step. A Deploy rebases onto the admitted generation when it
differs from the one its evidence was proven against: incumbent_generation_id
is rewritten, warming, route_preflight and isolation are dropped and the
Odin cursor is raised, nothing else. In Warming it waits (a gate note, no
error, so no abort) while another transaction owns or drains the target.
A Continuity is never rebased: when its target is owned or its generation
replaced it ends itself through the ordinary pre-fencing abort.
Tests drive a real continuity restart to Complete beside a deployment and
check the deployment rebases onto it and fences it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine::enter is the only writer of a move into the next phase;
transition and the Sealing tail both call it. Entering the phase where a
transaction takes its target (takes_target_at: Fencing, or Starting for
stop-then-start) goes through Engine::take_target, one CAS that expects the
transaction's own envelope, the target's admitted envelope as read (or its
absence) and every other non-terminal transaction of the target, and
writes only the transaction. Of two entrants from one view exactly one
wins; a losing deployment waits with the owner named, a losing continuity
is superseded.
exact_incumbent now says what a mismatch is: a fault, not a race.
Test helper: the brake release is per transaction (a world can hold two
deployments).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
owns_target_authority and blocks_new_target_mutation answered four
questions with one phase-blind rule. They become is_live, owns_target
(derived from the phase and takes_target_at) and drains_incumbent, each
read by the consumer that asks that question: sibling order, validation,
status, ports, freeze and the supervision gates.
Validation now holds, per target, at most one owner, one live Deploy and
one live Continuity, each refused with its own message, so a continuity
beside a pre-handover deployment is readable. Freeze and the mint gate are
still conservative (any live or draining transaction refuses), so no
intermediate commit lets two transactions coexist unguarded.
seeded_transaction mints a fresh command id when its default is taken, so a
world can hold two of one kind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a_dead_incumbent_is_restarted_alongside_a_deployment_that_is_building and
a_profile_deploy_freezes_over_a_crash_looping_target use only base API and
pin the states cut 2 (handover-phase-split) ends. They fail until the
predicate split and the open gates land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The clock-step test anchored a target at admitted_at 0, which the sustained-first
rule rightly reads as a sustained run; the unobserved-run test logged restarts
after its generation's admission. Both now describe the cases they name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
continuity_backoff decides the sustained reset first, so a standing anchor
(a run no pass observed) cannot hide it. The running-pass end_backoff stays:
it makes the next death wait from its own discovery, not from a stale anchor.
Behavioural tests: a brief run does not reset the streak, streaks 13..u32::MAX
still mint and store saturating, the unobserved sustained run resets, a running
pass keeps the re-materialization bound, owed_steps admission bound, the v1
lift's inner schema check. The startup message names the supervision schema;
ControlSnapshot::read drops its repeat of the shared reader's key check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit rewrote the file with CRLF; this one returns it to LF so
the range diff is the real change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The re-materialization charges no streak, so a streak of 0 gave Due{0} and
a failed re-materialization was followed at once by a charged attempt. It
is an attempt that failed on the generation: the next one waits the base
step, so a queued deployment gets its gap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes Soul pass 1 of cut c1-never-stop:
- the sustained-run reset reads the newest of the paging log and the
re-materialization, so a failed re-materialization is followed by a gap
- an observed-running pass clears the backoff anchor, so a standing anchor
cannot suppress the reset on a later death
- TargetSupervision with keys 7-9 is schema v2; the dual-shape decoder is
deleted and v1 records are lifted and migrated once at store open
(deletion line named at TARGET_SUPERVISION_SCHEMA_V1)
- tests: continuity mints at every streak 1..=12 with a full or short log,
the mint stores the reset streak, the schema ids name one shape each
- ROUTE_ACTUATION_CEILING doc no longer says continuity spends restarts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TargetSupervision gains the attempt streak, the backoff anchor and the
re-materialization bound (keys 7-9, same schema; pre-key records decode with
them defaulted). Every charged attempt waits backoff_wait(base, streak - 1,
300 s) from the first dead-and-free pass; a sustained run resets the streak.
WorkloadPort::lost_to_host_reboot lets a reboot-lost unit be re-materialized
once per generation with no step. The streak backoff, reboot and negative
tests land with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The paging window now only pages: restarts_page opens the incident and
reports; it ends no pass and aborts no transaction. Deletes next_restart_at,
continuity_is_waiting and the five tests that pinned the stop, the yield and
the mint-anchored spacing. The streak backoff follows in the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Written first against base API; expected to fail at this commit
(deployment aborted 'yielded to continuity' once the window slides).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>