Chapter 1 of 7 · 9 min read
Delegation buys a context boundary, not extra intelligence
On this page
Meridian Freight has an internal coding agent that turns SDK tickets into pull requests. The ticket was routine: the Python wrapper around their shipments API had no pagination, and callers were silently getting the first fifty results and treating them as the whole set. To write the pagination layer the agent needed one fact, which was the list of endpoints that accept a cursor query parameter. The source of truth for that is openapi.yaml, an OpenAPI specification — the machine-readable description of every endpoint a service exposes, with its parameters and its response shapes — generated from the service's route decorators on every deploy, 4,142 lines and roughly 55,000 tokens of YAML in which every endpoint carries the same six boilerplate error responses.
The agent read it. The read succeeded, the model summarised it correctly in the next turn, and then over the following eighteen turns it wrote a paginator for /v2/shipments, /v2/tracking-events, and /v2/carriers. The last of those has no cursor and never had one; it returns a fixed list of eleven carriers. The generated code called resp["next_cursor"] on a response that has no such key, and the test that should have caught it was written by the same loop against the same wrong belief.
A colleague looked at the transcript and proposed the obvious fix, which was to hand the spec reading to a subagent. The parent would ask a question, a child would read the file, and only the answer would come back. That proposal is right about the mechanism and, in this case, wrong about the choice, and the gap between those two things is most of what there is to understand about delegation.
What crosses the boundary and what does not
A subagent is a second run of the same model, started with a conversation of its own that begins empty, pointed at one bounded task, and given a restricted set of tools to carry it out. The run that delegates is conventionally called the parent and the run it starts is the child, and the child's conversation is discarded when it finishes, so the only thing that reaches the parent is the child's final message. The child is the same model on the same weights, so what differs between them is only what each is carrying. An agent's conversation has a fixed size, anything read into it stays there and is charged again on every later turn, and a single large file can occupy a quarter of that space for the rest of a session, so moving the read into a conversation that is thrown away afterwards is how the space gets bought back. You meet subagents in practice either as definition files that the agent framework loads from a directory — .claude/agents/ in Claude Code, one markdown file with frontmatter per subagent — or as a tool the main loop can call with a task description. The whole surface of the thing is visible in about a dozen lines.
---
name: spec-reader
description: Answers a single factual question about a large API
specification file. Use when the answer is small and the file is not.
tools: Read, Grep, Glob
model: inherit
---
You will be given one file path and one question. Read only what you
need to answer it. Return the answer and nothing else: no summary of
the file, no description of how you searched, no offer to do more.The tools line restricts what the child can invoke, the body replaces the parent's system prompt for the duration, and the transcript the child builds is discarded when it returns. What comes back to the parent is the child's final message, which is a normal assistant turn in the parent's conversation. Everything the child read, every intermediate thought, every tool result, stays on the child's side of the line.
That is the entire benefit, and it is worth being precise about why it matters rather than treating it as self-evident. The 55,000 tokens of openapi.yaml are not paid once. A conversation is stateless underneath, so every subsequent turn resends the whole window, which means those tokens are re-billed on turn 20, turn 21 and turn 22. Meridian's session ran 34 turns after the read. At Claude Sonnet's $3 per million input tokens, 55,000 tokens resent 34 times is 1.87 million tokens, about $5.60 for one ticket, and the SDK team files roughly forty such tickets a month. Prompt caching cuts the marginal reads to $0.30 per million, which brings the same figure to about $0.60, so the money is not the story unless caching is off. The window is the story: 55,000 tokens is 27% of a 200K window, occupied permanently, sitting between the original ticket and everything the model does next.
What delegation does not buy is any improvement in the model doing the reading. The child is the same model with the same failure modes, so if the parent could not tell from the spec which endpoints paginate, the child cannot either — it is not a specialist, and calling it one in the definition file does not make it so. The topic on sub-agent context isolation already established why a role label changes the register of an answer without changing the evidence behind it; the same caution applies here, and it applies more sharply because a subagent's fresh window is easy to mistake for a fresh mind.
The technique this replaced is worth knowing, because it explains the shape. Before delegation was a first-class feature, the standard answer to a window filling up was compaction: let the loop read whatever it needed, and when the window approached its limit, have the model summarise the conversation so far and continue from the summary. Compaction works, and every long-running agent still needs it, but it is lossy in a direction you do not control — the summariser decides what to keep across the whole history at once, so the ticket text, the file the loop was halfway through editing, and the boilerplate error responses from the OpenAPI spec are all compressed by the same pass. Delegation attacks the problem one step earlier by never admitting the bulk in the first place, which means the compression decision is made per task, at the moment the task is defined, by the person who knows what the answer is supposed to look like. That is a better place to make it, and it is the only real advance the pattern represents.
Three things get confused with delegation often enough to name. It is not a second opinion, because two calls to the same model on the same evidence are correlated, not independent. It is not parallelism, though it enables it — a child running while the parent waits is strictly slower than the parent doing the work, and the speedup only exists when several children run at once on genuinely separable units. It is not a reliability mechanism, because a child that misreads its input returns a confident wrong answer in forty tokens instead of a visibly confused transcript in four thousand, which is harder to notice, not easier.
The comparison that decides it
The colleague's proposal justified a subagent against doing nothing. Against doing nothing it wins easily, and that is why the framing is dangerous. The honest comparison puts it against the cheapest thing that also keeps the spec out of the parent's window, and for "which endpoints accept a cursor parameter" that is a script. The one below uses yq, a command-line tool that queries and filters a YAML document the way jq does a JSON one.
# Every path whose parameter list mentions a cursor, as YAML.
yq '.paths | to_entries[]
| select(.value | .. | select(tag == "!!str") | test("cursor"))
| .key' openapi.yaml"/v2/shipments"
"/v2/tracking-events"
"/v2/webhook-deliveries"
"/v2/rate-quotes"That ran in 84 ms on a laptop and returned 96 bytes. It is also, notably, correct where the model was not: /v2/carriers is absent and /v2/webhook-deliveries is present, and the agent's inline read had missed the second one as well as inventing the first. The result is deterministic, it costs nothing, it can be committed as a make target, and if the spec's shape changes the command fails loudly instead of hallucinating quietly.
| Inline read | Subagent | yq one-liner | |
|---|---|---|---|
| Tokens entering the parent | ~55,000 | ~120 | ~40 |
| Total tokens billed | ~55,000 | ~57,000 | 0 |
| Added wall-clock | 6 s | 19 s | 0.08 s |
| Model calls | 0 extra | 1 extra | 0 |
| Answer varies between runs | yes | yes | no |
| Works when the question is fuzzy | yes | yes | no |
The last row is the one that keeps the subagent in the picture. yq answers "which endpoints accept a cursor parameter" because that question maps onto a string match over a known structure. It does not answer "which endpoints look like they were meant to paginate but were never wired up", which is the question Meridian actually had underneath the one they asked, and which requires reading response schemas, comparing them against sibling endpoints, and forming a judgement. Delegation earns its keep on questions where a deterministic extractor cannot be written, and the way to find out which kind of question you have is to try writing the extractor first and see how quickly it becomes absurd.
The failure this ordering prevents is a specific and common one. A team hits a context problem, reaches for a subagent because that is the tool the platform advertises for context problems, and ends up with an agent architecture wrapped around what was a text-processing problem — two model definitions, a brief format, a result parser, and a nondeterministic answer where thirty characters of yq would have been exact. Nobody chooses that consciously. It happens because the delegation was compared against the status quo and won.
When the boundary is worth its price
Meridian's real answer was to do both. The yq command went into the SDK's Makefile and its output into the repository as docs/paginated-endpoints.txt, regenerated on every spec change, 96 bytes that the agent can read on any turn for essentially nothing. The subagent stayed in the toolkit for the class of question that produced the ticket in the first place, which was an audit of whether the wrapper's return types match what the endpoints actually return — 4,142 lines of spec against 2,300 lines of Python, a comparison with no extractor and a small answer.
That shape is the test. Delegation is worth its price when the ratio of input bulk to output conclusion is large, when the conclusion cannot be computed by a script, and when the parent has other work to do afterwards that the bulk would interfere with. Two of those three are about size and one is about what the parent still needs its window for, which is why the same delegation can be right at turn 40 of a session and pointless at turn 2.
The costs are not only tokens. A delegated call is a second inference, so it adds its own time-to-first-token and its own generation time, which is where the 19 seconds in the table comes from — the child had to read a 55,000-token file before it could say anything. It is also serialised: unless you fan out, which means starting several children at once and waiting for all of them together, the parent is blocked. And the answer arrives with no provenance unless you ask for it, so the parent is now holding a claim it cannot check without redoing the work that the delegation existed to avoid.
That last point is the one that undoes the arithmetic most often. Every number in the table assumes the child returns roughly 120 tokens, and nothing in the definition file above makes that true. A child that returns its working transcript instead of its conclusion hands the parent all the bulk it was supposed to absorb, plus the cost of the extra call, and the boundary you paid for evaporates at the moment of handoff. What the child returns, and in what shape, is not a detail of the delegation; it is the delegation.
Subagents — read the current version at https://daviddominguez.dev/learn/en/subagents/delegation-buys-a-context-boundary-not-extra-intelligence