Module 6 — Harness Teardowns

Teardown: Claude Code, Codex, and the Open Harnesses

Reading time ~18 min · Last reviewed Sep 2026 · Quiz at the end

Lesson 23 read one harness end to end: DeepSeek's dsh, where every subsystem is a plugin and the session event log is the single source of truth. This lesson does the opposite. It reads across many harnesses at once (Claude Code and the Claude Agent SDK, OpenAI's Codex, and a set of open harnesses: OpenCode, Pi, OpenHands, Qwen Code, Kimi Code, and LangChain's Deep Agents) and asks, dimension by dimension, where they agree and where they made different bets. One harness teaches you one team's answers. Eight at once show you which questions every team had to answer, and which answers have hardened into conventions.

One caution before the tables. These products ship weekly. Every claim below links a primary source (vendor engineering posts, official docs, or the repository itself) and reflects what those sources said in September 2026. Where I could not verify something from a primary source, the table says "not documented" rather than guessing. Treat the cells as a dated snapshot and the rows as the durable part.

Reading a harness: the ten dimensions

Lesson 2 introduced the seven subsystems of a harness. For comparing shipping products, it helps to cut the same material slightly differently, into ten questions you can ask of any harness and answer from its docs:

  1. Loop and protocol. How does a turn run, and how do clients talk to the loop?
  2. Context and compaction. How is the prompt assembled, how is the cache protected, and what happens when the window fills?
  3. Memory files. Which instruction files load, from where, in what order, with what budget?
  4. Tool surface. What does the model get out of the box?
  5. Extension model. Hooks, plugins, skills, MCP: how do you change behavior without forking?
  6. Permissions and sandbox. Who decides whether an action runs, and what enforces the boundary?
  7. Subagents. Can the agent delegate, with what isolation and limits?
  8. Surfaces and embedding. Where does it run, and can you put it inside your own product?
  9. Telemetry and data flows. What leaves the machine by default?
  10. License. Can you read, fork, and redistribute it?

The tool-surface row is deliberately thin in what follows. Lesson 2 already has the cross-harness tool table (file system, shell, dev IO, MCP and skills, web, delegation), and the convergence it describes holds for every product here. The interesting differences are in the other nine rows.

The comparison

Three tables, same rows. The first covers the two frontier-lab harnesses, the second and third the open ones.

Claude Code and Codex

