DEV Community

Cover image for Subagent Permissions Are Inherited by Default — and the Caps Live Inside Your Code
Logan for Waxell

Posted on Originally published at waxell.ai

Subagent Permissions Are Inherited by Default — and the Caps Live Inside Your Code

Read the Claude Agent SDK's subagent reference closely and one line does more architectural work than the rest of the page combined. Describing the tools field on an agent definition, the documentation says that if the field is omitted, the subagent "inherits every tool available to subagents." Not a subset. Not a prompt-negotiated scope. The full set, by default, because no one named a smaller one.

That is not a bug, and it is not carelessness. It is the ergonomically correct default for a feature whose main purpose is to let a developer delegate work without ceremony. But it establishes something worth stating plainly, because most teams shipping multi-agent systems in 2026 have not stated it: in current agent architectures, the delegation boundary is a context boundary, not an authority boundary. Spawning a child isolates what the child knows. It does not, by default, narrow what the child can do.

The defaults are documented, and they compound

The same page publishes the other three numbers that govern how far a delegation tree can spread. Spawn depth is capped by CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH, which defaults to three layers of subagents below the main agent. Concurrency is capped by CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS, which defaults to twenty running at once. Spend is capped by maxBudgetUsd in TypeScript or max_budget_usd in Python, and its default is no limit at all.

Read those together rather than separately. The concurrency ceiling is global across the tree — the documentation is explicit that it counts every subagent spawned through the Agent tool, not every subagent under one parent — so the fan-out is bounded at twenty concurrent, not twenty cubed. That is the reassuring half. The unreassuring half is that depth and concurrency bound shape while saying nothing about cost, which is why spend is a separate knob, and the one that ships unset.

Two further details matter for anyone reasoning about blast radius. Subagents can themselves spawn subagents, so a single prompt can grow into a tree rather than a list. And the decision to delegate belongs to the model: the documentation states that Claude decides on its own when to spawn a subagent and how many, notes that Opus 5 delegates more readily than earlier models, and tells operators to set the limits rather than trust the steer.

A version caveat cuts the same way. These defaults describe the TypeScript SDK from v0.3.219 and the Python SDK from v0.2.127 onward; on earlier releases, the documentation says, some of these limits are missing or default differently. A team running a version it pinned some time ago may have fewer bounds than the current docs describe, and nothing tells them so.

Inheritance is a security primitive, not an implementation detail

The academic work has converged on the same framing from the other direction. In When Child Inherits: Modeling and Exploiting Subagent Spawn in Multi-Agent Networks, submitted to arXiv on 8 May 2026, Ziwen Cai, Yihe Zhang and Xiali Hei model multi-agent systems specifically through the lens of what a spawned child receives from its parent. Their analysis names four ways current frameworks can violate trust boundaries: insecure memory inheritance, weak resource control, stale post-spawn state, and improper termination authority.

Note how those map onto the knobs above. Insecure memory inheritance is the context question. Weak resource control is the spend question. Improper termination authority is the question of who can stop a child three layers down that was spawned by a decision the operator never made. The paper's conclusion is the sentence to carry away: inheritance is not merely an implementation detail, but a central component influencing the security of multi-agent systems. Its point is architectural rather than vendor-specific — the risks are demonstrated across real agent frameworks rather than pinned on one. Which raises the obvious question: is anyone converging on a common answer?

Thirteen scaffolds, and no agreement on resource management

Benjamin Rombaut's Inside the Scaffold: A Source-Code Taxonomy of Coding Agent Architectures, submitted to arXiv on 3 April 2026 and revised on 10 April, is the most direct evidence available. Rombaut read the source of thirteen open-source coding agent scaffolds at pinned commit hashes and characterised each across twelve dimensions organised into three layers: control architecture, tool and environment interface, and resource management.

The headline finding is that scaffold architectures resist discrete classification. Tool counts range from zero to thirty-seven. Context compaction spans seven distinct strategies. Eleven of the thirteen agents compose multiple loop primitives rather than relying on a single control structure. Most usefully for this argument, Rombaut reports that the dimensions converge where external constraints dominate — tool capability categories, edit formats, execution isolation — and diverge where open design questions remain, naming context compaction, state management and multi-model routing among the divergent ones.

That split is the finding to sit with. Convergence arrived where something external forced it. The dimensions still in flux are the ones about how a run manages itself over time. An enterprise running agents built on more than one of these scaffolds is therefore running more than one answer to that question, expressed in more than one place, with no shared vocabulary between them.

The controls are real. Their location is the problem.

It would be wrong to characterise this as an industry that has not noticed. It plainly has. Arize's agent-harness guide treats subagent delegation as a first-class surface with its own trace evidence — parent and child IDs, handoff reason, task, tool scope, returned result — and its harness-selection checklist asks directly whether permissions are checked at execution time and whether high-risk actions can require human approval. Its section on ownership argues that sensitive actions should be enforced through deterministic permissions, hooks and approval mechanisms that do not depend on the model voluntarily following an instruction. That is a pre-execution enforcement position, argued by an observability vendor, and it is correct. (Arize announced on 13 August 2026 that it had signed a definitive agreement to be acquired by Dynatrace.)

Arize's own guide contains the line that makes the structural point better than a competitive framing would: what matters is not the product label, but "where the operational decisions live."

So: where do they live? In every source above, inside the harness. CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH is an environment variable passed to a query. max_budget_usd is a query option. Tool scope is a field on an agent definition in application code. Lifecycle hooks are functions registered by the application that owns them. Each is a real control, correctly placed for that application's developer — and out of reach of the person accountable for the estate.

