Project #3 had seatbelts and they were all correct. Every one of them was scoped to one loop.
Recursion multiplies per-loop limits instead of adding them.
An orchestrator allowed ten iterations, that hires three workers allowed ten each, has quietly authorised forty API calls against conversations that keep growing. Nobody wrote that number down. It is the product of two limits that each looked reasonable alone.
The five
| Seatbelt | Kind of limit | From | What it catches that the others don't |
|---|---|---|---|
MAX_ITERATIONS = 10 | per agent | #2 | One agent spinning. Kept at ten on purpose โ raising it would have hidden the effect being measured. |
MAX_CONCURRENT = 3 | rate | new | Three-wide is a bill times three, not times sixty. The model may ask for more; they queue. |
MAX_SPAWNS = 8 | quantity | new | An orchestrator that wants one agent per jar, hiring sixty of them three at a time, politely, within the concurrency cap. |
MAX_TREE_TOKENS | money | new | The only check that sees the whole bill. Shared by reference; workers spend from the boss's wallet. |
no spawn_agent for workers | structural | new | Infinite recursion. |
Five, because they catch five genuinely different failures. Note that the
second and third look similar and are not: MAX_CONCURRENT bounds how many run
at once, MAX_SPAWNS bounds how many run at all. An orchestrator can
respect a concurrency cap perfectly while hiring sixty workers in sequence.
The one worth stealing
That last row.
There is no depth counter in this codebase. A sub-agent is started with
delegate: false, so spawn_agent is not in its tool list at all.
runAgentLoop({
messages: [{ role: "user", content: briefing(task) }],
toolbox: context.toolbox,
gate: true,
delegate: false, // โ cooks don't hire cooks
budget: context.budget,
})It cannot recurse for the same reason you cannot dial a phone number that was never printed.
Prefer a design where the bad thing is unreachable over a check that catches it.
What it costs
Honesty about the tradeoff, because this design is not free.
Two levels is a deliberate structural choice, not a limitation you can lift by changing a constant. Three levels needs a real depth counter, and at that point everything above gets harder: the counter has to survive pauses, the budget has to be attributed further down the tree, and the approval card has to explain a path rather than a parent.
So the design is honest about what it is: a two-level tree, enforced by construction, with the escape hatch clearly signposted as a bigger piece of work rather than a config change.
One wallet, shared by reference
MAX_TREE_TOKENS deserves its own note, because it is the only seatbelt that
is not per-agent.
It is a single TreeBudget object passed down into every worker by
reference, so that the sentence "the whole run stops at 600,000 tokens" is
true rather than approximately true.