DimensionClaude Code / Agent SDK / Managed AgentsCodex (CLI, app, IDE, web)
Loop and protocolGather-context, act, verify loop (docs). Agent SDK is "a library that runs the Claude Code binary" in Python or TypeScript; other languages drive claude -p with JSON output (SDK overview). Managed Agents: stateless harness, durable session log, sandboxes behind execute(name, input) (Anthropic, Apr 2026).Responses API; every request is stateless (no previous_response_id) and each prompt is an exact prefix of the next (OpenAI, Jan 2026). App Server: bidirectional JSON-RPC over stdio with Item / Turn / Thread primitives (OpenAI, Feb 2026).
Context and compactionClears older tool outputs first, then summarizes; "Compact Instructions" in CLAUDE.md steer it; stops auto-compacting on thrash. MCP tool definitions deferred and loaded via tool search (docs).Auto-compacts past auto_compact_limit via the /responses/compact endpoint, which returns an opaque encrypted compaction item. Mid-session config changes are appended as new messages to protect the cache (OpenAI).
Memory filesCLAUDE.md at managed, user, project, and local scope; @path imports (four hops); path-scoped .claude/rules/; auto memory (first 200 lines or 25KB of MEMORY.md). Reads AGENTS.md by default when no CLAUDE.md is present (memory docs).AGENTS.md and AGENTS.override.md: global file, then one per directory from repo root down to cwd, concatenated, capped by project_doc_max_bytes (32 KiB default) (AGENTS.md docs).
Extension modelHooks on more than 30 lifecycle events with command, HTTP, MCP-tool, prompt, and agent handlers (hooks); skills per the Agent Skills standard (skills); plugins bundle skills, agents, hooks, and MCP servers; MCP.Hooks (PreToolUse, PermissionRequest, PostToolUse, Stop, compaction and session events; command or MCP-tool handlers) (hooks); skills in .agents/skills, plugins for distribution (skills); MCP tools.
Permissions and sandboxModes default, acceptEdits, plan, auto, dontAsk, bypassPermissions; allow/ask/deny rules, deny wins in every mode (modes). Auto mode: classifier sees user messages and tool calls, not tool outputs (Anthropic, Mar 2026). Bash sandbox: Seatbelt on macOS, bubblewrap on Linux, proxy with domain allowlist (sandboxing).sandbox_mode read-only / workspace-write / danger-full-access; approval_policy on-request / never (untrusted retired); approvals_reviewer user or auto_review. Seatbelt, bubblewrap, native Windows sandbox (sandbox docs). Reviewer agent sees tool outputs; circuit breaker after repeated denials (auto-review).
SubagentsMarkdown files in .claude/agents/ with tool, model, permission, and isolation frontmatter; own context window; nesting to depth 3 and 20 concurrent by default; built-in Explore, Plan, general-purpose (subagents).TOML files in .codex/agents/; built-ins default, worker, explorer; max_concurrent_threads_per_session; subagents inherit the parent's sandbox policy (subagents).
Surfaces and embeddingTerminal, desktop, IDE, web, Slack, CI; same loop everywhere (docs). Embed via Agent SDK; hosted via Managed Agents, with Anthropic-managed or self-hosted sandboxes (SDK overview).CLI, desktop app, IDE, web; codex exec for CI; TypeScript and Python SDKs (Python drives app-server) (SDK docs); App Server for product embedding. codex mcp-server has been removed.
Telemetry and data flowsUsage metrics on by default with the Anthropic API (DISABLE_TELEMETRY); metrics exclude code, prompts, file paths; transcripts stored locally 30 days; OpenTelemetry export available (data usage).Anonymous usage and health metrics on by default ([analytics] to disable); OTel log export opt-in, with user prompts redacted unless log_user_prompt is set (advanced config).
LicenseProprietary ("All rights reserved", Commercial Terms) (repo); Agent SDK governed by Commercial Terms.Apache-2.0, Rust (repo). Model access and managed services are separate.

OpenCode, Pi, OpenHands

DimensionOpenCodePiOpenHands
Loop and protocolClient/server: opencode starts a server and a TUI that is just a client; OpenAPI 3.1 spec at /doc generates the SDK (server docs).Sessions are a tree of JSONL entries; the active branch is the history. Print, JSON, RPC (JSONL on stdin/stdout), and in-process SDK all share one agent (how Pi works).Python Software Agent SDK plus an agent server (REST/WebSocket) for remote or Docker workspaces (SDK repo).
Context and compactioncompaction.auto (default on), compaction.prune of old tool outputs (default off) (config).Compacts when context exceeds window minus reserveTokens (16,384 default); summary entry replaces older messages, originals stay in the tree (compaction).LLMSummarizingCondenser with max_size and keep_first (condenser).
Memory filesAGENTS.md, CLAUDE.md as fallback, global ~/.config/opencode/AGENTS.md, plus ~/.claude/CLAUDE.md compatibility (rules).AGENTS.override.md, AGENTS.md, CLAUDE.md, loaded even when project trust is declined (security).AGENTS.md in the system prompt; CLAUDE.md and GEMINI.md also recognized (skills docs).
Extension modelJS/TS plugins on events such as tool.execute.before (plugins); skills from .opencode, .claude, and .agents skill folders (skills); MCP.TypeScript extensions loaded in-process register tools, commands, providers, event handlers, UI; skills; packages via npm or git.Skills in .agents/skills with optional keyword triggers; MCP.
Permissions and sandboxallow / ask / deny per tool with patterns, including a doom_loop permission; most tools allow by default; no sandbox documented (permissions).No per-call approval; tools run with the process's OS permissions. Docs recommend a container or VM; "project trust" gates loading of project extensions, not actions.Confirmation policies (AlwaysConfirm, NeverConfirm, ConfirmRisky) with LLM and pattern-based security analyzers (security); Docker sandboxing.
SubagentsBuilt-in build and plan agents, general subagent (repo).Not documented in core docs.Not documented in sources reviewed.
Surfaces and embeddingTUI, desktop (beta), IDE via ACP (opencode acp) (ACP), HTTP API and SDK.Terminal; RPC and TypeScript SDK for embedding.Web canvas, agent server, TypeScript client; can front Claude Code, Codex, or any ACP agent (repo).
Telemetry and data flowsSession sharing manual by default, can be disabled; telemetry not documented.Not documented in the docs reviewed.Not documented in sources reviewed.
LicenseMIT, TypeScript.MIT (repo).MIT.