Better defaults do not close that gap. A platform lead cannot set a spend ceiling inside an SDK call in another team's repository; a security engineer cannot register a lifecycle hook in a scaffold they did not write. A cap set per query, in process, by the developer is a build-plane control being asked to do operator-plane work. The delegation boundary is exactly where an estate-wide policy ought to be evaluated, and it is currently the boundary at which authority is inherited instead.

How Waxell handles this

Waxell's position is that the cap belongs outside the process that would otherwise have to volunteer to honour it.

Waxell Observe auto-instruments Python agent frameworks and enforces policy before the next step executes. Its multi-agent handling is built for the shape described above: a coordinator dispatches to a planner, which spawns researchers, which call tools — and Observe traces the full tree, with parent-child relationships detected automatically and session context propagating through nested calls without manual wiring. The 50+ policy categories that evaluate against that tree include the ones this problem needs, by name. Cost sets spending and token limits per agent, per user and per session. Rate-Limit caps invocation frequency to prevent runaway loops. Kill halts any agent or workflow immediately. Delegation is a published category in its own right. Rules are configured in the dashboard, not in the agent's code — which is the point.

Waxell Runtime answers the termination-authority half. For workflows built with the Waxell SDK, policies gate each step before it executes rather than scoring it afterward, and kill switches operate at the agent, workflow and session level. When a policy requires human input, the workflow checkpoints and pauses, then resumes from the exact step — which is what makes an approval hold usable on a long delegation tree rather than merely expensive.

Waxell MCP Gateway covers the tool calls those children make. Every tools/call that traverses the gateway is identity-resolved, policy-checked, fingerprinted and logged before the upstream sees it, and checked again on the way back. The relevant property here is indifference to tree depth: a tool call from a third-layer subagent is evaluated against the same tenant policy as one from the main agent, because the gateway sits in the protocol rather than in the process. A newly discovered tool stays at Pending review and cannot be called until an admin approves it, and policy changes reach the fleet within 30 seconds. Coverage is a function of what is configured through it — an agent holding direct upstream credentials is not in its path.

Observe is Python-scoped, and Runtime governs agents built on the Waxell SDK rather than arbitrary ones. Those are real boundaries, and they are worth knowing before assuming coverage.

FAQ

What are subagent permissions?
Subagent permissions are the set of tools and capabilities a spawned child agent is allowed to use. In the Claude Agent SDK they are expressed as the tools field on an agent definition, and the documentation states that when the field is omitted the subagent inherits every tool available to subagents. Listing tools explicitly narrows the set. A tool left out is not present in the subagent's session at all — the documentation notes there is no permission prompt and no error, the agent simply works without it.

Do subagents inherit the parent agent's permissions by default?
In the Claude Agent SDK a subagent inherits tool definitions from the parent unless the agent definition specifies a subset. Context is treated differently: unless the subagent is a fork, its context window starts fresh without the parent's conversation history or system prompt, and the only content passed in is the Agent tool's prompt string. Isolation of knowledge is the default while breadth of capability is the default — two different answers inside the same feature.

What is the default subagent spawn depth and concurrency limit?
Three and twenty. CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH defaults to three layers of subagents below the main agent, and CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS defaults to twenty running at once, counted globally across every subagent spawned through the Agent tool rather than per parent. Spend has no default limit; it is set with maxBudgetUsd in TypeScript or max_budget_usd in Python. These values describe the TypeScript SDK from v0.3.219 and the Python SDK from v0.2.127 onward — on earlier releases some limits are missing or default differently.

Why is subagent delegation an architecture problem rather than a configuration problem?
Because every control is correctly placed for one audience and out of reach for another. Depth, concurrency and spend caps are set per query, in application code, by the developer who wrote it. That works when one team owns one agent. It does not work when a platform or security function is accountable for agents built by several teams on several scaffolds. An estate-wide limit has to be evaluated somewhere every agent passes through, which is outside all of them.

What should teams do before standards catch up?
Set the caps you have rather than inheriting them, and name tool scopes explicitly on every agent definition instead of relying on the default. Then decide, deliberately, which limits must hold across the estate you are accountable for — typically spend, termination authority and tool access — and evaluate those at a point no individual agent can decline to honour. Treat delegation as a policy event that produces an audit record naming the parent, the child and the scope granted, rather than as an internal function call that happens to spawn a process.

Sources

  • Anthropic, "Subagents in the SDK" — Claude Agent SDK documentation (accessed 2 September 2026)
  • Ziwen Cai, Yihe Zhang, Xiali Hei, "When Child Inherits: Modeling and Exploiting Subagent Spawn in Multi-Agent Networks" — arXiv:2605.08460 (8 May 2026)
  • Benjamin Rombaut, "Inside the Scaffold: A Source-Code Taxonomy of Coding Agent Architectures" — arXiv:2604.03515 (3 April 2026, revised 10 April 2026)
  • Arize AI, "Agent Harness: Architecture, Tracing, and Evaluation" — arize.com (accessed 2 September 2026)
  • Arize AI, "Arize and Dynatrace: Making the World's AI Work" — arize.com (13 August 2026)

Originally published on the Waxell blog.


Your agents are already delegating, and the ceiling on how far is currently set by whoever wrote the query. Start free with Waxell Observe and one governed MCP upstream, name your tool scopes explicitly, and put the spend and kill limits somewhere a subagent three layers down cannot ignore them. Start free →

Top comments (0)