Skip to content

Engineering Guides 1 of 4

What Is Harness Engineering? How to Build AI Agents That Catch Their Own Mistakes

Lessons from OpenAI, Anthropic, Google and independent engineers, worked through one failure, with a way to test every change.

Harry Floyd 14 min read

How this is checked 85 claims · 84 verified at the source · last checked 22 Oct 2026
What would prove it wrong
The guide is wrong if a reader who follows the checklist (one-screen instructions file, silent checks with fix-carrying errors, tests the agent cannot edit, a done-list for long tasks) sees no change in how often their agent declares unfinished work done.

On the track record, not yet testable1 untestedThe re-check

Evidence
85 claims: 84 verified at the primary source and 1 from runs made for the piece.
Contents · 10 sections
  1. 01What a harness is
  2. 02Harness engineering vs context engineering
  3. 03Every addition is a hypothesis
  4. 04Fixing the renamed field, one layer at a time
  5. 05Keep the context clean
  6. 06Work that lasts longer than one session
  7. 07How to test a harness change
  8. 08Which fix for which failure
  9. 09The checklist
  10. 10The whole job

Lessons from OpenAI, Anthropic, Google and independent engineers, worked through one failure, with a way to test every change.

Harness engineering is designing everything around an AI model (its instructions, context, tools, checks and sign-off points) so the agent gets work right first time and catches its own mistakes. The model does the reasoning. The harness is much of what makes it reliable.

One failure runs through this guide. You ask a coding agent to tidy up a data model. It renames a field, the code compiles, the checks in front of it pass, and it reports success. Two other services that read that field quietly break. Thoughtworks engineers, who use this example, call it “locally correct, but systemically destructive,” and they say the gap between model intelligence and reliable delivery “is not primarily a model-quality issue.” What the agent lacked, in their words, was “system-level visibility, architectural guardrails and automated feedback mechanisms.”

This guide applies what OpenAI, Anthropic, Google, Thoughtworks and independent engineers have learned to that one failure, a layer at a time, and shows how to test each fix. If you have ten minutes, read section 3 and do the box in step 2 of section 4.

A dark glass sphere labelled model, held in a gold gimbal cage, with four tags on the cage: AGENTS.md, contract check, deny rule and held-out test.

What a harness is

The short version is a formula: Agent = Model + Harness. Birgitta Böckeler, writing on Martin Fowler’s site, splits the harness in two. Part of it comes built into the tool, such as the system prompt and the way it searches your files. The rest is an outer harness that you build for your own work and your own codebase.

She also gives the most useful working model. Guides steer the agent before it acts: instructions, skills, examples. Sensors check its work afterwards: tests, linters, type checks, review. Guides alone give you an agent “that encodes rules but never finds out whether they worked”; sensors alone, one “that keeps repeating the same mistakes.” The renamed field got past both. Nothing told the agent the field was shared, and nothing checked the services that read it.

Guides before, sensors after: the AGENTS.md line steers the agent, the contract check catches the rename, and a repeat updates the guide or adds a check.

Mitchell Hashimoto, who started calling it harness engineering in a February post, states the aim: “anytime you find an agent makes a mistake, you take the time to engineer a solution such that the agent never makes that mistake again.” Read “never” as the aim. Agents are not deterministic, so some failures come back by chance, and some fixes cause new problems. That is what section 3 is about.

For the case that the harness matters more than the model, see Harness Engineering: Same Model, Different Product.

Harness engineering vs context engineering

The short answer: context engineering decides what the model sees; harness engineering is the wider system that also prevents, checks and fixes. Anthropic calls context engineering “the natural progression of prompt engineering.” HumanLayer draws the line the other way, calling harness engineering “the subset of context engineering” done through the agent’s own configuration points.

Either way, the renamed field failed on both counts: the services that read the field were never in the agent’s context, and no check ran against them. Treat SmartScope’s comparison as a first question to ask: a single wrong answer suggests context, and quality that slips with repeated use suggests the harness, though one wrong answer can also come from the model, a tool or a missing check.

Every addition is a hypothesis

The reflex after a failure is to add something: another rule, another tool, another check. The best evidence so far says to test that reflex.

Two controlled studies have tested the instruction files at the centre of most harnesses. ETH Zurich researchers, working on Python repositories, found that context files, whether written by a model or by the repo’s own developers, did “not generally improve task success rates,” raised cost by over 20%, and did no better when shorter. The authors conclude the files are useful for “specifying non-standard coding practices.”

