Prologue
“Have you Harnessed today?”
In 2026, “Harness” is probably the most-used word in the AI agent space — and it comes with enormous misunderstandings. Look back at the past 2–3 years of AI development and you’ll see a mushrooming of agent frameworks, scaffolding, and orchestration tools, most of which sank deep into the mire of technical debt before vanishing without a trace.
In this article, I’m not going to dwell on the grand vision of harness engineering (a term that, in my view, carries the connotation of agents self-iterating and optimizing). Let’s just talk about the harness itself.
This article answers exactly one question: what are the irreplaceable core components of a future-facing, minimalist agent harness?
And, within today’s technical context, how to sidestep the technical debt accumulated over the past two years and choose — or build — the right agent harness.
Build the harness well, and the agent will take care of the rest.
Setting the Record Straight: What Exactly Is a Harness?
To understand what a harness really is, we need to do a bit of cyber-archaeology.
In traditional software engineering, a test harness is an automated environment built to execute tests, with a core goal of “de-manualization.”
In the LLM and agent space, harness was initially used more in the context of LLM evaluation. On complex benchmarks like SWE-bench, a bare LLM cannot score highly — it needs an external loop and tools to assist. At this stage, harness was confined to the eval domain.
It wasn’t until applications like Claude Code appeared that agent harness gradually took on a new meaning: the foundation that supports AI work.
The earliest source we can find: on September 23, 2025, developer Vivek Trivedy was the first to systematically articulate “agent harness” in a blog post.
“Agent Harness: External set of functionality to enhance a model’s runtime execution. …
But the concept truly exploded after two articles: Anthropic: Effective harnesses for long-running agents (Nov 26, 2025) and OpenAI: Harness engineering: leveraging Codex in an agent-first world (Feb 11, 2026).
Simply put, in one sentence: a harness is the software shell wrapped around an LLM — it turns a model that can only talk into an agent that can get things done, and even into a long-running agent that operates for hours at a stretch.
Various ecosystems, LangChain included, have been staking claims by inventing a dizzying array of concepts. So what exactly is the difference between agent frameworks, runtimes, scaffolding, and harnesses?
Drawing on LangChain’s article: Agent Frameworks, Runtimes, and Harnesses - oh my!
-
Runtime: the low-level infrastructure that solves “how an agent runs stably and persistently in production.”
-
Framework: provides a set of abstractions and programming models to help you “write agent applications more easily.”
-
Harness: layers default configurations, tools, and workflows on top of a framework, creating a “high-level kit you can grab and run tasks with out of the box.”
(Concepts are all man-made — the thing comes first, the concept follows.)
Design Philosophy: Less is More
The lessons distilled from How To Be A World-Class Agentic Engineer and Learning the Bitter Lesson converge on a single idea: Less is More. The various structures and inductive biases humans inject may buy short-term wins, but over the long run they harden into constraints — they should be progressively stripped away as models and compute improve.
Here is the corollary of The Bitter Lesson in the agent space, stated bluntly:
Every line of “smart” control logic you write in your harness is a bet against future model capabilities. Consider:
-
In 2024 you wrote an elaborate ReAct state machine → In 2025, models natively support multi-turn tool calling, and your state machine becomes dead weight.
-
In 2025 you wrote a complex planning module → In 2026, models’ reasoning capabilities are strong enough to plan autonomously, and your Planner becomes a ceiling rather than a scaffold.
This is not an argument against having a harness. It is an argument for asking, of every component you put in: “Could the model learn to do this on its own?”
If the answer is “sooner or later,” it is an auxiliary component — write it with a demolition plan from day one. If the answer is “never” (because it touches the physical world outside the LLM), then it qualifies as a core component.
Flipped the other way: minimal, least-necessary components are precisely what make a harness resilient enough to survive the next 3–6 months of model iteration.
Concretely, taking “long-running” as the target, harness components fall into two categories:
-
Core components: agent loop, context management, atomic core tools, file system, hooks and subagent isolation (or task management)
-
Auxiliary components: (short-term patches — destined for replacement in the long run)
Core Components
Agent Loop
Every story about agents begins with the agent loop. A good agent loop is necessarily lean — the LLM drives the decision-making, not a set of pre-scripted paths imposed by humans.
At the lowest level, an LLM is still an autoregressive model that predicts the next token. This fundamental premise holds for the foreseeable future. The Agent Loop does only one thing: execute tools → capture results → append to context → call the LLM again.
def agent_loop(query):
messages = [{"role": "user", "content": query}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
(from learn-claude-code)
This is the core loop of an agent. All the “intelligence” comes from the same place: the LLM’s internal reasoning as it predicts the next token. The harness simply feeds the tool response back in, so that this reasoning process can continue.
Of course, from an engineering and production standpoint, the agent loop is still this while True loop at heart — but it needs a substantial defensive layer to build resilience.
According to unofficial analysis of Claude Code’s source, queryLoop (src/query.ts:241) wraps the core loop with over 1,700 lines of defensive machinery, including:
-
Streaming handling
-
Permission checks
-
Tool format processing and validation
-
Error recovery
-
…
Context Management
Over the past six-plus months, the term “Context Engineering” has started appearing with increasing frequency. It isn’t about simple prompt tuning — it addresses a more fundamental tension: the contradiction between ever-accumulating messages and the LLM’s finite context window and attention budget. In a long-running scenario, continuously appending messages eventually leads to:
-
Context overflow: hitting the physical upper bound of the LLM’s context window
-
Context Rot: staying within the window doesn’t mean the model can process everything effectively — too much irrelevant information can severely degrade the model’s reasoning
How does a well-designed harness handle context?
Context management is not just about splicing and trimming prompts. The central principle is token cost — or more precisely, KV-cache hit rate. In other words: optimize context while preserving cache hits.
1. Microcompact
Cleans up intermediate tool results generated during earlier rounds of tool use. Based on source-code analysis, Claude Code distinguishes two approaches:
-
Time-based Microcompact: When the user returns after being away for a while, the server-side prompt cache has already expired. There is no cache to protect, so the harness directly constructs new message objects to replace old tool-result content.
-
Cached Microcompact (Context editing): While the cache is still alive, message content must not be replaced. Instead, the harness uses the Anthropic API’s Context editing mechanism to delete specific
tool_resultentries at the API level.
2. Summary, Compact, Auto-Compact
When a threshold is triggered — manually or automatically — the harness calls an LLM to summarize and condense the existing context, replacing the previous context with the result. In Claude Code, for example, the summary must include:
// The summary must contain the following 9 sections
1. Primary Request and Intent // The user's main request and intent
2. Key Technical Concepts // Key technical concepts involved
3. Files and Code Sections // Files and code sections touched
4. Errors and fixes // Errors encountered and how they were fixed
5. Problem Solving // Problem-solving steps taken
6. All user messages // All messages from the user
7. Pending Tasks // Tasks still pending
8. Current Work // Work currently in progress
9. Optional Next Step // Suggested next step (optional)
In the Anthropic Claude API, the system prompt is independent from Messages, which enables a “full summary” model — the entire message history is replaced with a summary.
By contrast, OpenAI’s Codex uses a layered approach of “initial context + most recent user messages (up to 20k tokens) + summary”:
3. Offload
Unlike compact, which is lossy compression, offload uses the file system to persist context into files for permanent storage, freeing the corresponding context-window space. The offloaded content can be loaded back on demand — this is lossless compression.
4. Isolation
For processes that involve extensive tool calls and exploration, large tasks are decomposed into smaller sub-tasks handled by subagents. This keeps each sub-task’s context independent and isolated, preventing context contamination.
The goal of context management: find the optimal balance among information completeness × KV-cache hit rate × token cost.
Atomic Tools
Strictly speaking, what LLMs call tool use isn’t really “using tools.” The model itself is merely outputting tool-call text in a special format; the actual tool execution is carried out by the harness, which parses that text and translates it into real calls against the environment.
But more tools is not better. An excess of tools not only reduces call accuracy but also eats up precious context, causing noticeable Context Rot. For an agent, what really matters isn’t the number of tools — it’s whether the tools possess:
-
Atomicity
-
Orthogonality
-
Completeness
-
Composability
Put another way, the tool layer should be “primitivized” as much as possible — each tool does exactly one well-defined thing, but these tools can be freely composed to build complex behaviors.
Whether it’s Manus’s three-tier tool design or Anthropic’s proposed tool-search-tool and programmatic tool calling, the core question behind both is the same:
For an agent / LLM, what counts as a “native tool”?
A native tool isn’t a highly specialized interface that engineers wrap for business convenience. It’s a tool form that the model is more familiar with, finds easier to understand, and can invoke more reliably. You could also call them agent-oriented tools.
bash is all you need
Low-level capabilities like Bash, file reads and writes, text editing, and regex processing are the most common operational interfaces in the Linux and software-engineering world. They appear with extremely high frequency in training data, which also makes them far more likely to fall within the training distribution of agentic RL. By contrast, those hand-crafted custom tools — with ambiguous semantics and wildly inconsistent interface styles — often impose a significantly heavier burden on the agent’s understanding and tool-selection process.
So, rather than giving an agent hundreds of highly specialized third-party plugins, it’s better to prioritize a small set of stable, low-level, composable foundational capabilities.
(CRUD for agents?)
File System
In an agent system, the file system means far more than just “saving files.”
On one hand, the file system can persist information that would otherwise live only inside the context window — intermediate results, plans, drafts, observation notes, and working memory (i.e., the context offload discussed earlier).
On the other hand, the file system itself is one of the most natural execution environments. Especially in coding-agent scenarios, code, configuration, logs, test output, scripts, and documentation are all organized around files to begin with. Rather than inventing yet another set of memory interfaces or tool abstractions, providing basic operations like read, write, and edit aligns better with how agents actually work — and is closer to real OS-level workflows.
For these reasons, the file system in a harness typically plays at least three critical roles:
-
Working Memory: stores intermediate state, task plans, incremental conclusions, and temporary artifacts
-
Execution Environment: hosts code, scripts, configuration, inputs/outputs, and run results
-
Isolation Boundary / Sandbox: constrains the agent’s operational scope and permissions through controlled directories, virtual file systems, or sandbox environments
The paper Everything is Context: Agentic File System Abstraction for Context Engineering borrows the Unix philosophy of “Everything is a file” and applies it to the agent’s file system.
Subagent
The subagent began as a context isolation mechanism. It has since evolved into a fundamental unit of task scheduling and multi-agent coordination.
In many tasks, the main agent’s context is an extremely precious resource. Operations like searching a large codebase, running tests, debugging errors, or performing long-running analysis tend to generate enormous amounts of procedural noise. If all those intermediate steps are stuffed into the main conversation, they not only pollute the primary context but also degrade the quality of subsequent reasoning and decision-making.
The natural solution, then, is: spawn a subagent and let it handle the “dirty work” in an isolated context.
The main conversation doesn’t need to consume the full process — it only needs to receive a summarized result. From the main agent’s perspective, it simply called a higher-order tool — an AgentTool — and then waits for (or later receives) a block of summary text, which it writes back into its own context as a tool_result. In other words, introducing subagents does not alter the structure of the core queryLoop.
Zooming out further, the subagent is also a task-scheduling mechanism.
This scheduling generally takes two forms:
-
Synchronous subagent: called like a regular tool, blocking until the result arrives
-
Asynchronous subagent: runs as a background task; the main conversation continues forward, and the result is collected later
Once you enter asynchronous mode, the system must maintain a global task registry (e.g., AppState.tasks in Claude Code), running in the background, and inject completed results back into the main agent’s agent loop as a message.
When multiple background subagents run concurrently, the subagent’s value is no longer just context isolation — it also becomes the starting point for multi-agent orchestration.
Hooks
To keep the agent loop lean, new capabilities should not keep piling into the loop itself.
Otherwise, a cycle that was originally just four steps — “execute tools → capture results → append to context → call the LLM again” — quickly gets dragged into a giant if-else state machine by all manner of business logic.
The point of hooks is to move that complexity outside the loop.
Through events like PreToolUse, PostToolUse, PreCompact, SubagentStop, and others, the harness can capture state at key inflection points, inject context, intercept risks, and validate results — without requiring the loop itself to understand every rule.
So the essence of hooks isn’t “a few more extension points.” It’s separating mechanism from policy:
-
The loop is responsible for advancing execution
-
Hooks are responsible for exerting control
On the surface, hooks just give you a list of event names. But more importantly, these events define the critical boundaries within an agent system.
Once the boundaries are clear, many capabilities no longer require direct modification of the loop itself — they can be injected as external policies:
-
Security policies can hook around tool calls
-
Memory governance can hook around compact operations
-
Task orchestration can hook onto the subagent lifecycle
-
…
Boundary Components
Beyond the six core components discussed above, there are a handful of concepts that come up often in conversation. Not every one of them is essential, but understanding where they sit helps you avoid over-engineering.
Checkpointing / Recovery
For a long-running agent, error recovery and state checkpointing genuinely matter. But from the harness’s perspective, checkpointing is more of a cross-cutting capability that spans multiple components: the file system handles persistence, hooks trigger saves at key milestones, and the agent loop resumes from the breakpoint. It’s hard to isolate checkpointing as a standalone “component” — it’s really the emergent result of several core components working together.
Planner / Todo-list
Remember in early 2026, Claude Code dropped its built-in todo-list tool. The reason was straightforward: the reasoning capability of the new model generation had become strong enough that an explicit, manually maintained todo list was no longer necessary. This is a perfect echo of The Bitter Lesson — the planning module you write today is quite likely the first auxiliary component to be ripped out tomorrow. If you absolutely must build one, make sure it can be cleanly detached rather than deeply coupled to the loop.
Skills
Skills are essentially a composite built from core components: a command registry, on-demand message injection, and a pile of markdown files. They don’t belong to the harness’s lower layer — they sit in the application layer above it.
MCP / Plugins
MCP is a protocol layer, not part of the harness itself. Supporting MCP is certainly a plus, but what it solves is “how to expose external resources,” not “what core capabilities a harness should have.”
Memory
Memory is undeniably important, but as of today the industry hasn’t converged on a canonical form. Whether you look at OpenClaw or Claude Code, the prevailing approach is a lightweight memory scheme based on markdown files. This “files-as-memory” approach may seem crude, but it aligns perfectly with the harness’s minimalist philosophy — instead of building an elaborate memory database, just let the agent manage its own long-term state through the file interface it already knows best.
Agent Team and Multi-agent
When you scale out to multiple agents, the harness components inside each agent stay exactly the same — the same agent loop, the same context management, the same atomic tools. The only new addition is the connection mechanism between agents.
If your harness is well-designed for a single agent, 90% of the infrastructure can be reused directly when you extend to a team. The difference between a subagent and an agent team member can be summarized in the table below:
| Dimension | Subagent | Agent Team Member |
|---|---|---|
| Lifecycle | Task complete → destroyed | Survives across tasks, may run for hours |
| Identity | Anonymous (only a task description) | Has a role identity (“reviewer”, “coder”, “tester”) |
| State | Stateless, vanishes after returning results | Has persistent state (its own context history, memory files) |
| Spawn method | Spawned by the main agent | Pre-defined or dynamically spawned |
From the harness’s point of view, a multi-agent system requires a few additional things:
-
Agent registry: who is running, what is their role, current status
-
Lifecycle hooks:
on_spawn,on_idle,on_terminate -
Graceful shutdown and resource cleanup: this is the most overlooked — and most critical — piece
A subagent is unidirectional (main agent dispatches a task → subagent returns a result), whereas an agent team needs bidirectional or even multi-directional communication. But the design of that communication should still follow the principle of “fit the LLM’s native habits”: a shared file system is, itself, the most natural communication channel.
Harness Engineering = Bigger Loop
Due to space constraints, I won’t go deep into harness engineering here. Borrowing the perspective from What Harness Engineering Discusses: A Unified Framework of Three Scaling Dimensions, the core keyword of harness engineering is scalability — extending across time, expanding across space, and evolving interaction paradigms.
From my perspective, I personally consider harness engineering and compound engineering to be synonyms. The real value lies in: how do you make an agent run better and better over time?
The prerequisite for all of this is a usable harness plus a bigger loop.
A complete agent system has two loops:
-
Inner loop (agent loop): the “tool call → result capture → context append → next decision” cycle within a single task. This is the primary focus of this article.
-
Outer loop (evolution loop): the “execute → evaluate → feedback → improve” cycle across tasks. This is the primary focus of harness engineering.
You can see these ideas at play in Andrej Karpathy’s autoresearch and in Ralph Loop.
The outer loop doesn’t need a separate architecture — it’s the inner loop’s core components, reused on a larger time scale. Hooks provide observation points, the file system provides persistent memory, subagents provide parallel experimentation capability, and context management ensures runs don’t spiral out of control.
Design Principles for a Good Harness
1. Minimality
If a component isn’t strictly necessary, don’t add it. For every component, ask yourself:
“If I remove this, does the agent stop working entirely — or does it just run a little less well?”
The former is core. The latter is auxiliary.
2. Removability
Auxiliary components must be cleanly removable, not entangled with core components.
This is The Bitter Lesson applied to engineering: whatever you write today,
must be easy for your future self, six months later, to delete cleanly.
3. Model Agnosticism
The harness should not bake in special-case adaptations for any particular model’s quirks.
A good harness: swap the model, change the API call, and it runs.
(Prompt optimization for a specific model is a different matter, of course.)
4. Evolution-Ready
The ultimate goal of a harness is not “run a task,”
but “provide an environment where the agent gets better over time.”
Every component’s design should answer:
How does it contribute data, feedback, or tunable parameters to the outer loop?
Conclusion
An agent harness does not get better as it gets more complex. Quite the opposite — its value lies in restraint.
The history of agents from 2023 to 2026 is, at its core, a history of human-designed control logic being continuously outcompeted by model capabilities. The real mission of a harness is to provide the model with a stable, safe, evolvable runtime environment — and then get out of the way, handing the decisions back to the model itself.
Further Reading
Anthropic: Effective harnesses for long-running agents
OpenAI: Harness engineering: leveraging Codex in an agent-first world
Agent Frameworks, Runtimes, and Harnesses - oh my!
Harness design for long-running application development
Difference Between Agent Harnesses & Agent Frameworks
Harness Engineering Is Cybernetics
The importance of Agent Harness in 2026
The Claude Code SDK and the Birth of HaaS (Harness as a Service)
Agent design lessons from Claude Code