Qwen Code, Kimi Code, Deep Agents

DimensionQwen CodeKimi Code CLILangChain Deep Agents
Loop and protocolOriginally based on Gemini CLI; headless mode and qwen serve daemon over HTTP + SSE (ACP) (repo).TUI; kimi acp for editors (repo). Replaces the archived Python Kimi CLI.A library, not an app: agents are LangGraph graphs with middleware; a prebuilt terminal agent also exists (repo).
Context and compactionNot documented in sources reviewed.Not documented in sources reviewed.Summarization and context offloading to the filesystem backend (docs).
Memory filesQWEN.md and AGENTS.md; configurable context.fileName (settings).AGENTS.md (watched and reloaded) (config).AGENTS.md as persistent memory.
Extension modelSkills, MCP, SDKs in TypeScript, Python, Java.Lifecycle hooks, plugin marketplace, skills, MCP.Middleware, custom tools, skills, pluggable backends.
Permissions and sandboxPlan, Ask Permissions, Auto-Edit, Auto (LLM classifier), YOLO (approval modes); Docker, Podman, or sandbox-exec.Always Ask (default), Ask When Needed (/yolo), Never Ask (/auto); plan mode; built-in dangerous-command guard (interaction).Human-in-the-loop interrupts; glob-based allow/deny file permissions; sandbox backends.
SubagentsSubAgents and Agent Teams.Built-in coder, explore, plan.task tool spawns subagents with fresh context.
Surfaces and embeddingCLI, VS Code, JetBrains, Zed; SDKs.Terminal; Zed, JetBrains via ACP.Embedded in your Python (or JS) application; LangSmith for tracing.
Telemetry and data flowsprivacy.usageStatisticsEnabled defaults to true.Not documented in sources reviewed.Tracing via LangSmith when you configure it.
LicenseApache-2.0.MIT, TypeScript.MIT.
Key idea

Read the tables row by row, not column by column. A column tells you what one product does this month; a row shows what eight teams all had to decide. Rows where every cell looks alike are settled conventions: adopt them and spend your design effort elsewhere. Rows where the cells disagree are the real design space, and that is where your harness needs a written reason for its choice.

Loop and protocol: one harness behind every surface

The most consequential architectural fact in the tables is easy to miss because it lives in the plumbing. Both frontier labs run one harness behind every surface they ship. OpenAI says so directly: the web app, CLI, IDE extension, and macOS app are all powered by the same Codex harness, and the App Server is "the critical link between them" (OpenAI, Feb 2026). They first tried exposing Codex as an MCP server for the VS Code extension, found MCP semantics a poor fit for streaming diffs and approvals, and built a JSON-RPC protocol that mirrored the TUI loop instead. Its three primitives are worth stealing for any agent API: an Item (typed unit of input or output with started, delta, completed events), a Turn (the work triggered by one user input), and a Thread (the durable, resumable, forkable container). Approvals are server-initiated requests that pause the turn until the client answers. That MCP-server path has since been removed from the product entirely (Codex SDK docs).

Anthropic arrived at the same shape from a different direction. Claude Code's docs state the loop is identical across terminal, desktop, IDE, web, Slack, and CI (Claude Code docs), and the Agent SDK is not a reimplementation but a library that runs the Claude Code binary (SDK overview). Managed Agents then pushes the harness out of the sandbox altogether. The harness became stateless, the session became a durable event log outside the context window, and sandboxes became "cattle" reached through a generic execute(name, input) tool. Anthropic reports p50 time-to-first-token fell roughly 60% and p95 by more than 90%, because sessions no longer waited for a container to boot before thinking (Anthropic, Apr 2026).