A smaller study, by Lulla and colleagues, ran agents with and without an AGENTS.md on 124 pull requests and found the file cut median runtime by 28.6% and output tokens by 16.6%, with completion about the same. The two teams measured cost differently (ETH counted inference cost; Lulla’s counted runtime and output tokens), so their efficiency results are not directly comparable.

Neither found a reliable gain in task success: developer-written files did slightly better in the ETH runs (2.4 percentage points on average, not statistically significant), and clearly better than generated ones.

Anthropic’s harness-design team gives the reason this matters beyond instruction files. Every part of a harness “encodes an assumption about what the model can’t do on its own,” and those assumptions “can quickly go stale as models improve.” Their working rule comes from an earlier post: “find the simplest solution possible, and only increase complexity when needed.” Thoughtworks puts it in three words: “Earn every rule.”

Treat every addition to your harness as a hypothesis. It costs tokens, time and attention, and it assumes something about the model that may stop being true. Make the smallest change that would have caught the failure, run the same kind of task with and without it, and keep it only if the difference pays for the cost. Section 7 shows how.

Fixing the renamed field, one layer at a time

Here is the failure again, fixed in layers: see what happened, make it less likely, catch it when it happens, and contain the damage. For a public contract, the check is the layer that has to hold; the instruction only makes the failure rarer.

Step 1: See what the agent saw

Before adding anything, look at what entered the agent’s context on the failed run. Sourcegraph’s test is to log it, “then check whether the answer was in there.” In the rename it was not: the services that read the field live elsewhere, and the agent never opened them. OpenAI’s line covers it: “anything it can’t access in-context while running effectively doesn’t exist.” If the agent never encountered the dependency, find out why. It may need a clearer instruction to search for it, access to the right repository, or a check that catches what it missed.

Step 2: Make it less likely with one guide

The cheapest change is a line in the agent’s standing instructions file. AGENTS.md is the shared format, read by many coding agents; Claude Code reads CLAUDE.md first and, from v2.1.277, falls back to AGENTS.md when a project has no CLAUDE.md. For the rename, the line might read: Field names in orders/schema are a public contract; before renaming one, search the other services for it and list what reads it. That is the kind of non-standard rule the ETH study found files are good for.

Keep the rest of the file lean. OpenAI’s team tried “one big AGENTS.md” and it failed; for one thing, a giant file “crowds out the task.” Theirs is now “roughly 100 lines,” a “table of contents” pointing to a docs/ folder. HumanLayer keeps theirs “under 60 lines.” In a large repo, put a nested AGENTS.md in each project; agents read the nearest file, “so the closest one takes precedence,” as agents.md puts it.

Software Mansion’s guide cites Anthropic’s Claude Code docs on what goes in and what stays out; here is a shortened version:

Keep what the agent can't find for itself (commands, your non-default rules, how to test, decisions and gotchas, pointers to docs) and cut what it can.

Do this first (10 minutes): Open your instructions file. Delete anything the agent could work out for itself, move long sections into separate docs with one-line pointers, and add the one rule your agent most recently broke.

Step 3: Catch it with one sensor

An instruction can be ignored; a check runs every time. For the rename, that is a contract check: each consuming service lists the fields it reads, in a file the agent cannot edit, and the check fails when the schema no longer has one of them. A test that searches the code for the schema’s own field names looks similar and misses this case, because it never looks for the field the agent just deleted. A first version can be this small (TypeScript, with Vitest and a Zod schema):

// contract.test.ts: runs in CI.
// This file, contracts/ and the CI job sit outside the agent's write access.
import { test, expect } from "vitest";
import { OrderSchema } from "../orders/schema";
import consumers from "../contracts/consumers.json";
// { "billing": ["customer_id", "total"], "shipping": ["customer_id", "address"] }

test("every field a consumer reads still exists", () => {
  const fields = Object.keys(OrderSchema.shape);
  const broken = Object.entries(consumers).flatMap(([service, needed]) =>
    needed.filter((f) => !fields.includes(f)).map((f) => `${service} reads ${f}`));
  expect(broken, `${broken.join("; ")}: keep the old name or update the consumer`).toEqual([]);
});

Run against the renamed schema (Vitest 5 with Zod), it fails like this, and it passes again once the old name is back:

FAIL  tests/contract.test.ts > every field a consumer reads still exists
AssertionError: billing reads customer_id; shipping reads customer_id: keep the old name or update the consumer: expected [ 'billing reads customer_id', …(1) ] to deeply equal []

