Skip to content

    Back to articles

    6 min read

    Put a hard budget around nested coding-agent work

    Fan-out is a resource tree

    A coding agent that can delegate work is not just doing several things at once. It is creating a tree of work whose branches can consume model calls, tool access, wall-clock time and attention. I would design that tree as a resource-control problem before treating it as a productivity feature.

    A recent overview of Claude Agent SDK updates reports nested subagents with caps among its October changes. That is a useful prompt for an architectural question, not proof that a particular SDK setting makes an agent safe or economical. The Claude Agent SDK subagents documentation describes controls for nesting depth, concurrent agents and spend. Those controls address different failure modes, and I would not collapse them into one idea of “the budget.”

    Consider a supervisor that delegates a code review to several agents, each of which can delegate again. A small initial request can turn into many active workers. Even if the SDK limits the depth of the tree, branching at every level can still produce a large workload. Conversely, a low concurrency cap can keep the active workload small while agents queue up and extend the run. These are separate dimensions: how many agents may exist, how many may work at once, and how much total work the run may consume.

    I would make the first decision about delegation structural: which roles are allowed to spawn agents at all? A top-level coordinator might need that capability; a file reader, test summarizer or leaf-level reviewer usually does not. If every agent can delegate, every prompt becomes part of an open-ended resource policy. If only designated supervisors can delegate, the shape of the work is easier to inspect and constrain.

    Put invariants below the prompt

    Prompts are useful for explaining intent: split independent investigations, avoid duplicate work, and stop when the evidence is sufficient. They are not where I would enforce a hard limit. The model is being asked to choose actions; the surrounding system should decide which actions remain possible.

    The SDK or orchestration layer should own limits that must hold regardless of what the model says: maximum delegation depth, concurrent-agent capacity and total spend. Tool access belongs there too. A worker asked to inspect code should not receive write or shell capabilities by default merely because the supervisor has them. I would give a supervisor the smallest set of tools needed to delegate and synthesize, then give each worker only the tools its assigned task requires. Where a task needs approval for a risky operation, that should be an explicit permission boundary rather than a sentence asking the agent to be careful.

    Here is the shape I would start with in a TypeScript SDK integration. The values are illustrative starting points, not recommendations; I would choose them against the task and the cost of interruption. The SDK documentation describes maxBudgetUsd and the subagent depth and concurrency environment variables as controls for these boundaries (subagent limits in the SDK docs). I have left the delegation tool out of the worker’s allow-list on purpose: this is a leaf worker, not another supervisor.

    process.env.CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH = "1";
    process.env.CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS = "3";
    
    for await (const message of query({
      prompt: "Inspect the assigned module and report relevant risks.",
      options: {
        cwd: repositoryPath,
        allowedTools: ["Read", "Glob", "Grep"],
        maxTurns: 12,
        maxBudgetUsd: 2
      }
    })) {
      handleMessage(message);
    }

    I would treat this as a sketch, not a copy-paste guarantee. The precise option names and behavior depend on the SDK version, so I would verify them against the version actually deployed. The important design choice is that the cap is applied by the runtime around the agent, not entrusted to its prompt. A prompt can explain what happens when a limit is reached; it cannot be the mechanism that enforces the limit.

    Budget time and capability as well as spend

    A spend cap is necessary but incomplete. It may bound model charges without bounding how long the task occupies a worker, how long a tool call hangs, or what a worker can do before the spend is reached. I would give each run an elapsed-time deadline in the host application and make tool calls subject to timeouts too. A deadline should stop or cancel outstanding work, not just stop the coordinator from waiting for it.

    Tool access is also a resource and a risk. Read-only tools let an agent investigate without changing the repository. Write access can create conflicts when multiple agents touch the same files; shell access can have effects beyond the files under review. I would grant these capabilities per role, and I would make write-capable work either sequential or explicitly partitioned so that parallel workers do not have competing ownership of the same change. That is my design preference, not a claim that a particular agent SDK automatically provides file-level isolation.

    I would also distinguish a per-agent turn limit from a run-wide budget. A local limit can stop one worker from looping, but it does not necessarily say how much work the whole tree may perform. The coordinator needs a shared envelope that accounts for delegated work, not a fresh allowance for every child that makes the aggregate invisible. If the SDK exposes a run-wide spend limit that includes subagents, I would use it as the hard stop and still keep local limits for containment. If its accounting does not cover a resource I care about, I would track that resource outside the SDK.

    Those distinctions matter operationally. A task may stop because it reached the spend cap, the deadline, a concurrency limit or a permission boundary. I would report which one happened and preserve the useful partial results. Otherwise, “agent failed” hides the difference between an unreasonable task and a sensible task that simply ran out of its allocation.

    Make the limit part of the contract

    Before enabling nested work, I would write down the budget in terms the system can enforce: who may delegate, how many workers may run, what each worker may use, and the run’s spend and time ceilings. I would also decide what the user sees when a limit is reached. Does the coordinator return partial findings, ask for permission to continue, or stop with a clear explanation? That behavior should be designed rather than left to a model improvising under pressure.

    Then I would test the boundary conditions. Give the coordinator a task that encourages unnecessary delegation. Make a worker attempt a tool it should not have. Let a tool stall. Check that hitting the aggregate budget stops outstanding work and that the final result says why it stopped. These are not elaborate benchmarks; they are checks that the controls actually sit on the execution path.

    Nested agents can be useful when work divides cleanly, but parallelism is not free. My default would be shallow delegation, narrow tool permissions and a run-wide hard budget, with deeper trees enabled only where the task justifies the added control problem. Prompts can guide good resource use. The SDK and host application must enforce the limits.

    Drafted with AI assistance, reviewed and edited by Ali.