The open harnesses converge on the same idea at smaller scale. OpenCode's TUI is literally a client of a local HTTP server with a published OpenAPI spec (OpenCode docs). Pi's print, JSON, RPC, and SDK modes "use the same agent and session mechanisms" (Pi docs). And dsh, from lesson 23, reaches it through profiles over a plugin graph. The lesson for your own harness: separate the loop from the UI on day one, with a protocol between them that has explicit item lifecycles and server-initiated approval requests. OpenAI's own TUI began as a special case that talked to core types directly, and OpenAI says it plans to refactor it onto the App Server like any other client.

Context: caching discipline and compaction

OpenAI's loop post is the clearest public statement of a rule lesson 6 covers in general form: keep the old prompt an exact prefix of the new one. Codex deliberately does not use server-side conversation state, keeping every request stateless for Zero Data Retention customers, which makes the loop quadratic in bytes sent. Prompt caching makes sampling linear again, but only if nothing earlier in the prompt changes. So Codex treats cache misses as bugs. Changing tools, models, sandbox config, or working directory mid-session all break the prefix, and the fix is to append a new developer or user message describing the change rather than edit the original. One early MCP integration enumerated tools in a nondeterministic order and silently destroyed cache hits (OpenAI, Jan 2026). The MCP specification has since absorbed that lesson: the 2026-07-28 revision says servers SHOULD return tools/list in deterministic order to improve prompt-cache hit rates (MCP changelog). Dsh's "surface node" design in lesson 23 is the same discipline expressed as architecture.

Compaction strategies differ more than caching strategies. Codex calls a dedicated /responses/compact endpoint that returns an opaque, encrypted compaction item, so the summary preserves "latent understanding" the client cannot read. Claude Code clears old tool outputs before summarizing, lets you steer the summary through a "Compact Instructions" section in CLAUDE.md, and gives up with an explicit error rather than looping when one huge output refills the window after each summary (Claude Code docs). Pi keeps the original entries in its session tree and inserts a summary entry that later requests use in their place, so compaction is lossy for the model but lossless for the log. OpenHands and OpenCode expose knobs (event-count thresholds, keep-first counts, tool-output pruning). The common pattern across all of them is prune tool output first, summarize second, the same order dsh uses. Tool results are the bulkiest and least reusable tokens in a coding session. Removing them first costs the least.

Pitfall

Treating context changes as free. A harness feature that rewrites the system prompt, reorders tools, or honors an MCP list_changed notification mid-session can look harmless in a demo and quietly multiply your inference bill in production. Codex's team describes having to be "diligent" about exactly this. Add a prefix-stability check to your harness tests: replay a session and assert that each request's prompt is a prefix of the next, except where compaction deliberately started a new series.

Permissions: three philosophies of saying no

The permissions row is where the harnesses disagree most, and the disagreement is principled. There are three families.

Rules and prompts. The classic model is pattern rules (allow, ask, deny) plus a human who answers the prompts that fall through. OpenCode is a clean example: per-tool rules with wildcards, a doom_loop permission that catches repeated identical calls, most tools allowed by default, and no execution sandbox documented (OpenCode docs). Kimi Code's three modes and Qwen Code's Plan / Ask / Auto-Edit / YOLO ladder are variations on the same theme. The weakness is well documented. Anthropic found users approve 93% of permission prompts, which means the prompt has stopped being a decision and become a reflex (Anthropic, Mar 2026).

Sandbox first, then a reviewer at the boundary. Codex makes the OS sandbox the primary control. In workspace-write, anything inside the workspace just runs; approval only enters when the agent tries to cross the boundary (network, writes outside writable roots, escalated commands). Its auto-review option swaps the human for a separate reviewer agent at exactly that point. OpenAI is explicit that this is "a reviewer swap, not a permission grant": it never widens the sandbox. The reviewer sees a compact transcript that includes tool calls and tool outputs, but not hidden reasoning. A denial comes back to the main agent with an instruction not to work around it, and a circuit breaker aborts the turn after 3 consecutive denials or 10 in the last 50 reviews (Codex auto-review docs). One subtlety from the loop post matters here: Codex's sandbox applies to its own shell tool, and MCP tools are responsible for their own guardrails (OpenAI, Jan 2026).

