Companion to Two agents, one night, 158 million tokens. About four minutes to read.
The picture
A parent model delegates a job to a worker and then has nothing else to do. It wants to know when the worker is finished. It has one tool for that: wait up to N seconds for the worker, then tell me what happened. The default N is sixty.
BUSY parent (rule holds) IDLE parent (rule fails)
dispatch worker dispatch worker
work on own task ─┐ wait 60 s ─── "not yet" 115k tokens
work on own task │ worker finishes wait 60 s ─── "not yet" 115k tokens
work on own task │ notification lands wait 60 s ─── "not yet" 115k tokens
read result ◄─────┘ wait 60 s ─── "not yet" 115k tokens
... × 66 ...
1 wake-up, paid once wait 60 s ─── "done" 115k tokens
Each "not yet" is a full activation. The parent re-reads its entire conversation to learn that a minute has passed. The rule that says do not poll is obeyed as long as the parent is busy, because a busy parent is woken by the worker's completion in the normal course of its next step. The moment the parent is idle, the only way it knows how to wait is the sixty-second wait, and the rule has nothing to grip.
What the base case measured
| Measure | Value |
|---|---|
| Waits issued by the parent | 89 |
| At sixty seconds | 88 |
| Timed out without a result | 66 |
| Returned a completion | 23 |
| Tokens in activations that ended in a wait | 10,281,999 |
| Of which cached re-reads | 10,153,856 |
| Mean tokens per waiting activation | 115,528 |
| Share of the whole run | 6.5% |
| Share of the run's list-price cost | 12.7% |
The largest single turn issued 62 waits, 50 of which timed out. An earlier, smaller build showed the same pattern at five waits. This is a mechanism, not a one-off.
Upper bound, labelled as a counterfactual: if every timed-out wait had been replaced by a silent notification, the 66 activations at the mean size would not have happened, about 7.6 million tokens. The 23 completions would still have cost a wake-up each. That is the most an event-driven design could remove here, not what it would save.
Why the two agents differ
On Claude Code, a background worker or a background shell command notifies the parent when it finishes. The harness wakes the parent once, on the event. There is nothing to poll, and the guardrail simply says: dispatch, then continue or end the turn.
On Codex, in the runtime we measured, a worker's completion message does not trigger a parent turn. A finished worker cannot wake an idle parent. The parent's only options are a bounded wait or a scheduled check, so a declarative "do not poll" rule cannot be followed by an idle parent. The fix has to be structural.
Three ways to wait
- Model polls. The parent waits N seconds, wakes, re-reads everything, decides to wait again. Cost: one activation per N seconds for as long as the worker runs. This is the trap.
- One long wait. The parent waits once, for as long as the worker could plausibly need. Cost: one activation if the estimate is right, two if it is not. Our interim rule sets N to five minutes, which cuts the polling cost by five with no other change.
- Zero-token watchdog. A plain script, not a model, reads the worker's transcript counters every thirty to sixty seconds and stays silent while the worker is healthy. It speaks once: on completion, on a budget breach, or on a stuck signature such as the same failing call repeated. The parent is woken exactly once. Cost while waiting: zero model tokens. The design, with a tested reference sketch, is in the zero-token watchdog explainer.
What the criteria will look for
Two telemetry-only tests decide whether the fix worked, over three consecutive qualifying sessions per agent:
- No repeated waiting. Wait, timeout, wait again with no other work in between: zero occurrences.
- No short checks. Smallest wait timeout at least 300 seconds on Codex; status checks while a worker runs, zero on Claude.
Neither field exists in the telemetry yet. Adding them is fix number four in the article.
Top comments (0)