vi

blog

Claude Code subagent cost: cut 40-78% per implementer call

A Claude Code subagent loads tens of thousands of tokens of rules and skills at every start, then repeats them over dozens of turns. I measured it from transcripts, replaced it with a dedicated agent, and compared 3 real subagent-driven-development runs.

Published
Claude CodeSubagentsuperpowersToken cost
2 stacked horizontal bars: the top bar is long and nearly fills the frame, labeled 68,168 startup tokens for a general-purpose Claude Code subagent; the bottom bar is a tenth as long, labeled 6,781 tokens for a dedicated implementer agent

A Claude Code subagent is expensive because 3 things multiply: the context loaded at startup, the number of turns, and the model picked at call time. The delegation prompt barely matters. A dedicated agent cut startup context from 68,168 to 6,781 tokens in a measurement on a scratch repo; in 3 real runs, cost per Sonnet implementer call dropped 40-78% against the old baseline for the same role.

Subagent cost sits in startup context, not in the delegation prompt

I run about 20 Claude Code sessions in parallel on a single Mac, a separate session per project. The superpowers plugin is on by default, so long work follows the subagent-driven-development (SDD) skill: the main session orchestrates, each task gets a fresh implementer subagent and its own reviewer, and the fix loop has a round cap. When the plan is done, the strongest model reviews the whole branch. A turn is one round trip between the subagent and the model plus its tools.

7 days of numbers for the general-purpose agent, read only from usage fields in transcripts:

ModelCallsAvg turnsStartup contextTokens read from cache
Opus24941~60k1.54 billion
Sonnet35225~65k1.13 billion

A different sample of 10 subagents gave startup context (called ctx0 from here on) averaging ~83k tokens, while the delegation prompt was 1-2 thousand characters. The rest is what the subagent loads on its own, measured in characters:

  • User-level CLAUDE.md, ~11.5k characters.
  • The repo's AGENTS.md, the biggest part: one repo has a 95k-character file, about 50k tokens. My repos pull it in through an @AGENTS.md line in CLAUDE.md.
  • The skill list, ~25k characters.
  • SessionStart hook output, the agent list, tool definitions.

Every turn the subagent re-reads that context plus what has piled up since (tool output, earlier turns), 130-150k tokens per turn on average.

Cache reads inflate raw token counts, so use cost_units

Cache reads cost 0.1 times the input price. The 1.54 billion figure sounds scary, but most of it is cheap cache reads. What multiplies cost is the number of turns and the tool output carried through each one. So I measure with a weighted cost, in units of the model's base input price:

text
cost_units = input + 1.25 × cache_write_5m + 2 × cache_write_1h
           + 0.1 × cache_read + 5 × output

Compare cost_units within one model. Do not draw conclusions from raw tokens.

Measure ctx0 and cost_units from transcripts

Each subagent has its own transcript at subagents/agent-*.jsonl. ctx0 is input + cache_read + cache_creation of the first assistant message. The agent-*.meta.json file gives the agent type, model and call id; match that id to the Agent call in the parent session's transcript to see whether a model was passed.

To compare configurations I used a scratch repo that copies the rules of a real repo (the real repo stays untouched) and ran claude -p with a Sonnet parent session and an empty MCP set (--strict-mcp-config). The parent calls exactly 1 subagent for 1 fixed job: read go.mod, return 3 lines. Each configuration ran 2 times, and ctx0 matched to the token across the 2 runs. Whether the skill list reaches a subagent comes from grepping the skill_listing attachment in the transcript, not from asking the model.

Drop CLAUDE.md and the Skill tool: ctx0 down 93% in the measurement

Scratch repo carrying the rules of a repo with a 95k-character AGENTS.md:

Configurationctx0
general-purpose68,168
Dedicated agent, narrow tools, still loads CLAUDE.md55,437 (-19%)
Dedicated agent, omitClaudeMd: true, with Skill tool13,009
Dedicated agent, omitClaudeMd: true, no Skill tool4,857 (-93%)
Built-in Explore15,338
Custom Explore, omitClaudeMd, no Skill~4.2k

The 2 middle rows show the skill list weighs ~8k tokens and only appears when the agent has the Skill tool. The SDD prompts call no skill, so dropping the tool costs nothing.

Per the subagent docs, the frontmatter supports omitClaudeMd (start without the user, project and local CLAUDE.md files in initial context), tools, maxTurns and effort. The docs say omitClaudeMd needs Claude Code v2.1.271 or later, and a subdirectory's CLAUDE.md still loads when the agent works with files there. My implementer agent, trimmed:

yaml
---
name: sdd-implementer
description: Implements one plan task from its brief file
model: sonnet
effort: high
maxTurns: 60
omitClaudeMd: true
tools: Read, Edit, Write, Bash, Grep, Glob
---