A classifier on the transcript. Claude Code's auto mode layers a model-based classifier over rules and a sandbox. Built-in allowlisted reads and in-project edits skip it; shell commands, external operations, and out-of-project access go through it. The key design choice is what the classifier is not shown: Claude's own prose, tool outputs, and file contents. It judges only user messages and the actual tool-call payloads, so the agent cannot talk its way past it and injected text in a tool result cannot address it directly. A separate server-side probe scans tool outputs for injection and adds a warning to context. Anthropic published its error rates honestly: 0.4% false positives on real traffic, but a 17% false-negative rate on a small set of real overeager actions (Anthropic, Mar 2026). Qwen Code also offers an LLM-classifier Auto mode (Qwen Code docs), and OpenHands offers an LLMSecurityAnalyzer alongside deterministic pattern analyzers and an ensemble that takes the highest risk (OpenHands docs).

Notice the direct disagreement between the two frontier designs. Codex's reviewer sees tool outputs because context makes a better judge. Claude Code's classifier is denied tool outputs because tool outputs are where prompt injection lives. These are different bets on whether the larger risk is an under-informed judge or a manipulated one, and lesson 14 gives you the threat model to choose. At the far end sits Pi, which does not gate tool calls at all and says plainly that watching the transcript and reviewing changes "do not create a security boundary." Its answer is to run the whole agent inside a container or VM (Pi security docs). That is a legitimate position, arguably the most honest one, as long as the operator actually does it.

Here is roughly the same local policy ("edit inside the repo, ask before leaving it, keep network narrow") in the two frontier harnesses. The shapes differ in a telling way. Codex expresses it as a boundary plus an escalation policy. Claude Code expresses it as a mode plus rules plus an optional sandbox.

# ~/.codex/config.toml  (Codex)
sandbox_mode          = "workspace-write"   # OS-enforced: edits and routine commands inside the workspace
approval_policy       = "on-request"        # stop when an action needs to cross the boundary
approvals_reviewer    = "user"              # or "auto_review": a reviewer agent decides instead
project_doc_max_bytes = 32768               # AGENTS.md budget (the default)

[sandbox_workspace_write]
writable_roots = ["/tmp/build-cache"]       # widen the boundary narrowly, not globally
// .claude/settings.json  (Claude Code; comments shown for teaching, remove in real JSON)
{
  "permissions": {
    "defaultMode": "default",                // Manual: ask before edits and shell commands
    "allow": ["Bash(npm test)", "Read"],     // pre-approved
    "deny":  ["Bash(git push *)"]            // deny wins in every mode, even bypassPermissions
  },
  "sandbox": {
    "enabled": true,                         // Seatbelt / bubblewrap around Bash and children
    "network": { "allowedDomains": ["github.com", "*.npmjs.org"] }
  }
}
Key idea

Separate the boundary (what an action can physically reach, enforced by the OS or a VM) from the judge (who decides whether to cross it: a rule, a human, a reviewer agent, a classifier). Every mature harness now has both, and every serious incident pattern involves confusing them. A classifier is not a sandbox, and a sandbox with danger-full-access is not a boundary. Lesson 12 covers the boundary; lesson 13 covers the judge.

Where the harnesses converge

Memory files: AGENTS.md won, CLAUDE.md coexists

Every harness in the tables reads a markdown instruction file from the repository, and almost all of them read AGENTS.md. The format is now stewarded by the Agentic AI Foundation under the Linux Foundation and emerged from Codex, Amp, Jules, Cursor, and Factory (agents.md). The loading semantics have converged too: a global file, then files from the repo root down to the working directory, with nearer files later in the prompt so they win. Claude Code, historically the CLAUDE.md holdout, now reads AGENTS.md by default when no CLAUDE.md is present and can load both together (Claude Code memory docs). OpenCode, Pi, and OpenHands read CLAUDE.md as a fallback. The upshot: write one short AGENTS.md, and have CLAUDE.md import it if you also use Claude Code. OpenAI's harness-engineering post supplies the sizing advice: roughly 100 lines, treated as "the table of contents," pointing at a docs/ directory that is the real system of record (OpenAI, Feb 2026). Lesson 9 develops that idea.

