feat(#10): Wave C — multi-gate resume, TTY inline confirm, parked-run e2e - #123
Conversation
Two parallel human_gates both park at the fixpoint; approving one advances its branch and the run re-parks on the rest, and approving the rest completes the run -- which the slice-2/3 machinery already supported, now pinned by a test. A gate that stays awaiting across a resume (unapproved) is a CONTINUATION, not a new park: the scheduler is seeded with the gates already awaiting from the prior parked run (awaiting_seed) and re-parks them silently, so gate_awaiting is emitted once on first park rather than re-emitted on every resume. A gate newly reached during a resume still emits on its first park. Slice 5 of #10 (Wave C). TTY inline confirmation and the parked-run e2e follow.
In an attended (TTY) session `caw run` now prompts at each awaiting gate inline: yes approves it and the run continues, no rejects it and ends the run. The CLI loops run -> prompt -> resume until the run reaches a terminal, showing the gate's prompt text. In a non-TTY session the run parks for `caw resume` (unchanged), so the executor is untouched -- this is pure CLI orchestration over the existing park/approve/reject primitives. Slice 6 of #10 (Wave C). The real parked-run report e2e follows.
The parked-run e2e deferred from #90: a real agent Node runs through execute_run, the run then parks at a downstream human_gate, and `caw report` surfaces the parked run, the succeeded agent node, and the awaiting gate from persisted State in JSON and Markdown. Confirms the Reporter renders parked/awaiting without change (an awaiting gate is not a failure: its `error` is None), end to end with a real agent CLI. Slice 7 of #10 (Wave C).
$review: Standards and Spec ReviewFixed point: StandardsNo Standards findings. The changed files follow the repo's Human Gate / Await vocabulary, keep the executor and CLI changes on the existing seams, preserve the status/event ownership pattern, and match the surrounding seam/e2e test style. Spec
Verification
Verdict: request changes for the TTY multi-gate decline path. |
code-review-and-quality reviewFindings
Other Axes
Verification
Verdict: request changes. |
… rest (#10 review) Addresses the #123 review (two reviewers, one correctness finding): a declined TTY gate could be lost when several gates are awaiting. `_drive_tty_gates` prompted every awaiting gate first, then resumed only after the loop -- so answering `n` to the first of two parallel gates and then EOF/aborting at the second exited "Aborted" with the run still `parked`, both gates `awaiting`, and no gate_rejected written. Because ANY rejection ends the run (ADR 0010), the FIRST decline now commits immediately and stops prompting: the later gates' decisions can no longer matter, so no subsequent prompt or abort can drop a recorded decline. Approvals still batch until the pass approves every gate. Also completes the module exit-code contract for the gate semantics (parked = 0, rejected = 1). Regression test: two parallel TTY gates with input `n\n` ends the run rejected and persists a gate_rejected event (one answer suffices -- the second gate is never prompted).
|
Thanks — the TTY multi-gate decline finding was a real correctness bug. Fixed in 4d87481. Root cause confirmed. Fix. Because any rejection ends the run (ADR 0010), the first decline now commits immediately and stops prompting — the later gates' decisions can no longer matter, so no subsequent prompt or abort can drop a recorded decline. Approvals still batch until the pass approves every gate (an approval being deferred is benign: the gate stays awaiting and is resumable). Regression test (as requested): Also completed the module-level exit-code contract for the gate semantics (parked = 0, rejected = 1), which had drifted. Gates: |
Final wave of the Human Gate (#10), per ADR 0010, on top of Wave A (#120) and Wave B (#122). With this wave, every #10 acceptance criterion is met.
What's in this wave
Slice 5 — multi-gate resume + re-park event dedup (
feat(executor))awaitingacross a resume (unapproved) is a continuation, not a new park: the scheduler is seeded withawaiting_seed(gates already awaiting from the prior run) and re-parks them silently, sogate_awaitingis emitted once on first park, not re-emitted every resume. A gate newly reached during a resume still emits on its first park.Slice 6 — inline TTY confirmation (
feat(cli))caw runprompts at each awaiting gate inline (showing the gate'sprompt): yes approves and the run continues, no rejects it and ends the run. The CLI loops run → prompt → resume until a terminal. In a non-TTY session the run parks forcaw resume(unchanged) — pure CLI orchestration over the existing park/approve/reject primitives, the executor untouched.Slice 7 — real parked-run e2e (
test(e2e), deferred from #90)execute_run, the run parks at a downstreamhuman_gate, andcaw reportsurfaces the parked run + succeeded agent node + awaiting gate from persisted State (JSON + Markdown). Confirms the Reporter renders parked/awaiting with no change (an awaiting gate is not a failure: itserroris None).Tests / gates
gate_awaiting; TTY approve → success; TTY decline → rejected (exit 1); real-agent parked-run report e2e.ruff check+ruff format --check+mypy --strictclean (61 files), 504 non-e2e pass, and the new parked-run e2e passes withCAW_E2E_AGENT=claude.#10 acceptance criteria — all met
caw resume --approveflips to succeeded and resumes (Wave B)caw resume --rejectends the run as rejected, not resumable (Wave B)gate_awaiting/gate_approved/gate_rejectedEventscaw reportit (markdown + json), asserting parked + awaiting (Wave C)Closes #10.
🤖 Generated with Claude Code