Here's a piece of arithmetic almost everyone running AI agents gets wrong.

You have a supervisor process that checks on a worker agent every 5 minutes. The worker finishes a task at some random moment. How long does it sit idle before the supervisor notices?

Two and a half minutes, right? Half the interval. That's the textbook answer, it's what queueing theory says about uniformly distributed arrivals, and it's the number people plug in when they pick a polling interval.

We measured it. The real answer is five minutes. The full interval, every single time.

That's not a rounding error. It means every capacity estimate built on the half-interval assumption is optimistic by 100% — and it's systematically wrong in exactly the situation where operators are most likely to rely on it.

Here's what we found, and what it means if you're running agents in production.

What We Actually Tested

We built a deliberately boring comparison. One worker agent, one fixed task, one fixed scoring rubric. The only thing that changed was how the supervisor decided when to wake up and hand over the next instruction:

Structural difference between a polling supervisor and an event-driven supervisor Left: a polling supervisor is woken by its own timer and asks the worker whether it is idle. The worker has no channel back, so a completion cannot reach the supervisor. Right: an event-driven supervisor is woken by the worker itself. A stop hook writes to a mailbox, a filesystem watch wakes the supervisor, and a capped timer remains only as a backstop. POLLING the supervisor wakes itself TIMER every 300s SUPERVISOR "are you idle yet?" WORKER No return channel. A finished worker cannot say so. EVENT-DRIVEN the worker wakes the supervisor SUPERVISOR deliver next task WORKER MAILBOX stop hook CAP 300s Timer survives only as a backstop — see the failure mode below.
The change is structural, not a tuning parameter. Polling has no path from a finished worker back to its supervisor; event-driven adds one and demotes the timer to a safety net.

Same task both ways: build a small three-page web app against 45 automatically-scored requirements, revealed one at a time so the worker never sees the whole list. Same time limit. Same everything else.

Crucially, this was pre-registered — hypotheses, methods, and the analysis plan were written down and frozen before any data existed. We'll come back to why that matters, including where our own process failed.

Polling versus event-driven agent supervision over the same 15 minutes Two timelines covering an identical 900-second window. The polling supervisor completes 3 work cycles, each a short burst of work followed by a long idle gap until the next 300-second tick. The event-driven supervisor completes 30 cycles, each a short burst followed by a roughly equally short gap. Same 15 minutes. Same worker. Same task. Orange = agent working. Grey = agent idle, waiting to be noticed. POLLING (300s interval) idle until next tick 3 cycles EVENT-DRIVEN (wake on completion) 30 cycles Idle time per cycle, measured: 323s polling vs 31s event-driven.
The polling supervisor is not slower because it thinks harder. It is slower because it is asleep.

The Results

What we measuredPollingEvent-drivenDifference
Idle time per cycle323 s31 s10.4×
Idle time, once warmed up301 s15 s19.4×
Work cycles completed7192.7×
Final rubric score47%73%1.6×
Commits produced8.5212.5×
Requirements per hour9.974.57.5×
Measured differences between polling and event-driven supervision Four measures, each shown as a pair of bars scaled within its own panel. Requirements completed per hour: polling 9.9, event-driven 74.5. Work cycles completed: polling 7, event-driven 19. Commits produced: polling 8.5, event-driven 21. Idle seconds per cycle, where lower is better: polling 323, event-driven 31. MEASURED, TWO RUNS PER ARM — GREY = POLLING, ORANGE = EVENT-DRIVEN REQUIREMENTS PER HOUR 9.9 74.5 7.5× more work per hour WORK CYCLES COMPLETED 7 19 2.7× more turns in less time COMMITS PRODUCED 8.5 21 2.5× more shipped increments IDLE SECONDS PER CYCLE — LOWER IS BETTER 323 31 10.4× less dead time
Bars are scaled within each panel, not across panels. Two runs per arm — enough to establish direction, not enough to pin the magnitude.

Two runs per arm. The within-arm consistency was tight — the two polling runs came in at 299 s and 347 s, the two event runs at 31 s and 32 s. This isn't a noisy measurement.

The headline: the event-driven supervisor got 7.5× more work done per hour. Not because it made the worker smarter. Because it deleted the waiting.

The Finding That Actually Transfers

The 10× number is the sales pitch. This next part is the useful engineering.

