Skip to content

Quiet-to-due Monitor recovery qualification

δΈ­ζ–‡ι•œεƒ

This checkpoint adds composition evidence for T2 and the recovery verification RFC. Base e38b057b3 already contains the effect-identity repair from #4335. An older installed runtime can still exhibit that repaired defect. The remaining repair handles an unpolled, bound Monitor that becomes blocked: its original Turn now exposes conditional lifecycle recovery instead of ordinary execution or conflicting replan selection. No new RPC or persisted schema is added.

Missing counterexample

A quiet heartbeat automatically commits an observation without a Todo. If a Monitor becomes due during that same Turn, its explicit binding and actual observation must remain possible. The first observation cannot settle the later Todo or occupy its effect identity. Testing a newly due Monitor after an ordinary unbound guard misses this prerequisite: the quiet poll has already committed.

flowchart LR
    Q[Quiet guard: no Todo] --> O[Commit quiet observation]
    O --> D[Monitor becomes due]
    D --> B[Bind original Turn to Monitor]
    B --> P[Commit exact poll once]
    P --> L[Discard caller response]
    L --> R[Read back and retry original poll]
    R --> S[Turn settled; no quota debit]
    S --> N[Material successor uses a new Turn]

Executable boundary

tests/control_plane/test_monitor_quiet_due_recovery.py enumerates twelve journeys: legacy Markdown, canonical File and canonical SQLite, each with unchanged and material observations, with and without a peer-scoped user gate and reminder. Each journey uses real CLI subprocesses, real TS effects and isolated provider state. It performs two exact retries and one conflicting result retry. The response is deliberately discarded after successful command completion; this models acknowledgement loss, not a process crash inside commit.

Independent assertions require two distinct polls (quiet and Todo-bound), unchanged original observation, no refresh/spend records, no mutation on replay or conflict, one material generation and successor only in the material case, and settled readback that cannot execute further work in the original Turn. The Monitor remains open; material successor selection uses a fresh Turn.

Five additional journeys block an already-bound Monitor before its poll: legacy, File and SQLite soft claims, plus File and SQLite hard leases. The unmodified base incorrectly returns normal_run in this fixture; other frontier states can attempt a conflicting replan binding. Head returns the existing unsettled_host_turn_recovery mode with the original identity and no delivery authority. Two readbacks preserve blocked state. Only after the fixture's blocker is resolved does the test execute the projected restore command, re-enter the original guard, poll and settle without spending quota. The projected restore carries the independently verified reason and --clear-resume-when, as required by the existing lifecycle owner. Hard-lease restoration grants no execution authority: polling without a lease remains rejected, and a fresh lease is acquired before the exact poll. Two additional File/SQLite cases execute the same projected restore against an active holder and verify rejection with Todo, lease and Goal state unchanged.

Sensitivity was checked in a disposable checkout of the same base: restoring Turn-only effect allocation makes the unchanged/legacy journey fail at its first bound poll with heartbeat_receipt_identity_conflict. Unmodified base passes all six journeys. This is a deliberate historical-rule mutation, not a claim that the current base fails. The temporary mutant is not a shipped fixture.

An additional counterexample supplies an incomplete Todo frontier with no aggregate work lane and an unrelated peer gate. The exact settled Monitor still has to return heartbeat_settled_skip. Base instead allows ordinary execution because the adapter drops its typed phase when the aggregate lane is absent. The adapter now preserves the bound Monitor projection independently of aggregate coverage; TS still owns poll verification and phase classification. This regression is red before the adapter change and green after it.

A further composition case settles an advancement Turn, then moves its primary Todo to blocked before an independent due Monitor is observed. The emitted poll previously failed because admission accepted only open/done primaries. The existing TS transaction owner now accepts this later hold only when exact settlement readback is settled. Unsettled holds still fail closed; the Monitor must independently pass its due, owner, gate and lease checks. No held Todo is reopened, no new advancement is admitted and no extra debit is created. Synthetic TS regressions cover the accepted hold and identity/gate rejections (including deferred primaries); one CLI journey exercise in-flight writeback, spend, hold, poll and exact replay. The accepted blocked-primary TS case fails before the change.

Ownership and limits

Boundary Existing owner retained
Selection arbitration work_items/action_portfolio.ts
Monitor transaction and immutable replay quota/monitor_poll_commit.ts
Observation and successors in canonical authority coordination/todo_monitor_poll.ts
Turn closeout readback quota/settlement_readback.ts, quota/settlement_phase.ts
Bound Monitor lifecycle recovery quota/blocked_wait.ts
Bound phase projection into an optional aggregate lane work_items/work_lane.py
Legacy effect-id compatibility and transport quota/monitor_poll.py

The related refactor shares the current-Turn recovery envelope between causal waits and blocked Monitor recovery in blocked_wait.ts. Python passes the already verified Monitor phase through the existing request and renders the typed repair; it owns no second lifecycle rule. The added phase field is optional: earlier requests retain their causal-wait behavior. Only active, correctly owned blocked Monitors with poll_due qualify; missing, duplicate, foreign, archived and already polled inputs do not enter this restoration route. Tests assert these exclusions.

Default behavior changes for blocked, unpolled replay, for bound Monitor projection when an incomplete frontier has no aggregate lane, and for independent auxiliary observation after an exact settled advancement is held. The emitted restore command is conditional on a verified resolved blocker; the projection neither reopens the Todo nor establishes a poll/closeout receipt. Retain the blocker when unresolved. The existing Todo writer still enforces mutation authority. Runtime request count is unchanged; no Python rule deletion or performance gain is claimed. Moving the retained Python effect-id compatibility resolver needs separate pending-receipt/caller characterization and is deferred.

This qualifies a bounded Monitor closeout and CLI successor-selection sequence, not full M2/M3: lease transfer, mid-commit crashes, PostgreSQL, scheduler dispatch and original-context App/Lark delivery are outside this test. Existing pending- wait recovery tests separately cover original binding retention on File/SQLite. No model, external provider or benchmark job runs. There is no frontend change. Reverting restores the previous projection without rewriting persisted data, but also restores the blocked-Monitor and missing-lane recovery gaps.