feat(#10): Wave B — approve / reject a parked gate via caw resume - #122
Conversation
`caw resume <run-id> --approve <node-id>` (repeatable) advances a parked run:
each named gate must be awaiting, is flipped awaiting -> succeeded with an
`{"approved": true}` output and a gate_approved event, and the existing
seed-satisfied resume path then unlocks its dependents. Approving a node that is
not an awaiting gate is a config-class refusal (exit 2); a gate left unnamed
re-parks.
Slice 3 of #10 (Wave B). Rejection follows.
`caw resume <run-id> --reject <node-id>` (repeatable) ends a parked run: each named gate must be awaiting and is driven to `rejected`, the run is recorded `rejected` (a decided terminal refused by Resume Eligibility), and the gated downstream never runs. Any rejection ends the run, so a co-named --approve does not save it. `caw resume --reject` exits 1 (a non-success terminal); RunResult carries rejected_node_ids and reports `rejected`. Slice 4 of #10 (Wave B).
$review: Standards and Spec ReviewReviewed PR #122 against Standards
Spec
Verification run:
Summary: 1 Standards hard violation, 1 Standards judgement call, 0 Spec findings. Worst issue: the public/internal Resume wording is now stale relative to parked/rejected Human Gate semantics. |
code-review-and-quality reviewFindings first:
What looks good:
Validation:
Overall: request changes. |
…review) Addresses the #122 review (two reviewers): - Dedupe duplicate decision ids before any State write. A repeated `--approve gate --approve gate` previously validated both against the pre-mutation `awaiting` status, then wrote the first approval and crashed on the second `record_attempt`'s attempt PK (sqlite IntegrityError -> exit 3) with State half-mutated. Duplicate `--approve`/`--reject` now collapse to one (order-preserving), so a repeat is idempotent -- one approval, one gate_rejected event, one rejected_node_ids entry. - Refresh the stale Resume wording. The `resume` command and `resume_run` docstrings said only "interrupted or failed / re-run incomplete nodes"; they now name parked human-gate advancement and rejected-run refusal, matching CONTEXT.md Resume Eligibility and ADR 0010. - Document the reject early-finalization as the narrow, deliberate exception: a rejected parked run has no nodes to execute or tear down, so it finalizes directly to `rejected` rather than routing through `_drive_scheduler` / `_finalize_crashed_run`. Tests: duplicate `--approve` and `--reject` are idempotent; a co-named `--approve`/`--reject` for one gate ends the run as rejected (reject dominates).
|
Thanks — addressed in a381384 (TDD for the bug, doc/comment for the rest). [Required] Duplicate decision flags crashed non-atomically. Confirmed and fixed — this was the real bug. [Hard] Stale Resume wording. Fixed. The [Judgement] Reject early-finalization branch. Took the "document the narrow exception" option you offered. The reject branch now carries a comment explaining it is the deliberate narrow exception: a rejected parked run has no nodes to execute and none in flight to tear down, so it finalizes directly to [Coverage] Mixed Gates after the fix: |
Second of three waves for the Human Gate (#10), per ADR 0010, on top of Wave A (#120, merged). This wave advances or ends a parked run from the CLI. Multi-gate / TTY / parked-run e2e are Wave C.
What's in this wave
Slice 3 — approve (
feat(executor))caw resume <run-id> --approve <node-id>(repeatable) advances a parked run: each named gate must beawaiting, is flippedawaiting → succeededwith an{"approved": true}output and agate_approvedevent, and the existing seed-satisfied resume path (Failure semantics: retries, timeouts, cancellation, and resume #6) then unlocks its dependents — no new scheduler re-entry.Slice 4 — reject (
feat(executor))caw resume <run-id> --reject <node-id>(repeatable) ends a parked run: each named gate is driven torejected, the run is recordedrejected(a decided terminal, refused by Resume Eligibility —is_resumable("rejected") == False), and the gated downstream never runs. Rejection takes the deliberate-termination path, NOT the failed-dependency skip rule (ADR 0010 as revised in docs(adr): ADR 0010 — await parking and the human gate (#10 design alignment) #117).--approvedoes not save it (validation still requires every named node to be an awaiting gate).caw resume --rejectexits 1 (a non-success terminal).RunResultnow carriesrejected_node_idsand reportsrejected;succeededisFalsefor both parked and rejected runs.Tests / gates
succeeded,gate_approved,{"approved": true}); reject ends the run (gate + runrejected, downstream unreached, not resumable,gate_rejected).resume --approve→ exit 0 + downstream runs;resume --reject→ exit 1 + rejected, no downstream.ruff check+ruff format --check+mypy --strictclean (60 files), 497 non-e2e pass, real-agent e2e sanity (graph run + CLI) 6 pass.Acceptance criteria progressed (#10)
caw resume <run-id> --approve <node-id>flips the gate tosucceededand resumes; downstream executescaw resume <run-id> --reject <node-id>drives the gate and run torejectedand ends the run; not resumablegate_approved/gate_rejectedEvents🤖 Generated with Claude Code