skribbl
productpricingfree!questionswriting
download
guides · 7 August 2026 · 9 min read

Using a second agent to review the first one has written

Why a cold second model catches what the author cannot, and what it still misses.

skribbl/writing/guides
the cheapest quality step there is

An agent reviewing its own diff agrees with itself. A different model reading the same diff cold does not, and it is the cheapest quality step available: review is mostly input tokens, and input is the cheap end of every rate card. How to wire a reviewer, what to put in its prompt, and what it reliably misses.

why an agent reviewing its own diff agrees with itself

Ask an agent to review the code it just wrote and it will mostly tell you the code is fine. Not through vanity. Through context. Everything that led to the current diff is still in its window: the reasoning that chose this approach, the constraint it decided was acceptable, the shortcut it decided was deliberate. Asked to review, it re-reads its own justification and finds it persuasive, which is unsurprising, because it wrote it.

A second session reading the same diff gets none of that. It sees the code and no story. So the questions it asks are the questions a reader asks rather than the ones a writer asks: why is this function called twice, what happens when this list is empty, where is the caller that made this parameter optional. Some of those have good answers the author knows. That is fine, and it is the point. You want the questions surfaced, not pre-answered.

We have not benchmarked this and will not pretend to a number. The argument is mechanical rather than measured: the reviewer's value comes entirely from what it does not know, so anything that leaks the author's reasoning into the reviewer reduces the value. Which gives you the one design rule that matters more than every other decision in this post.

Show the reviewer the diff, not the transcript. Pasting the author's conversation into the reviewer is the single most common way people build this and it defeats the mechanism. You have recreated self-review with extra steps and a second bill.

A different model helps on top of a different session, for a separate reason. Models have correlated blind spots within a family and less correlated ones across families. If the author and the reviewer are the same model, the reviewer is blind to roughly the class of thing the author was blind to. Using a different vendor breaks that correlation, which is also the practical argument for keeping more than one CLI installed. If you only have one, use it anyway in a fresh session. Most of the benefit is the reset.

why this is the cheapest quality step available

Review is cheap for a reason worth understanding, because understanding it tells you where else the same trick applies.

Writing code is expensive in the way that costs most. The author reads a large amount of the repository, holds it, and then writes a large amount of output: implementation, tests, explanations, revisions after each thing it tried did not compile. Reviewing is the opposite shape. The reviewer reads one diff and some context, and writes a short list. Its output is measured in a page of text.

On every published rate card we have read, output tokens are priced several times higher than input tokens. So a step whose output is a page is structurally cheap regardless of which vendor you use, and it stays cheap when prices move, because the ratio is the durable part. The exact arithmetic, with the cache mechanics that dominate a real session, is in what agents cost per month and the cost of a context window. Both of those are measurement posts and own their numbers; this one deliberately does not.

THE AUTHORReads much of the repository, writes implementation, tests and prose. Output-heavy, and the output is the expensive kind of token.
THE REVIEWERReads one diff plus a little context, writes a short list. Input-heavy, and input is the cheap end of every rate card.
THE DIFFERENCEA quality step that costs a fraction of the work it is checking. Which is unusual: most quality steps in software cost more than the work, which is why they get skipped.

One honest caveat on the cheapness. If you are on a subscription rather than paying per token, the reviewer is not free, it consumes the same usage window as everything else. On a long day with several agents running, a reviewer on every commit is a real fraction of your allowance, and the way you find out is a rate limit at the wrong moment. That is the same argument as setting a token budget: the cost you cannot see is the one that surprises you.

two ways to wire it

As a hook

The hook version is automatic and unmissable, which is its whole advantage. You do not decide to run the reviewer, and therefore you cannot skip it on the afternoon when you are tired and it would have mattered most.

The natural attachment point is Stop, which fires when the agent has finished responding. The hook takes the diff from the working directory, sends it to a second model non-interactively, and writes the result somewhere you will look. The full mechanics of hook configuration, exit codes and payloads are in the hooks guide; the part that matters here is the shape:

#!/bin/sh
# .claude/hooks/review.sh - registered on Stop.
set +e

diff=$(git diff HEAD 2>/dev/null)
[ -n "$diff" ] || exit 0

