The Review Spawn Threshold: Automated Review Is a Producer of Work
Almost every tool we have adopted in the last year sits on the same side of the pipeline: it helps something get written. Plan, generate, refactor, scaffold, chain. The review side got a checkbox — "and it reviews your code too" — and was mostly left alone.
The constraint moved anyway. Read practitioners talking about coding agents right now and the complaint has shifted from "the output is bad" to something more expensive: reviewing the output costs more than producing it. Not in attention alone — in machine time, in queued work, in the length of the list somebody has to decide about.
We run a fleet of agents against a real store, and we hit this from an angle that is easy to miss when a human is the only reviewer in the loop: our automated review was a producer of work, not just a consumer of it. Once we saw that, the fix stopped looking like a better reviewer and started looking like a threshold.
The loop
Our queue enforces a rule we still believe in: every task that writes code auto-chains a review task to a different role, in a fresh process, with its own instructions and its own memory. We have written about why the verifier must not share state with the builder, and about why an agent claiming done is not the same as done. Both of those still hold. Neither of them is what broke.
What broke is the arrow pointing the other way. The reviewer's severity table said, in effect: for a minor issue, create a fix task. That fix task is a coder task. Coder tasks auto-chain a review. That review's severity table says the same thing.
Write it out as a loop and the shape is obvious:
coder task -> review -> finding -> coder task -> review -> finding -> ...
Each pass has a gain: how many new tasks one review files, on average. If that number is at or above one, the loop does not converge. It does not matter that each finding was correct. Correctness is not the property under discussion. Gain is.
And reviews are very good at finding things. A careful reviewer reading a diff always comes up with something: a comment that no longer matches the code, a test whose name promises more than its assertion checks, a lint rule that exists but never runs in CI, a hardening idea that is not exploitable today. Every one is a true observation. Filed as a task, every one is also a unit of production capacity.
The scarce resource is not attention
For a while we thought the problem was list length — too many open items, a backlog nobody wanted to read. That framing let us tolerate it, because a long backlog feels free.
It is not free, and the reason is the deploy path. We run one self-hosted CI runner and it drains sequentially. Every filed follow-up eventually becomes a push, and every push occupies that runner for a full build-test-deploy cycle — minutes, not seconds.
So the accounting is: a filed cosmetic finding costs a deploy slot. A customer-facing bug fix, pushed at the wrong moment, queues behind a comment-drift correction and every task that correction spawned. The review loop was not competing for anyone's attention. It was competing for the only path to production.
That is the same lesson as cost per completed task, rotated ninety degrees. There we cared what a finished task consumed. Here we care what an unfinished, unrequested task consumes just by existing in a queue that drains through a single pipe. Cycles spent are not value delivered.
Why "file it as low priority" is not a fix
The first instinct is to keep filing and sort by priority. It does not work, for a reason worth stating plainly: priority is a sorting function, and the problem is a production rate.
Low-priority items in an automated system do not sit quietly — something eventually readies them, an agent picks them up, and they become pushes. Deferring production is not reducing production. If inflow exceeds drain, ordering the inflow changes which thing is late, not whether things are late.
The only lever that changes the gain governs what a review is permitted to open new work for at all.
The threshold
So we wrote one, and it is deliberately narrow. A review may spawn a follow-up task only for a behavioural defect it actually reproduced. Three conditions, all required, all stated as claims the reviewer has to be able to make in the first person:
- Observable wrong behaviour. Something computes, renders, denies, spawns, or reports incorrectly. Not "could break if someone later wires this up."
- You reproduced it. You ran it and saw the wrong output. Not read-the-source-and-inferred.
- Someone is affected today. A user, an agent, or an operator who would act on the wrong output right now.
And an explicit below-the-line list, because a threshold that only exists in the abstract gets argued with:
- Documentation and comment drift, stale prose, misleading labels on behaviour that is correct
- A test whose name is broader than its assertion, where the assertion still passes
- Lint that CI does not actually run
- "Hardening only / no runtime change expected / not currently triggered / not exploitable today"
- Speculative future regressions
Those findings do not disappear. They go in the review report, a durable artifact attached to the task. They just do not become new work.
One more clause turned out to matter more than any of the above: if a single review produces three or more sub-threshold findings, note them together and file nothing. Three small findings in one place is a signal about a subsystem, not three independent jobs; a periodic sweep batches them by shared file if they ever justify a commit. Without that clause, "file only real defects" quietly becomes "file five real-ish defects" — the same loop with better manners.
The other half: claims versus evidence
A threshold governs what review is allowed to add. There is a symmetric failure on the reading side, and skipping it gets you an equally broken system that merely looks calmer.
Ask what "verified" means to whatever reads the status. Most status readers count a claim: the run exited zero, the record says complete, the row is green. That is testimony, not evidence — and it fails in one direction, because the process writing the claim is the same optimistic process that did the work.
The version that bit us hardest: a tool whose entire job was producing artifacts had a reasonable per-item skip branch. Nothing to do for this item? Skip, continue. A run where every item took that branch produced nothing, exited zero, and reported success. Downstream, the health reader computed "hours since last successful run" from that record and got zero. Everything was green for months, and the thing being measured had never happened.
Two rules came out of it, and both are needed, because fixing one leaves the other broken:
- A producer that exists to make artifacts must fail when it made none. Count verified outputs; a zero-output run is not a success regardless of how cleanly it exited.
- A consumer must refuse to count a zero-output run as a success. The producer fix does not retroactively clean a history that already reads green, and the reader is where the lie gets believed.
Connect that back to review and the two halves lock together. An unbounded reviewer manufactures work. A status reader that accepts claims manufactures confidence. Run both and you get a system that is maximally busy and maximally certain, with the relationship between those two facts and reality unmeasured.
The honest seam
The threshold drops real findings on the floor. That is the trade, not a side effect. Documentation drift accumulates. Test names stay wrong. We keep a standing list of things we know are slightly off and are not fixing this week.
What we bought is a property we did not have: if something got filed, it is real. A queue where every item is a reproduced defect affecting someone today is one you can drain, argue about, and trust. A queue that mixes those with a reviewer's honest opinions about naming is one nobody reads — which is how genuinely urgent items sit for a week under a pile of correct observations.
The residual risk: the threshold is enforced by instructions, not by tooling. A rule an agent reads is weaker than a gate that returns non-zero, and we know that better than most. Making it a gate is work we have not done yet.
Next time: what a review report should contain so that a below-threshold finding is still findable six months later — and why the artifact, not the task, is the durable half.