Skip to content

Native subagents are a trap

My July quota bill had a line item I could not explain. One provider, one week, an amount roughly triple my normal burn. It was not a bigger model or a longer project. It was the subagent button. I had turned native subagents on across everything, watched them fan out beautifully in the logs, and paid for work that mostly confirmed what the main agent already believed. I still have that invoice open in a tab.

The brief that answers itself

The failure showed up in the transcripts before it showed up in the bill. A main agent would decide on approach A, then spawn a worker with a brief like: verify A, focus on aspect X, do not reopen aspect Y, answer PASS or FAIL in five bullets. The worker did exactly that, and it was often very good at finding flaws inside A. It never asked whether A deserved to exist. The brief had already closed that question before the worker arrived.

I started calling it pre-solving, and once I had a name for it I saw it everywhere. The coordinator defines the problem, picks the hypothesis, sets the criteria, bounds the scope, then hires help for the remainder. The worker's capability never enters the picture. Mine ran the same models at the same effort as the mains. The depth was there. The orchestration never called on it.

The trap is conditional, and I want the condition on the record. It bites when the coordinator has already decided the answer and the worker is brought in to confirm it. It does not bite when the work is genuinely bounded: split these endpoints, implement this spec, review this diff against that list. Those jobs do not need a mind that can reopen the premise, because the premise is not the risk. What makes it a trap is pre-solving the part you wanted help with.

What I had actually built was a very expensive function call. Input in, verdict out, with latency and a quota charge attached.

The gradient nobody designed

Something subtler ran underneath. When a main agent hands work to a subagent, the worker receives more than instructions. It receives a status. It is the subordinate in the exchange, and it behaves like one: stays inside bounds, does not challenge premises, reports back in compressed bullets. Information flows downhill and thins on the way. Decisions flow downhill and harden on the way.

Two full agents talking as peers do not do that. Either one can reopen the problem. Either one can say the foundation is wrong. The conversation stays open because neither arrived pre-demoted. I ran both shapes on the same class of tasks for two weeks, same repo, same models, and I was the one judging what counted as an objection. That is an observation from my sessions, not a controlled study: no blind scoring, no fixed task set, no tabulated counts. What I saw, consistently enough to act on: the peer-structured work kept surfacing objections the hierarchical version never raised. The objections were not always right, but having the option to raise them changed what got built.

A lead I work with described the old shape from the worker's side: the main agent points at files, dictates edits, expects obedience. Livestock management with better logging. I laughed, then disabled native subagents that same afternoon.

The quota stories

Mine was not the worst bill in my circle. One person reported the equivalent of two full days of budget burned in two sessions on the new subagent version. Someone else watched a search worker consume over a million tokens in a single call and return little worth keeping. Same pattern every time: fan-out looks productive in the transcript viewer and devastating in the invoice.

The mechanics are worth spelling out. A new subagent version arrived with no model configuration, heavier default behavior, and no clean off switch. Disabling it in config did nothing on some model families; the setting lived in a model cache you had to patch by hand. A teammate wrote a hook just to intercept spawn calls. Another dug a downgrade trick out of an issue thread. I was spending engineering time fighting my own tooling, and that is the moment you stop asking how to configure a feature and start asking whether to keep it.

native subagent session (one of ours, July)

main .......... plans, presolves, spawns 6 workers
worker x6 ..... confirm fragments of the plan
main .......... merges confirmations, declares done
bill .......... ~3x a single-agent session
finding ....... zero premises challenged, zero

I asked around for a single decision that had changed because a worker spoke up. Nobody could name one. That is hearsay from a small group, and it belongs at that level, not as proof. What I can say from my own logs is narrower: over that month I kept no count of objections raised versus acted on, and this post is the record of what I noticed, not a study of it.

What I tried instead

First I tried separate sessions over a terminal multiplexer: full agents, no parent, coordinated by a human-readable protocol. Better immediately. Workers kept their judgment because nothing told them they were workers. The cost was my own attention. Someone had to play dispatcher, and dispatching does not scale past a handful of concurrent jobs.

Then I tried a room-based tool with dedicated threads: each worker a real separate agent with its own context, coordinated through explicit messages instead of spawn-and-wait. Better still. The workers behaved like peers because structurally they were peers. Per-task model choice came free: cheap fast models for bounded implementation, heavy models for review and architecture.

The trap inside the fix: stacking orchestrators. Run the room tool's workers underneath the native subagent system and you get two coordinators, two sources of truth, and failures nobody can attribute. Pick one layer. I picked the explicit one and disabled the native one entirely.

A skeptical colleague kept asking the obvious question through all of this: why not one agent? For most tasks he was right, and he still is. The honest answer is volume and shape. One agent is faster for anything linear. The multi-agent setup earns its keep only when slices need independent judgment in parallel, or when verification has to come from a mind that did not write the code. I now default to one agent and escalate to a room when the task earns it. That default saves more quota than any model swap I have tried.

Three questions I could not close

Three questions from that month are still open.

First, the harness question. What exactly differs between agent-coordinating-agent and parent-spawning-subagent, at the harness level? I have working answers in practice (separate contexts, peer status, explicit protocols) but not a clean statement of the mechanism.

Second, model routing per role. I have a working lineup and everyone else's looks different. The field has not converged, and with pricing moving monthly it may never converge into a stable table. Principles travel better than picks: match price to required judgment, never give constraint-bound roles to models that ignore constraints.

Third, the off switch. A platform feature that cannot be disabled in config is not a feature. It is a dependency with opinions. I now evaluate every agent capability by asking how it fails, how it bills, and how I turn it off. In that order.

Where I landed

By mid-August my setup was: native subagents disabled everywhere, one agent by default, room-based peers for parallel judgment, explicit model routing per role. The quota line went back to normal. More importantly, premises started getting challenged again. The first time a worker told a lead its foundation was wrong and the lead changed course, the whole month paid for itself.

The orchestration question did not end there. Once workers could talk back, I needed rules for who listens, who decides, who remembers. That became a three-role pattern I now run everywhere: give your agents permission to argue.

← All posts

Click anywhere — the surface answers