Certified Agent Harness Engineer
Course sections

M6.1

The delegation decision

1,138 words · about 7 min

Concept

The default answer is no.

That is not rhetoric; it follows from the arithmetic. Delegating replaces one continued conversation with: a prompt you must write precisely enough that a context-free agent can act on it, a child session with its own system prompt and tool schemas (a fixed overhead in the low thousands of tokens before any work happens), a full agent loop that may take minutes, a result you must then read, and — if the child is a fork — a duplicate of the parent's history riding along in every child request. Against that you save exactly one thing: the intermediate tokens the child generated and discarded.

So delegation pays when the ratio of discarded intermediate tokens to returned tokens is large. Concretely, that means:

  • Search over a large surface with a small answer. "Find every call site of this deprecated function across 4,000 files" burns enormous tool output and returns a list. Excellent delegation.
  • Independent branches that will not need to see each other's work. Three unrelated bug investigations. Real parallelism, no coordination cost.
  • Work whose intermediate state is genuinely irrelevant. A test-suite run that fails eleven times before passing; the parent needs the final diff, not the eleven failures.
  • Blast-radius isolation. Processing untrusted content in a child so an injection reaches a bounded authority — the Module 5 hook. Note the caveat carried forward from M5.6: the summary itself can carry the payload.
  • Capability asymmetry. A stronger, slower, more expensive model for one hard sub-question, while the parent stays on a cheap fast model.

And it does not pay when — this is the list that matters:

  • The task is small. If the child's answer is three lines, you paid a session's fixed overhead plus round-trip latency to save three lines of thinking. Just do it.
  • The parent already has the context. This is the single most common error. You have read the file, traced the call graph, and understood the bug; delegating the fix means either re-teaching all of that (a fork, paying the tokens twice) or throwing it away (a spawn, so the child rediscovers what you already knew).
  • The work needs continuous judgment. A child gets one prompt and returns one result. If the right next action depends on what the previous action revealed, and you are the one who can judge it, a child is a worse you with less information.
  • The subtasks are interdependent. Two children editing the same file produce a merge conflict neither can see. Coordination through a parent that cannot observe either child's intermediate state is not coordination.
  • You would have to explain more than you would have to do. If the prompt is longer than the patch, the prompt is the work.

Mechanism: the delegation break-even

Mechanism

Write it down as a rough model. Let T_in be the tokens the child consumes, T_out the tokens it returns, T_seed the inherited history (zero for a fresh child), and T_self what the same work would have cost inline in the parent. Delegation wins on context when T_out + T_seed_amortized << T_self, and wins on latency only when branches run concurrently and the barrier is not dominated by one slow branch.

The practical form of this model is a question with three parts: How much does the child throw away? How much does it hand back? How much did I have to tell it? If the answers are "not much," "a lot," and "a lot," you have found an anti-pattern.

Worked example — the tempting refactor

Worked example

A user asks: "Rename SubagentResult.diagnostic to SubagentResult.note across the repo and update the docs."

The over-delegating instinct: spawn three children — one for packages/, one for docs/, one for tests. It reads like parallelism.

What actually happens: each child must independently discover the rename convention, the codebase's TypeScript conventions, and whether diagnostic appears in unrelated contexts (it does — job diagnostics, error diagnostics). Three children now make three inconsistent judgment calls about which occurrences are in scope. The parent receives three prose summaries and cannot verify any of them without reading the diffs, which means reading the work anyway. Total cost: three session overheads, three sets of duplicated discovery, plus the parent's review — strictly worse than doing it inline.

The correct decomposition, if any: delegate the search ("list every occurrence of the identifier diagnostic with 3 lines of context, grouped by whether it refers to the subagent result field"), which discards a large amount of grep output and returns a compact list. Then do the edits in the parent, where the judgment lives. One child, sharply bounded, returning a structure.

That is the shape to internalize: delegate the funnel, keep the decision.

Tradeoffs

Delegation trades parent context for child context plus coordination overhead. It converts a token cost into a latency cost when serial, and a latency cost into a concurrency cost when parallel. It trades observability for isolation — you cannot see inside a child mid-run, which is precisely what makes it safe and precisely what makes it hard to debug. And it trades the parent's accumulated judgment for a clean slate, which is a benefit only when the accumulated judgment is noise.

How `dsh` does it

How dsh does it

dsh exposes delegation to the model as an ordinary tool, registered by packages/subagent/tool-subagent. The tool's guidance is deliberately conservative, and the workflow tool's README states the policy for the heavier orchestration path in one sentence: use it "only on an explicit user ask for a workflow / large orchestration; prefer plain subagent calls for one or two delegations."

The strongest structural expression of "don't delegate" is a config key: maxDepth, default 3, where 0 forbids delegation entirely. A deployment that does not want a delegating agent does not rely on prompt guidance; it sets the depth budget to zero and the tool refuses.

How others differ

Claude Code ships both subagents (fresh context, defined by markdown files with their own tool allow-lists) and forks. Its documentation is explicit that forks "inherit everything," which makes them a different cost class rather than a different flavour. Its subagent budget controls — max_turns and max_budget_usd — both default to no limit, which is a defensible product decision and a deployment hazard: an unattended fan-out has no ceiling unless you set one.

Amp takes the sharpest opposing position with its Oracle: a read-only, stronger-model consultant that is deliberately not auto-invoked. The design says the model should not decide to spend the expensive delegation; the user should. Aider's architect mode splits by capability type (a reasoning model plans, a cheaper model edits) within a single turn rather than spawning a child at all — evidence that "use two models" and "use two agents" are separable ideas that the field often conflates.


Check yourself

3 questions on M6.1

Start drill