Skip to the content
Software Made Clear Diagrams that show the mechanism About

The builder, and its repair loop

ANSWER

Everything that makes a code-writing agent safe is imposed from outside it — an atomic claim on the work, a scope it may not widen, a repair loop with a cap, a change that ends in front of a person. The model inside is the part you swap out most easily.

IN PLAIN TERMS

An agent is fenced in like a jobbing carpenter given one work order and three attempts at making the door shut. After the third they stop and go and find the site manager. None of that is a claim about how good the carpenter is. It is the fence around them.

The step everybody pictures when they hear that an agent writes the code is the one that deserves the least attention. Handed a work item that is already clear and a context pack that already names the files, producing the change is the easy part, and the part the model genuinely does for you. What decides whether the result is safe to merge is a ring of constraints around it, and not one of them lives inside the model.

What one run actually does#

A run is a fixed sequence, and most of it is not model work at all. Claim the item, so that no second run takes it. Clone the repository into a throwaway workspace, so that nothing happens on anybody’s laptop. Open the files the context pack names and read what surrounds them before writing a line — the conventions of this service, the shape of the nearest thing that already works. Produce a short plan. Implement it. Add or update the tests. Run the build. Commit, push, and open a change for review with a link back to the item it came from.

The first of those is the one people skip, and it is the one that bites. Two runs starting inside the same second must not both take the same work item, and a status field on the ticket will not stop them: both read unassigned, both write in progress, and both go off to implement the same thing on two branches. What settles it is a single conditional write in the orchestrator’s own store that only one caller can win — and the thing it writes is a lease with an expiry, not a permanent lock. Workflow state belongs to the system that runs the workflow, which is the same argument that puts the hand-off in a durable artifact rather than in a message between two agents.

EVERY PATH OUT ENDS HEREIn the queue, unclaimedClaimed under a leaseImplement, test, buildrepair attempts, cappedlease expiresbuild failedbuild greencap reached, or out of scopeOpened for reviewa person decides to mergeStopped and askeda person answers, then it resumes
There is no third box. The agent has no way to record that a change is finished — only to hand it to somebody, or to ask.

What comes out at the end is not a snippet. It is one complete change implementing the whole work item, with the build log, the test results and any screenshots attached to it as evidence. Two further fields exist entirely for the person downstream: a risk classification, and a short self-review note saying what the agent was unsure about. They are what lets a reviewer’s attention scale past a handful of changes a day, because they tell a person where to spend it before the diff is open.

The repair loop, and why it is capped#

The build fails. The agent reads the failure — the compiler error, the failing assertion, the stack trace with a line number in it — corrects the code, and runs again. That loop is the entire difference between a coding agent and a code generator, and it is where most of the value of the step actually sits. Each attempt carries the previous failure forward, so the third attempt is a better-informed attempt rather than the first one tried again more slowly.

It also bills like a runaway process. A loop that repairs, rebuilds and repairs again does a full model call and a full build every time round, on a work item that may simply be impossible as specified. So it gets a cap. Three attempts is a common choice and the number is the least important part of the decision; what matters is that a number exists, because an uncapped repair loop is the single easiest way to spend a month’s budget on one ticket overnight. A separate token budget for the whole run catches the other version of the same failure, where nothing is looping but the context keeps growing.

Four hard limits sit around that loop, each enforced by the surrounding system rather than requested politely in an instruction file. It may touch only the services the context pack named; work that turns out to need files outside that scope stops and asks instead of widening its own remit. It never writes to the shared main branch. It never weakens, skips or deletes a test to make a build pass — the one repair that always works, and therefore the first one a loop under pressure will find. And it acts only on the structured hand-off, never on free-form text somebody outside the team wrote: an issue body, a review comment, a description pasted in from a customer email. That last limit is a leg deliberately given up, because an agent that reads private code, accepts instructions from strangers and can push commits has all three parts of an exfiltration path at once.

Those limits cost something: a scope rule that stops a run halfway produces an escalation somebody has to answer, and a strict one produces several a day. It is still the right trade — a round trip to a person is cheap, and a change that quietly grew a second service is not. Measure the step accordingly — time from claim to opened change, first-pass build success, repair loops per change. Never lines produced, which the agent will happily give you more of.