Two details differ and matter. Budgets: Codex stops concatenating at 32 KiB by default, and Claude Code recommends under 200 lines per file. Trust: Pi loads AGENTS.md and CLAUDE.md even when you decline to trust the project, and warns you to treat them as untrusted input, while Claude Code asks for approval before following imports that point outside the repository. A memory file in a cloned repository is text written by a stranger that lands in your system prompt. Harnesses are starting to treat it that way.

Skills: one folder format everywhere

The Agent Skills format (a folder with a SKILL.md carrying a name and description, plus optional scripts and references) was originally developed by Anthropic and released as an open standard (agentskills.io). Claude Code, Codex, OpenCode, OpenHands, and Pi all appear in its client list, and Codex's own App Server post cites skills as the example of a cross-provider standard done right (OpenAI). Every implementation uses progressive disclosure: only names and descriptions load at startup, and full instructions load when the skill is selected. Codex caps the startup listing at 2% of the context window or 8,000 characters (Codex skills docs). Folder locations still vary (.claude/skills, .agents/skills, .opencode/skills), and each harness adds extensions such as Claude Code's context: fork or OpenHands' keyword triggers. Portable skills stick to the core fields.

MCP and ACP: two protocols, two directions

MCP connects the agent to tools; every harness here speaks it. Its 2026-07-28 revision made the core stateless, removed the initialize handshake, and moved tasks into an extension (MCP changelog), changes that suit harnesses running many short-lived sessions. Lesson 11 covers the design questions. The Agent Client Protocol runs the other way, connecting an editor to an agent. It standardizes editor-to-agent communication and reuses MCP's JSON representations where possible (ACP docs). OpenCode, Kimi Code, and Qwen Code ship ACP entry points, and OpenHands can front any ACP agent. The two frontier harnesses instead lead with their own richer protocols, and OpenAI names the tradeoff: cross-provider protocols tend to converge on the common subset of capabilities.

The filesystem-and-shell core

Finally, the tool surface itself has converged on the shape in lesson 2's table: read, edit or patch, glob and grep, a shell, web fetch, a todo or task tracker, and delegation. Deep Agents states the lineage outright: it was "inspired by Claude Code," an attempt to identify what makes it general-purpose (Deep Agents repo). A harness missing one of those tools needs a reason.

Where they diverge

One product harness versus embeddable libraries

Codex and Claude Code are products first that expose their harness for embedding: the App Server, Codex SDK, and codex exec on one side, and the Agent SDK, headless mode, and Managed Agents on the other. OpenAI now frames Codex explicitly as a platform, an open-source harness that applications can embed, with "model access and managed services" remaining separate (OpenAI Developers). Deep Agents and OpenHands' SDK sit at the opposite pole. They are libraries you compose, with pluggable backends for state, filesystem, and sandbox, and no assumption that a terminal UI exists. The real question is who owns the loop's defaults: the vendor, tuned against its own model and evals, or you, with control of every seam and the obligation to tune it.

Classifier versus rules

Rules are auditable but breed approval fatigue; classifiers and reviewers cut fatigue but add a judge with nonzero miss rates. The field is converging not on one answer but on layering: deterministic denies no mode can override, a sandbox for blast radius, and a model-based judge only for what falls between.

Plugin-everything versus opinionated core

Dsh (lesson 23) is the extreme of one pole: no privileged core, even the agent loop is a replaceable plugin. Pi is close behind with a minimal core and in-process TypeScript extensions that can register tools, providers, and UI. OpenCode's npm-installable plugins sit in the middle. Claude Code and Codex keep an opinionated core and expose extension through bounded seams: lifecycle hooks with defined blocking semantics, skills, subagents, and MCP. Pi's extension docs note that an extension "can inspect prompts, tool calls, files, credentials, and session history," which is the price of total flexibility. Claude Code's hooks are narrower but have defined semantics. Exit code 2 from a PreToolUse hook blocks the call; other nonzero codes are non-blocking errors (hooks docs). Narrow seams are easier to secure and to keep stable across releases. Open cores are easier to bend.

Telemetry and data flows

