ADR 0002: Approval-gated tasks never auto-promote to dispatch¶
Status¶
Accepted
Context¶
Not every task on the board can be graded by a deterministic checker. Some
carry verify_path = 'approval' because the acceptance criterion is a human
judgment call (irreversible action, aesthetic call, a decision only Aaron can
make) rather than something CI can assert. internal/hub/task_lint.go
already names the two closing paths explicitly: a leaf needs either a pr
evidence row or an approval evidence row before it can move to done, or
it "will silently stall -- it can never reach
verified" (task_lint.go:3-4).
The overnight build runner (ARGUS-228) polls a next-task endpoint and
builds whatever it returns, unattended, with no human in the loop mid-run
(overnight-build skill: "you are HEADLESS: there is no human to grant
anything mid-run"). If an approval-gated task were ever eligible for that
endpoint, an agent would either fabricate the human decision to unblock
itself or stall the run waiting on an answer that structurally cannot
arrive -- both outcomes observed and logged as failures (ARGUS-602: runs
that ask for permission mid-run and wait are recorded failed/no_progress).
Decision¶
The task-selection query the hub-agent next-task endpoint runs excludes
verify_path = 'approval' tasks unconditionally, alongside backlog-lifecycle
and blocked tasks (internal/hub/next_task_test.go:16,61-70 pins this:
"human-only (verify=approval) ... tasks are excluded"). Approval-gated work
is never returned to an autonomous dispatch loop, full stop -- there is no
override, no "only if idle," no confidence threshold. The exclusion is
structural (a query predicate), not a judgment call the agent makes at pick
time, so it cannot be reasoned around by an agent convinced this particular
approval is obviously fine.
Consequences¶
- A human must move approval-gated tasks themselves (or record the approval evidence row) before they can close; they simply never appear as overnight-build candidates.
- The overnight loop can run genuinely unattended without a "wait for permission" failure mode, because the class of task that would trigger it is filtered out before an agent ever sees it.
- Mis-tagging a task's
verify_pathasprwhen it actually needs a human call is now the only way this gate can be bypassed -- that risk is owned by whoever files the task, not by the dispatch loop.