We predicted the polling arm would average about 150 seconds of idle time per cycle — half the interval, the textbook answer. It came in at 301 seconds. Our prediction was wrong by a factor of two, and we want to be clear that it was wrong in a direction that happened to flatter our conclusion. So we went and worked out why.

The half-interval formula assumes the worker finishes at a random moment within the interval. That's true when the work arrives from outside your system. It is not true in a supervised loop, because the supervisor's own delivery is what starts the worker's next turn.

Follow the sequence:

  1. Supervisor wakes on a tick and delivers the next instruction.
  2. Worker starts immediately, works, and stops ~15 seconds later.
  3. Supervisor is now asleep. It doesn't wake until the next tick — a nearly full interval away.
  4. Repeat.

The loop phase-locks. The worker doesn't land randomly between two ticks; it reliably just misses one. In the polling runs, 7 out of 7 cycles landed between 270 and 308 seconds against a 300-second interval. Every one.

Why a polling supervisor pays the full interval rather than half of it Top row: the textbook model assumes worker completions fall at random points between supervisor ticks, giving an average wait of half the interval, about 150 seconds. Bottom row: because the supervisor's own delivery starts the worker's turn, completions land a few seconds after a tick every time, so the wait is nearly a full interval. Measured values were 270 to 308 seconds against a 300 second interval. THE MODEL EVERYONE USES completions assumed to fall uniformly at random → average wait = I/2 average wait ≈ 150s WHAT A SUPERVISED LOOP ACTUALLY DOES the supervisor's own delivery starts the turn, so the worker always finishes just after a tick measured wait 270–308s — 7 of 7 cycles, against I = 300s orange = working    dark = idle, unnoticed
The loop phase-locks. The worker does not land uniformly between two ticks — it reliably just misses one, so the wait is a full interval rather than half.

A polling supervisor driving a worker whose turns are short relative to the interval pays roughly the full interval per cycle, not half of it.

This is a general property of closed-loop polling with short work units. It's not specific to our task or our tooling. And the practical consequence is blunt: at a 5-minute poll interval, work that an event-driven loop finishes in 16 minutes takes 45 minutes.

If you have ever sized a polling interval by reasoning "average wait is half the interval, so 5 minutes is fine" — that estimate was optimistic by 2×, and it gets worse the faster your worker is.

Wait — Did Faster Just Mean Sloppier?

Reasonable objection. The event arm scored 73% on the rubric versus 47% for polling. Maybe waking the worker more often produced better work rather than just more of it — or maybe it was rushing, and the rubric was measuring something else.

We pre-registered a check for exactly this, and it came back clean in both directions.

Score per turn was statistically equivalent between arms — a gap of 8.7%, well inside the 25% band we'd committed to in advance. And a cleaner measure sealed it: requirements satisfied per turn was exactly 1.0 in all four runs, with zero repeated requirement IDs. Not a single turn in either arm was wasted, redone, or interrupted mid-thought.

So the mechanism is purely mechanical. The event arm fit 19 turns into 967 seconds where polling fit 7 into 2,705. Same quality of work per turn. Vastly more turns.

The gain is idleness removed, not work rushed. That distinction matters, because it means the result should hold for your workload too — you're not buying speed by trading away correctness.

The Trap in Event-Driven Loops

Event-driven won decisively, but it shipped with a defect worth knowing about before you build one — because it cost us 315 seconds on the first cycle of every single event run.

Here's the event log:

11:07:45  worker_stop

11:07:45 woke event ← signal fired in the same second 11:07:45 classify BUSY ← still mid-render → skipped 11:12:45 woke timeout ← 300s backstop; nothing else could re-trigger 11:12:46 classify IDLE → delivered

The signal arrived on time. The supervisor woke on time. Then it looked at the worker, saw a UI still finishing its render, classified it BUSY, and discarded the wake.

That's the bug: BUSY is transient, but it was treated as durable. And here's the asymmetry that makes it dangerous — once an event-driven supervisor throws away a wake, nothing can re-trigger it. The worker is already idle. It will never signal again. The loop is dead until a timeout backstop rescues it.

A polling loop has no equivalent failure. Its next tick arrives regardless of what the last one concluded. Polling is worse on average and more forgiving at the edges.