# Fresh session, no memory of how the code got written.
printf '%s' "$diff" | claude -p "$(cat "$CLAUDE_PROJECT_DIR/.claude/review-prompt.md")" \
  > "$CLAUDE_PROJECT_DIR/.review.md" 2>/dev/null

exit 0

Note the two guards and the exit 0 at the end. A reviewer hook that exits non-zero on a blocking event can stop the agent it is supposed to be checking, and a review step is emphatically not worth wedging a session over. The hooks guide has the general form of that rule; this is one instance of it.

A tempting variant is to make the review blocking: refuse to let the turn end until the reviewer is happy. Do not, at least not at first. You have built a loop between two models with no human in it, and its failure mode is expensive in tokens and invisible until you read the bill. If you want the reviewer to be able to send work back, put a cap on the number of rounds and read the transcript for the first week.

The failure this prevents: reviewing only the diffs you suspected were risky. Those are not the diffs where the bug is; the bug is in the one that looked routine.

As a second agent

The second-agent version is a terminal, in a directory, with a person deciding when to run it. It is less automatic and considerably more flexible, because you can talk back to it.

# in a directory that is not the author's worktree
cd ~/code/myapp
git diff main...feat/checkout > /tmp/review.diff

codex "Read /tmp/review.diff. You did not write it and have no context on
       why it was written this way. Review it against the rules in REVIEW.md."

Two details make this work rather than merely happen. Run the reviewer in a different directory from the author, ideally your main checkout rather than the agent's worktree, so it cannot accidentally edit the code it is reviewing and so a stray build does not fight the author's. And give it a branch diff, not a working-tree diff: main...feat/checkout is the whole piece of work, which is the unit a human would review, whereas git diff is whatever happens to be uncommitted right now.

The reviewer should not be allowed to fix what it finds. It is not being asked to write code, it is being asked to produce a list you decide on. A reviewer that also edits is an author, and now nobody has read that diff.

what goes in the reviewer's prompt

This is where the technique lives or dies, and it is worth committing the prompt to the repository as a file rather than retyping it. A committed REVIEW.md can be improved every time a reviewer misses something, which is how it gets good, and it applies to every agent that reads it.

Five things it must say

  • That the reviewer did not write this. One sentence, stated plainly: you have no context on why these choices were made, do not assume there was a good reason. Without it, a model asked to review will manufacture a charitable justification for anything odd, because that is what reading code usually requires.
  • What this repository's rules actually are. Not general good practice. The specific things that are true here: errors are returned not thrown, this directory is generated, that module is deprecated, migrations are append-only. A reviewer without these reports style opinions, because style opinions are all it has.
  • The severity ladder, with what belongs on each rung. Correctness bug, security issue, data loss, performance cliff, missing test, style. Naming the ladder is what lets you ask for the top of it.
  • The output format, exactly. File, line, severity, one sentence on the consequence, one sentence on the fix. A format with a consequence field forces the model to say why the finding matters, and findings that cannot fill that field are the ones you wanted dropped.
  • Permission to say nothing. An explicit line: if the diff is fine, say "no findings" and stop. Models are strongly disposed to produce output when asked for output, and this is the sentence that gives an empty answer a name.
# REVIEW.md

You are reviewing a diff you did not write. You have no context on why any
choice was made. Do not assume there was a good reason.

## This repository
- Errors are returned, never thrown, outside of src/bin/.
- src/db/schema.generated.ts is generated. Never suggest editing it.
- Anything under migrations/ that already exists has been applied and is history.
- All money is integer minor units. A float touching a currency is a bug.

## Report only these, in this order
1. Correctness: it does not do what the surrounding code implies it does.
2. Security: injection, secrets in source, authorisation missing on a new path.
3. Data loss: a delete, a truncate, a migration that drops or rewrites.
4. Concurrency: a race, an unawaited promise, a lock held across an await.
5. A new code path with no test.

## Do not report
Formatting, naming, import order, comment style, "consider extracting",
"could be more idiomatic", or anything the formatter or linter already owns.

## Output
At most five findings, most severe first. Each one:
  file:line - SEVERITY - what breaks, in one sentence
            - the smallest fix, in one sentence
