Software Engineer's Blog

Agent Loops in Claude Code: Knowing When to Stop

Agent Loops in Claude Code: Knowing When to Stop

Give an agent a long job and one of two things tends to go wrong. It stops too early, declaring victory while the build is still red. Or it never stops, burning tokens on a task it can’t tell it has finished. I’ve built two of these loops and a completion gate on this blog, and the bug was never in the looping. Writing them is what convinced me the loop is the easy part.

The hard part of an agent loop isn’t repeating work. Repeating work is a while line. The hard part is a measurable stop condition: separating the part of “done” a process can verify from the part that still needs judgment, and moving that mechanical verdict off the worker’s unchecked self-report. It’s layer 4 of how I think about AI engineering, and it’s where most of the actual difficulty lives. Plenty is written about running verification. The harder question, the one this essay is about, is whether the verifier itself can be trusted.

The official map: four kinds of loop

Anthropic’s Claude blog frames a loop as “agents repeating cycles of work until a stop condition is met,” and sorts loops into four types by their trigger, how they stop, the primitive they use, and the task each suits: turn-based (a prompt, ending when Claude judges it’s done), goal-based (/goal, ending at the goal or a turn cap), time-based (/loop and /schedule, on an interval), and proactive (an event or schedule with no human in the loop).

That’s a useful coordinate system. It tells you what kicks off the next turn and when control comes back to you. But it stops at the taxonomy. Pick a type and you still haven’t answered the question that actually bites: what decides “done,” and can that decision be trusted?

The axis the types don’t name: convergent vs exploratory

There’s a second axis, orthogonal to the trigger, and it’s the shape of the stop condition itself. A convergent loop asks is it right yet? It runs a PASS/FAIL check and stops the moment the check passes. An exploratory loop asks is it better? It tracks a metric it tries to improve, and runs until a human cuts it off by time or iteration count. These are not the four types over again; a goal-based loop can be either. Add the stop-shape axis to the official map, and each type gets easier to reason about: what success looks like and, more importantly, what can vouch for it:

Official typeHow it startsTypical stop shapeWhat can verify it
Turn-baseda user prompteithera human, a skill, or a command
Goal-based (/goal)a manual /goal commandusually convergentthe /goal evaluator, or a Stop hook
Time-based (/loop)a set intervaleither or ongoinga task-specific check against external state
Proactivean event or scheduleinner task converges, outer routine persistsworkflow-level checks

The official types describe the loop’s control flow: what triggers it, how it stops, which primitive runs it. Convergent versus exploratory describes the shape of success. A timer can start another turn, but it can’t confirm the work succeeded. You need both axes to answer “what do I actually use.”

I’ve built both ends. The Ralph loop is the convergent case: it keeps going until a real command exits zero and releases its completion-promise string. Auto Research is the exploratory case: one number out of score.sh, keep the change if it improves, roll back with git if it doesn’t, stopped by a turn cap so it can’t run forever. Same umbrella, opposite stop conditions. Knowing which shape you’re in tells you whether you’re writing a PASS/FAIL check or a metric and a rollback.

The blank the map leaves: who enforces the verdict

Here’s what the taxonomy doesn’t touch. The weakness is clearest in turn-based and goal-based loops. In a plain turn, the worker itself decides when to stop. With /goal, a separate small model does the deciding, but it judges only what the worker has surfaced in the conversation: it never runs your tests or reads the repo. So it isn’t quite the model grading its own homework. It’s a grader working from the student’s report rather than inspecting the work itself, and the student controls what goes into the report.

The fix isn’t a better prompt. It’s to move the mechanical part of the verdict off the model and onto a process: a command-based Stop hook that verifies completion with an exit code rather than a sentence in the chat. That is generator versus evaluator applied to loop termination, with the evaluator swapped from a second model to a shell script. The model can still judge meaning; the script verifies the part that can be measured.

The failure mode all three share

Once you look at completion as a signal to be enforced, the three cases start to rhyme. Ralph can false-pass when a shell pipeline swallows a non-zero exit. Auto Research can hack its metric, improving the number while the real result gets worse. A goal loop can delete the failing test and pass. Different loops, one failure: when the stop signal and real success come apart, the agent optimizes the signal, because that’s the shortest path. So the practical rule for any loop type is the same. After you pick it, protect its stop signal against the shortcuts the agent is most likely to take. A signal you mistake for unforgeable can be worse than no signal, because it looks like safety.

None of this was new to me, exactly. I spent thirteen years on telecom backends where a release gate was not a suggestion. A build didn’t ship because someone felt good about it; it shipped because a pipeline exited zero. Agent loops need that same discipline, and it transfers cleanly: the stop condition is a release gate, and “the model says so” is the one input a release gate never accepts.

Where the model runs out

Two honest limits. The taxonomy is a map, not a recipe. Time-based and proactive loops get less implementation detail in the official overview, especially around unattended safety and failure recovery, and running an agent on a schedule raises safety questions that a stop condition alone doesn’t answer. And not every “done” reduces to an exit code. “The API reads cleanly” is a real acceptance criterion with no command behind it; that half stays with a human or a model, and the gate only covers the mechanical half. The point was never that everything is a test command. It’s that the part which can be a command should be one, and the loop should stop on that, not on a vibe.

Takeaways

  • The loop is the easy part. Defining the stop condition, and independently enforcing the part that can be measured, is the work.
  • Two axes, not one. The four official types describe control flow (trigger, stop, primitive); convergent versus exploratory describes what “done” means. Use both to choose.
  • Whatever type you pick, assume the stop signal will be gamed and protect it against the shortcuts you can anticipate. Ralph’s hidden pipeline exit, Auto Research’s metric hacking, and a goal loop’s deleted test are the same bug three times.

Sources