Practical Guidelines for Harness Engineering with Claude Code
Seven categories of control around the model, and how to build them
Abstract — Harness engineering treats everything around the model — prompts, tools, sandboxes, permissions, feedback loops — as a single control system to be designed. Drawing on Böckeler's guide/sensor framework and classical control theory, this essay proposes seven categories of harness component (guides, sensors, rails, scaffolds, exemplars, mirrors, and throttles) and maps each to concrete Claude Code surfaces. It closes with a practical build order and an honest look at the discipline's limits.
In February 2026, Mitchell Hashimoto published a short note on his AI adoption journey in which he reached for a term he wasn't sure had been coined yet: harness engineering. He defined it practically. Every time the agent made a mistake, you took the time to engineer a solution such that it would never make that mistake again. A line in the harness is a fossilised failure. The harness grows by accretion.
A month later, Vivek Trivedy at LangChain gave the idea its cleanest formulation:
Agent = Model + Harness.
If you're not the model, you're the harness. Everything that isn't the weights — the system prompt, the tools, the filesystem, the sandbox, the orchestration logic, the hooks, the guardrails — is harness. And the harness, not the model, is increasingly where the engineering work happens. LangChain's Deep Agents team demonstrated the point empirically by moving GPT-5.2-Codex from outside the top thirty (52.8%) to rank five (66.5%) on TerminalBench 2.0, an agentic-coding benchmark, by changing only the harness. The effect replicates elsewhere: the same Claude Opus scores 42% on CORE-Bench with a generic scaffold and 78% inside Claude Code, and a Stanford/MIT paper reports that identical weights produce materially different benchmark scores depending on the harness wrapped around them.
That is the scene. The rest of this essay is about what harness engineering actually looks like when you have to build one, ship it, and live with it.
1. Prompt and context engineering vs harness engineering
The terminology has shifted twice in three years, each move reflecting where the real engineering leverage sat. Prompt engineering (2022–2024) optimised the single exchange — find the magic phrasing, the right examples, the clever instruction that unlocked the model. Context engineering (2025) reframed the problem: the model's behaviour depends on everything in its window, not just the prompt, so the work became curating what the model sees — retrieval, memory, compaction, tool descriptions. Harness engineering (2026) is the third turn. Once the model is capable enough and the context is well-managed, the bottleneck migrates outward again, this time to the entire execution environment around the loop: the tools themselves, the sandbox, the feedback signals, the permission boundaries, the recovery logic. Each layer subsumes the ones before it. A good harness contains good context engineering; good context engineering contains good prompting. You don't throw the earlier disciplines away — you embed them:
2. Böckeler's 2×2
A useful sharpening came from Birgitta Böckeler at Thoughtworks, whose Harness Engineering for Coding Agent Users gave the discipline its first serious conceptual framework. Böckeler organises harness controls along two axes. The first distinguishes guides — feedforward controls that steer the agent before it acts, like AGENTS.md files, coding conventions, and reference architectures — from sensors, feedback controls that observe after the agent acts and enable correction, like linters, type checkers, and review agents. The second distinguishes computational controls (deterministic, fast, CPU-bound — a type checker, a structural test) from inferential ones (semantic, slow, GPU-bound — an LLM-as-judge, an AI reviewer). The result is a 2×2 with four quadrants. Böckeler's central point is that you need controls in more than one: feedback-only harnesses correct each mistake as it happens but do nothing to keep the next session from rediscovering it, while feedforward-only harnesses encode rules but never find out whether they worked.
Böckeler's framework is more than a taxonomy — it is a practical checklist. When I sit down to harness an agent, I walk each quadrant and ask what I have: a computational sensor running? An inferential guide in place? Am I lopsided — all feedforward, no feedback? That alone has sharpened how I build. But her vocabulary pointed at something deeper. Feedforward and feedback are not metaphors; they are terms from control theory, and once I started reading the harness through that lens, the mapping ran further than I expected. The same lens, turned on Claude Code, makes its feature set look less like a grab-bag of flags and config files and more like the component inventory of a closed-loop1 controller.
3. Seven categories
Böckeler's guides and sensors cover two of the roles a harness component can play. I find it useful to split the remainder into five further categories.2 They are not orthogonal axes — they overlap, they interact — but each corresponds to a distinct role a component can play in a closed-loop system, and the distinctions are worth preserving because they clarify what kind of failure a given surface is equipped to catch.
Guides, already introduced above, are feedforward controllers. They bias what the agent will produce by presenting conventions, architectural context, and domain vocabulary before it acts. They never observe the output; their job is to set up the inputs well.
Sensors, likewise introduced above, are feedback loops. They observe what the agent did — compiled, linted, typechecked, reviewed — and produce an error signal that re-enters the loop. Without them the agent is running open-loop, and in open-loop mode minor deviations cascade into trajectory drift.
Rails are hard constraints: saturation limits, guard conditions, safety interlocks. They are deterministic by construction. Where a guide is advisory and a sensor is reactive, a rail makes a whole region of the action space physically unreachable. This is the only layer where the word cannot is literally true; everything else is persuasion.
Scaffolds are the process topology — the hierarchical, modular architecture of the loop itself. Sub-agent decomposition, writer/reviewer separation, staged pipelines. Scaffolds decide what the control problem even is, by partitioning it into smaller problems each with their own small loops.
Exemplars are reference signals — setpoints. They don't tell the agent what rule to follow; they show it what "done" looks like. A good worked example is worth a page of prose because the agent can pattern-match to a trajectory rather than reason toward one.
Mirrors are observers — devices that force the system to produce an estimate of its own state before acting. Plan mode, extended thinking, explicit self-critique. The output of a mirror is not code; it is a reading of internal state that the rest of the loop can then act on. In control-theoretic terms, a mirror closes an inner loop around the agent's own reasoning before the outer loop ever sees a tool call.
Throttles are actuator-saturation limits. Turn caps, cost budgets, permission-mode escalation. They don't change what the agent does; they change how much it is allowed to do before a human or another controller has a chance to intervene. Throttles are what keep an agent that has diverged from diverging catastrophically.
Guides and sensors together are Böckeler's 2×2. Rails, scaffolds, exemplars, mirrors, and throttles fill out the rest of the controller's component inventory. A harness covering all seven is not automatically good, but a harness missing one of them tends to fail in a characteristic way: an agent without guides rediscovers the project's conventions from scratch every session; without sensors, it repeats the same lint violation across twenty files because nothing told it the first one was wrong; without rails, rm -rf and DROP TABLE sit inside the action space, one wrong turn from firing; without scaffolds, long sessions drift as context rots and the agent loses the thread; without exemplars, the agent invents a new file layout every time because nothing shows it what "done" looks like; without mirrors, confident wrongness goes unchallenged because nothing forced the agent to externalise a plan first; without throttles, a diverging run burns through the budget before anyone is around to catch it.
The diagram borrows the standard block-diagram conventions of control engineering. Rectangles are processes that transform a signal; circles are junctions where signals meet. The large circle marked Σ is a summing junction — the reference signal from Exemplars enters with a + and the signal returned by Sensors enters with a −, so the wire leaving Σ carries the error between where the agent is and where it ought to be. The smaller circle marked + is a feedforward junction, where Guides add bias into the control signal without waiting for an error to develop. Arrows are signal flow, and the dashed rectangle is not a process at all — it is a logical grouping that marks which blocks belong to the scaffold's inner topology, the region a writer/reviewer split or a sub-agent decomposition would further subdivide. The palette carries meaning too: green reuses Böckeler's colour for reference and feedforward (Exemplars, Guides); amber marks the feedback path (Sensors); the red accent on Rails flags the only block whose job is to make part of the action space physically unreachable; the darker slab in the centre is the plant (the Model) and the other forward-path blocks sit in neutral grey, distinguished by their position in the loop rather than by hue.
4. The iteration loop
A framework of seven categories describes a harness at rest. It does not say how one comes into being. In practice the harness grows by a loop Hashimoto stated in one sentence and most practitioners rediscover on their own: observe a failure, add the smallest constraint that would have prevented it, verify that the constraint holds, and — eventually — discard it when the model outgrows it.
Observe is the hardest part. The failure that warrants a harness change is rarely the loud one. It is the quiet one the agent repeats — a test pattern it keeps almost getting right, a filename it keeps misspelling, a review comment it keeps not addressing. The minimal constraint is the next move: a line in CLAUDE.md if the fix is advisory, a hook if it needs enforcement, a deny rule if it must never happen again. The discipline is to pick the weakest surface that will hold; stronger constraints are harder to remove later. Verify means replaying the situation and watching the constraint bite — a smoke prompt, a canned replay, something that turns "I added a rule" into "the rule fired." Discard is the part nobody does. As models improve, yesterday's scaffolding becomes today's drag, and a harness that never sheds weight becomes a museum of past failures rather than a working controller.
5. Implementing the seven for Claude Code
Claude Code ships with a surface for each of the seven. None is brand new in isolation — hooks, permissions, and sub-agents all predate it — but the combination is what makes the harness a coherent object to engineer against. What follows is one concrete way to instantiate each, in roughly the order I reach for them.
Guides → CLAUDE.md and system prompt. A top-level CLAUDE.md at the repo root, optional per-directory files for subsystems with their own conventions, and the system prompt itself. Keep each entry short, specific, and falsifiable. "We use pytest, not unittest" beats "write good tests." Remember that guides are advisory — Claude does not always follow CLAUDE.md. They bias behaviour; they do not enforce it. Anything you need enforced belongs below, in rails or sensors. The anti-pattern is bloat: past a thousand lines, the rules start crowding each other out, and an ETH Zurich study found that agent-generated AGENTS.md files actively hurt performance. Write them by hand, keep them short, and prune ruthlessly.
Sensors → hooks and review sub-agents. PostToolUse hooks that run formatters, linters, type checkers, and scoped test subsets against the files the agent just touched. The output flows back into the conversation as an error signal and the loop closes. The PID3 analogy is loose — these are not literal PID terms — but the vocabulary is suggestive: a type checker acts like a proportional term, reacting to the error in front of it, and accumulated lint debt acts like an integral term, pointing at drift. For the inferential quadrant, a reviewer sub-agent invoked after substantive edits — an LLM-as-judge pass scoped to the diff, not the whole repo — catches the semantic mistakes the computational sensors can't see. The anti-pattern is running the full five-minute test suite on every edit: by the time the signal arrives the agent has moved on, and the context budget is gone. Scope the sensor to the blast radius of the change and keep it silent on success.
Rails → deny permissions, sandboxing, and containers. Hard "cannot" comes in three concentric rings, each catching what the inner ring couldn't. The innermost is harness rails: deny entries in permissions.deny for destructive commands (rm -rf, force-push, migrations against prod). These bound Claude's built-in tools but don't see Bash subprocesses — Read(./.env) blocks the Read tool, not cat .env. The next ring is process rails: /sandbox filesystem and network rules over the bash subprocess. The outermost is OS rails: a devcontainer with an outbound firewall, non-root user, and read-only host mounts — catching what the inner two cannot, a malicious MCP server or compromised dependency. If an invariant matters it goes into one of these layers; which layer depends on what the invariant is about. Invariants in CLAUDE.md are wishes; invariants enforced by these three layers are laws. The anti-pattern is over-fencing — so many deny rules that the agent is interrupted every few turns for approval, until the operator starts clicking through without reading. Rails should be few and load-bearing, not exhaustive.
Scaffolds → sub-agents, slash commands, and skills. Custom slash commands for repeated workflows (/review, /ship, /postmortem). Sub-agents for tasks with their own context lifetime — a research sub-agent whose transcript doesn't pollute the main thread, a reviewer sub-agent that only sees the diff. Skills for packaged procedures the agent can invoke by name without re-specifying the steps. Each of these is a way of carving a big loop into smaller, tractable ones. The failure mode to watch for is the sub-agent fleet — ten or fifteen parallel workers that eat the context budget before producing anything shippable. Five or six is usually the ceiling, and the right question before spawning a sub-agent is whether the work is actually independent; sequential refactors are almost always faster in one agent with a big context than split across many.
Exemplars → reference files and worked examples. Not "write Pythonic Python" but a pointer to specific python file as the canonical shape. Not "structure tests like this" but three fixture files the agent can pattern off. Exemplars are cheap because you're already writing the code; you just have to mark which files are the ones to copy from and keep the pointers fresh as the code evolves. The anti-pattern is letting them rot — a reference file the agent dutifully copies from long after the real convention has moved on.
Mirrors → plan mode and extended thinking. Plan mode for any change whose scope isn't obvious, extended thinking for problems where the first answer is usually wrong, and a writer/reviewer split for anything user-facing. Force the agent to externalise its state — read the plan, correct the plan, then let the corrected plan drive the action. A mirror is worth more than another guide when the failure mode is confident wrongness rather than missing information. The trap is ritual reflection — plan mode invoked reflexively when the task is mechanical, adding latency with no correction to show for it.
The category name takes a moment, because plan mode or extended thinking would feel more familiar — and each is a mirror, but neither is the category. What unifies them is not that they plan, but that they force the agent to externalise state that would otherwise stay implicit. The plan you can read is a mirror. The reasoning trace you can audit is a mirror. A mirror is what you put in front of an agent that cannot see its own face. The framing also pairs cleanly with sensors: sensors catch wrongness after a tool call; mirrors catch it before. Together they cover the agent on both sides of the action — the world after, and the agent itself before.4
Throttles → turn limits, cost caps, and permission modes. Three orthogonal rate caps on what the agent can do per session: iteration (--max-turns), spend (--max-budget-usd for print-mode runs, or a Stop hook on the transcript for interactive sessions), and authority (--permission-mode, set to plan, acceptEdits, or dontAsk according to how much you trust the run).5 Pair each throttle with a human-review checkpoint at its boundary, so that when the throttle fires something is waiting to catch the work. Blast-radius controls — branches, worktrees, containers — are sometimes lumped in here, but they bound where the agent acts, not how fast or how much; they belong with rails. The cautionary tale here is the overnight retry loop — an agent hammering a flaky API while everyone slept, burning $400 in tokens by morning. No individual call was wrong; there was simply nothing between the agent and the budget.
6. Building in order
None of this is built at once. In practice a harness grows in roughly this sequence.
First, a short CLAUDE.md — sixty lines, not six hundred. Style, the commands you run, a handful of iron laws. Then one Stop hook that formats and typechecks, silent on success and noisy on failure; that alone closes the tightest inner loop. Then one PreToolUse deny rule for the bash commands you never want the agent to reach. The first time you run autonomously, set explicit turn and budget caps; whatever you guess will be too tight, and that is the right error to make. After that, move repeated workflows out of CLAUDE.md and into skills as they stabilise — the harness gets thinner where it can afford to. Introduce sub-agents when, and only when, long sessions start showing context rot; the sub-agent is a context firewall before it is a role-player. When more than one person works on the repo, put the harness in a devcontainer with an outbound firewall so every engineer starts with the same controller. Evals come last — until you know what "good" looks like on your repo, scoring is guesswork.
A harness built this way is an accumulating asset. It compounds across sessions, its discipline transfers across models even when the specific CLAUDE.md lines do not, and, if you tend it well, eventually sheds the parts it no longer needs. Every entry records a lesson once learned the hard way; a good harness is also a quiet one. The measure of the discipline is not how much scaffolding you have accumulated, but how much of yesterday's you managed to delete today.
Future work and thanks
Thanks to my colleague Christian Dalager for valuable feedback, especially for asking what a harness looks like for a non-coding agent — say, one that scans procurement documents. How the seven categories transpose beyond code is an open question I'd like to pursue next.
All feedback is welcome — from questions to spotted errors and misunderstandings.
References
Osmani, Addy (2026-04-19). Agent Harness Engineering. https://addyosmani.com/blog/agent-harness-engineering/
Böckeler, Birgitta (2026-04-02). Harness engineering for coding agent users. https://martinfowler.com/articles/harness-engineering.html
Kyle (2026-03-12). Skill Issue: Harness Engineering for Coding Agents. https://www.humanlayer.dev/blog/skill-issue-harness-engineering-for-coding-agents
Trivedy, Vivek (2026-03-10). The Anatomy of an Agent Harness. https://www.langchain.com/blog/the-anatomy-of-an-agent-harness
Trivedy, Vivek (2026-02-17). Improving Deep Agents with harness engineering. https://www.langchain.com/blog/improving-deep-agents-with-harness-engineering
Lopopolo, Ryan (2026-02-11). Harness engineering: leveraging Codex in an agent-first world. https://openai.com/index/harness-engineering/
Hashimoto, Mitchell (2026-02-05). My AI Adoption Journey.
Notes
--max-turns N and --max-budget-usd $X are print-mode flags only — they apply to claude -p ... and exit non-zero on hit; there is no interactive equivalent. For interactive sessions the budget cap is a Stop hook on the transcript, parsing each message's JSONL usage block (input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens) against current model rates and emitting {"continue": false, "stopReason": "..."} when the threshold is crossed. The hook input itself does not contain token counts — the most common implementation mistake — so the hook must read transcript_path and sum across messages. A reference implementation is available as a gist. The third cap, --permission-mode, takes one of six values — default, acceptEdits, plan, auto, dontAsk, bypassPermissions — and is the right cap to escalate over the lifetime of a long autonomous run: start in plan, step down to acceptEdits once the plan is reviewed, reserve bypassPermissions (equivalent to --dangerously-skip-permissions) for runs inside a devcontainer where the blast radius is already bounded. Inside any session, /usage prints the current spend estimate; on API-billed accounts the Console workspace spend limit is the only server-enforced cap, set on the auto-created Claude Code workspace; Pro and Max plans expose no user-set cap, only Anthropic's rolling quotas.
↩