Cadora · walk-away review
A blocking gate holds the conductor process hostage to the reviewer's calendar. Walk-away review inverts that: the run parks — agents' finished work archived, process ended cleanly — your phone buzzes, you decide from wherever you are, and a later cadora resume applies the decision without re-running or re-paying a thing.
--on-review park changes what a review gate does with the process. When design reaches its gate, the run doesn't block — it lets the wave drain (the sibling test-plan finishes and records; nothing is stranded in memory), writes one self-contained park record with every pending gate, and terminates cleanly.
Exit code 75 is EX_TEMPFAIL: "waiting for a human" is not a failure, and wrappers and schedulers can tell the two apart. The park record (runs/pipeline-1/park.json) embeds the topology, the gate specs, and the execution contract — a resume depends on nothing but the archive. The manifest stays "ok": null; in flight is the truth. The laptop closes.
--notify-url is an ntfy-style webhook: one POST, the body is the message. Exactly two moments reach your pocket — a gate starts waiting for review, and the run parks. Everything else is dashboard material, not a buzz.
The webhook is fire-and-forget — a daemon thread with a short timeout that swallows every failure. A dead endpoint never delays a node, corrupts telemetry, or fails a run. The webhook is a courtesy; the archive is the record.
The dashboard binds loopback (127.0.0.1:8765) and stays there — from the phone you reach it through a tunnel, e.g. tailscale serve 8765. The run wears a parked pill, and the run page opens the ⏸ Parked triage panel: each pending gate as a card — node id, cost so far, and the changed documents as links.
Mobile is triage + decide — gates you already understand. Deep reading and annotation stay on desktop, where the full review surface lives. And the dashboard's staleness check knows the difference: parked is the one state where "untouched for days" is healthy, not a zombie.
A parked run has no live process to receive a verdict, so the dashboard stores it in the archive: runs/pipeline-1/parked-decisions.json. The binding is the point — the decision is tied to (a) that one node, (b) the SHA-256 of the exact bytes the reviewer saw, hashed at decision time, and (c) the declared identity, re-checked at resume against the recorded allowlist.
Written atomically, one entry per node. A second decision for the same gate is refused with a conflict — one gate, one stored verdict.
Headless — no reviewer present, none needed. The resume rebuilds the run from the park record, applies the stored decision, and drives the downstream nodes to completion.
What the resume does and does not repeat: the parked node's agent work is not re-run and not re-paid — $1.8342 is the same dollar, recorded once. The deterministic gate is re-checked, and the workspace fingerprint is verified (drift refused unless --allow-drift, and recorded in the evidence either way). Completed outputs reload verbatim from the archive, so downstream prompts render byte-identical to a never-parked run. And the parked downtime lands in review_wait_seconds — never in the node's signed duration_seconds.
A stored decision is honored only if it still describes reality. Edit a document after the decision and the SHA binding shows its teeth — the verdict is discarded loudly (telemetry event parked_decision_discarded) and the gate re-asks rather than certify bytes nobody reviewed.
Identity faces the same wall. A stored decision whose reviewer is not in --reviewers is rejected at resume exactly like a live impostor — including abort: an unlisted abort cannot kill a run. A rejection never re-runs the agent and never consumes a revision; the gate simply remains open.
Every stored decision is consume-once: it is taken from the file before it is judged, so a rejected verdict cannot lurk and retry forever. And the allowlist itself is not overridable at resume — the policy recorded when the run parked is the policy that governs it.
Waves can park more than one gate — two reviewed siblings park once, together, in a single record. When only some are decided by morning, resume in park mode: --on-review park applies whatever was decided and re-parks the rest.
Exit 75 again, and park.json now holds only the undecided gates. Re-parking is stable: an undecided gate parks again with nothing executed and nothing re-charged — a park is idempotent until a human actually decides.
The manifest's human_reviews carries the whole story: who decided, through which surface, over exactly which bytes — and when the human decided, not when the resume got around to applying it. review_policy records the allowlist that was in force.
Both duration_seconds and review_wait_seconds flow into the signed evidence pack — the agent's work and the human's deliberation, each honestly its own number. The pack itself is the evidence-pack journey.
The frames above reproduce real cadora run / resume output and the live dashboard's parked triage panel — rendered as editable HTML rather than raster captures, so they can be lifted into Figma or iterated directly. Walk-away review is the durable form of the human-in-the-loop gate: same decisions, same evidence, no laptop held hostage. Flags and file formats are detailed in the user manual.