Agent harness engineering: the agent is the model plus its harness

Agent harness engineering: the agent is the model plus its harness
Top 30 → Top 5on Terminal Bench by changing only the harness, according to Trivedy's team
6behaviours that each harness piece derives from
< 60lines in HumanLayer's AGENTS.md
4pillars to configure a harness: prompt, tools, context and subagents
It's not the model, it's the harness: agent harness engineering explained
I also explain it in this video, on my Articulator EN channel.

What a harness islink

The article opens with a rule: any time you find the agent making a mistake, you take the time to engineer a solution so that it never makes that mistake again. And with a diagnosis: we have spent two years arguing about which model is smartest, when the model is only one input into a running agent. The rest is the harness: the prompts, tools, context policies, hooks, sandboxes, subagents, feedback loops and recovery paths.

Osmani's thesis is that a decent model with a great harness beats a great model with a bad harness. He says he has watched it play out in his own work over and over. He takes the definition from Viv Trivedy, whom he credits with coining the term harness engineering:

Agent = Model + Harness. If you’re not the model, you’re the harness.

A harness is every piece of code, configuration and execution logic that isn't the model. A raw model is not an agent; it becomes one when the harness gives it state, tool execution, feedback loops and enforceable constraints. Concretely, it includes:

  • Prompts: the system prompt, CLAUDE.md, AGENTS.md, skill files and subagent prompts.
  • Tools: tools, skills and MCP servers, with their descriptions.
  • Bundled infrastructure: filesystem, sandbox and browser.
  • Orchestration: subagent spawning, handoffs and model routing.
  • Hooks and middleware for the deterministic parts: compaction, continuation and lint checks.
  • Observability: logs, traces, and cost and latency metering.
Anatomy of a harness: the model in the middle, surrounded by context injection, control flow, action, persistence and observation
The model reasons and decides; everything else is harness.

Simon Willison reduces the loop part to its essence: an agent is a system that "runs tools in a loop to achieve a goal". The skill is in designing both things, the tools and the loop.

That is a lot of surface area, and it is yours, not the model provider's. Claude Code, Cursor, Codex, Aider and Cline are all harnesses. The model underneath is sometimes the same, but what you experience is dominated by what the harness does. The loud debate is on the model side; most of the leverage, Osmani says, sits on the other.

If you want Trivedy's full derivation, I covered it in The anatomy of an AI agent.

It's not the model, it's the configurationlink

There is a pattern many engineers fall into: the agent does something dumb, the model gets the blame, and the problem is filed under "wait for the next version". The harness mindset rejects that default, because the failure is usually legible:

What went wrongWhat you change in the harness
The agent didn't know about a conventionYou add it to AGENTS.md
It ran a destructive commandYou add a hook that blocks it
It got lost in a 40-step taskYou split it into a planner and an executor
It kept "finishing" broken codeYou wire the typecheck into the loop as back-pressure

HumanLayer puts it this way: "it's not a model problem. It's a configuration problem." They call it a skill issue.

Osmani also picks up a data point that shows up in both Trivedy's write-up and HumanLayer's: on Terminal Bench 2.0, Claude Opus 4.6 running inside Claude Code scores far lower than the same model in a custom harness, and Trivedy's team moved an agent from Top 30 to Top 5 by changing only the harness. The article gives no scores.

The explanation he offers is that models are post-trained coupled to the harness they were trained against. Moving them into a different one, with better tools for your codebase, a tighter prompt and sharper back-pressure, can unlock capability the original harness was leaving unused. It is the opposite of "just wait for GPT-6": the gap between what today's models can do and what you see them doing is largely a harness gap.

The ratchet: every mistake becomes a rulelink

The most important habit, according to Osmani, is treating agent mistakes as permanent signals. Not as anecdotes, and not as "bad runs" to retry. He calls it the ratchet, the toothed wheel that only turns one way.

