Skip to content

AI CODING ASSISTANTS

Claude Code Subagents Guide: One Turn Instead of Eight

A claude code subagents guide built by dispatching this repository's own two subagents. Same audit, same answer: 1 parent turn against 8, and a quarter of the parent context.

Every claude code subagents guide explains the file format and stops there. This one dispatches them. We took a job this repository already had — run its three editorial checks and report what failed — and ran it six times: three times delegated to a committed project subagent, three times inline in the main conversation. Same answer every time. Delegated it took the parent one turn; inline it took seven, then eight, then eight. Measured 2026-09-09 on Claude Code 2.1.266, Windows 11, against the two subagents in this repository's own .claude/agents/.

Key takeaways

  • The same audit cost the parent 1 turn delegated against 7 to 8 inline, and 6,573 characters of parent context against 26,255 — a 4.0x difference.
  • The subagent's 17,382 characters of command output never entered the parent conversation. It is in a separate transcript file, and that file is the whole mechanism.
  • Delegation is not a cost saving. Three runs each: $0.1033 delegated, $0.1309 inline — but two of the three inline runs were individually cheaper than every delegated run.
  • A subagent's model: field is honoured: with the parent on claude-sonnet-5, modelUsage came back carrying both claude-sonnet-5 and claude-haiku-4-5.
  • The --output-format json envelope carries a subagent_stats block — spawned, refused, killed, depth — that the binary marks @internal.

What delegation buys

Not intelligence, and not money. A subagent is a second conversation: its own context window, its own system prompt, its own tool list. What crosses back is the summary it writes at the end. Everything else — the commands, the output, the reasoning — stays on the other side.

That is worth stating as a cost, because it is one. The subagent pays its own prompt from scratch, including your CLAUDE.md. You are buying a clean parent conversation and paying a second prompt for it. Whether that trade is worth making is the thing this article measures rather than asserts. If you are new to the tool itself, the complete guide covers the surfaces this one assumes.

The experiment

One job, written once. This repository has an editorial gate — three npm scripts that check heading anchors, content rules and types — and it has a committed subagent whose entire purpose is to run them:

.claude/agents/content-auditor.md
---
name: content-auditor
description: Runs the repository's editorial checks against content/ and reports only the failures. Use before publishing an article, or after editing any file under content/.
tools: Bash, Read, Grep, Glob
model: haiku
color: yellow
---

You audit this repository's articles against its own editorial checks. You do not
write or edit files; report findings and stop.

Two arms, three runs each, both on Haiku, both with the same three npm commands on the allowlist:

Terminal
# delegated
claude -p "Use the content-auditor subagent to audit this repository's content,
  then tell me in one line what it reported." --output-format json --model haiku

# inline — the Agent tool removed, so it has to do the work itself
claude -p "Audit this repository's content by running its editorial checks:
  npm run check:anchors, npm run check:rules and npm run typecheck.
  Then tell me in one line what you found." --output-format json --model haiku \
  --disallowedTools "Agent,Skill"

Turns and cost come from the JSON envelope. Parent context is measured the only way that is honest — by reading the session transcript off disk and counting the characters of message content and tool results that landed in it.

The result

ArmTurnsParent contextDenialsCost, 3 runs
Delegated1, 1, 17,373 · 5,943 · 6,4040, 0, 0$0.1033
Inline7, 8, 825,015 · 26,865 · 26,8863, 4, 4$0.1309

All six runs returned the same answer — no blocking failures, 89 known ordered-list violations across 13 articles — so nothing was traded away for the shorter conversation.

Three things in that table are worth separating, because they are usually collapsed into one claim.

Turns collapsed to one. Not "fewer". The delegated parent called Agent once and wrote its summary. There is no second round trip, because there was nothing left for it to decide.

Parent context fell 4.0x, from a mean 26,255 characters to 6,573. That is the number people mean when they say subagents save context, and it is real.