The reviewer has only Read, Grep, Glob, Bash, no Write or Edit, and a 20-turn cap. After installing them, the probe gave ctx0 6,781 for the implementer and 6,208 for the reviewer, against 68,168 before. In the 2 real runs ctx0 was higher than the probe (10.9-11.8k); I have not isolated the cause.

The agent no longer loads AGENTS.md, so the delegation prompt has to name which AGENTS.md sections matter, by heading, plus the files to read. The agent lists the headings and reads only those sections, not the whole 95k-character file.

Most Claude Code subagent Opus cost comes from picking Opus at call time

Only 22% of 7-day Opus cost came from calls that passed no model. Only that slice can be changed with CLAUDE_CODE_SUBAGENT_MODEL, because per the docs (since v2.1.251) the model resolves in this order: the model parameter at call time, then model in the frontmatter, then the environment variable, then the main session's model. Most of the rest is Opus picked at call time: SDD in one repo took 52% of Opus cost (implementer, reviewer and final review all on Opus), and writing plans added 20%.

The built-in Explore uses the main session's model. Per the docs, when the main session runs Fable through a subscription plan, Explore runs on Opus. To change it, define an agent with the same name, Explore, and it overrides the built-in.

The model rules per role that I kept:

  • Implementer: Sonnet. Escalate to Opus at rounds 4-5 of the fix loop or for an architectural task.
  • Reviewer: Sonnet, but Opus when the diff touches security, permissions, concurrency or a data migration. The reviewer is the quality gate, so it does not get downgraded where the risk is.
  • Whole-branch final review: Opus.
  • CLAUDE_CODE_SUBAGENT_MODEL set to Sonnet as the default. I do not use the _FORCE variant (it forces every subagent onto one model) because the final review needs Opus.

I do not edit superpowers files (an upstream update would overwrite them) and I do not override general-purpose (every other subagent would lose CLAUDE.md). I also do not build business-role agents: an earlier test cost ~2.4 times the tokens, and the model delegated to the right role 0 of 5 times.

3 real runs: a direction, not an A/B test

Runs 1 and 2 were in the same Go repo with a web app, on the night of 4 Nov. Run 3 started after every session restarted with the new configuration. All numbers come from a script that reads usage from transcripts. The tasks differ in each run, so treat the numbers as a direction.

The baseline is the 7 days before the configuration change (up to 4 Nov 01:23 local time), in the same repo, on the old SDD setup with general-purpose. I split the baseline by role by reading the text of each call's description and delegation prompt. This is a heuristic classification that matched 251 of 252 Sonnet calls:

RoleModelCallsAvg turnsctx0cost_units per call
ImplementerSonnet13740.769.4k1.13M
ReviewerSonnet998.470.2k0.24M
ImplementerOpus4171.071.1k2.27M
ReviewerOpus4424.567.9k0.78M
Final reviewOpus2537.770.2k1.06M

cost_units on each row is in the base input price of that row's model, so Opus and Sonnet rows are not comparable.

Run 1: 1 SDD plan with 8 tasks, a whole-branch final review, and a fix pass after it. Sonnet implementer, Sonnet reviewer (Opus for 3 risky tasks: a migration, seeding real data, a data-exposure boundary), Opus final review.

RoleCallsAvg turnsctx0cost_units per call
Sonnet implementer919.210.9k0.25M
Sonnet reviewer65.710.2k0.08M
Opus reviewer37.310.2k0.12M
Opus final review (general-purpose, still loads the rules)13486.7k0.89M

The Sonnet implementer against the baseline for its role: cost_units per call from 1.13M to 0.25M (-78%), turns from 40.7 to 19.2, ctx0 from 69.4k to 10.9k. The Sonnet reviewer went from 0.24M to 0.08M (-67%).

Quality in run 1 (there is no earlier quality baseline to compare): 0 fix-loop rounds across all 8 tasks, no Critical or Important findings. The Opus final review found 8 Minor issues, 7 of which the per-task reviewers had already flagged; the fix pass handled 6 and the re-review confirmed them.

Run 2: 5 plan streams in parallel through a workflow, one worktree each. Each branch gets a final review, then they merge one by one into an integration branch and the full e2e suite runs.

RoleCallsAvg turnsctx0cost_units per call
Sonnet implementer1824.211.8k0.33M
Sonnet reviewer128.111.1k0.09M
Opus reviewer (risky diffs)36.311.2k0.08M
general-purpose Opus (final review, branch merges, e2e)54988.8k1.09M

The Sonnet implementer: 1.13M down to 0.33M (-71%), 27 implementer calls in runs 1 and 2 combined.

Run 3: one orchestrating session drove 4-5 plans in parallel for about 5 hours, after every session restarted with the new configuration. In total 126 subagent calls and 56.9M cost_units; the table lists 4 roles, 119 of the 126 calls.

