Writing
How to review a pull request your AI agent wrote
A method for reviewing agent-written code: start at the system, not the diff. What to read first, what to skip, and how to know when you are done.
Code review was designed around a constraint that no longer holds: that writing code is slow. A human author produced a change over hours or days, and the review was cheap relative to the writing. Reading a diff line by line was affordable because there was not much diff.
An agent inverts that. A prompt that took forty seconds to write produces sixty files, and the diff is now the expensive artefact. Reading it top to bottom is not thoroughness — it is the most expensive possible way to discover that fifty-two of those files were an import reshuffle.
So the method below does not start with the diff. It starts with the system, and it reaches the diff only for the parts that earned it.
1. Find out what the change is before reading any of it
Before opening a file, answer one question: which parts of the system are different now? Not which files — which components. A component is the unit you already think in: the auth layer, the billing domain, the request pipeline.
Most agent-written changes touch far fewer components than files. A sixty-file change is very often two or three components plus the mechanical spread of a rename. Once you know it is "session handling moved out of Auth into a new component, and Checkout now depends on authentication at request time", you know what kind of review this is, and you have not read a line yet.
If you cannot get that summary from the PR description, that is itself a finding. An agent that cannot say what it changed structurally usually did more than one thing.
2. Read the new files first, and read them for placement
Agents create files very easily. It costs a model nothing to introduce
token-cache.ts, and it costs your architecture quite a lot.
For each new file, you are not asking "is this code correct". You are asking four placement questions:
- What component does it belong to?
- What responsibility does it introduce that did not exist?
- Who calls it?
- What does it call?
The fourth one is where the real defects live. A new file that calls something across a domain boundary — a billing helper reaching into auth — is a structural decision the agent made silently, and it is much cheaper to reverse now than after two more PRs build on it.
The specific thing to watch for: a new abstraction next to an existing one that
already does the job. TokenService appearing in a codebase that already has
TokenManager is the single most common agent failure we have seen. Both are
reasonable. Together they are a bug that takes six months to surface.
3. Follow the blast radius, not the file list
For every existing file the change modified, the question is what else now behaves differently. If a function's signature changed, everything that calls it is in the change whether or not it appears in the diff. The name for that question is change impact analysis, and it predates agents by decades — what is new is only how much of it a single PR now needs.
This is the part that rewards being mechanical about it. Walk outward from each changed symbol: who imports it, who calls it, one hop, two hops. Stop when the callers are clearly unaffected — a changed private helper with one caller in the same file is a one-hop walk.
Two hops is usually where it stops mattering. If you find yourself four hops out and still worried, the change is coupling-heavy and that is the review comment.
4. Check the three things a diff hides
These do not look like much in a file list and each one is a production incident in waiting:
- New routes. An added HTTP endpoint is new public surface. Is it authenticated? Was that deliberate, or did the agent copy an unauthenticated handler next door?
- Schema changes. A migration is the one thing in the PR you cannot revert by reverting the PR. Read every one, every time.
- New dependencies. A package added to solve a two-line problem is a supply chain decision made by something that does not have to maintain it.
5. Stop when you can describe the change
The stopping condition is not "I read everything". It is: could you explain this change to a colleague without opening the PR again?
If yes, you are done, even if you read eight files out of sixty. If no, you are not done, even if you read all sixty — and the thing you are missing is almost always structural rather than line-level.
What this method does not do
It does not tell you whether the code is correct. Nothing about reading a change at the system level catches an off-by-one, and no amount of architecture review substitutes for tests. What it catches is the class of problem that tests do not: code that works and is in the wrong place.
That distinction matters more with agents than it did without them. Agent-written code usually works — it is trained on code that works. What it lacks is the context that a component already exists, that this domain does not call that one, that the codebase settled this argument two years ago. Those are the defects you are reviewing for now, and they are all visible at the system level and nearly invisible in a diff.
Where a tool helps
Everything above is doable by hand and worth doing by hand at least once, because it teaches you what your own architecture looks like under pressure. It is also mechanical, which is the argument for automating it: grouping files into components, walking the import graph outward, spotting a second abstraction beside a first, and flagging routes, migrations and dependencies are all things static analysis does exactly rather than approximately.
That is what Cutplane does — it is the reason it exists, and the example report is the whole method applied to one real public pull request, with every claim linked to the line it came from. You can also run it from your terminal.
← All writing