If nothing meets the bar above, output exactly: no findings

The failure this prevents: a review that is technically correct and operationally useless, because it spent its attention on things a linter already handles and never got to the unawaited promise.

stopping the forty nitpicks

The first reviewer anyone builds returns forty findings, of which two matter. This is not a model failure, it is a specification failure: asked to review, with no bar and no budget, a model reports everything it noticed, and it noticed everything.

Four things fix it, and they compound.

  • Cap the count. "At most five findings, most severe first." The cap does the work indirectly: it forces a ranking, and ranking is the step that discards the nitpicks. Asking for "only important findings" does not work, because important is not a bar the model can apply without a comparison.
  • Write the exclusion list. Naming what not to report is more effective than describing what to report, because it is checkable. "Do not report formatting" is a rule with an obvious violation; "report significant issues" is not.
  • Require a consequence. Every finding must say what breaks. A nitpick cannot fill that field without becoming visibly silly, and the model will drop it rather than write "consequence: the import order is unconventional".
  • Delete the categories your tooling already owns. If the formatter runs on every edit, formatting cannot be a finding. If the type checker runs in CI, type errors cannot be a finding. Every category you can move to a deterministic tool is a category the reviewer's attention no longer spends, and deterministic tools are better at those categories anyway.

There is a fifth, subtler one. Give it a smaller diff. Review quality falls off with diff size for the same reason human review quality does: a thousand-line diff gets skimmed, by anybody. If your agents are producing diffs too large to review properly, the reviewer is not the thing to fix. That is a signal about how the work was split, which is the subject of running agents in parallel.

what it reliably misses

This is the section that decides whether you use the technique correctly, so it is the one worth reading twice. A reviewer that sees a diff can only reason about the diff. Everything below is a category it will miss consistently, not occasionally, and no prompt fixes any of them.

PRODUCT INTENTIt cannot tell you the feature is not what was asked for. The code can be correct, tested, elegant, and solve the wrong problem, and the reviewer will approve it warmly. Only a person who knows what was wanted catches this.
ANYTHING OUTSIDE THE DIFFA helper that already exists three directories away, a convention held in two other files, a caller that will now break. The reviewer sees changed lines. It does not know what it was not shown.
PRODUCTION REALITYThat the table has forty million rows and this migration takes a lock. That this endpoint is called in a loop by a mobile client. Facts about your running system are not in the source, so they are not in the review.
CROSS-BRANCH INTERACTIONTwo diffs each correct alone and wrong together. A reviewer looking at one branch is structurally unable to see this, and it is the failure mode that gets more likely with every extra agent you run.
THE MISSING THINGIt reviews what is there. Absent error handling on a path nobody wrote, a case in an enum nobody handled, a test file that was never created: absence is much harder to notice than presence, for models and people alike.
SEVERITY IN YOUR CONTEXTIt can say a value might be null. It cannot say whether that null reaches a billing path or a debug log. Severity is a property of your system, not of the code.

There is also a failure mode of the reviewer itself, and it is the one to watch for in the first fortnight: confident wrongness about the code it was not shown. Asked to review a diff that calls a function defined elsewhere, a reviewer will sometimes reason about what that function probably does and report a bug in the interaction. The reasoning is fluent, the finding is specific, and it is about a function that does not behave that way. Give the reviewer read access to the repository if you can, which reduces this substantially, and treat any finding about code outside the diff as a question rather than a fact.

The correct summary is narrower than the pitch. A second model is a good check on the code and no check at all on the decision. It replaces the read-through where you would have caught the unawaited promise. It does not replace you deciding whether this was the right change, which is the part that was always yours and remains the ceiling on how many agents one person can actually run.

where it sits in a day

The order that works, and the reason for the order, is that each step is cheaper than the one after it and therefore should fail first.

  • Formatter and linter, on every edit. Deterministic, instant, free. Runs as a PostToolUse hook so the model never spends a token on it.
  • Type checker and tests, before the review. If the branch does not compile, the reviewer is reading code that cannot run, and it will spend its five findings on consequences of the thing the compiler already told you.
  • The second model, on the branch diff. Once per piece of work, not once per commit. A piece of work is the unit a human reviews and it is the unit that has a coherent intent to check against.
  • You, reading the diff. Last, and not optional. The reviewer's output is a list of places to look first, which is genuinely valuable and is not the same thing as having looked.

