Todos los artículos

Building a Harness for Autonomous Coding Agents

29 de junio de 2026

#AI#Software Engineering#ai agents#llm#engineering leadership
Building a Harness for Autonomous Coding Agents

One of our agents was asked to build a UI component in a project that had standardized on a component library — DaisyUI, in this case. The task was well specified. The agent read the spec, read the relevant source, and produced a clean, coherent set of styles: custom class names, hand-picked color values, everything in place. It looked finished. It was also wrong, because it never touched the library's primitives. It had rebuilt from scratch the thing the project had already standardized on.

It looked finished. It was also wrong, because it never touched the library's primitives. It had rebuilt from scratch the thing the project had already standardized on.

Nothing in the output signaled the problem. If we had asked the agent how sure it was, it would have said "very," and it would have said the same after we corrected it. The model could not tell the difference between having what it needed and having enough to produce something that looked right.

That gap is what I want to talk about, because it is the failure that does real damage. People expect models to fail by producing nonsense, but nonsense is cheap — you see it immediately and throw it away. The expensive failure is the one we just hit: a plausible artifact that is wrong because the agent was missing a specific piece of context and went ahead without it. It had enough to be plausible. It did not have enough to be correct, and it had no way to know which situation it was in.

The expensive failure is the one we just hit: a plausible artifact that is wrong because the agent was missing a specific piece of context and went ahead without it.

You cannot fix this with confidence scores, because the score comes from the same system that is wrong. Instruction-tuned models are reliably overconfident, and the certainty they report does not track how often they turn out to be right. It gets worse across a chain: put three slightly overconfident agents in a row and the odds that all three got it right are lower than any single step would lead you to believe, while the final answer arrives looking just as assured as a correct one would. A system cannot audit its way out of this on its own. The check has to come from outside.

You cannot fix this with confidence scores, because the score comes from the same system that is wrong.

So the principle we work from is plain enough to state in a sentence: the structure around the model should make the wrong thing hard to do, and impossible to do quietly. That structure is what I mean by the harness.

The thing that makes it work is the line between the model and everything around it. The model is good at turning context into output, and that is the only thing we ask of it. The harness is ordinary deterministic software. It decides what context the model receives, checks what comes back, and rules on whether the work can move forward. The model produces, and the harness decides whether to accept what it produced. Keeping those two jobs apart is what lets you reason about the system at all: the unpredictable part is boxed in, and the part enforcing correctness is plain code you can test.

What I'm describing here is the discipline, not the implementation. The mechanisms we use are our own, and I'm staying at the level of principle. The principles travel fine on their own.

Checking the context before the agent starts

The most useful thing the harness does happens before the agent writes a line. An agent that never sees an incomplete picture cannot build on one.

Every kind of task needs certain things known up front. A UI task needs the design system and the patterns already in use. A data migration needs the source and target schemas and a way to roll back. An authorization change needs the permission model and the isolation rules. None of this is exotic. The point is that you can write down what each kind of work requires, and then check — before the agent starts — that all of it is present and unambiguous. When something is missing, the agent does not start and improvise. It stops and asks one specific question.

This changes what a refusal means. An agent that won't proceed because its inputs are inadequate hasn't failed; the harness has done its job. The refusal is a signal that the spec upstream is incomplete, and fixing it there fixes it for every task that would have inherited the same hole. Agents are eager to be helpful, and that eagerness is the liability. Hand one an ambiguous task and it will cover the gap with something plausible rather than admit it is missing a piece. The job is to make guessing impossible, so the only moves left to the agent are to do the specified thing or to escalate.

An agent that won't proceed because its inputs are inadequate hasn't failed; the harness has done its job.

That only holds if the specification is something you can check against. Prose isn't — two people, or two runs of the same agent, will read the same paragraph and build different things. A spec that states each requirement plainly, ties every acceptance criterion to something a test can actually evaluate, and writes the edge cases out instead of leaving them implied becomes the thing the harness measures the output against. The agent doesn't read it for inspiration. The harness reads it to decide whether the work passes, and a criterion that fails doesn't get closed with a note in the margin. It gets reopened against the criterion it missed.

Failing loudly

The quiet failures are the ones that cost the most. An agent that routes around a red test, or writes a gap down as "known" instead of fixing it, hands a broken foundation to everything built on top of it, and the longer that sits unnoticed the more expensive it is to unwind.