RoleCallscost_units per callOld baseline, same role
Sonnet implementer450.68M (0.43M without the 5 capped tasks)1.13M
Sonnet reviewer350.11M0.24M
Opus reviewer (diffs touching security, concurrency)310.23M
general-purpose Opus (final reviews, re-reviews, 1 plan)81.01M

The Sonnet implementer is down 40% against the 1.13M baseline, and 62% if you leave out the 5 capped tasks (0.43M). The Sonnet reviewer is down 54%. The 2 Opus rows have no same-role baseline to compare. Every implementer and reviewer went to the right slim agent, starting at a ctx0 of 10-12k against 70-90k before, and every call named a model, even though the rule was only a paragraph in CLAUDE.md, with no enforcing hook.

What broke in the 3 runs: maxTurns, repo rules, diff bundles

The general-purpose Opus agent is the next bottleneck. In run 2 it made 5 of 38 calls but used 5.46M of 12.8M cost_units (43%), because it still loads ~89k of rules context every turn. The 12.8M total adds Sonnet and Opus units, each in its own model's base price, so in money terms Opus's share is larger. The next job is a dedicated agent for final review and branch merges.

ctx0 is now only 7% of an implementer's cost. Most of the remaining cost is probably tool output carried through every turn, such as e2e logs and screenshots. That is a hypothesis; I have not measured it.

A maxTurns cap of 40 is too low for long tasks. In run 2, 4 of 5 streams stopped with the message "completed without calling StructuredOutput". The real cause was 5 implementers hitting exactly the 40-turn cap mid-task, counted by message id in the transcript. 40 is not a workflow cap: general-purpose in the same workflow ran up to 138 turns.

5 cap hits gave 4 stopped streams because one stream had 2 implementers hit the cap, on task 1 and task 5. On task 1 the script retries once with a stronger model when an agent returns an empty result, and that retry rescued it; on task 5 it did not, so the stream stopped. 3 other streams had 1 implementer hit the cap each and stopped. The fifth stream ran through to the merge.

