Ask a session to review the code it just wrote and it will defend it. It is still holding every decision it made, including the wrong ones, and each of those decisions came with a reason that was persuasive at the time. A review run from inside that context reads the code the way its author meant it, which is the one reading that cannot find the mistake.
People do the same thing. An engineer rereading their own pull request at the end of the day sees what they intended to write. The fix for people is old and well understood: someone else reviews it. The fix for agents is the same, and it is cheaper to apply, because a fresh session costs almost nothing to start.
Why the writer defends
A writing session accumulates context as it works. It reads the ticket, explores the code, tries an approach, hits an error, adjusts. By the end, its context holds a story about why the code looks the way it does. Asked to review, it checks the code against that story, and the code matches, because the story produced it.
That is why self-review finds the shallow problems and misses the deep ones. A typo, a missing import or an unused variable shows up regardless of context. A requirement that was misread at the start does not, because the misreading is part of the story. The session will explain, accurately and in detail, why the code does what it was told. It was told the wrong thing.
Review runs in a session that did not write it
So review runs as a separate session, set up so that it cannot inherit the writer's story. It receives 3 things:
- The specification the change was meant to satisfy
- The change itself, as a diff against the main branch
- The acceptance criteria, in the checkable form agreed before work started
It receives nothing about how the code came to be. No transcript of the writing session, no summary of the approach, no explanation of the trade-offs. Those are exactly the inputs that would let it agree with the writer.
The reviewer is asked a narrow question. Does this change satisfy the specification and the criteria, and does it fit the codebase it lands in? Style comments are left to the formatter and the linter, which do that job without opinions.
What a fresh session finds
Of the defects caught before merge on our own projects, 1 in 5 was found only by that separate pass. The writing session had already passed it. The proportion matters less than the kind of defect, because the kind decides what the defect would have cost later.1 in 5defects caught before merge that only the separate review session found
What a fresh session finds tends to fall into 3 groups:
- A requirement quietly violated, where the code does something reasonable that the specification ruled out
- An edge case named in the specification and never handled, usually the second or third case in a list
- A pattern invented where the codebase already had one, such as a new retry helper written beside an existing one
Formatting problems barely appear. The writing session handles those itself.
What the defects cost later
Each of those 3 groups is cheap to fix in review and expensive almost anywhere else. A violated requirement found in review is a small edit. Found in user acceptance testing, it is a conversation about scope. Found in production, it is an incident with a customer attached.
The invented pattern is the slowest to hurt and the hardest to remove. A second retry helper looks harmless in one pull request. The next agent session sees 2 helpers and copies whichever it found first. A quarter later the codebase has 4 ways of doing the same thing, each slightly different, and nobody remembers which one is correct.
A pattern invented once gets copied. An unhandled edge case waits for a customer to find it.
The rule is older than agents
The rule applies well beyond agents. Any review where the reviewer shares the writer's context is weaker than it looks. That is why a second engineer on a pull request finds more than the author reading it again, and why a clean-context session beats both on the mechanical checks: it reads the specification and the diff with equal attention and with no memory of the shortcuts.
A person still owns the sign-off on the finished change. The review session produces findings. A named engineer and a test lead decide whether the change, with its tests, is finished in the sense they would defend. That split keeps the machine doing what it does well, which is reading closely without fatigue, and keeps the judgement with someone who can be asked about it later.
Setting it up on your team
The setup is small. Write the review instructions once and keep them in the repository beside the code, so they are versioned with it. Give the reviewer read access only. Make the review a required check before merge, with its findings attached to the pull request where the human reviewer will see them.
Then watch what it finds for a month. The first few weeks usually surface defects the team recognises immediately, and a few that start an argument about what the specification meant. Those arguments are the most useful output of the whole exercise. They show where the specification was ambiguous before anyone wrote code against it, which is the cheapest moment to settle the question.