Cost did not move in a useful direction. The delegated arm is cheaper in total, but that total is carried by one expensive inline run at $0.0756; the other two inline runs cost $0.0274 and $0.0279, each below every delegated run. On three runs a side, the honest reading is that cost is a wash and turns and context are not.

Where the work actually goes

The delegated parent transcript contains exactly one tool call. The work is in a second file, and it is not hidden — it is a sibling of the session:

~/.claude/projects/<project>/
<session-id>.jsonl                                   ← the parent
<session-id>/subagents/agent-aa62b5fff93eb2c06.jsonl ← the subagent
<session-id>/subagents/agent-aa62b5fff93eb2c06.meta.json

Every entry in the subagent file carries isSidechain: true, an agentId, and an attributionAgent field naming the definition it came from. The sidecar is small and useful:

agent-a86b2f011d9eec794.meta.json
{
  "agentType": "content-auditor",
  "description": "Audit repository content",
  "toolUseId": "toolu_01Q6CCnWZBwQoVeSJSC4Z4N9",
  "spawnDepth": 1,
  "requestShape": "foreground",
  "requestNonInteractive": true
}

spawnDepth is how you find nested work later, and toolUseId is what ties the subagent back to the exact Agent call in the parent that started it. Across our six spawns requestShape came back background four times and foreground twice, from prompts that never asked for either — and it matched the envelope's started_in_background count exactly in all six.

The three delegated runs put 17,642, 16,754 and 17,750 characters into those sidechain files. That is the check output, the npm noise, the intermediate reasoning. None of it is in the parent conversation, and the parent still got the number right.

The envelope nobody reads

--output-format json returns a subagent_stats object. It is not in the help output and the schema in the binary describes it as @internal, but it is the only complete accounting of what a session's delegation actually did:

claude -p --output-format json
{
  "spawned": 1,
  "requested": { "background": 0, "foreground": 0, "unset": 1 },
  "started_in_background": 1,
  "max_depth": 1,
  "spawned_by_subagents": 0,
  "completed": 1,
  "failed": 0,
  "killed": { "parent": 0, "user": 0, "system": 0 },
  "refused": { "depth_limit": 0, "concurrency_limit": 0, "budget": 0 },
  "by_type": { "content-auditor": 1 }
}

The block has one blind spot worth knowing before you build anything on it: it does not see agents started by a script. A workflow that ran three of them reported spawned: 0 here, which dynamic workflows measures against the identical fan-out written as three Agent calls.

The field descriptions are in the binary and they are precise about things the documentation is not. max_depth is "Deepest spawn: 1 = started by the main thread, 2 = by a depth-1 subagent." killed.system covers "the --max-budget-usd halt, the sweep of background subagents when an SDK or IDE client interrupts, or -p giving up on a background subagent still running at its wait ceiling." refused counts only the three named caps — "other denials, such as an unknown agent type, are not counted."

What a subagent inherits

It does not start clean. Grepping the sidechain transcripts for a line unique to this project's CLAUDE.md finds it in all three delegated runs, delivered as an attachment of type instructions — the same way the parent gets it.

So your project instructions are paid for twice: once in the parent, once in every subagent you spawn. This repository's CLAUDE.md is 9,586 bytes, and that is the floor under every delegation.

What it does not inherit is the conversation. The subagent gets the prompt the parent wrote for it and nothing else — no history, no earlier files, no prior decisions. That is the isolation working as designed, and it is also the single most common reason a delegated task comes back wrong: the parent knew something it did not think to pass on.

The model: field is real, and it is the most underused line in the format. Running the same delegation with the parent on Sonnet returns two models in one session's modelUsage:

modelUsage keys, parent started with --model sonnet
claude-sonnet-5
claude-haiku-4-5-20251001

Both, because content-auditor declares model: haiku and got it. An expensive session can hand its bulk grunt work to a cheap one without the operator changing anything.

Write one

Nothing about the format is hard. The value is in the three fields people leave off.

.claude/agents/dep-checker.md
---
name: dep-checker
description: Reports outdated and vulnerable npm dependencies. Use before a release, or when the user asks what needs upgrading.
tools: Bash, Read, Grep, Glob
model: haiku
---

