Parent: #583
Context
The workflow I authored needed four human decision points inside one run — approve the extracted claims, approve the adjudication, approve the fix plan, approve the commit — each one a place where a person reads an artifact and the work must not continue without them.
CAO has no primitive for this, so I hand-built it:
- marker files under
state/gates/<n>-<name>.approved;
- a
finish() helper that writes a human-readable NEXT-STEP.md (what was produced, what to read, the exact command to resume) and then exits;
SystemExit(0) for the exit — because a non-zero exit reports the run as failed, which misrepresents a deliberate, successful stop as a fault. That distinction cost a round to discover and is invisible in the artifact.
It worked well enough that I would keep the shape. But every author will rebuild it, every author will get the exit-status detail wrong once, and an operator reading cao workflow status cannot tell "stopped for a human" from "crashed".
The roadmap's approval is pre-run: resolve the plan, present it, user approves, then "run and observe". The target-experience diagram has exactly one approval, before execution.
That is the right model for a short deterministic job, and insufficient for the long-running staged workflows the roadmap explicitly wants to validate against ("a staged product-build workflow", "the existing PR review workflow"). A PR-review workflow that cannot stop for a human between adjudication and fixing is not the workflow anyone wants — the whole point is that a person adjudicates.
Proposal
Make the mid-run gate a primitive:
gate("plan-approved",
summary="7 fix clusters across 3 waves",
artifacts=["state/artifacts/plan.md"]) # blocks until approved
- A run parked at a gate has its own terminal state —
awaiting_approval, distinct from both completed and failed — surfaced in status, results, and the MCP tools.
- CAO renders the operator view: which gate, what to read, the exact resume command. (This is what I hand-wrote as
NEXT-STEP.md; the runtime knows all of it.)
cao workflow approve <run_id> <gate> / reject <run_id> <gate> [--reason], with the decision, actor, and timestamp recorded in the journal so an approval is auditable evidence rather than the presence of a file on someone's laptop.
- Resume from a gate reuses journaled work for everything before it (depends on Milestone 0).
- A rejection is a first-class outcome, not a crash.
Acceptance criteria
Parent: #583
Context
The workflow I authored needed four human decision points inside one run — approve the extracted claims, approve the adjudication, approve the fix plan, approve the commit — each one a place where a person reads an artifact and the work must not continue without them.
CAO has no primitive for this, so I hand-built it:
state/gates/<n>-<name>.approved;finish()helper that writes a human-readableNEXT-STEP.md(what was produced, what to read, the exact command to resume) and then exits;SystemExit(0)for the exit — because a non-zero exit reports the run asfailed, which misrepresents a deliberate, successful stop as a fault. That distinction cost a round to discover and is invisible in the artifact.It worked well enough that I would keep the shape. But every author will rebuild it, every author will get the exit-status detail wrong once, and an operator reading
cao workflow statuscannot tell "stopped for a human" from "crashed".Gap vs #583
The roadmap's approval is pre-run: resolve the plan, present it, user approves, then "run and observe". The target-experience diagram has exactly one approval, before execution.
That is the right model for a short deterministic job, and insufficient for the long-running staged workflows the roadmap explicitly wants to validate against ("a staged product-build workflow", "the existing PR review workflow"). A PR-review workflow that cannot stop for a human between adjudication and fixing is not the workflow anyone wants — the whole point is that a person adjudicates.
Proposal
Make the mid-run gate a primitive:
awaiting_approval, distinct from bothcompletedandfailed— surfaced instatus, results, and the MCP tools.NEXT-STEP.md; the runtime knows all of it.)cao workflow approve <run_id> <gate>/reject <run_id> <gate> [--reason], with the decision, actor, and timestamp recorded in the journal so an approval is auditable evidence rather than the presence of a file on someone's laptop.Acceptance criteria
awaiting_approval, neverfailed.statusnames the pending gate, the artifacts to read, and the resume command.