Harness Engineering: Why Capable Agents Still Fail
Everyone has access to the same models now. The same coding
agents, the same context windows, the same tool calling. And
yet I keep watching the same model produce mergeable,
production-quality work in one repository and confident
garbage in another.
The variable is not the model. The variable is everything
around the model. I have started calling that layer the
harness, and I now believe harness engineering is the actual
job when you work with coding agents.
The Capability vs Harness Gap
Benchmarks measure a model in isolation: one prompt, one
answer, one score. Delivery measures a system: the model plus
the instructions it loads, the tools it can call, the tests
that judge its output, and the gates its work has to pass
before anyone calls it done.
Those are different things, and the gap between them is where
most agent failures live. When an agent produces something
unusable in my repos, the autopsy almost never finds a
capability problem. It finds a harness problem:
- The contract was ambiguous, so the agent picked an
interpretation silently and built the wrong thing well.
- The docs the agent loaded were stale, so it reproduced a
convention we had already retired.
- "Done" had no runnable definition, so the agent declared
success and nothing could contradict it.
None of those improve when the next model ships. A more
capable model executes a broken contract faster. If your
harness cannot catch a wrong answer, a smarter agent just
gives you wrong answers with better prose.
Open Loops Produce Confident Garbage
The default way people use coding agents is an open loop:
describe the task, receive the diff, skim it, merge it. The
agent asserts that it finished, and the assertion is the only
evidence.
A closed loop looks different. Every unit of work carries its
own proof: a check that fails before the change and passes
after it. The agent is not trusted to declare success. A
command exits 0 or it does not.
In my own workspace, every planned unit of work ships with
two files next to the task description: a prompt that states
the contract, and a validation file listing the exact
commands that must exit 0 before the unit can be labeled
done. Test first where the change is testable, so the failure
is observed before the fix exists. Then an independent review
pass, run by a different agent than the one that wrote the
code, before anything merges.
That structure sounds heavy until you watch what it catches.
It catches scope creep, because the diff is judged against a
written contract instead of a vibe. It catches the agent
quietly weakening a test to make it pass. It catches the
"everything works" summary that does not survive contact with
the actual test suite. Open loops let all three through,
politely and with great confidence.
The Repo Is the System of Record
Agents have no memory between sessions. Whatever context they
need has to be loaded, and whatever is not written down does
not exist. That constraint, which looks like a weakness,
forced a discipline on my repositories that I should have had
all along: the repo itself is the system of record.
Concretely, that means:
- One canonical instruction file that every tool reads, so
Claude Code, and any other agent runtime, see the same
rules. No per-tool forks that drift apart.
- Plans live in the repo as files with explicit states, not
in someone's head or a chat scroll. An agent can pick up a
half-finished epic and know exactly which units are done,
which are ready, and what proof each one requires.
- Decisions live in architecture decision records. When an
agent asks "why is it built this way," the answer is a file,
not archaeology.
- Banned patterns are written down and enforced with greps in
review. My rules ban dict fallbacks on typed schemas,
archaeology comments, and defensive code for impossible
cases, because those are exactly the failure modes agents
reproduce at scale if you let one instance through.
The repo stops being just the place code lives. It becomes
the interface between human intent and agent execution. Every
improvement to that interface compounds, because every future
session, human or agent, reads it.
What This Changes About the Job
The uncomfortable version: if an agent with the right harness
can produce a mergeable diff, then writing the diff was not
the valuable part. The valuable part is everything I had to
build so the diff could be trusted: the contract, the failing
test, the validation gate, the review pass, the written
rules.
The engineering effort moves up a level. Less time typing
implementation, more time specifying what correct means and
building the loop that verifies it. That is not less
engineering. It is the same discipline that made CI/CD and
code review standard practice, applied to a collaborator that
works fast, forgets everything, and never gets tired of being
checked.
Capable agents are already here. Working systems are still
rare. The difference between the two is built, deliberately,
one contract and one verification loop at a time, and that
work has a name: harness engineering.
I turned the full playbook into a course, from specs and
delegation to verification loops and context continuity. If
you want the deep version, start with the
Harness Engineering course.