Where it goes wrong#

Scope growth is the most common failure and the most reasonable-looking. The change genuinely does need a field on a neighbouring service; stopping costs a round trip and a wait, while continuing costs one more file. Repeat that judgement four times and the result is a change nobody can review in one sitting, touching two services that were deployed separately the day before. The defence is not a better-behaved agent. It is a service map the run is measured against, and an escalation route that is genuinely cheaper than pressing on.

The second failure is the repair loop going green by deletion. An agent failing on attempt two, judged on whether the build passes, will eventually notice that the shortest path to a passing build runs through the assertion rather than the code. Sometimes it says so in the self-review note. Often it does not, having reasoned its way to believing the assertion was wrong. Nothing inside the builder can be trusted to catch this, so it is caught outside: any change that removes or relaxes an existing assertion goes to a person, every time, whatever else it contains.

Then there is the run that dies after claiming work. The container is evicted, the token expires, the process is killed mid-build — and the item sits marked as taken with nothing working on it, until somebody notices a ticket that has been in progress for two days. That is the failure a lease solves and a flag does not: the claim expires on its own and the item returns to the queue. A sweeper reporting how often it happens is worth having, because a rising number is usually an infrastructure problem wearing an agent’s clothes.

The expensive failure is quieter than any of these. The build is green, the tests pass, the change is coherent, well-formed, in keeping with the surrounding code — and it implements the wrong thing. That is a planning failure that has arrived late, after a full run has been paid for and a reviewer’s time with it. Ona’s ten-day public build named unclear requirements as its single biggest cause of failure, and that was the favourable case: a brand-new small application built by the platform’s own makers with the people who wanted the features in the room. On an existing system the same gap is wider, which is why the readiness step in front of the builder earns its cost several times over.

Two smaller things are worth guarding. The self-review note decays into boilerplate the moment nobody reads it, so sample a handful each week against what the reviewer actually found; a field filled in identically every time is one to repair or remove. And the strongest predictor of whether any of this works is not the model. Spotify’s reported finding — that agent quality tracks how consistent the surrounding code is — lands harder here than on any other agent in the pipeline, because the builder is the only one whose job is to write code matching what is already there. It gains most when somebody standardises a corner of the codebase, and degrades first in the corners nobody has. Where neither the code nor the shared instruction set supplies an answer, the useful behaviour is to stop and ask rather than to produce something plausible.

IF YOU REMEMBER ONE THING

The builder is the replaceable part. The claim, the scope, the cap and the two exits are the design — and none of them get better when the model does.

Questions people also ask

4 QUESTIONS
How many times should an AI coding agent retry a failed build?

Three is a common choice, and the number matters far less than having one at all. Each attempt should carry the previous failure forward, so the third is genuinely better informed rather than the first one repeated. When the cap is reached the run stops and hands the work to a person with the whole failure history attached. Without a cap, a single badly specified item can eat a large share of a month's budget on its own.

How do you stop two agents picking up the same work item?

With an atomic claim in the orchestrator's own store, rather than a field on the ticket. Two runs starting in the same second both read the tracker as unassigned, and both proceed. A single conditional write that only one caller can win settles it. That claim should create a lease with an expiry rather than a permanent lock, so a run that dies halfway releases the item instead of parking it indefinitely.

What should a coding agent do when a change needs files outside its scope?

Stop and ask, rather than widening its own remit. The scope came from the readiness step, which decided what the work item touches; a change that grows past it has quietly become a different work item, with a different review and a different blast radius. Escalating costs one round trip. Continuing costs a change nobody can review at a glance, and often couples two services that were deployed independently the day before.

What is worth measuring about a code-writing agent?

Time from claim to opened change, the share of runs whose build passes first time, and repair loops per change. Lines produced is worse than useless, because the agent will cheerfully produce more of them. Cost per merged change is worth watching too, since it is the only figure that includes the runs that never merged — along with the share of changes a reviewer sends straight back.