Claude Code sends usage metrics by default on the Anthropic API, excluding code, prompts, and paths, and keeps plaintext transcripts under ~/.claude/projects/ for 30 days (Claude Code data usage). Codex sends anonymous usage and health metrics by default and redacts user prompts from opt-in OTel logs unless you enable them (Codex config docs). Qwen Code's usage statistics default to on. Dsh, per lesson 23, uploads the canonical session log to DeepSeek by default, a much larger data flow than metrics. For several open harnesses I could not find telemetry documented at all. That is not the same as "none," and your security review should treat it as a question to answer by reading the source.

Harness changes are releases

In April 2026 Anthropic traced a period of degraded Claude Code quality to three harness changes, none of them to the model: a default reasoning effort lowered from high to medium, a bug that cleared prior reasoning on every turn instead of once after an idle hour, and a system-prompt line capping text between tool calls at 25 words, which cost about 3% on their evals. Their fixes were process fixes: per-model evals for every system prompt change, soak periods, and gradual rollouts (Anthropic, Apr 2026). Read that alongside Anthropic's observation that "every component in a harness encodes an assumption about what the model can't do on its own" (Anthropic, Mar 2026), and alongside OpenAI's claim that retained reasoning and compaction alone roughly tripled one model's ARC-AGI-3 score (OpenAI Developers). The harness is not packaging around the model. It moves results by amounts that rival model upgrades, in both directions, so it needs the same release discipline. Lesson 18 and lesson 21 are how you give it that discipline.

Choosing or building

The real decision is not "which harness is best" but which of three relationships to have with one.

Adopt a harness when the work is software engineering by people at terminals and in IDEs, and your differentiation is not the agent itself. The frontier harnesses have absorbed years of tuning against their own models, and you get prefix-stable prompt assembly, tested compaction, OS sandboxes, and a permission system for free. Your job becomes configuration: a good AGENTS.md, a small set of skills, deny rules, hooks for your non-negotiables, and a sandbox policy you have actually tested. Prefer open source when you need to audit data flows or switch model providers; prefer a proprietary harness when its model pairing wins on your own evals.

Embed an SDK when the agent belongs inside your product: an operations console, a review bot, a support tool. Codex App Server and the Claude Agent SDK give you a production loop with your UI, your tools over MCP, and your approval flow. OpenAI's sample app puts the agent beside a dashboard and requires human approval before any consequential write. Deep Agents or the OpenHands SDK fit when you want a model-agnostic library and you are prepared to own tuning. Embedding a harness keeps its defaults, including its telemetry defaults, so read that row before you ship.

Build your own when the loop itself is the product or the research object: a non-coding domain whose tool surface and permission model look nothing like a repository and a shell, a model you are co-designing with its harness (as DeepSeek did), or a search loop over harness designs (lesson 22). Even then, steal the conventions from the converged rows. Read AGENTS.md, load Agent Skills, speak MCP, keep prompts prefix-stable, prune tool output before summarizing, and separate boundary from judge. Build only the rows where you have a reason to differ, as the capstone does.

Pitfall

Choosing on benchmark screenshots. A harness's leaderboard score is a harness-and-model pair measured on someone else's tasks, and lesson 19 shows how much the harness alone can move it. Run your top two candidates on twenty of your own tasks, with your own AGENTS.md, and compare cost per successful task, not headline pass rate.

A teardown worksheet

Use this for any harness, including one that did not exist when this lesson was written. Fill every field from a primary source and write "not documented" when you cannot. The blanks are findings.

harness:            # name, version or commit, date reviewed
sources:            # primary URLs only; mark anything secondary
loop:
  turn_shape:       # model call -> tool calls -> repeat; stop condition
  client_protocol:  # JSON-RPC / HTTP / RPC over stdio / in-process only
  state:            # where history lives; stateless requests? resumable? forkable?
context:
  prefix_stability: # what edits the prompt mid-session? appended or rewritten?
  compaction:       # trigger, order (prune tool output? summarize?), what survives
memory_files:       # names, scopes, load order, byte budget, trust handling
tool_surface:       # diff against lesson 2's table; what is missing, and why?
extension:
  hooks:            # events, handler types, how a hook blocks
  skills:           # Agent Skills compatible? progressive disclosure?
  mcp: / acp:       # client, server, both, neither
  plugins:          # in-process code? same privileges as the agent?
