Your AI coding agent just said "Done ✅".
Who checked? The same agent that wrote the bug.
This is the structural blind spot of agentic coding, and no amount of "please double-check your work" in the prompt fixes it. An LLM that just wrote the code is the worst-positioned reviewer of it — it shares the exact context, assumptions, and blind spots that produced any bug. A self-graded "done" is a claim, not evidence.
So I stopped letting my agent grade its own homework. Here's the gate I built into Claude Code, and the portable pattern underneath it.
The pattern
claim → independent re-derivation → human ratification → automated trigger
Four parts, each closing a specific failure mode. Nothing here is Claude-specific — it's a template for any agent that reports its own success.
1. Independence beats "check harder"
The fix isn't a better prompt telling the agent to re-read its diff. It's an independence boundary: a fresh, adversarial subagent — explicitly told to disprove the claim — re-verifies from scratch against live running state. Not the diff. Not the plan the author already believed. The actual output of the actual system, right now.
I ran this on a real refactor. The verifier re-ran the tests itself, re-checked the commits were pushed, grepped for dead code the main agent claimed to have removed, and returned a structured verdict:
VERDICT: 6/6 passed
Different context catches what the author is constitutionally blind to. A second cheap pass beats the original agent trying harder — because "harder" still runs through the same blind spot.
2. A verdict is not a decision
The machine verifies; the human ratifies. The verdict surfaces through a sign-off gate with three explicit paths:
- ✅ accept — close it out
- 🔍 show-evidence — re-run a specific check live, in front of me, first
- ❌ reject — treat as a bug report
"Show-evidence" is the one that matters. It turns the operator from a rubber-stamp on a green checkmark into someone the agent has to defend the claim to with artifacts. In Claude Code this is the AskUserQuestion tool; in your stack it's whatever forces a human to say yes on the record.
3. Automate the trigger, or it never fires
A verification step you have to remember to run is a verification step you skip under deadline. So the whole gate is triggered by a Stop hook that fires after the agent edits files:
# verify-done-nudge.py — a Claude Code Stop hook
if payload.get("stop_hook_active"):
return # LOOP GUARD: this Stop follows our own block — let it through
# ... only fires if the turn used Edit/Write/MultiEdit
# ... and stays silent if recent text already shows verify/tests/PASS/pushed
print(json.dumps({
"decision": "block",
"reason": "You edited files but haven't verified. Run /verify-done "
"(independent re-check + sign-off) before declaring complete."
}))
Automate the trigger, not the judgment. That's the difference between a gate and a good intention.
4. The engineering that makes it safe
The credibility (and the interesting part) is in the guards. This hook runs on every Stop event, so it has to be bulletproof:
-
Loop guard. A Stop hook that re-invokes the agent could recurse forever. Claude Code sets
stop_hook_active = trueon the Stop that follows your own block — check it and bail, so the gate fires at most once per work-cycle. - Fail-open. Any error — missing transcript, malformed JSON, anything — returns cleanly and lets the session end. A broken verifier should never block you from finishing work. It degrades to the old behavior; it doesn't brick your session.
- No LLM in the hook. Pure shell, no network, no model call — just a bounded tail-read of the transcript. A hook that calls an LLM bills silently on every turn and can time out. The hook decides whether to nudge; the agent does the thinking.
Where it fits in a workflow
One subtlety if you orchestrate multi-agent workflows: the verify phase can run inside a background workflow (fan out several adversarial verifiers in parallel, return a verdict), but the confirm phase can't — an interactive sign-off has to happen on the main thread. So: the workflow verifies, the main thread confirms with the returned verdict. Keep the human ratification out of the background.
Steal the pattern
claim → independent re-derivation → human ratify → automated trigger
If your agent can say "done," it can say it too soon. Give it a hostile second opinion, make a human ratify, and wire the trigger so the check can't be skipped when it's inconvenient.
Separation of powers — applied to a single agent's workflow.
Top comments (1)
Running a similar gate taught me two things the hard way. First: feed the verifier the original request, not the author's summary. A verifier that reads the implementer's plan inherits its framing, and the point of the fresh context is to inherit nothing - I have watched an adversarial subagent wave a change through because its input was already the author's story about what happened. Second: fail-open needs a canary. When the transcript format or hook payload changes shape, a fail-open gate degrades to no gate, and nothing announces that. Logging every abstain and alerting when the gate has not actually fired across N sessions that contained edits catches it. Fail-open is the right call for the user; unmonitored fail-open is how gates quietly die.