So failures have to be loud and they have to be immediate. A broken precondition or a failing test stops the work and says exactly what went wrong, specifically enough to act on. Nothing downstream begins until the thing it depends on is genuinely finished, not merely marked finished. And "done" is a state the harness verifies, not one the agent is trusted to declare.

The same applies to the system's honesty about itself. When a task gets closed too early — marked complete while the suite is still red, say — the correction and the reason for it go into the permanent record, next to the original call. A truthful account of a mistake you caught is worth more, a few months on, than a clean record that buried it. Coverage thresholds get the same treatment. A coverage gate isn't ceremony; it's a floor on how much of the code has actually been exercised by tests. You can override it, but only through a decision that someone made on purpose and logged. A gate you can slip past without leaving a trace has stopped being a gate.

Sending the right context, and not much else

Once you can fit a great deal into a context window, the temptation is to send everything and let the model sort out what matters. That goes badly. A model's attention is finite, and it gets worse at using what you gave it as the window fills with marginal material — the effect people have taken to calling context rot. Sending more is not the same as sending what the task needs.

Which means assembling context is real work, and it belongs to the harness, or its concomitant tooling, rather than the agent. The agent should get a package built for the task in front of it, drawn from a maintained, authoritative account of what the project knows: the decisions that were made and why, the patterns that count as canonical, the invariants that are not allowed to break. Finding and choosing that material is the harness's responsibility. The account has to be kept current, because stale canonical knowledge is worse than none — the harness will assemble the wrong context and hand it over with full confidence. Anything that changes the architecture should have to update that account before it counts as done.

The agent should get a package built for the task in front of it, drawn from a maintained, authoritative account of what the project knows: the decisions that were made and why, the patterns that count as canonical, the invariants that are not allowed to break.

Gating the actions that can't be taken back

A few kinds of action need handling that the others don't, and the harness should decide which by what the action is, not by what the model concludes about it.

Escalation is the first. "Stop and wait for a human" isn't enough on its own. In an asynchronous pipeline, blocking on a synchronous approval just manufactures a bottleneck and a pile of stale state, and a bare "I'm stuck" is close to useless to whoever picks it up, since they have to reconstruct everything the agent already had in front of it. A useful escalation carries the kind of problem it is, the exact condition that set it off, and enough context to resolve it, pointed at whoever can actually make the call. Whether an action needs approval shouldn't be the agent's decision either. Leave that to the model and a sufficiently confident line of reasoning will talk itself out of asking.

Idempotency is the second. Pipelines get retried, migrations get re-run, jobs die and come back. Running an operation twice should leave you where running it once would have. That belongs in the spec for anything that changes state — what counts as a duplicate, how you detect one, what the second run does — and it belongs in the tests, which should run the operation twice and confirm the second run changes nothing. Idempotency you happened to get by luck is an outage waiting for a retry to find it.

The ways agents fail

Run these systems long enough and the failures stop being abstract. They get names. There's the plausible hallucination, the coherent output that's wrong for lack of one fact. Pattern drift, where the agent ignores your conventions and invents its own. Silent ambiguity resolution, where it makes a choice and never tells you it made one. Then scope creep, premature completion, the confident wrong answer, debt quietly handed downstream, attention rotting as the context fills with noise. Every defense in the harness is built against one of these. Naming them precisely is how you know what it is you're defending against.

What the discipline buys you

A harness sounds like it would slow the agent down. In practice it does the reverse. An agent working inside a good one is usually faster and more correct than one without, and you can audit what it did afterward, which you can't really say about an agent improvising on its own. The constraints are doing the useful work there, not getting in the way of it.

Everyone wants to hand more of the engineering cycle to these agents, and I think that's the right instinct. But you earn that handoff one step at a time. You can't responsibly automate a process you don't trust, and the trust has to come from the structure around the model — not from the model's own assurance that it has things under control. We already know what that assurance is worth.

About Heavy Chain Engineering

Jason Vertrees is the founder of Heavy Chain Engineering — disciplined software-delivery modernization for the AI era. Heavy Chain helps lower middle-market vertical SaaS companies and PE firms turn scattered AI usage into measurable delivery leverage: 85% faster feature velocity, six-to-eight-week projects shipped in days. If you want help building an AI-native engineering org, book an AI Delivery Assessment.