permissions:
  boundary:         # OS sandbox, container, VM, none; what the sandbox covers (MCP tools?)
  judge:            # rules / human / reviewer agent / classifier; what the judge can see
  hard_denies:      # what no mode can override
subagents:          # definition format, context isolation, depth and concurrency limits
surfaces:           # CLI / IDE / web / desktop / CI; one loop behind all?
embedding:          # SDK languages, protocol stability promises
data_flows:         # telemetry defaults, transcript upload, local retention, opt-outs
license:            # and what is NOT covered (model access, hosted services)
open_questions:     # every "not documented" above, with an owner to resolve it
In practice

This week, run the worksheet on the harness your team uses most, then on one open alternative from the tables. Answer every field from primary sources. Then do three concrete things. First, run a replay and check prefix stability on one real session: does anything rewrite the prompt mid-session? Second, write down your harness's boundary and its judge as two separate sentences. If you cannot, you have found your first governance gap. Third, list every "not documented" in the data-flows row and assign someone to answer it from source code before your next security review.

Key takeaways

Check your understanding

1. Your team is building an internal agent with a web UI and an IDE plugin. The first version runs the agent loop inside the web backend, and the IDE plugin reimplements the loop in TypeScript. What does the teardown suggest?

Two loops drift apart in prompt assembly, compaction, and permissions, so behavior differs by surface and every fix lands twice. Both frontier labs consolidated onto one harness for exactly this reason.
OpenAI tried this for VS Code and found MCP semantics a poor fit for streaming progress, diffs, and approvals. It later removed the Codex MCP-server path in favor of the App Server.
Correct. This is the Codex App Server pattern (Item / Turn / Thread over JSON-RPC, approvals as server requests that pause the turn), and Claude Code, OpenCode, and Pi follow the same principle of one loop with thin clients.
Moving the loop to one client just swaps which surface is special. The web UI would still need its own loop or would depend on a plugin process.

2. You add a feature that re-sorts the tool list alphabetically whenever an MCP server reconnects mid-session. Output quality is unchanged, but inference cost per session rises sharply. What is the most likely cause?

Correct. Cache hits require an identical prefix, and tools sit near the start of the prompt. Codex hit exactly this bug with nondeterministic MCP tool ordering, and the MCP 2026-07-28 spec now asks servers for deterministic tools/list order for this reason.
The scenario says output quality and behavior are unchanged. Ordering affecting tool choice would show up as a behavior change, not a pure cost increase.
Possible in principle, but the change you shipped was reordering. Re-sorting an otherwise identical list is enough to invalidate the cache.
Reordering doesn't lengthen the list. Compaction frequency depends on token count, not order.

3. A security reviewer asks why Claude Code's auto-mode classifier is not shown tool outputs, while Codex's auto-review agent is. Which answer best reflects the design tradeoff?

Neither source frames the choice in terms of model strength. It is a question of what information the judge should trust.
Anthropic runs a separate server-side probe that screens tool outputs and adds a warning to context. The classifier is only one layer.
The docs are explicit and opposite. Claude Code's classifier excludes tool outputs and Claude's prose, while Codex's reviewer sees tool calls and outputs but not hidden reasoning.
Correct. An under-informed judge and a manipulated judge are both real risks. Each vendor chose which one to minimize, and both layer the judge on top of deterministic rules and a sandbox.

4. A product team wants an agent inside their incident-management console that uses company tools and requires human sign-off before any remediation. They have no plans to tune an agent loop themselves. What is the best starting point?

Building makes sense when the loop is the product or a research object. This team explicitly doesn't want to own loop tuning, so building buys them cost without benefit.
Correct. This is the embed case: the application owns UI, context, tools, and approvals, while a production harness (Codex App Server, Claude Agent SDK, or a library such as Deep Agents) owns the loop. Check its telemetry defaults before shipping.
This loses the console's context, puts no approval gate inside the product, and creates an unaudited copy-paste path.
Leaderboards measure harness-and-model pairs on someone else's tasks. Evaluate candidates on your own tasks, looking at cost per successful task, embedding fit, and data flows.