The Harness Is Not Neutral

33,000 tokens. That is what Claude Code 2.1 sends to the model API before it reads a single character of your prompt — system blocks, tool schemas, injected scaffolding. The task was 22 characters: "Reply with exactly: OK." The harness payload was 1,500 times larger than the task itself.

OpenCode, running the same model on the same machine, sent 7,000 tokens for the same task. Same result. One sixth the overhead.

These numbers come from a wire-level analysis published on July 12 by Systima, an applied AI research team that spliced a logging proxy between two coding harnesses and a model endpoint to capture exactly what each one transmits. They are not estimates. They are metered API usage from captured request payloads. And they reveal something the agent industry has been quietly avoiding: the harness is not neutral infrastructure. It is an opinionated middleware layer that silently shapes cost, latency, context budget, and model portability — and almost nobody can see what it does.

The Floor

Systima's experimental design was simple. Take Claude Code and OpenCode, pin both to Claude Sonnet 4.5, strip all configuration to a bare workspace with no instruction files, no MCP servers, no user settings. Ask each to reply with "OK." Measure everything the harness sends before the model processes the prompt.

The breakdown:

  • System prompt: Claude Code — 27,344 chars across 3 blocks. OpenCode — 9,324 chars in 1 block.
  • Tool schemas: Claude Code — 27 tools, 99,778 chars. OpenCode — 10 tools, 20,856 chars.
  • First-message scaffolding: Claude Code — 7,997 chars of injected <system-reminder> blocks (agent catalogues, skill listings, user context). OpenCode — none.
  • Calibrated first-turn payload: Claude Code ~32,800 tokens. OpenCode ~6,900 tokens.

Tool schemas are the dominant term for both harnesses. Roughly 24,000 of Claude Code's 33,000 tokens are tool definitions — including background agents, cron scheduling, worktree management, and push notifications. Tools the user will never invoke in a "reply OK" task are nonetheless serialized into every single request.

Stripping tools entirely isolates the system prompt. Even with zero tools, Claude Code's instruction set weighs 6,500 tokens. OpenCode's is 2,000. The residual is behavioural doctrine: tone rules, safety guidance, task-management instructions, environment description. The harness has opinions about how the model should behave, and those opinions cost tokens on every turn.

The Model-Conditional Prompt

Here is a finding that should unsettle anyone building agent infrastructure. Systima re-ran the baseline test on Claude Fable 5 — Anthropic's newer, larger model. The gap narrowed from 4.7x to 3.3x. Not because OpenCode changed. OpenCode's payload was byte-identical across both models.

Claude Code's system prompt is model-conditional. It sent 27,787 characters of instructions to Sonnet 4.5 but only 10,526 to Fable 5. Tool schemas were also trimmed — from 99,778 to 82,283 characters. Same 27 tools, less doctrine.

Think about what this means. The harness is making decisions about what instructions to send based on which model it detects. It is adapting its behaviour to the model — which means it has a model-specific opinion about what the model needs to hear. Your harness is not a pipe. It is a translator with its own agenda, and the translation changes depending on who it's talking to.

The Configuration Multiplier

The floor is the best case — an empty workspace with no configuration. Real sessions do not look like this. Systima measured each layer that production usage stacks on top.

The instruction file. A real 72KB AGENTS.md from a production repository added roughly 20,000 tokens to every single request in both harnesses. Claude Code's metered total jumped from 39,005 to 59,243 tokens. OpenCode's went from 13,152 to 33,336. One file nearly quadruples a lean harness's baseline.

One operational footnote worth noting: Claude Code 2.1.207 ignored AGENTS.md entirely and only ingested the file when it was renamed to CLAUDE.md. An ignored instruction file is silent. You think your agent is following your guidelines. It isn't even reading them.

MCP servers. Five modest, credential-free MCP servers added 5,000 to 7,000 tokens per request, growing Claude Code's tool count from 27 to 69 and OpenCode's from 10 to 52. Production servers with rich APIs ship schemas several times larger.

The "everything" number. When Systima ran a real working configuration — instruction file, MCP servers, installed plugins — Claude Code's first request carried roughly 75,000 tokens with 118 tools. OpenCode's carried 90,817 tokens with 179 tools. Before the user has typed a word, the session is 40% of the way through a 200K context window.

That number is the one that matters. Context-window consumption is immune to prompt caching. An 85K-token bootstrap occupies the same space whether you paid 1x for a cache write or 0.1x for a cache read. It shrinks the room for actual code, forces earlier compaction, and spends yet more tokens summarizing what was there before.

