Skip to content
Kumar Chandrachooda
AI Engineering

Four Ways Out of an Infinite Loop

The Code-Review-Simplify loop has no natural end - an agent will polish forever if you let it. The four breakout constraints I arm around the loop, and how I match the exit to the risk of the work package.

By Kumar Chandrachooda 13 Mar 2026 8 min read
Four gates cut into the wall of a loop that would otherwise run forever

The Code-Review-Simplify loop from part 4 has a property I did not fully appreciate until I had run it a few dozen times: it never wants to stop. A human refactoring session ends on its own — you get tired, the sprint ends, the diff starts to feel done. An agent has none of those instincts. Ask it to review its own output and it will find something, because finding things is what a review pass is for. Ask it to simplify and it will oblige, and then find something new to say about the simplification. Left alone, the loop is a perpetual-motion machine that burns time and tokens converting good code into slightly different good code.

So when I adapted the loop into my own practice, the exits became as much of the design as the loop itself. A loop without an explicit exit condition is not a process; it is a hope. This part covers the four breakout constraints I put around the loop — an iteration cap, a quality threshold, a time budget, and a diminishing-returns test — and, more usefully, how I decide which of them to arm for a given piece of work. To keep the ledger honest: these constraints are my extension to the borrowed PREVC skeleton, not established industry practice, and the calibration numbers below are the ones that work for me. Treat them as defaults to argue with, not findings.

Why the loop will not stop on its own

The loop exists because agents get you most of the way to a correct solution and the remainder needs human-guided iteration — Addy Osmani's “80% problem”, which part 4 leaned on to justify making that iteration systematic rather than ad hoc. But systematising the iteration creates a new failure mode: the iteration itself becomes the work. Review always has an opinion. Simplify always has a target, because the over-scaffolding it exists to strip out can be re-litigated indefinitely — every abstraction removed makes some other structure look newly questionable.

Worse, the two steps can ping-pong. A simplification changes the code; changed code invites fresh review; fresh review suggests a restructure; the restructure invites fresh simplification. I have watched an agent extract a helper function in pass two and inline the same helper in pass four, both times with a confident one-line rationale. Neither pass was wrong, exactly. The code was oscillating between two acceptable shapes, and nothing inside the loop could notice that, because each pass only ever sees the current shape and its own suggestion. The stopping decision has to live outside the loop. That is what a breakout constraint is: a rule, decided before the loop starts, that says what “finished” means for this work package.

Exit one — the iteration cap

The bluntest constraint and the one I arm by default: a maximum number of passes per work package, usually three. Suppose I am building an invoicing service — call it Ledgerline — and the current work package is “parse supplier remittance CSVs into the normalised payment model”. Pass one produces a working parser wrapped in a factory, a strategy interface, and a configuration object for the two formats we actually receive. Pass two's review flags the speculative machinery; simplify collapses it to a function per format and a dispatch table. Pass three's review comes back with wording preferences. That is the cap firing at exactly the right moment: the third pass is no longer buying correctness, it is buying tone.

The cap's real value is not the ceiling itself — it is that the cap converts non-convergence into a signal. If a package hits three passes and substantive issues are still arriving, the loop is not the problem; the package is. It is too big, or too vague, or hiding a dependency nobody named. The correct move is not a fourth pass. It is back up the workflow: re-plan the package, or take it into Review and split it. A cap without that rule is just rationed thrashing.

Exit two — the quality threshold

The cap is a ceiling; the quality threshold is a door. It says the loop may end early, the moment a review pass reports no new over-engineering and no suspected hallucinations against the specification. Plenty of work packages genuinely converge in one or two passes, and forcing them through a fixed count wastes the loop's budget on work that was already done.

The threshold has one sharp requirement: you must define “clean” against the specification, not against the agent's capacity for commentary. An agent asked “what would you improve?” will always answer — that question has no empty response. The review question that can actually come back clean is narrower: does the output implement the work package as specified, without invented interfaces and without structure the spec did not ask for. Phrase it that way and a clean pass means something.

One habit I will offer here with an honest hedge: in my own practice I often run the review twice — once myself, once with a second model that did not write the code. I have no measurement behind this, only the repeated experience that the second reader surfaces things the first missed, and vice versa. Take it as practice advice from the field, not a finding.

Exit three — the time budget

The third constraint is a clock: a fixed time allowance per work package, thirty minutes as my default. Time-boxing is old discipline, but what it protects against here is new. An agent operating at full speed can mutate a codebase substantially in half an hour, and the loop gives that mutation a respectable-sounding purpose. Without a clock, “we are still improving the retry handler” can absorb an afternoon while the rest of the breakdown structure sits untouched.

When Ledgerline's webhook retry handler hits its time budget mid-way through a third pass, the budget forces a decision that the loop would otherwise never pose: is the current state shippable? If the last review pass was clean apart from cosmetics, I take the code as it stands and move on. If it was not — if the time ran out while real issues were still surfacing — then, exactly like the iteration cap, the breakout is diagnostic. A package that cannot reach acceptable inside its time allowance was mis-scoped, and the fix is upstream in Planning, not another lap.

Exit four — the diminishing-returns test

The subtlest constraint watches the delta between passes rather than counting them. Somewhere in most loops there is a pass where the diff stops touching behaviour: a rename, a comment reshuffle, a private method relocated for taste. Motion without progress. When the change between pass n and pass n−1 is no longer something I would have requested as a reviewer, the loop is finished, whatever the cap says and however much clock remains.

This one resists automation more than the others — “meaningful change” is a judgement, not a threshold you can compute cheaply — so my working version is a reading discipline: before allowing another pass, read the previous pass's diff first and ask one question. Would I have asked for this? If the honest answer is no, the loop already ended one pass ago; stop paying for echoes.

Matching the exit to the risk

The four constraints are not a menu where you pick one. They combine, and the first to fire wins:

Constraint Kind of exit What firing tells you
Iteration cap ceiling convergence failed — re-plan the package
Quality threshold door convergence succeeded — take the win early
Time budget clock the package outgrew its slot — rescope
Diminishing returns judgement the work finished before the loop noticed

The design decision is which constraints to arm and how tightly, and that should track the risk profile of the work package, not a house rule. A payment-authorisation module in Ledgerline gets five loops and a strict quality threshold, with no time budget at all — I would rather spend the afternoon than ship a fast mistake into the money path. A log formatter gets two loops and a hard time cap, because the worst failure it can produce is an ugly line in a file. The breakout constraint is a per-package decision, and setting it is part of Review, not an afterthought during Execution. When the human refines the breakdown structure, each package should leave Review wearing its exit conditions.

One last discipline that pays off two phases later: record which constraint fired for every package. A breakdown where most packages exit through the quality threshold is telling you the planning was good. A breakdown where the cap keeps firing is telling you something too, and the Validation phase is where that telemetry gets read — I will come back to it in part 7.

The constraints decide when the review inside the loop stops looking. What it should be looking for deserves a part of its own, because the mistakes worth fearing are not the ones that announce themselves. Next, the mistakes that compile — the four hallucination patterns the Review step exists to catch.