Reviewing agent output
Day 24: Reviewing agent output is the core engineering skill now
Your coding agent can explain any line of its diff, confidently and in clean English. Some of those explanations were invented after the code was written. If an agent writes most of your team's code, the approve button is now where your team actually makes engineering decisions. Most of us were never taught how to review in the first place, and the habits we picked up reading colleagues' PRs break in specific, predictable ways when the author is a model.
1. The 'why did you do it this way?' moment
Here's a review exchange that has probably happened on your team this month:
Reviewer: Why did you move from a per-request DB connection
to a module-level pool in this handler?
Agent: A module-level pool avoids connection churn under load
and cuts latency on the hot path, which matters given
this endpoint's traffic profile.
Reality: Nobody mentioned load. The endpoint gets a few calls an hour.
The pool pattern was in another file the agent had in context,
and it copied it. The explanation came after the code.The answer sounds reasonable. That's exactly the problem. Load never came into it. The explanation is a story built afterwards to fit code that already existed. Turpin et al. (2023), 'Language Models Don't Always Say What They Think', showed that when a model explains its own reasoning, the explanation can leave out what actually drove the answer. Ask an agent 'why?' and you get a plausible reason. Not necessarily the real cause. Sometimes there wasn't one.
That costs more than it seems. Review was never only about reading code. Bacchelli and Bird studied code review at Microsoft (ICSE 2013) and found that developers expect review to find defects, but in practice a lot of its value comes from understanding the change and sharing knowledge across the team. When a colleague says 'I used a pool because of the Black Friday spike', you check their intent against your own picture of the system. An agent's diff doesn't come with that kind of intent, so there's nothing to check.
2. Three ways agent diffs break the review habits we have
(1) No intent. Trust the behaviour, not the explanation. When a human changes a test, there's usually a reason you can push on. An agent aiming for 'make CI green' can get there by weakening the test. Take this one: a test asserted `expect(total).toBe(107.10)` after tax. The agent's refactor broke rounding, and the new diff asserts `expect(total).toBeCloseTo(107, 0)`. Commit message: 'made assertion robust to floating-point variance'. It reads like a careful improvement. What it actually does is let the bug pass. You can't check the author's reasoning, so check what the code does.
(2) Fluent errors. The 'this looks sloppy' instinct never fires. Human mistakes tend to come with tells, like odd naming, inconsistent formatting or a leftover TODO, and experienced reviewers slow down when they see them. Agent mistakes come nicely formatted. Look at `await billingClient.invoices.finalizeWithRetry(id, { backoff: 'exponential' })`. The naming matches your internal SDK. The options object looks idiomatic. The method doesn't exist. Your SDK has `finalize()` and a separate retry wrapper. With luck, the type checker catches it. In a dynamic language, or behind an `any`, it blows up at runtime. Spracklen et al. (2024), 'We Have a Package for You!', found that code models regularly invent package names. People now call this 'slopsquatting', because attackers can register those invented names.
(3) Volume. Far more diff, same attention. Say, for illustration, your team now produces about ten times the diff it used to. Nobody's attention grew ten times. And big changes are where coupling hides. Picture a 1,400-line 'refactor notifications' PR. On line 912, a shared `formatUserName()` helper starts returning `null` instead of an empty string for users with no display name. The notifications code handles that fine. The invoice PDF generator, which nobody touched and which isn't in the diff, now prints 'Dear null'. A diff shows you what changed. It can't show you what depends on it.
3. What multi-agent research already knows, in plain language
Researchers in multi-agent systems and AI safety have worked on one question for years: when one agent produces something, how do other agents, or people, check it? Engineering teams are now rediscovering their answers under new names. Three ideas are worth knowing.
- ▹Verification asymmetry. For many problems, checking an answer is cheaper than producing it. Confirming a list is sorted is easier than sorting it. The catch is that this only holds when the checker has an independent signal, like running the code, a type system, a spec or a second source of facts. Without one, checking turns back into generating, and you've done the work twice.
- ▹Correlated error. When the same model reviews its own output, it tends to repeat its own mistakes. Both passes draw on the same knowledge and the same blind spots. Two checks that fail together are worth roughly one.
- ▹Adversarial critique and debate. A critic is most useful when it has different information or different incentives from the author. Its job is to find what's wrong. Approving isn't part of it.
Here's the evidence, and what each piece means for your PR:
- ▹Huang et al. (2023), 'Large Language Models Cannot Self-Correct Reasoning Yet'. With no external feedback, asking a model to check and revise its own reasoning didn't reliably improve answers, and sometimes made them worse. For your PR: asking the same agent 'are you sure?' is not review.
- ▹Zhou et al. (2023, arXiv 2308.07921), on code-based self-verification with GPT-4 Code Interpreter. The model was prompted to write and run code that checks its own answers. The paper reports 84.3% zero-shot accuracy on the MATH benchmark, against a previously reported 53.9%. That headline number also uses verification-weighted majority voting, so not all of the gain comes from self-checking alone. It looks like a counterexample, but it isn't one. The independent signal was code execution, not the model's opinion of itself. For your PR: self-checks work when they run something. Ask for executed evidence, not a confident summary.
- ▹Irving, Christiano and Amodei (2018), 'AI Safety via Debate', and later empirical work such as Du et al. (2023) on multi-agent debate and Khan et al. (2024), 'Debating with More Persuasive LLMs Leads to More Truthful Answers'. Two agents arguing opposite sides can help a less-informed judge reach the truth, though the results depend on the setup. For your PR: a critic told to argue that the diff is wrong is more useful than one asked to summarise it.
- ▹Gu et al. (2024, arXiv 2411.15594), 'A Survey on LLM-as-a-Judge'. Using one LLM to evaluate another's output is an established method with its own literature, and reliability, consistency and bias are still open problems. For your PR: a reviewer bot is a known technique, and it needs evaluating before you trust it.
- ▹arXiv 2504.07887, on bias elicitation with an LLM-as-a-judge. The authors compared candidate judge models to pick the most reliable one, and some jailbreak attacks worked across model families. The paper is about social bias in text, not code, so take it as an analogy only. For your PR: if your reviewer model and your generator come from the same family, they may share blind spots, which makes the reviewer less independent than it looks.
Two code-specific papers point the same way. DeepCodeSeek (arXiv 2509.25716) flags 'API leaks' in existing code-to-code benchmarks and builds a new dataset from real enterprise code to avoid them. Its retrieval method reaches 87.86% top-40 accuracy at finding the APIs a piece of code needs. Read that number the other way round: even with a purpose-built retriever, the needed API is missing from the top 40 candidates about one time in eight. Code generated in that situation can look perfectly plausible while calling the wrong thing. Separately, arXiv 2508.08322 splits generation and validation into separate agent roles and reports better reliability than a single-agent baseline. Its evidence on a large codebase is described as qualitative, so treat it as a promising pattern rather than a proven result.
4. The reframe: adversarial reading, not proofreading
Most of us review by proofreading. We read top to bottom and react to whatever looks off. That works when mistakes come with visible signs. Agent output mostly doesn't have them. The stance that works is adversarial reading: assume there's a bug, then go looking for where it would be.
- ▹Proofreading asks 'Is this well written?' Adversarial reading asks 'If this were wrong, how would it be wrong?'
- ▹Proofreading follows the diff top to bottom. Adversarial reading starts from the spec and the riskiest lines.
- ▹Proofreading takes the author's explanation as context. Adversarial reading treats it as a claim to check.
- ▹Proofreading sees clean, consistent code as a good sign. Adversarial reading sees it as neutral, because fluency costs a model nothing.
- ▹Proofreading counts green tests as evidence. Adversarial reading first asks whether the tests changed in the same diff.
- ▹Proofreading tries to cover every line. Adversarial reading picks a few paths and traces them end to end.
5. Practical techniques, as a checklist you can paste
Each item maps back to one of the research ideas above. Reviewing against the spec builds on the spec-writing days earlier in this series. Without a spec, you have nothing independent to check the diff against, and you're back to proofreading.
## Agent-authored change: review checklist
### Against the spec, not the diff
- [ ] Linked spec/ticket: ____
- [ ] I re-read the spec BEFORE opening the diff
- [ ] Each acceptance criterion maps to code + a test (list them)
- [ ] Anything in the diff NOT asked for by the spec is justified or removed
### Tests are the highest-risk lines
- [ ] Any modified/deleted assertions? Each one explained by a spec change
- [ ] No loosened matchers (toBeCloseTo, any(), snapshot updates) without reason
- [ ] At least one new test fails on the old code (I checked)
### Independent checking
- [ ] Ran by a separate critic with different context (not the authoring session)
- [ ] Critic was prompted to find faults, not to summarise
- [ ] Executed evidence attached (test run, repro, screenshot), not just claims
### Things that must exist
- [ ] Every new import/package is in the lockfile and is the intended package
- [ ] Every new API/SDK method call verified against source or docs
- [ ] New config keys/env vars exist in all environments
### Blast radius
- [ ] Changed shared helpers/types: searched all callers outside the diff
- [ ] Changed return shapes (null vs empty, error vs throw) checked at call sites
### Sampling (for large diffs)
- [ ] Diff > ~400 lines: split, or I traced ___ paths end to end instead of skimming
- [ ] Paths chosen: riskiest (money, auth, data deletion) + one randomA note on sampling. When volume is high, skimming everything gets you shallow coverage of every line and real checking of none. In my experience, tracing two or three paths properly catches more of the bugs that matter. Pick them by risk, plus one at random, so the agent's work can't drift toward the paths you always check. The ~400-line threshold is a team heuristic. Set your own number.
6. The second-order problem: the apprenticeship gap
Every technique above depends on judgment. You have to know which assertion change smells wrong, which helper has callers you didn't see, and which 'idiomatic' SDK call doesn't fit your codebase. Most senior engineers built that judgment by writing code, breaking it and debugging it at 2am. Their review skill grew out of years of writing.
So if a junior engineer spends their first two years approving agent diffs, where does their judgment come from? Nobody knows yet. Lisanne Bainbridge described the pattern in 'Ironies of Automation' (1983): automate the routine work and you remove the practice operators need for the rare moment they have to take over. Current tooling makes this worse. Systems like the one in arXiv 2508.08322 present 'minimal human intervention' as a selling point, which means fewer people doing either the writing or the checking. I don't know of solid longitudinal evidence yet on how engineers who learned mainly through review perform as reviewers, and you should be suspicious of anyone who claims to. Treat this as an open risk you manage, not a solved problem.
7. What an EM can do about it this quarter
- ▹Agent-off katas. Once a week, for 60–90 minutes, people solve a small, real problem from your codebase by hand. The point is practice, not output. Rotate the problems through the parts of the system people normally only see through agent diffs.
- ▹Rewrite a rejected diff by hand. When an agent diff gets rejected, have a junior implement the same spec without the agent, then sit down with a senior and compare the two. The differences are the lesson.
- ▹Paired adversarial review. Put two reviewers on one PR. One defends the diff and the other tries to break it, then they swap. Use seeded diffs (real agent PRs with a known bug planted) so you can see who finds what.
- ▹Make review quality visible. Every month, sample approved PRs and trace production defects back to whoever approved them, without blame. Use what you find for coaching, not punishment. Count good catches in performance reviews and promotion cases too, because a reviewer who blocks a bad migration has prevented an incident.
- ▹Give every approval an owner. The approver signs off on behaviour against the spec, not on 'looks fine'. If no name is attached to an approval, nobody can be held accountable for it.
Closing
The skill shift isn't from writing to prompting. It's from writing to doubting well. Next in the series, we push those doubts into the pipeline. That means designing the automated checks and critic agents that run before a human ever opens the diff, and deciding which of those checks you can actually trust.
Extend your knowledge
- ▹Read Huang et al., 'Large Language Models Cannot Self-Correct Reasoning Yet' (2023), side by side with Zhou et al., arXiv 2308.07921. Together they show when self-checking works and when it doesn't.
- ▹Read Lisanne Bainbridge, 'Ironies of Automation' (1983). It's short, and it described the apprenticeship gap more than 40 years before agents turned it into a code review problem.
- ▹Before you build or buy a reviewer bot, skim 'A Survey on LLM-as-a-Judge' (arXiv 2411.15594). Focus on the sections about judge reliability and bias.
- ▹Exercise: take one merged agent PR from last month, plant a fluent bug in it (a made-up method or a loosened assertion), and run it through your current review process. Note who catches it and how long it takes.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.