Run `npm outdated` and `npm audit --omit=dev`. Report only packages that are
behind a major version or carry a high or critical advisory, one line each, with
the current and target versions. Do not edit any file. If both commands are
clean, say so in one line and stop.
  • description is the trigger, not a label. It is matched against what the user is asking for, so write it as the request you expect to hear. This is the same mechanism that decides whether a skill ever gets chosen, and it fails the same way.
  • tools is a routing decision as much as a restriction. See the next section but one.
  • model is where the saving is. A reporter that greps and summarises does not need your session's model.

Confirm it loaded without spending a token — the probe from our earlier article still works on 2.1.266:

Terminal
claude --agent nope --init-only
# → --agent 'nope' not found. Available agents: claude, content-auditor,
#   Explore, general-purpose, Plan, roadmap-scout, statusline-setup

Seven here: five built in, two from .claude/agents/.

The built-in five

Their definitions are in the binary, and they are not variations on a theme:

AgentToolsModelGets your CLAUDE.md
general-purposeeverythinginheritsyes
Exploreall except Agent, Edit, Write, NotebookEdit, ExitPlanMode and the Artifact toolsinherits, capped at opusno
Plansame exclusions as Exploreinheritsno
statusline-setupRead and Edit onlypinned to sonnetyes
claudeeverythinginheritsyes

Two of those cells are worth stopping on. Explore and Plan both set omitClaudeMd: true — your project instructions are deliberately withheld from them, which is a sensible default for a search agent and a trap if you assumed otherwise. And statusline-setup is pinned to Sonnet regardless of what your session is running.

Explore also carries two different descriptions in the binary, a full one and a lean one, and they disagree: the full text offers quick, medium and very thorough as search-breadth values, the lean text offers only medium and very thorough.

The limits

Three caps, all read out of the 2.1.266 binary, all with an environment variable and a default:

  • Nesting depth: 3. CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH. Over it, the spawn is refused with "Subagent nesting limit reached (depth N of M). Complete this task directly using your tools instead of spawning another agent."
  • Concurrent subagents: 20. CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS. Over it: "Concurrent subagent limit reached. You can run N subagents at once. Do not retry."
  • Budget. --max-budget-usd, which stops running background subagents and counts them under killed.system.

A subagent is not the only way to get work off the critical path, and it is the most expensive of the three. Claude Code background tasks measures the other two — a detached session and a detached shell command — and only one of them survives the session that started it.

The depth cap is mostly theoretical, and the binary says so in the schema: "depth_limit stays near zero in practice: a subagent at the nesting limit is normally not offered the tool at all." The reason is in the table above — Explore and Plan do not have Agent in their tool list, so the two agents most likely to be spawned in bulk cannot spawn anything.

What did not work

The first attempt to prove the tools: list is enforced proved nothing. We asked content-auditor — whose tools line is Bash, Read, Grep, Glob, with no Write — to create a file. It refused, and the refusal looked like exactly the result we wanted. It was not: the subagent made zero tool calls and quoted its own system prompt back at us, which says "You do not write or edit files; report findings and stop." That measures the prompt, not the allowlist. A real test needs an agent whose instructions ask it to write and whose tool list does not let it, and that pair is not what this repository ships.

The first run measured the permission harness instead of the subagent. Run under --permission-mode acceptEdits, the subagent was refused Bash and came back asking for approval — and a subagent in -p mode has nobody to ask. The four PowerShell and Bash denials in that envelope looked at first like the subagent violating its own tools list, since PowerShell is not on it. Reading the sidechain transcript corrected it: the subagent made one Bash call, and all four denials were the parent retrying after the subagent gave up.

⚠️ The inline arm's denial count is a property of this machine. Every inline run burned three or four turns being refused PowerShell before falling back to Bash, because this is Windows and the parent has both. The subagent went straight to Bash on the first try — its tools list does not name PowerShell, so it never considered it. That is a real advantage of a narrow tool list, and it also means part of the seven-against-one turn gap is a Windows tool-selection artefact rather than pure delegation overhead.