How an event-driven supervisor loses a wake and stalls for a full backstop interval The worker stops and the filesystem watch fires in the same second, but the supervisor classifies the pane as busy because its display has not finished rendering, and discards the wake. The worker is now idle and will never write the mailbox again, so nothing can re-trigger the supervisor until the 300 second capped timer fires. With a ten second settle re-check, the same cycle completes in about 30 seconds. THE WAKE THAT GETS THROWN AWAY pane still mid-render classify BUSY → wake discarded SUPERVISOR WORKER 11:07:45 worker stops 300s dead — the worker is idle and will never signal again nothing can re-trigger the supervisor in this window 11:12:45 CAP fires WITH A 10s SETTLE RE-CHECK same code path, one added re-check 30s — re-check catches IDLE, no fall-through to the backstop
An edge-triggered loop gets no second chance: once a wake is discarded the worker is already idle, so only the backstop rescues it. On any transient-negative classification, schedule a re-check instead of returning to the blocking wait.

The rule: in an edge-triggered loop, a discarded wake is a liability. On any transient-negative classification, schedule a re-check — never return to the blocking wait.

The fix was small: catch the BUSY, wait a short settle delay (10 seconds), re-check. Confirmed in a follow-up run — first cycle dropped from 315 seconds to 30, with no fall-through to the backstop.

Which also means the 10.4× figure we're reporting understates the real gap. It includes a startup penalty we've since eliminated. We're reporting it anyway, because it's what the sealed experiment measured.

What To Do With This

Four practical takeaways, in order of how much money they'll save you:

1. If your workers signal completion, use the signal. Every mainstream agent runtime can emit a completion hook. If yours can, polling is leaving roughly 90% of your wall-clock throughput unclaimed. This is the single highest-leverage change in the list.

2. If you must poll, stop using half the interval in your estimates. Budget the full interval per cycle. Then notice that the penalty scales with how short your work units are — a supervisor polling every 5 minutes over 15-second tasks is idle 95% of the time. Shrink the interval or shrink the loop.

3. Build the backstop anyway. Event-driven supervision is faster and more fragile. A periodic timeout that fires regardless of event state is what turned our worst-case bug into a 315-second delay instead of a permanent hang. Keep it even when events are working.

4. Never treat a "not ready" signal as final. Re-check after a short delay. This one line of defensive logic was worth 300 seconds per run in our data.

The Part Most Vendors Would Leave Out

We'd rather you trust the parts of this that hold up than believe all of it.

The sample is small. Two runs per arm, pre-registered as underpowered. We report no p-values and no confidence intervals, because computing them on n=2 would imply a precision the design can't support. The direction of a 10–20× effect with tight within-arm replication is not in doubt. The exact magnitude is.

Our own pre-registration seal was broken, and we found it. We hash-seal the frozen analysis plan specifically to prove we didn't invent hypotheses after seeing results. When we audited it, the recorded hash matched no file that has ever existed — the plan had been amended in place before it was ever committed to version control, so version control never captured the thing the hash described. The seal has never verified, at any commit.

We could have quietly re-hashed the current file. Instead: the broken hash stays in the record marked unrecoverable, and this study's hypotheses should be treated as pre-specified on our assertion and the narrative record, not on cryptographic evidence.

The lesson generalizes past our lab: a hash computed before the content is under version control pins nothing. If you're building audit trails for AI systems — and increasingly you have to — seal only committed content, and treat a freeze as a commit hash, not a timestamp in prose. We now verify every seal in the same session it's created. The tool that caught this found it on its first run.

Two dependent variables were structurally coupled, and we didn't pre-register that they were. One requirement revealed per turn means "rubric score" is largely a re-expression of "cycle count." We flagged it in the write-up rather than presenting them as two independent confirmations.

None of that changes the headline. All of it changes how much weight the decimal places deserve.

The Bottom Line

If you're orchestrating AI agents and your supervisor is on a timer, you are probably paying the full poll interval in dead time on every cycle — not half of it, as the standard estimate suggests. On a 5-minute interval with fast workers, that's roughly a 7× throughput tax for no gain in quality.

The fix is architectural, not a matter of tuning: let the worker tell you it's done. Keep a timeout backstop. Never trust a transient "busy."


Running multi-agent workflows and not sure where your throughput is going? Idle time in orchestration loops is invisible in most dashboards — it looks like the agent is working when it's actually waiting. We help teams instrument and re-architect agent pipelines so the compute you're paying for is compute that's running.

Book a consultation →