The Cache Stability Problem

Prompt caching is the mechanism that makes large payloads economically survivable. Write the prefix once at a 1.25x premium, read it back at a tenth of the price thereafter. In theory, a 33K-token system prompt costs $0.16 to write and $0.013 to read — manageable if the prefix stays stable.

Systima hashed the tools array and system blocks of every request in their dataset to measure cache stability. The results were stark.

OpenCode emitted byte-identical prefixes across every request and every run. Three separate sessions produced the same tools bytes, the same system bytes, the same message bytes. Repeat runs wrote zero cache tokens and read everything. The prefix was a fixed, stable artifact.

Claude Code emitted three distinct request classes per session: a warmup probe, the main conversation, and subagent calls — each with its own prefix and therefore its own cache entry. Its system bytes also varied between sessions in the same workspace. Its first-message scaffolding varied between runs. On the identical file-summarise task, Claude Code wrote up to 54x more cache tokens than OpenCode.

Cache writes are billed at a premium. A harness that rewrites its prefix mid-session is not just paying for the write — it is invalidating the cache entry that subsequent requests would have read from. The cost compounds across every turn in the session.

The Subagent Multiplier

Subagent delegation is where token consumption explodes. Systima asked each harness to fan a small task out to two parallel subagents.

The same task that cost 121,000 tokens done directly cost 513,000 tokens when fanned out — a 4.2x multiplier. Every subagent pays its own bootstrap: its own system prompt, its own tool schemas, its own first-message scaffolding. Then the parent agent ingests each subagent's full transcript, which means the parent's context window grows by the entire subagent conversation.

Delegation is powerful. It is also the single largest token multiplier measured in the study. If your heavy sessions are surprising you on the billing dashboard, this is the first place to look.

There is one caveat that tempers the story. On multi-step tasks, Claude Code's total converged with OpenCode's. Claude Code batched tool calls into fewer requests — 3 requests for a write-run-test-fix loop, versus OpenCode's 9. Because the baseline is re-sent on every request, request count multiplies baseline. OpenCode paid its 7K baseline nine times. Claude Code paid its 33K three times. The totals landed in the same neighbourhood.

This means the harness's tool-calling strategy — parallel batching versus serial execution — is a co-equal variable with baseline payload size. A large-baseline harness that batches aggressively and a small-baseline harness that serializes can converge on the same total cost. The question is which failure mode you prefer: paying more per turn, or paying for more turns.

What Happens When You Switch Models

The Systima analysis shows what harnesses send. A separate account published the same day by Ploy, an AI website builder, shows what happens when you try to change what they send to.

Ploy migrated their production agent from Claude Opus 4.8 to GPT-5.6 Sol — released by OpenAI on July 12. The headline numbers were compelling: builds finishing in 3m42s versus 8m00s, at $2.22 per build versus $3.06, with a visual quality score of 0.970 versus 0.936. Two times faster, 27% cheaper, better output.

Getting there took weeks of engineering. Because the harness was not neutral.

The first discovery: tool-call behaviour is provider-specific and silently destructive. Ploy's code tool has 25 parameters — one required, 24 optional. Claude sends the two or three it's using and omits the rest. GPT-5.6 sends all 25, every time, inventing plausible values for the ones it doesn't need: offset: 0, timeout: 120000, siteId: "00000000-0000-0000-0000-000000000000".

Those invented values are indistinguishable from intended ones. Ploy's file-read implementation treated offset: 0 as a real argument. The result: 52% to 64% of GPT-5.6's file reads came back empty. The tool returned success: true either way, so the model had no signal that it was reading blank files. It just did the work worse, with more calls.

Prompting did not fix this. A tool-description directive to "omit unused parameters": still 25/25. Per-property "OPTIONAL, omit if unused" hints: still 25/25. OpenAI's strict mode: identical behaviour. This is baked into how the model emits function calls. The fix that worked was a schema transform at the provider boundary — rewriting every optional property to anyOf: [T, null] for OpenAI-family models only, then stripping nulls before validation. After the fix: empty reads went from 52% to 0%, and the agent needed roughly 30% fewer tool calls for the same work.

The second discovery: prompt caching is architecturally different between providers. Ploy's agent opens with a static prefix of roughly 29K tokens — tool schemas plus system prompt. On Anthropic, marking cache breakpoints is sufficient; the cache is org-scoped without key partitioning. Any conversation, any workspace, one shared entry. Cache hit rates ran 92% to 96%.

