---
title: I deleted five AI review agents and the reviews got better
description: A five-specialist review fan-out looked thorough. One session cost $5.88 and 905 seconds for zero findings, and that's what actually changed the design.
date: 2026-09-22
area: ai
tags:
  - claude-code
  - agents
  - ci
draft: false
---

The first version of an automated MR reviewer I built ran five specialist subagents in parallel:
security, reliability, performance, testing, impact. An orchestrator fanned out to all five,
collected their findings, and wrote a summary. It looked thorough. More agents reading the diff
from more angles has to mean better coverage, right up until you look at what that actually costs
and what it actually catches.

The session that changed my mind cost $5.88 and ran for 905 seconds. It produced no verdict. One
of the specialists had a `Bash(grep:*)` grant, issued a `git show | sed | grep` pipeline, and the
grep pattern itself matched a denied command name in the tool policy. The call got blocked, but
the session didn't error, it parked. From the outside a parked session and a turn-budget
exhaustion look identical. They are not the same failure and they don't have the same fix. One
means widen a grant or stop shelling out; the other means raise the turn cap. Confusing them
wastes another expensive run figuring out which one you're actually looking at.

## What actually needed a model

Most of what the five specialists were checking didn't need judgment at all. Security scanning,
secret detection, terraform blast-radius, coverage math: these are deterministic. A shell script
either finds a hardcoded credential or it doesn't. So the rewrite pulled all of that out into
plain detectors, eleven of them, that run before any model is invoked and cost zero tokens. What's
left for the model is the part that's genuinely a judgment call: does this change do what the
ticket says it does, does it break something a linter can't see, is the architecture sound.

That's one judgment pass, not five. A second pass validates the first one's output against a
strict schema, because a model asked to emit structured findings will drift from the schema over a
long session and nothing downstream should have to guess whether `severity: "pretty bad"` means
BLOCKER or MAJOR. If the validator finds a broken contract, one repair pass gets the offending
finding re-emitted, once. A clean review is exactly two model calls. Not two agents doing two
things. Two calls, one flow, in order.

## Coded gates decide when the expensive pass runs

The interesting design choice isn't the review agent, it's the gate in front of it. Whether the
deepest pass runs (a full architectural read, not just "did the diff look reasonable") is decided
by code, not by the model deciding it needs to think harder. The gate looks for concrete signals:
infrastructure-as-code in the diff, a new module entry point, a schema or migration touched, a
manifest changed beyond its version field, more than 25 files.

That gate used to be the default. It got overridden to run on every single review, and the reason
is worth stating plainly because it's a real cost tradeoff, not a compromise dressed up as one: a
pure-prose or pure-config change trips none of the coded gate's conditions, so the pass most
likely to catch a design-level problem was the one that reliably didn't run on the changes least
likely to look risky on paper. Running it every time means paying for the deep pass on every
review instead of a subset of them. That's accepted, explicitly, as the right trade.

## The verdict is never trusted from the model

The last piece is the one I'd tell anyone building something like this to copy first: the pipeline
never trusts the model's own verdict field. Whatever the model says the outcome is, the driver
recomputes the actual blocking decision in code from the findings array. This exists because four
earlier copies of that same blocking logic, scattered across the pipeline, had already been caught
drifting from each other. Now there's a self-test that asserts all the paths agree, and the
recomputed verdict, not the model's stated one, is what a downstream approval job reads.

None of this is about distrust of the model doing the reading. It's about not asking a model to
also be the arbiter of its own output format, and not asking five agents to do what one detector
script already does for free. Fan-out is a cost multiplier that reads as thoroughness. Push
everything deterministic out of the loop first. Pay for judgment only on what's actually
ambiguous, and check its work in code every time.