It catches a removed top-level field and nothing subtler; a production check should also compare types, required fields and the consumers’ real interfaces. And it protects you only while the agent being checked cannot change it, so keep the test, the consumer list and the CI step that runs them out of its reach.

Run it automatically after every edit, and follow four rules that make sensors work.

  1. Silent success, verbose failure. That is Thoughtworks’ name for it. HumanLayer learned why the hard way: when they ran the full suite after every change, “4,000 lines of passing tests would flood the context window,” and the agent lost track of its task.
  2. Write the error for the agent. OpenAI writes custom lint messages that “inject remediation instructions into agent context”; Böckeler calls this “a positive kind of prompt injection.” For the rename, the check above says: billing reads customer_id; shipping reads customer_id: keep the old name or update the consumer.
  3. Promote repeated rules into checks. A rule you keep re-adding to your instructions “should be promoted into a mechanical sensor,” says Thoughtworks. Software Mansion’s split: “Written instructions should cover the cases that require judgment. Hooks should cover the cases that do not.”
  4. Protect the checks that decide “done”. Agents need to write and change tests as part of the work. What they must not be able to edit is the check that says the work is finished, and an instruction alone will not hold that line. For a lock that does, see Lock the Tests, Then Test the Lock.

Step 4: Contain it where the stakes are high

Some changes should not go through on the agent’s say-so. Thoughtworks keeps human confirmation for “irreversible or high-blast-radius choices,” such as changes that span several repositories, schema migrations or public API modifications; everything else proceeds with “sensible defaults.” A renamed field in a public contract is exactly that kind of change.

There are four ways to stop an action, and they differ in strength. An instruction asks. A hook runs your own script around a tool call and can block it: HumanLayer’s denies any command that tries to run database migrations and tells the agent to ask a human. A deny rule blocks matching tool calls; in Claude Code, “Deny rules prevent Claude Code from using the specified tool,” and they are checked first, though another tool or command may still reach the same thing. A sandbox limits what any command can reach, such as which folders it can write to.

Hooks carry their own risk: Claude Code’s docs warn that they “execute shell commands with your full user permissions.” Use the strongest layer the risk calls for, and give each agent the fewest permissions it needs; a review agent with read-only tools, as Thoughtworks notes, simply can’t write.

Step 5: Test the fix

Each of those layers is a hypothesis about this failure. Run the rename task, and a few like it, with and without the new line, the check and the stop, and see which one changed the outcome. Section 7 sets out how.

Keep the context clean

The rename needed one fact, the list of services that read the field, and none of the rest of the repository. That is the general aim. As tokens increase, Anthropic explains, “the model’s ability to accurately recall information from that context decreases,” so the goal is “the smallest possible set of high-signal tokens.” Its cookbook compares three ways to free up space.

Three ways to free up context: clear old tool output, compact the conversation, or keep notes outside the window.

If you build on the API, start with clearing, which Anthropic calls “one of the safest lightest touch forms of compaction”; the agent can always fetch the result again. When you compact, tell the summary what to keep. Sourcegraph’s compaction prompt keeps the task, the files already inspected, decisions and why, and “approaches already tried that failed, and why.” Leave that last item out “and the fresh window will cheerfully retry them.”

Tools take space too. Anthropic’s tool search loads tool definitions only when the agent needs them, about 85% fewer tokens in its example, and Claude Code now defers MCP tool definitions “until Claude needs them.”

Send noisy research to a subagent and have it return the answer with its evidence and any open questions. Anthropic says a subagent may use “tens of thousands of tokens” and return “often 1,000-2,000 tokens,” and Sourcegraph warns that “a sub-agent that retrieved badly still returns a confident summary.”

Every connector and skill also puts someone else’s text in the agent’s context. Software Mansion describes a published attack that hid instructions inside a skill in invisible Unicode characters. Read every third-party skill or connector before installing it. That lowers the risk without removing it: hidden text can get past a read, and a connector’s list of tools can change after you have read it.

Memory needs curating. On Databricks’ internal benchmark of enterprise tasks, their own memory system scored 22.3% with no memory, 35.8% with searchable raw session traces, and 66.1% with lessons drawn from those traces (68.4% with both). Their caution: “more history is not necessarily better memory.” Give memories an expiry too. Sourcegraph’s example is a memory about a system the team changed weeks ago, which “is worse than no memory: the agent holds a confident, wrong prior it will defend.”

Work that lasts longer than one session

