Expand description
One spawn attempt’s stale---resume retry decision, plus any terminal
error-result line observed for it, as a single explicit state machine.
This replaces three fields that used to live directly on
persistent::PersistentInner — pending_resume_retry,
confirmed_stale_resume_retry, and pending_error_result_line — which
were mutated directly by four independently-scheduled tokio tasks
(stdin writer, stdout reader, stderr reader, process-waiter) racing on
a shared mutex. That shape caused the exact bug this module exists to
prevent, twice: issue #2368’s original report, and a live-reproduced
recurrence (agent “Marks”, 2026-07-30) where the stderr reader’s
poison_resume promotion landed ~500ms before the stdout reader
finished draining lines, and a check of only one of the two fields
missed the stash entirely.
update() is a pure (state, event) -> (state, effects) function —
every transition is exhaustively unit-testable without a real child
process, a real tokio task, or any timing assumptions. Callers
(the I/O tasks persistent/spawn.rs starts: persistent/stdout_reader.rs
and persistent/process_waiter.rs) are responsible for actually executing
the returned ResumeEffects (persisting/dropping/flushing a line,
firing the retry, publishing terminal status) — this module only
decides, never performs I/O.
Every event that could affect in-flight state carries the exact
generation it was observed for. update() ignores an event whose
generation doesn’t match the state’s currently-tracked generation, so
a stale event from an already-resolved generation can never corrupt a
later, unrelated generation’s state — generation numbers are never
reused (see spawn_generation’s doc comment in persistent/mod.rs).
Structs§
- Queued
Retry 🔒Entry - One redeliverable stdin message in a
RetryPayloadbatch: the formatted JSON plus its queue identity. - Retry
Payload 🔒 - The spawn config + the growing batch of stdin messages to redeliver if
this generation’s
--resumeturns out to be stale. Named separately from the state enum soMessageAppendedToRetryBatchcan grow it in place without reconstructing the whole variant.
Enums§
- Resume
Effect 🔒 - What the caller must actually DO in response to an event — kept
separate from
ResumeStatesoupdate()stays a pure function with no I/O, fully exercisable in a unit test. - Resume
Event 🔒 - Events the stdin writer / stdout reader / stderr reader / process-
waiter sensor tasks report. Each carries the exact spawn
generationit was observed for. - Resume
State 🔒 - One spawn attempt’s resume/error-line lifecycle.
- Session
Outcome 🔒 - A resume attempt’s outcome, once definitively known. See
ResumeEffect::EmitSessionOutcome’s doc comment for how this is used.
Functions§
- capture_
resolution_ 🔒effects - Effects of a
SessionCapturedthat resolves tracking (a different sid, or a confirmed success): flush any held error line, then emit the session outcome. - resolve_
superseded_ 🔒generation - Resolves a PRIOR generation’s still-unresolved tracking
(
AwaitingOutcome/ConfirmedRetry) that a fresh spawn is about to supersede. - update 🔒
- The pure state transition. See the module doc comment for why this shape exists and what it replaces.