A stuck agent is a fixed point in its own policy. Break it with the cheapest perturbation that works — then cap the recovery so it can't loop on itself.
Why this, for you: you've watched an agent edit the same file ten times, each patch a little
different, none of them green — while the token meter spins. The fix isn't "catch it sooner." Detection already
fired. What you're missing is a recovery move that fits the way it's stuck, and a cap so the
recovery doesn't become its own runaway.
Detection catches a stuck agent; recovery is what the harness does next. They're separate
disciplines, because the cheap fix that breaks a repeater fails on a wanderer — and the heaviest move, handing off to
a human, is a poor first choice when one nudge would have sufficed.
1 Stuck vs. slow-but-converging
Recovery must not fire on legitimate slow work. The clean separator is a progress metric that can only
rise when real work is done — failing tests resolved, unique sources gathered, checklist items completed.
A loop is stuck when the progress metric is flat across N heartbeats while activity
continues; it's converging slowly when the metric is still rising, even by small increments.
Why insist on a progress metric? Because activity proxies — API call counts, file edits, log volume — keep rising
during a stuck loop too. They cannot tell the two apart. Tune N against the workload: a tight refactor on one file
looks identical to an edit loop if N is set too low.
2 Read the shape before you reach for a move
Stuck states come in three shapes, and the right recovery depends on the shape. Wrong-shape recovery makes things
worse — telling a wanderer to "try a different approach" sends it further off-goal; telling a repeater to "reassess
the goal" leaves the failed action unchallenged.
Shape
Symptom
Recovery move
Repeater
Same action, same result, repeated
Nudge naming the failed action; ask for a different approach
Wanderer
Activity continues but nothing connects to the goal
Reassess the goal — "what would move the needle?"
Looper
Alternates between a few actions without resolving
Context reset — wipe the priors that anchor the oscillation
3 Climb the recovery ladder one rung at a time
Each rung is a strictly larger perturbation of the agent's policy. You can't know in advance which rung suffices,
so you climb — the cheapest rung that breaks the fixed point is the right one.
#
Rung
Cost
1
Nudge — one message naming the observation
One token's worth; ignorable
2
Replan / reflect — restate goal, list what's tried, propose a new plan
A reasoning turn, still in-context
3
Escalate model or effort — stronger model / bigger budget
Compute
4
Context reset from a progress file — reload durable state, git-revert
Loses in-context state
5
Human handoff — surface state + failed attempts
Human capacity
6
Abort with a logged failure
The run
Context reset can wipe load-bearing work
Rung 4 is safe only when state has been externalised cleanly. If the agent built a useful partial
plan in the window that was never written to a progress file or git, "reset from progress file" loses it — and the
next iteration just repeats the work. Reset rides on trajectory logging being already in place.
4 Bound the recovery itself
Recovery has the same runaway problem as the original loop. Without a cap, the harness builds a recovery-loop on
top of the loop. Two bounds keep that from happening:
Per-incident attempt cap — a three-strikes rule: after three consecutive attempts on the same
stuck signal, climb to the next rung instead of repeating the current one; after three rungs fail, escalate
to a human.
Iteration-cap backstop — a hard maximum iteration count per conversation catches everything the
pattern detectors miss. Pattern detectors skip iterations that differ each time but are equally unproductive; the
hard cap is defence-in-depth behind them.
Half of automated recovery moves don't work in the wild — in one 220-loop dataset, only 6 of 12
responses reduced their target signal, and one detector generated 13.3× more signals than it suppressed by firing on
its own output. Treat every rung as a hypothesis: measure its effect on the target signal before trusting
it.
# Recovery, gated by shape and capped two wayson detector_fire(signal):
shape = classify(signal) # repeater | wanderer | looperif attempts[signal] < 3: apply(rung[shape]) # same rung, 3 strikeselse: rung = next_rung(rung); attempts[signal] = 0
if iteration > MAX_ITERS: abort_and_log()# hard backstop
↪ Your win: a stuck loop you can break on purpose
Separate detection from recovery — detection flags; recovery decides the move.
Use a real progress metric — flat-metric-while-active is stuck; rising-metric is slow. Activity proxies lie.
Name the shape first — repeater / wanderer / looper; wrong-shape recovery backfires.
Climb the ladder — nudge → replan → escalate → context reset → human handoff → abort.
Cap recovery twice — three-strikes per incident, plus a hard iteration backstop, and measure each move.
Retrieval practice — recall, don't peek
Question 1What cleanly separates a stuck loop from one that is converging slowly?
Question 2You diagnose a wanderer — activity continues but nothing connects to the goal. The right move is to…
Question 3Why climb the recovery ladder one rung at a time instead of jumping to a context reset?
Question 4Beyond the per-incident three-strikes cap, what backstop catches unproductive iterations the pattern detector misses?
Question 5 · spaced recall from an earlier lessonThe Go / No-Go gate says you should spend another iteration only when…
Ask me anything. Want help picking a progress metric for your workload, or wiring the three-strikes
cap into your harness without it fighting the iteration backstop? Next in Part 4: Improvement & Flywheel
Loops — closing the outer loop so the system gets better between runs.