Srinath Therampattil
Back to blog

Harness Engineering: The Layer That Actually Decides Whether Your Agent Works

Same model, wildly different reliability, depending on what's built around it. What harness engineering actually means, the layers a production harness needs, and an implementation-level look at the three real ways to build one — Claude Agent SDK, OpenAI Agents SDK, and rolling your own with LangGraph or a raw tool loop.


I’ve watched the same underlying model produce wildly different reliability depending on nothing but what’s built around it. That gap has a name now: harness engineering, a term that only entered mainstream use in early 2026 but describes something teams doing this work have been reinventing independently for a while. The one-line version of the idea, worth quoting directly: “if you are not the model, you are the harness.”

The clearest proof I’ve seen is Microsoft’s SRE agent team, who moved from over a hundred bespoke tools to a filesystem-based context system and watched their “Intent Met” score on novel incidents climb from 45% to 75%, without touching the model underneath. That’s not a prompt tweak. That’s the harness doing the work the model gets credit for.

What a harness actually has to do

A production harness has five jobs, and skipping any one of them shows up as a specific, recognizable failure mode. Tool orchestration decides what the agent can act on and in what order — skip it and you get an agent with either too little reach to be useful or too much to be safe. Verification loops decide how the agent knows a step actually worked, rather than just looks finished. Context and memory decide what the agent still remembers by hour six of a long task. Guardrails decide what it’s not allowed to do regardless of what it’s asked. Observability decides whether you can explain, after the fact, why it did what it did.

None of these are about the model getting smarter. They’re about giving a fixed model a place to operate where its mistakes are contained and its progress is legible — the same argument I’ve made about action-taking agents on enterprise platforms, just generalized past any one domain.

Three real ways to build one

Claude Agent SDK: one agent, a computer, and rules about how it’s allowed to use it

This is the Claude Code engine packaged as a library — an opinionated, batteries-included harness where the vendor owns the agent loop and you steer it rather than build it from scratch. The model gets a real environment (filesystem, bash, web, MCP servers) and you constrain it with two complementary mechanisms.

Permission evaluation runs in a fixed order on every tool call: hooks fire first and can deny a call outright before anything else sees it; deny rules are checked next, where a bare tool name removes it from the model’s context entirely and a scoped pattern blocks matching calls even under the most permissive mode; ask rules route to a callback you write for a live decision; and only after all of that does the permission mode — full autonomy, auto-approved edits, or plan-only — get a say. Implementation note: use hooks for invariants that must hold no matter what the session is doing, and the callback for policy that depends on what the agent is currently working on. They compose; most production setups use both.

Context is managed for you the way it is in Claude Code itself — it fills as the agent works and gets compacted automatically. The lever worth knowing is a pre-compaction hook that fires just before older turns get summarized away, which is your one chance to snapshot state to disk before it’s gone, or to defer the compaction if the moment is wrong. This is what makes an agent that runs for hours able to pick up meaningfully where it left off, instead of quietly forgetting the first half of the task.

This shape is the strongest fit when the job looks like doing real work inside an environment — code, files, systems, a long autonomous run — rather than routing a conversation.

OpenAI Agents SDK: many lightweight agents, and handoffs between them

Where the Claude Agent SDK gives one agent a computer, this SDK’s model is a set of narrow specialist agents that pass a conversation between each other. A handoff isn’t a special primitive under the hood — it’s implemented as an ordinary tool call the model chooses to make, which means it shows up in the trace and follows the same name-and-description-driven selection as any other tool. Guardrails run alongside the agent as input and output checks that can trip a tripwire and halt the run — an off-topic filter, an injection-detection check, anything that needs to stop the interaction rather than just log a warning.

Implementation note, and the part that catches people: input guardrails apply only to the first agent in the chain, and output guardrails apply only to the agent that produces the final response. If agent B in a three-agent handoff chain goes off the rails, the guardrails you attached to agent A never see it. Guardrail placement has to follow the handoff graph, not just sit on the entry point and assume coverage.

This shape earns its keep when the job looks like routing between specialists — support, sales, voice, anything spanning multiple model providers — more than when it looks like one agent grinding through a long task alone.

LangGraph, or a raw tool loop: when you want to own the state yourself

Both SDKs above are opinionated about the loop so you don’t have to think about it. LangGraph, or building directly on a tool-use loop against the Messages API, is the option for when that becomes the wrong trade — you need explicit branches, retries, and human-approval points modeled as first-class parts of the graph, not implicit behavior an SDK decided for you. It works with any model provider, which the two SDKs above don’t, and gives you production-grade checkpointing so a long-running workflow is resumable by construction rather than by whatever compaction hook you remembered to wire up.

Implementation note: the cost is real — you own the state schema, every edge, every retry policy, and there’s meaningfully more boilerplate before anything runs. Reach for this when you’ve outgrown an SDK’s implicit loop, not as the default starting point; the boilerplate is a bad trade if a batteries-included harness would have covered the job.

Which one to reach for

Match the tool to the shape of the job, not to a vendor preference. An agent doing work in an environment — writing code, operating on files, running for hours unattended, the kind of unattended loop I’ve written about with Codex and Claude Code — fits the Claude Agent SDK’s model best. An agent routing a conversation between specialists, especially across more than one model provider, fits the OpenAI Agents SDK’s handoff model best. An agent that needs explicit, resumable, auditable control over every branch and retry — the kind you’d put in front of an incident-response workflow or anything touching money — is where LangGraph or a raw loop earns its extra boilerplate.

The harness is the part you actually own

Models get swapped constantly — a new one ships, benchmarks move, you upgrade. The harness is what survives that churn: the verification loop that catches a bad output, the guardrail that holds regardless of which model tripped it, the log that lets you explain a decision months later. The practice worth adopting isn’t picking the trendiest framework. It’s the same habit that’s true of the model choice underneath it: every time an agent makes a mistake, that’s a harness gap to close, not a prompt to tweak and hope.

Sources: Harness Engineering: Making AI Coding Agents Work in 2026 · Claude Agent SDK: Build Your Own Agent Harness · Intercept and control agent behavior with hooks · Handoffs — OpenAI Agents SDK · Guardrails — OpenAI Agents SDK · LangGraph vs OpenAI and Claude Agent SDKs Compared