When a job spans several sessions, Anthropic’s long-running agents experiment is the most concrete guide. Each new session “begins with no memory of what came before,” and a later agent would “look around, see that progress had been made, and declare the job done.”

Their fix: a first session writes a feature list of everything “done” means, every item marked failing, in JSON because the model is “less likely to inappropriately change or overwrite JSON files compared to Markdown.” Every later session updates a progress file, ends with a commit so bad changes can be rolled back, and starts by reading both and checking the basics still work.

How to test a harness change

Use this on any change to your harness, including the contract check from section 4. It needs no special tooling, but it does take a fixed set of tasks and a few dozen agent runs. Treat it as a pilot: it will show a large effect, and it cannot prove a small one.

The four-setup test: your original harness, plus the instruction, plus the check, plus both, all graded by the same acceptance tests.

  1. Pick the task class your agent failed on, and collect ten or so real examples. Keep a few back that you never look at while building the fix; those held-out tasks show whether the fix generalises or only fits the examples.
  2. Grade every setup the same way. Decide what “done” means before you start, with held-out acceptance tests the agent never sees, and use them for all four setups. If the contract check were also the grader, setup C would win by definition.
  3. Run each setup on the same tasks, several times. Google’s team recommends running evals in batches, because single runs “can be noisy due to nondeterminism of AI models.”
  4. Record six numbers: tasks passed, tasks verified done by a check you trust, human corrections needed, tokens, time and cost. Count cost honestly; in data from Anthropic’s research system, agents used about 4 times the tokens of chat, and multi-agent systems about 15 times.
  5. Keep the cheapest setup that holds up. If C does as well as D, the instruction line is not earning its place. If none beats A, the failure may lie elsewhere, or the effect may be too small for a test this size; add tasks before you decide.

That comparison measures outcomes. Google’s team separates outcome benchmarks from behavioural evals: checks of one action at a time, such as whether the agent searched the other services before renaming, which show why a score moved where a big benchmark only shows it moving “by a few percentage points.” Make each check strict for simple tasks, or use an AI judge where several paths are fine. When you change model, run the whole test again; the assumptions in your harness may have gone stale.

Which fix for which failure

Most agent mistakes fall into a few kinds, and each has a cheapest fix worth trying first. This table is our synthesis of the guides above; treat each row as a starting hypothesis.

Which fix to try first for each kind of failure, and what to escalate to if it recurs.

The rename starts in the first and third rows. Once the line is in AGENTS.md, a repeat moves it to the second: the agent knew the rule and broke it, so the next fix is the contract check, and the stop on public API changes is the escalation.

The checklist

This week

☐ Your agent’s last mistake has one check that would have caught it, silent when it passes, with the fix in its error.

☐ Your instructions file holds only what the agent can’t find for itself, plus the rule it last broke.

☐ Actions you can’t undo (migrations, deletes, pushes) are blocked by a deny rule, a sandbox or a hook.

☐ The checks that decide “done” are out of the agent’s reach.

Next

☐ Your latest fix has been run against the setup without it, on held-out tasks.

☐ Noisy research goes to a subagent, which returns the answer with its evidence.

☐ Only the tools and connectors you use are switched on, and every third-party one has been read.

☐ Long tasks have a done-list and a progress note.

Once it works

☐ Each failure you’ve fixed has a small behavioural test.

☐ On a failed run, you check whether the answer was ever in the context.

☐ Memories have an expiry or a review date.

☐ After a model change, every part of the harness is re-tested, and what no longer helps goes.

The whole job

When the agent makes a mistake, find the smallest fix that would have caught it, test it against the setup without it, and keep it only if it earns its place. This week, start with the last mistake your agent made.

Further reading: if you read three of the originals in full, make them Böckeler on guides and sensors, Anthropic on agents that work across sessions and Thoughtworks on the renamed field. Every other source is linked where it is used. They date from June 2025 to October 2026, and every link and quotation was checked on 22 October 2026.

The next guide in this series, AI Agent Permissions, follows one deleted branch through five guards and shows which of them hold.

The newsletter · Free

Subscribe to get the next structural lens in your inbox.

Several essays a week, plus Notes through the week.

Signup completes on Substack. Unsubscribe from any email.

If an essay was worth one, buy me a coffee.

Practise what it teaches: the Learn skill “Build checks into the harness” in Check an agent's work, a step on the path Ship a coding agent you can trust.

Claude and ChatGPT can search this archive and its claim ledgers once you add the connector.

2 other essays point here