His example: the agent ships a PR with a commented-out test and he merges it by accident. That is an input, and it produces three changes:

  • In AGENTS.md: "never comment out tests; delete them or fix them".
  • In the pre-commit hook: it greps the diff for .skip( and xit(.
  • In the reviewer subagent: a commented-out test is a blocker.
The ratchet: every agent mistake becomes a permanent rule in the harness
A ratchet only turns one way: every real failure leaves a rule behind.

The discipline works in both directions. You only add a constraint when you have seen a real failure, and you only remove it when a more capable model has made it redundant. Every line in a good AGENTS.md should be traceable back to a specific thing that went wrong.

That is why Osmani calls this a discipline rather than a framework: the right harness for your codebase is shaped by your failure history. You can't download it.

From behaviour to piecelink

To design a harness, the framing from Trivedy that Osmani finds most useful is to work backwards: start from the behaviour you want (or want to fix) and derive the piece that delivers it. If you can't name the behaviour a component exists to deliver, it probably shouldn't be there.

Behaviour you wantHarness piece
Working with real data durablyFilesystem and Git
Writing and executing codeBash and code execution
Executing without breaking anythingSandboxes with good default tooling
Remembering and learning new thingsMemory files, web search and MCP
Performing over long contextsCompaction, tool-output offloading and skills
Long-horizon workRalph loops, planning and verification
Six behaviours and the harness piece that delivers each one
Each harness piece exists because the model can't deliver that behaviour on its own.
Filesystem and Git

The most foundational primitive, and an underrated one because it is boring. A model can only operate on what fits in its context. With files, the agent gets a workspace to read data, code and docs, a place to offload intermediate work, and a surface to coordinate with other agents and with people. Git adds versioning: tracking progress, rolling back errors and branching experiments.

Bash and code execution

The main loop is a ReAct loop: the model reasons, acts through a tool call, observes and repeats. But the harness can only execute the tools it has logic for. Either you pre-build one for every possible action, or you give the agent bash and let it build what it needs. Osmani compares it to teaching someone a single kitchen gadget versus handing them the kitchen.

Sandboxes

Bash is only useful if it runs somewhere safe. Running agent-generated code on your laptop is risky, and a single local environment doesn't scale to many parallel agents. A sandbox allows command allow-lists, network isolation and environments created and torn down per task. A good one ships with language runtimes and packages, Git, test CLIs and a headless browser: that is what lets the agent observe its own work and verify it.

Memory and search

A model knows nothing beyond its weights and its context, so the only way to add knowledge is to inject it. The harness loads AGENTS.md on every start; when the agent edits it, what it learned carries into the next session. It is a crude but effective form of continual learning. For what didn't exist at training time, web search and MCP tools like Context7 bridge the cutoff.

Osmani stresses that the model doesn't configure its execution environment: deciding where the agent runs, what is available and how it verifies its output are all harness-level calls.

Context rotlink

Context rot is the observation that models get worse at reasoning and completing tasks as the context window fills up. Context is scarce, and a harness is largely a mechanism for spending it well. The article lists three techniques that show up repeatedly, plus a fourth that Anthropic adds:

  • Compaction. When the window is close to full, the harness summarizes and offloads older context so the agent can keep working. Letting the API error is not an option in production.
  • Tool-output offloading. From a 2,000-line log, the harness keeps the head and the tail in context and stores the rest in a file the agent can read on demand.
  • Skills with progressive disclosure. Loading every tool and MCP at startup degrades performance before the first action. Skills reveal instructions and tools only when the task calls for them.
  • Full context reset. For really long jobs, the harness tears the session down and rebuilds it from a compact hand-off file. Anthropic is explicit that compaction alone wasn't enough. Osmani compares it to onboarding a new engineer.
Techniques against context rot: compaction, tool-output offloading and skills with progressive disclosure
Context is scarce: the harness decides what goes in, what gets summarized and what goes to disk.

Long-horizon worklink

This is the hardest thing to get right. Today's models stop early, decompose complex problems poorly and lose coherence when the work stretches across several context windows. The harness has to be designed around all three.

Ralph loop

A hook intercepts the model when it tries to exit and re-injects the original prompt into a fresh context window. Each iteration starts clean but reads the state the previous one left on the filesystem. It turns a single-session agent into a multi-session one.

Plan and verification

The model decomposes the goal into steps, usually in a plan file on disk, and the harness reminds it how to use that file. After each step it verifies: hooks run the test suite and loop failures back to the model with the error text, or the model reviews its output against explicit criteria.

Generator and evaluator

Anthropic reports that splitting generation and evaluation into separate agents works better than self-evaluation, because agents tend to grade their own work too kindly. Osmani compares it to GANs, but for prose.

Related to the last one is the sprint contract: generator and evaluator agree on what "done" means before any code is written. Osmani says that, in his own workflows, writing down the done-condition before starting has caught more scope drift than any prompt change. That is his experience, not a measurement.

Hooks: the enforcement layerlink

Hooks separate "I told the agent to do X" from "the system enforces X". A hook is a script that runs at a specific point in the lifecycle: before a tool call, after a file edit, before a commit or on session start. They are for the things the agent should never forget but often does:

  • Check types, lint and tests after every edit, and surface the failures.
  • Block destructive commands: rm -rf, git push --force, DROP TABLE.
  • Require approval before opening a PR or pushing to main.
  • Format on write, so the agent doesn't spend tokens on whitespace.

The principle Osmani takes from HumanLayer is that success is silent and failures are verbose. If the typecheck passes, the agent hears nothing. If it fails, the error text is injected into the loop and the agent corrects itself. That makes feedback almost free in the common case and directly actionable when something goes wrong.

Hooks: success is silent, and failures send the error text back into the agent loop
If everything passes, silence. If something fails, the error goes back to the agent.

AGENTS.md and toolslink

For Osmani, the rulebook at the root of the repo is still the highest-leverage configuration point, because it lands in the system prompt on every turn. Conventions go there: package manager, test framework, formatting, "never touch /legacy", "always use our logger". He gives two lessons:

  • Keep it short. HumanLayer keeps theirs under 60 lines. Every line competes for attention, and the more rules there are, the less each one matters. A pilot's checklist, not a style guide.
  • Earn each line. A rule should trace back to a specific past failure or a hard external constraint. Otherwise it is noise. Ratchet; don't brainstorm.

The same discipline applies to tools. Each tool's name, description and schema are stamped into the prompt on every request. Osmani holds that ten focused tools outperform fifty overlapping ones, because the model can keep the whole menu in its head.

HumanLayer also flags a security concern: since tool descriptions populate the prompt, any MCP server you install is trusted text the model will read. A sloppy or malicious MCP can prompt-inject your agent before you have typed anything.

What it looks like in productionlink

The clearest public picture of a mature harness, for Osmani, is Fareed Khan's breakdown of Claude Code's architecture. It is an estimate, not an official blueprint. The master agent loop sits at the centre, with seven layers around it:

LayerWhat it contains
InputUser interface, session manager and permission gate
KnowledgeSkill registry, context compressor, task graph and memory store
IntegrationMCP runtime and external servers
ExecutionTool dispatch, streaming runtime and prompt cache
OutputVerified task results
ObservabilityEvent bus and background executor
Multi-agentSubagent spawning, teammate mailboxes, FSM protocol, autonomous board and worktree isolator
Estimated Claude Code architecture according to Fareed Khan: the master agent loop at the centre and seven layers around it
Claude Code's architecture, layer by layer. It is Fareed Khan's estimate, not an official blueprint.

Almost every concept above shows up there as a named component. Context injection is the knowledge layer. Loop state lives in the memory store and the worktree isolator. Destructive-action hooks sit behind the permission gate. Subagent context firewalls are the entire multi-agent layer. And the tool dispatch registry is where both MCP servers and bash plug in.

Harnesses don't shrink, they movelink

The naive story is that a better model makes the harness obsolete: if the model can plan, no planner; if it stays coherent over long tasks, no context resets. There is some truth in it. According to Osmani, Opus 4.6 largely killed "context anxiety" (Sonnet 4.5 used to wrap up work early as it approached what it thought was its limit), and all the scaffolding he was writing against that failure six months ago is now dead code.

But the ceiling moved with the model. Tasks that were out of reach are now in play, and they bring their own failure modes: now you need a multi-day memory policy, a harness that coordinates three specialized agents, or evaluators for design quality in generated UIs. Anthropic puts it like this:

Every component in a harness encodes an assumption about what the model can’t do on its own.

When the model learns to do something, that piece is no longer holding anything up and should come out. When it unlocks something new, new scaffolding is needed to reach the new ceiling.

There is also a loop between harness design and training, which Trivedy names explicitly: a useful primitive is discovered in the harness, standardized into the product, used when training the next generation, and the new model gets better at exactly that primitive.

The loop between harness and training: a primitive is discovered, standardized, used in training, and the next model improves at it
Today's harness trains tomorrow's model.

Today's agent products are post-trained with the harness in the loop. That is why Opus 4.6 feels different inside Claude Code than inside another harness, and why changing a tool's logic sometimes causes strange regressions. A truly general model wouldn't care whether it edits with apply_patch or str_replace, but co-training creates overfitting.

Two practical consequences follow: a harness is a living system, not a config file you write once; and the "best" harness isn't necessarily the one the model was trained inside, but the one designed for your task.

Harness as a servicelink

Another idea from Trivedy is Harness-as-a-Service: we are moving from building on a model API, which gives you a completion, to building on a harness API, which gives you a runtime. The Claude Agent SDK, the Codex SDK and the OpenAI Agents SDK all point that way: they ship the loop, the tools, context management, hooks and sandbox primitives, and you customize them.

The default path used to be building everything: your own loop, your own tool calling, conversation state and approval flow. Now it is picking a harness framework, configuring it along its four pillars (system prompt, tools, context and subagents) and putting your effort into the prompts and tools of your domain. That is what makes the skill issue tractable: you don't rebuild the agent every time something fails, you tune a configuration surface that is already well factored.

And it is the best argument for starting messy. The line is Trivedy's:

Good agent building is an exercise in iteration. You can’t do iterations if you don’t have a v0.1.

Where this is goinglink

Put the top coding agents side by side (Claude Code, Cursor, Codex, Aider, Cline) and they look more like each other than their models do. The models are different; the harness patterns are converging. Osmani doesn't think that is an accident: the industry is finding the pieces of scaffolding that actually bear the load.

Trivedy frames three open problems:

  • Orchestrating many agents working in parallel on a shared codebase.
  • Agents that analyze their own traces to find and fix harness-level failures.
  • Harnesses that assemble the right tools and context just in time for each task, instead of being pre-configured at startup.

With that last one, Osmani says, the harness stops being static config and starts looking more like a compiler.

What is data and what is opinionlink

The article is an opinionated synthesis, and it is worth keeping things apart:

  • Terminal Bench comes without numbers. Opus 4.6 scoring "far lower" inside Claude Code and the Top 30 to Top 5 jump are third-party results (Trivedy and HumanLayer) that Osmani cites without scores.
  • "A decent model with a great harness wins" is the author's thesis, backed by his experience. It is not a measurement.
  • Ten tools versus fifty is also an opinion, not an experiment.
  • The 60 lines are HumanLayer's practice, not a general rule.
  • The Claude Code diagram is Fareed Khan's estimate.
  • The rule that opens the article is shown as a quote, but with no attribution on the page.

There is also a detail that doesn't match in the article's last figure. Its alt text describes the loop between harness and training, but the image shows something else: "The harness doesn’t shrink. It moves.", across three model generations. The loop is only in the text.

In shortlink

The agent is the model plus the harness, and the harness is yours. When the agent fails, the useful question isn't which model comes next, but which harness piece would have prevented the failure: a line in AGENTS.md, a hook, a sandbox, a separate evaluator. And when the model improves, the harness doesn't go away: you take out the pieces that no longer hold anything up and add the ones the new ceiling needs.

In one sentence

Every agent mistake is a signal to tighten the ratchet: add a rule when something fails, and remove it when the model no longer needs it.

Which specific failure does each line of your AGENTS.md come from?

Sourceslink

Continue reading

Last posts -->

Have you seen these projects?

Gymnasia

Gymnasia Gymnasia
Expo
React Native
TypeScript
OpenAI
Anthropic

Fitness app with two agents that run entirely on the device, with no backend, so the user's data never leaves the phone. A BYOK conversational coach with local tools and a remote system prompt with offline fallback, plus a food estimator that pulls macronutrients out of a photo of the plate, with barcode scanning against OpenFoodFacts.

LangGraph Deep Researcher

LangGraph Deep Researcher LangGraph Deep Researcher
Python
LangGraph
FastAPI
React
TypeScript
Docker

Multi-agent research system built with LangGraph. A supervisor breaks your question down into topics and launches search sub-agents in parallel; each one compresses its findings before handing them to a writer agent that produces the final sourced markdown report. Live streaming over WebSockets, a configurable model per role and bring-your-own API keys that are never persisted server-side.

Tau

Tau Tau
Python
LangChain

Multi-agent tutoring system for secondary school students, with one agent per subject and course material written and validated by a team of teachers. It was used with real students at a private school in Spain and at a secondary school in Colombia.

View all projects -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to watch any talk?

Last talks -->

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to train your model with these datasets?

short-jokes-dataset

HuggingFace

Dataset with jokes in English

Use: Fine-tuning text generation models for humor

231K rows 2 columns 45 MB
View on HuggingFace →

opus100

HuggingFace

Dataset with translations from English to Spanish

Use: Training English-Spanish translation models

1M rows 2 columns 210 MB
View on HuggingFace →

netflix_titles

HuggingFace

Dataset with Netflix movies and series

Use: Netflix catalog analysis and recommendation systems

8.8K rows 12 columns 3.5 MB
View on HuggingFace →
View more datasets -->