A fuse has to sit above the tail of normal work, or it cuts off runs that were going to succeed. Take these from your traces rather than from intuition; the p99 is almost always further out than people expect.
| Failure | What catches it | At | Status |
|---|---|---|---|
| Hang: nothing is happening | Nothing catches it | n/a | uncovered |
| Spin: iterations without progress | Iteration fuse | 60 passes | covered |
| Slow tool call | Nothing bounds one call | n/a | uncovered |
| Runaway spend | Iteration fuse and cost per pass | $5 | within cap |
What each fuse catches
Six ways a run goes wrong and which limit ends it. The two rows that matter most are the first and the last two: a hang is invisible to the counter, and nested work is invisible to both unless a budget is carried through every call.
| Failure | Wall-clock deadline | Iteration fuse | Note |
|---|---|---|---|
| Tool call hangs indefinitely | catches it | cannot | counter freezes, never fires |
| Loop spins without progress | late | catches it | clock fires eventually, at full cost |
| One slow but returning call | partial | cannot | needs a per-call timeout |
| Model requests tools forever | catches it | catches it | either one ends it |
| Nested agents multiply the work | partial | cannot | needs a task-level budget |
| Task is impossible | catches it | catches it | both fire, neither tells you why |
Late means the clock ends it only after the full budget has been spent, which is protection against unbounded loss rather than against waste. Partial means it bounds the run but not the underlying cause.
A worked example
An agent with a 20-iteration cap and a 15-minute timeout. Two limits, both configured, and the team reports the loop as bounded on both axes.
The timeout is implemented as a check at the top of each iteration: if (elapsed > limit) break. It is correct, it is tested, and it has never failed to fire. It is also not a time fuse, because a hung tool call never returns, so the loop never reaches the top of the next iteration and the check is never evaluated. Both limits guard the spin edge, and the hang edge is uncovered.
There is no per-call timeout either, so a single call to a degraded dependency blocks the run indefinitely. Nothing in the system can end it: the counter has frozen, the timeout is unreachable, and the process sits there holding its resources until somebody notices.
The fix is small and it is not a tuning change. Move the deadline out of the loop, into something the loop cannot block, and add a per-call timeout so one dependency cannot take the run with it. The iteration cap can stay exactly as it is, because it was always guarding the other edge.
The arithmetic, so you can check it
The wall-clock deadline is p99 duration x margin. The iteration fuse is the smaller of p99 iterations x margin and spend cap / cost per iteration, and which of the two binds is worth knowing: if spend binds first, you are cutting off working runs to control cost.
Neither fuse is looking at your task. Both bound the damage of a failure; neither detects that the work is done. A run stopped by the hang fuse and a run stopped by the spin fuse are different diagnoses, and if they return the same thing to the caller you have thrown away the finding. The completion signal is a separate thing entirely, and it has to be a value computed in code from ground truth.
The full argument is in a time fuse and an iteration fuse are not the same thing. For what a tree of individually capped runs can spend, use the agent reliability calculator.
Questions
Both, because they guard opposite edges. A hung call does zero iterations, so the counter freezes and can never fire; only a clock catches that. A spin does iterations without making progress, and only the counter catches that. Two limits on the same edge is one protection with two names.
Because the counter is incremented by the loop, and a hung loop is not looping. The count stops advancing at whatever it had reached and stays there, so the limit is never approached let alone crossed. This is why every embedded watchdog counts time rather than iterations and runs on its own oscillator.
Outside the loop's execution, by something the loop cannot block. A timeout checked at the top of each iteration is not a time fuse, because a hang never reaches the top of the next iteration. If the only thing that would end a hung run is a check inside the thing that is hung, there is no hang protection at all.
Above the tail of normal work, with margin, so they do not cut off runs that were going to succeed. Take the p99 from your own traces rather than from intuition, since it is almost always further out than people expect, then multiply. If a spend cap forces the iteration fuse below your p99, you are cutting off working runs to control cost, which is a legitimate choice worth making deliberately.
No, and that is the limit worth stating. Both are backstops that bound the damage of a failure. Neither looks at your task, so neither can distinguish a run that finished from one that ran out of budget. That still requires a measure of remaining work computed in code, and the two exits must be distinguishable to the caller.