The web task in run 1, with an 18.7-minute e2e run, already used 39 of 40 turns. I raised the cap to 60 at 03:03 after run 1, but the orchestrating session in run 2, opened at 02:20, kept cutting implementers at 40 turns from 07:41 to 08:17, which is 4.5-5 hours after the agent file was edited. With the cap at 60, run 3 still had 5 of 45 implementers hit it (web screen tasks with e2e): 60 is still tight when a task has e2e. 3 lessons:

  1. Editing an agent in a plugin may not reach a running session. Another session picked up the new agent and its edited body right away while the orchestrating session kept the old cap, and I cannot explain why. Per the docs, agents in ~/.claude/agents/ are file-watched so an edit takes effect right away, while plugins load when a session opens or when you run /reload-plugins (plugin loading). The safe move per the docs, which I have not tried myself: after an edit, run /reload-plugins or reopen the session.
  2. The orchestration script should treat a null result as a hit cap and call again with "continue the unfinished part". Through the regular Agent tool, a subagent that hits maxTurns returns output marked partial and the main session can resume that agent, per the docs. Inside a workflow the result becomes null; a community issue describes exactly this misreport (I could not read the issue's content). In run 3, all 5 implementers that hit the cap of 60 were resumed by the orchestrating session through the Agent tool and finished.
  3. The error message names no cause; you have to count turns in the transcript to find it.

Agents missing repo rules. Run 1 had 3 Minor issues of this kind: leaving orphaned CSS although the rule existed, using git stash to prove a test fails before the fix, and editing a demo fixture to match a key that a new test catches.

The git stash case is the dangerous one, because the stash stack is shared across every worktree and session of the repo: another session pops it and takes your changes. I added a ban on git stash to the implementer agent and require it to prove the test fails by running the new test before writing code. Across 66 subagent transcripts after the rule was added, git stash was used exactly once, in the run 2 orchestrating session that still ran the old agent definition, so the rule's effect is not proven. Sessions opened after the restart: 0 uses.

The orchestrator does not know what the agent already carries. The main session assumed the agent lacked the temp-file rules and pasted them into each prompt, while the agent already had them. The lesson: list for the orchestrator exactly which rules each agent ships with.

The diff bundle for the reviewer balloons. The script that packs a diff for the reviewer produced 113-133 KB bundles when the diff had very long lines, such as one-line JSON. The fix I propose (not done yet): drop long-line files from the plain diff and print a separate word-diff.

Rules drift between agents. To keep the rules from drifting between copies, the agent body is generated from one shared rules file with a <!-- include: rules.md#Section name --> marker, and the build script errors if a section is renamed. Including a whole section is too coarse for a read-only agent: Explore got file-writing rules it cannot use.

After run 2: step-by-step reports and a dedicated final review

2 more changes followed run 2:

  • The implementer now writes its report from the start and commits after each green test step, so the next run reads the report and git log and carries on. The idea matches Anthropic's article on harnesses for long-running agents: a progress file plus git history tells a fresh session where the work stands. This went in around 10:30 on 4 Nov, I have not measured its effect on its own, but in run 3 all 5 implementers that hit the cap finished after a resume, and the orchestrating session credits this rule.
  • A dedicated final-review agent (Opus, no CLAUDE.md, read-only) ran as a canary next to general-purpose. The threshold to switch: it catches every Critical and Important finding of the old setup, and cost drops by at least 30%. Results below.

Canary for final review: the dedicated agent is 28% cheaper but missed 1 Important finding

The canary ran on one branch of a side project (Go backend, TypeScript web UI), reviewed after all tasks were done. All 3 versions got the same review package (diff, plan, ledger) and the same prompt. The project session checked every finding against the code.

VersionReal Important findings caughtTurnsctx0cost_unitsTime
general-purpose Opus (current)2/22085.6k0.61M226 s
Dedicated final-review agent, Opus, no CLAUDE.md1/2279.5k0.44M (-28%)254 s
Same dedicated agent, Fable0/2369.5k0.58M (-5%)397 s

The dedicated agent failed both thresholds. It missed 1 real Important finding: a config field written as null, or as a folded multi-line string, was classed as less privileged than it really is. Cost dropped only 28%, under the 30% bar.

The smaller ctx0 (9.5k against 85.6k) did not pay for itself here. Without the repo rules loaded up front, the agent spent more turns reading (27 against 20), which ate most of the saving and came with a lost real finding. For per-task implementers and reviewers the same trick still pays, as the tables above show. My explanation for the difference: a whole-branch review touches many rule sections at once. I did not measure that separately.

Fable as the final reviewer gave no gain: 0 of the 2 real Important findings caught, the slowest run, and cost close to Opus. Each version still caught a few real Minor findings the other versions missed, so a second opinion has some value, just not enough to replace the current one.

Decision: the final review stays general-purpose on Opus. The dedicated agent stays in the kit, unused. Small sample: 1 branch, 1 run per version, 2 Important findings.

Run 3 in full: 4 places that still cost money

  1. The turn cap keeps the work, not the cost. 5 of 45 implementers (web screen tasks with e2e) hit the 60-turn cap. The orchestrating session resumed each one, and the work finished thanks to the rule "write the report early, commit after each green step". But a resumed agent keeps its growing context, so these 5 tasks cost 44% of all implementer spend; the 2 biggest ran about 100 turns each. Fix: when writing the plan, split any task likely to need more than about 40 turns.
  2. Fix loops can grow in scope. One branch's final review led to 5 fix rounds, one escalated to Opus, about 7M in total (about 13% of the run). The code was a hand-written parser behind a security rule, "never report a lower permission level". Each re-review probed further and found one more input shape that slipped through. Every one was a real bug, but each one opened a new round. The right fix only came in round 4: treat any non-simple input as the most privileged level (fail-closed). The rule I took from it: a re-review checks only the previous round's findings, and new findings go to the ledger instead of a new round; if a hand-written parser or guard still leaks at round 2, switch to fail-closed instead of patching shape after shape.
  3. Side jobs still went to the fat agent. Re-reviews after the final review and branch merges ran as general-purpose, starting at about 87-90k context: about 6M across 6 calls. A slim agent does the same work at about 0.25M per re-review.
  4. The orchestrator is the next cost. The orchestrator alone cost 22.7M, about 29% of the run's total cost (on top of the 56.9M for subagents). Its average context was 219k, with a peak of 367k and 8 compactions in 5 hours, because one session held 4-5 parallel plans. I have not changed anything here yet; it is an open point.

I folded the first 3 lessons into one general rule for every repo that runs plans with subagents: scoped re-reviews on the slim reviewer, branch merges on the slim implementer, fail-closed at round 2, and splitting tasks over about 40 turns. I have not measured that rule's effect.

Not measured yet: total savings across the whole system

3 runs show cost per implementer call and ctx0 dropping clearly; run 1 quality passed, but there is no earlier baseline to compare. They do not show total savings for the whole system. Run 2 lacks full quality numbers because 4 streams stopped before the final review step; the stream that ran through had 0 fix rounds on its task, and there is nothing more. Run 3 ran its full 5 hours, but the orchestrator cost (22.7M) sits outside the per-call numbers above. The 7-days-before versus 7-days-after numbers for the whole system should be ready around 11 Nov.

Update 11 Nov: the 7-day before and after totals will be added here.

Related posts

all posts →