One habit worth adopting: read the reviewer's findings before you read the diff, then read the diff anyway. Findings first tells you where the attention should go. Reading the diff anyway is what stops the reviewer's silence from becoming your approval, which is the way this technique quietly stops working after about a month of it being right.

common questions

Can AI review its own code?

Badly. An agent asked to review a diff it just wrote is being asked to contradict its own reasoning while that reasoning is still in its context, and it mostly agrees with itself. A separate session that sees only the diff and not the conversation that produced it gives usefully different answers, and a different model gives more different answers again. The practical rule is that anything which leaks the author's reasoning into the reviewer, including pasting the transcript, reduces the value of the review.

Why is AI code review cheap compared to writing code?

Because review is nearly all input tokens and almost no output tokens. The reviewer reads a diff and writes a short list, where the author read much of the repository and wrote a large amount of code, tests and explanation. On every published rate card we have read, output tokens are priced several times higher than input, so a step that produces few output tokens is inherently cheap. On a subscription rather than per-token billing it is not free, though: it draws on the same usage window as everything else.

Should the reviewer be a different model from the author?

A different session is what matters most, and a different model helps on top of that. The gain from a fresh session is that the reviewer has no memory of the reasoning that produced the code, so it asks a reader's questions rather than re-reading a writer's justification. The gain from a different model is that its blind spots are not correlated with the author model's. If you only have one CLI installed, use it in a fresh session anyway; most of the benefit is the reset.

How do I stop an AI reviewer producing forty nitpicks?

Cap the output count in the prompt, give it a written list of what not to comment on, and require every finding to carry a file, a line and a concrete consequence. A reviewer told to return at most five findings ranked by severity has to rank, and ranking is the step that discards the nitpicks. Move every category your formatter, linter or type checker already owns out of the reviewer's scope entirely, because deterministic tools are better at those categories and cheaper.

What does an AI code reviewer miss?

Anything requiring knowledge outside the diff. It cannot tell you the feature was not what the customer asked for, that the code duplicates something in a file it was not shown, that a migration will lock a table because it does not know the table size, or that a change is fine in isolation and wrong in combination with a change on another branch. It also struggles with absence: a missing error path or an unwritten test is much harder to notice than a wrong line. It is a check on the code, not a check on the decision.

one implementation

We build a macOS app called Skribbl that puts agents and their terminals on one canvas, and the reviewer pattern is the one it was shaped around: a second agent, in its own node, that can be handed work by the first one only if you have drawn a line between them. Cutting the line revokes it. That is our answer to the authority question rather than a claim that it is the only one, and it is described honestly against the alternatives on the comparison page.

None of this post needs it. A reviewer is a prompt file, a diff and a second terminal, and every command above runs on a plain machine. If you want the wiring rather than the argument, the hooks guide has the hook mechanics in full, the docs describe the app, and the download page has the build. If you are running one agent at a time, a second model on the diff is still the first upgrade worth making, and it costs nothing to try on the next branch you finish.

READ NEXT
Claude Code hooks: a practical guide with examplesEvery event, the payload it carries, and the exit code that blocks a tool call.12 minHow to run AI coding agents in parallelHow to split the work, and the honest ceiling on how many you can review.10 minWhat AI coding agents cost per month, with the arithmeticThe per-turn arithmetic, and the point where a subscription stops being cheaper.11 min
ON THIS PAGE
why self-review does not workwhy review is the cheap steptwo ways to wire itwhat goes in the promptstopping the forty nitpickswhat it reliably misseswhere it sits in a daycommon questionsone implementation
run them on a canvasSkribbl puts every agent, its terminal and what it is spending on one board. macOS, one day free.

get the next one by email.

One email when there is something worth reading. Unsubscribe is one click and it is in every issue.

get me
productpricingdocsquestionswhat it iscomparereleaseswritingnewsletterlaunchesprivacycancel
give them infinity.© skribbl