Form 4 made fully concrete: the actual files on disk, then the complete session dialogue — worker implements, grader fails it, worker fixes, grader passes it, loop closes.
Two roles, deliberately separated:
The worker — your main Claude Code conversation. It reads the task, writes the code, runs the tests. It is also, left alone, the one who decides "looks done" — which is exactly the conflict of interest we're removing.
The grader — a subagent defined in .claude/agents/diff-grader.md. Per the docs, it runs in "its own context window with a custom system prompt" and "does not see your conversation history… or the files Claude has already read." (Sub-agents) It can't be swayed by the worker's reasoning because it never sees it. The only bridge between them is a short delegation message the worker composes when handing off.
A small real project. Three files matter for the verification loop (highlighted); the rest is ordinary code.
# Task: parseDuration Add parseDuration(str) to src/duration.js. Parses "1h30m" → milliseconds. ## Acceptance criteria 1. Supports units h, m, s — alone and in combination ("2h", "1h30m", "1h30m15s"). 2. Returns milliseconds as an integer. 3. Invalid input throws an Error that names the offending token AND lists the valid units (actionable, not "invalid input"). 4. New behavior is covered by tests; all tests pass.
--- name: diff-grader description: Grades the current diff against TASK.md acceptance criteria. Use proactively after implementing a task, before declaring work done. tools: Read, Grep, Glob, Bash --- You are an adversarial grader. You did NOT write this code and you do not trust the implementer's claims. Your job is to try to FAIL the diff. Procedure: 1. Run `git diff` to see the change. Read TASK.md for the criteria. 2. Gather your own evidence — run the code and tests yourself. Never take the diff's word for behavior you can execute. 3. For EACH acceptance criterion: write a 1–2 sentence critique, then grade PASS / FAIL / UNKNOWN (UNKNOWN = not enough info to decide). Report ONLY correctness and requirement gaps — no style or refactor notes. End with exactly one line: VERDICT: PASS (only if every criterion is PASS) VERDICT: FAIL — <failing criteria numbers and one-line reasons>
## Definition of done
After implementing any task, delegate to the diff-grader subagent.
Do NOT declare work complete until it returns VERDICT: PASS.
Quote its verdict in your summary.
CLAUDE.md makes grading non-optional (the gate). The description field tells Claude when to delegate — "Claude uses each subagent's description to decide when to delegate." The markdown body is the rubric — everything from Lesson 2: binary, decomposed, critique-then-verdict, UNKNOWN escape hatch, bounded to correctness only.What follows is an annotated session. Numbered notes ⬇ point at the load-bearing moments.
diff-grader before declaring done.Task(...) line) is the only thing the grader receives from this conversation — a one-sentence task summary. Not the chat history, not the worker's reasoning, not its claims about test coverage. (Docs: "Claude composes a delegation message that summarizes the task.")The worker's 5 tests all pass. The grader is about to grade the four criteria. What does it find? (Hint: look at that regex — /(\d+)([hms])/g — and ask what happens to input like "1h30x".)
parseDuration("1h30x"): it returned 3600000 — the global regex skipped the unmatched token "30x" and silently half-parsed the input. And when it does throw (no matches at all), the message is "Invalid duration", which names neither the offending token nor the valid units. FAILparseDuration("1h30x") itself. This is "show evidence, don't assert success" applied to the judge. The worker's blind spot (it wrote the tests around its own regex) is exactly what a fresh context catches.VERDICT: FAIL — … is one line, fixed format. That's what lets the loop act on it mechanically instead of interpreting an essay.diff-grader.md)git diff, TASK.md, its own test runsThat right-hand column is the point. Independence isn't a vibe — it's enforced by the context architecture. (Sub-agents docs: "fresh, isolated context window… does not see your conversation history.")
In this example the gate is a CLAUDE.md rule — strong, but advisory. Escalation path when the stakes rise:
CLAUDE.md rule (what you saw) → a /grade slash command so you can demand a grade at any moment → a Stop hook that runs a script checking for VERDICT: PASS and blocks the turn from ending without it — deterministic, "for actions that must happen every time with zero exceptions." (Hooks) That Stop-hook version is a great Lesson 4.
TASK.md, diff-grader.md, the buggy-regex starting point — as a tiny repo on your machine so you can run the loop yourself and watch the grader fail it live. Or I'll adapt diff-grader.md to one of your actual projects.