GPT-5.6 dropped partial-prefix matching entirely. Implicit caching now only creates whole-prompt entries keyed on the latest message. A new conversation sharing the 29K static prefix cached 0% of it. Every conversation re-billed the full prefix at the uncached rate, plus a 1.25x cache-write surcharge that GPT-5.6 applies whether or not you use caching.

The intended mechanism is explicit: prompt_cache_breakpoint markers plus a mandatory prompt_cache_key. And the key is part of cache identity. Identical prompt, different key: zero cache hits. Each key maps to a cache node that sustains roughly 15 requests per minute before traffic fans to other nodes with independent, cold caches.

Ploy's per-conversation key: 0% first-call hits. One global key: production traffic obliterated the 15 rpm budget; requests spilled to cold nodes. Per-workspace key — all conversations in a customer workspace share entries, per-key traffic stays low: 83.7% first-call hits. Total uncached input dropped 28%, and GPT-5.6's per-suite cost landed below Opus's.

Every dollar of the cost gap they had been staring at was cache misconfiguration, not model pricing. As the Ploy team put it: "If you're cost-comparing models and one of them has a cold cache, you are comparing your config, not the models."

The third discovery: reasoning replay is provider-specific. GPT-5.6's Responses API replays prior-turn reasoning as server-side item references by default. Ploy's conversations started intermittently failing mid-session with Item 'rs_...' not found. The fix was store: false, which makes the SDK request encrypted reasoning content as self-contained blobs instead of pointers to server state. With server-side reasoning state in the loop, the effective prompt can change upstream of you even when the bytes you send are append-only.

Three engineering discoveries. Three places where the harness was silently specialized around one provider's conventions. None of them were documented. None of them were obvious. All of them had to be discovered, one eval failure at a time.

The Opacity Problem

Both of these analyses required building custom instrumentation. Systima spliced a logging proxy between harness and endpoint. Ploy built a provider-boundary schema transform after triaging hundreds of eval traces. Neither harness — Claude Code, OpenCode, or any other widely used coding agent — provides wire-level visibility into what it sends. There is no --dry-run that shows you the full request payload. There is no dashboard that breaks down token consumption by component: system prompt, tool schemas, instruction file, MCP schemas, conversation history.

You are paying for tokens you cannot see, consumed by a harness you cannot inspect, tuned to a model you may not be running next quarter. This is the opacity problem, and it is the structural issue that both of these analyses expose from different angles.

The agent industry has spent two years optimizing for model selection. Benchmarks, leaderboards, pricing comparisons — all focused on the model. The harness was treated as plumbing: something you install, configure once, and forget. The data says otherwise. The harness is where the tokens go. The harness is where the cost lives. The harness is where model portability breaks. And it is the one layer in the stack with zero observability.

What to Do This Week

Three concrete actions, drawn from what these analyses revealed:

Instrument your harness. You cannot manage what you cannot measure. Build or install a logging proxy between your harness and the model endpoint. Capture the full request payload — not just the usage block, but the actual JSON body. Break down token consumption by component: system prompt, tool schemas, instruction files, MCP schemas, conversation history. The first time you see the breakdown, you will find at least one thing that surprises you.

Audit your instruction file weight. A 72KB AGENTS.md adds 20,000 tokens to every request. If your instruction file has accumulated cruft — deprecated guidelines, framework-specific notes that no longer apply, verbose style guides — trim it. Every token in that file rides on every turn of every session. Also: verify your harness is actually reading it. Systima discovered Claude Code 2.1 ignores AGENTS.md and only reads CLAUDE.md. An instruction file that isn't loaded is worse than no instruction file, because you think the agent is following rules it has never seen.

Build provider-boundary abstractions before you need them. The Ploy migration took weeks because their harness was silently specialized around Claude's conventions — tool-call style, caching model, reasoning replay. If you might ever switch models — and you will — build the seam now. Wrap tool schemas in a provider-aware layer that handles parameter omission differently for Claude-family versus OpenAI-family models. Abstract caching configuration behind an interface that can express both Anthropic's breakpoint model and OpenAI's key-partitioned model. The cost of building these abstractions upfront is a fraction of the cost of discovering them through production failures.

The harness is not neutral. It never was. The question is whether you treat it as the opinionated middleware layer it actually is — or keep pretending it's a pipe.