⚠️ Context is measured in characters of transcript, not tokens. The transcripts are the only place the parent's and the sidechain's content can be separated and counted the same way. The 4.0x ratio is between two numbers derived identically; it is not a token count and is not offered as one.

Best practices

  • Delegate output volume, not difficulty. The win is proportional to how much noise the task generates. A subagent that reads two files and answers is pure overhead.
  • Set model: on every reporter. A grep-and-summarise agent on Haiku under a Sonnet session is the cheapest structural saving in the format.
  • Write description as the request you expect, not as a job title. It is matched against the user's words.
  • Keep the tool list tight, for routing as much as for safety. Ours saved three turns of failed PowerShell attempts by not offering the option.
  • Pass the context it cannot see. The subagent gets your prompt and your CLAUDE.md, and nothing from the conversation. Whatever you know that it needs goes in the prompt.
  • Read subagent_stats when you automate. refused and killed are the difference between "the agent found nothing" and "the agent never ran".

Common mistakes

Expecting a subagent to save money. Symptom: the bill does not move, or goes up. It re-pays the system prompt and your CLAUDE.md on every spawn. It saves your context window and your turn count; budget for it as an overhead you accept, not a discount.

Assuming it can see the conversation. Symptom: it asks for something you established ten turns ago, or solves the wrong problem confidently. It starts with your prompt and nothing else. This is the failure mode of delegation, and it is not fixable from the agent's definition.

Trusting the parent's permission_denials. Symptom: a CI check reports zero denials while a subagent quietly did nothing. The subagent's denials are in its sidechain transcript only.

Writing the guardrail into the prompt and calling it enforcement. Our own content-auditor says "you do not write or edit files" — and that sentence, not the tools list, is what it quoted when it refused. Prompts are followed; tool lists are the mechanism. Set both and rely on the second.

Reaching for a subagent when the job needs a hook. If it must happen every time regardless of what the model decides, delegation is the wrong shape entirely — that comparison is a separate measurement.

Conclusion

Delegate when the work is verbose and the answer is short. On this repository's editorial audit that meant one parent turn instead of eight and a quarter of the parent context, for the same answer, at no reliable saving in dollars. Write the description as the request you expect to hear, set model: on anything that only reports, keep the tool list narrow, and read subagent_stats before you believe a delegated job did what it said. If your job is a standing rule rather than a request, stop here and write a hook instead.

Frequently asked questions

What is a Claude Code subagent?
A separate agent loop with its own context window, its own system prompt and its own tool list, started by the Agent tool. It does the work in its own conversation and returns only a summary to yours. We measured the same repository audit both ways: delegated it cost the parent one turn and 6,573 characters of context, run inline it cost seven or eight turns and 26,255.
Do Claude Code subagents actually save context?
Yes, and the ratio was about four to one on our job. Three delegated runs put a mean 6,573 characters into the parent conversation against 26,255 for three inline runs, because the subagent's 17,382 characters of command output stayed in a separate transcript file. The parent saw one Agent tool call and one summary paragraph.
Are subagents cheaper than doing the work inline?
Not reliably. Across three runs each the delegated arm cost $0.1033 and the inline arm $0.1309, but two of the three inline runs were individually cheaper than every delegated run. Delegation buys turns and parent context, not dollars, because the subagent re-reads the project instructions and pays its own prompt.
Where are Claude Code subagent transcripts stored?
In a subagents directory beside the parent session file, at ~/.claude/projects/PROJECT/SESSION-ID/subagents/agent-ID.jsonl, with an agent-ID.meta.json sidecar naming the agent type, the spawn depth and whether it ran in the foreground or background. Every entry carries isSidechain true and an attributionAgent field.
How many subagents can Claude Code run at once?
Twenty by default, raised with CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS. Nesting is capped at depth 3 by default, raised with CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH. Both values are read out of the 2.1.266 binary. In practice the depth cap is rarely reached, because Explore and Plan are not given the Agent tool at all.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.