Back to blog

Five AI Reviewers Approved the PR That Broke Prod — And None of Them Lied

Sep 19, 2026
Five AI Reviewers Approved the PR That Broke Prod — And None of Them Lied

3am, phone buzzing on the nightstand — that specific vibration pattern that means prod, not spam. Auth was throwing null pointer exceptions on every expired session, which at our traffic meant the pager went off within minutes of the bad deploy landing. I pulled up the PR half-asleep expecting the usual culprit — someone skipped review, rushed a hotfix. Instead: five green checkmarks. Five separate reviewer agents, five approvals, zero objections. Nobody was lazy. Nobody was stupid. Each one was simply right about the eight lines it happened to be looking at.

The setup that felt obviously smart

A few months earlier we'd built a multi-agent PR review pipeline at PhoenixDX. The pitch practically sold itself: big PRs take forever because one human has to hold the entire diff in their head at once. So split the diff into hunks, hand each hunk to its own agent, run them in parallel, aggregate the verdicts at the end. Faster reviews, wider coverage, nobody glazing over by hunk 40 of 40. It's the same argument people make for microservices — decompose, parallelize, scale — and it sounded just as obviously correct.

The PR that took prod down was a token-validation refactor. One hunk, in `auth/middleware.ts`, ripped out a null check on the decoded token — the commit message called it "dead code, the caller already validates." A second hunk, in `session/handler.ts`, added a new caller that read a field off that same token without checking it, because the schema comment implied the field was always populated after the refactor. Look at either hunk on its own and there's nothing to flag. The reviewer on `middleware.ts` saw a null check disappearing next to code that clearly guaranteed non-null three lines up — approve, correctly. The reviewer on `handler.ts` saw an unchecked field read, checked the type signature, saw "required" not "optional" — approve, also correctly. The bug lived entirely in the gap between the two hunks. No agent ever saw that gap, because no agent was ever shown both hunks at once.

This isn't a prompting problem

My first instinct — everyone's first instinct — was to blame the prompt. Just add a line: "flag it if you're unsure about cross-file effects." That's a band-aid, and a dishonest one, because you're asking an agent to be suspicious of information it was never given. It can't reason about a caller it never saw. It's the same failure mode as reviewing a PR by reading only the lines with a `+` or `-` in front of them and skipping everything around them: technically you saw the change, you just never saw it in context.

The real problem was we'd optimized for the wrong invariant. Splitting a diff into independent hunks quietly assumes hunk-local correctness adds up to PR-level correctness. It doesn't — not when the bug is a *relationship* between two edits rather than a flaw in either one alone: a contract broken between caller and callee, a null check that moved instead of disappearing, an invariant that used to hold "by construction" and quietly stopped holding because the construction changed somewhere else in the same diff. Human reviewers catch this because a human skimming a PR builds a rough mental model of the whole change before zooming into any one hunk. Our pipeline skipped that step entirely. It went straight to hunks and never came back up for air.

  • Local correctness ("this hunk is internally consistent") and global correctness ("this PR is safe to merge") are not the same claim. Treating them as equivalent was the real bug here — not the missing null check.
  • The failure hides inside the aggregator's own output. Five approvals read as more trustworthy than one, when it should be the opposite the moment those approvals are structurally blind to each other.
  • You can't parallelize review the way you'd parallelize a build, because that assumes each review is an independent task. A diff isn't a bag of unrelated edits — it's a graph of dependent ones.

What we changed

We didn't rip out the fan-out — for a genuinely large PR, parallel review still earns its keep. What we added was a synthesis step that runs *before* the hunk reviewers, not after. One agent reads the full diff plus enough surrounding context — changed function signatures, every call site the PR touches, changed type definitions — and builds a short dependency map: "hunk A and hunk B both touch the shape of the decoded token object." That map gets attached to every hunk reviewer's prompt as required reading, and any hunk flagged with cross-hunk dependencies gets handed to a single agent that reviews both hunks together, instead of two agents that each see half the picture.

The cost is real — the mapper pass adds latency and burns tokens, and joint reviews run slower than parallel hunk reviews ever did. We paid it anyway, because the alternative was fast, confident, and wrong. Every agentic pipeline eventually makes this exact trade somewhere: speed bought from decomposition versus correctness bought from context. You don't get to dodge the trade-off by throwing more agents at it — more agents reviewing narrower slices makes the coverage illusion stronger, not weaker, unless something in the system is explicitly on the hook for stitching the slices back together.

Here's the part that generalizes past PR review, to anyone building agent pipelines at all: any time you fan work out to parallel agents and merge their verdicts with a simple AND, go find the invariant that lives *between* the pieces, not inside any single one of them. If the honest answer is "nothing, they're genuinely independent," fan out and don't look back. If the answer starts with "well, actually..." — that's your null check, and someone's going to remove it at 2pm on a Friday. The five agents that wave it through won't be lying. They'll each be telling the truth about the eight lines they were shown.

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.

Ask me anything about “Five AI Reviewers Approved the PR That Broke Prod — And None of Them Lied” — trade-offs, decisions, or the story behind it.