Loop Engineering: How to Design Agent Loops That Actually Converge
TL;DR
The architecture side of loop engineering: plan/act/verify cycles, convergence criteria, retry policies, budget-bounded loops, and the loop-until-dry pattern. Concrete TypeScript-shaped patterns for building agent loops that stop when they should.
If you watched Loop Engineering in 9 Minutes, you know the pitch: stop prompting, start building loops. And the definitive guide covers the commands, goal, loop, routine, in Claude Code and Codex.
This post is the layer underneath. When you build your own agent systems, in TypeScript, with the SDKs, you do not get a /goal command handed to you. You get a model, some tools, and a while loop you have to design yourself. How that loop is shaped determines whether your agent finishes a multi-hour task or burns $40 rewriting the same file eleven times.
Loop engineering, as a practice, is designing that loop: what one iteration does, how the agent knows it is done, what happens on failure, and what hard limits keep it from running forever. Here is how I structure it.
Every agent is a while loop with opinions#
Strip away the branding and every agentic system is the same skeleton. Anthropic's building effective agents essay defines agents as "models using tools based on environmental feedback in a loop." OpenAI's practical guide to building agents says the same thing: a loop of model calls and tool executions that runs until an exit condition fires. OpenAI even published a post unrolling the Codex agent loop that shows the production version is still, at heart, this:
// Illustrative pseudo-code, not a real SDK
while (!done && budget.remaining()) {
const action = await model.decide(context);
const result = await execute(action);
context.append(result);
done = checkExit(context, result);
}
Everything interesting about loop engineering lives in three of those identifiers: checkExit, budget, and what one pass through the body actually contains. Most agent failures I have debugged trace back to one of those three being an afterthought.
Structure the body: plan, act, verify#
The naive loop body is "model picks a tool, run it, repeat." That works for short tasks and drifts badly on long ones. The pattern that holds up is giving each iteration an explicit plan/act/verify shape, which is the same insight behind the ReAct paper (interleave reasoning with action so each step is grounded in the last observation):
- Plan: the agent states, in the transcript, what it is about to do and why. This is not decoration. It gives the verify step something concrete to check against, and it is what you read when the loop goes sideways.
- Act: run the tools. Edits, commands, API calls.
- Verify: run an independent check that does not trust the agent's self-report. Tests, a typecheck, a linter, a schema validation, a second model grading the output.
The verify step is the whole game. Anthropic's guidance on agent harnesses and iteration frames the agent loop as gather context, take action, verify work, repeat, and is blunt that agents without a feedback signal plateau fast. An agent that greps its own diff and declares victory is a random walk. An agent that must make pnpm test pass is doing gradient descent.
In Claude Code specifically, you can enforce verify mechanically with hooks: a Stop hook that runs your test suite and blocks completion, or a PostToolUse hook that lints after every edit. The agent literally cannot claim done until the check passes. When I build custom loops, I replicate this: verification is code in the harness, never a question posed to the model.
// Illustrative: verification lives outside the model
async function verify(task: Task): Promise<VerifyResult> {
const tests = await run('pnpm test --filter', task.scope);
const types = await run('pnpm exec tsc --noEmit');
return {
passed: tests.ok && types.ok,
feedback: [tests.failures, types.errors].flat(), // fed back into context
};
}
The feedback field matters as much as passed. Failed verification is not an error state, it is the input to the next iteration. That is the core idea of Reflexion: agents improve dramatically when failure signals are turned into explicit verbal feedback they can condition on next pass.
Convergence criteria: define done before you start#
"Loop until the task is done" is not a convergence criterion, it is a wish. A loop converges when its exit condition is objective, checkable by code, and monotone-ish, meaning progress toward it is measurable.
Good convergence criteria I actually use:
- Test-defined: all tests in a named scope pass. The strongest one. Write failing tests first, then loop the agent against them.
- Diff-defined: the loop exits when an iteration produces no changes. This is the classic fixed-point pattern, and it is how "loop until dry" works (more below).
- Count-defined: the queue is empty. Zero lint errors, zero unlabeled issues, zero broken links.
- Judge-defined: a separate model call scores the output against a rubric and it clears a threshold. Weakest of the four, use only when nothing mechanical exists, and pin the rubric in writing.
The anti-pattern is asking the agent "are you done?" as the exit check. Models are optimistic. They will say yes. Your loop condition should never be a vibe.
One more subtlety: check for progress, not just completion. If verification fails with the identical feedback two iterations in a row, the loop is stuck, not converging. Detect that and change something, or stop:
// Illustrative: stall detection
if (hash(result.feedback) === hash(previous.feedback)) {
stalls++;
if (stalls >= 2) return escalate(task); // new approach, or a human
} else {
stalls = 0;
}
Retries with escalation, not repetition#
A retry that replays the same prompt into the same context is a coin flip you already lost once. Retries should escalate through distinct strategies:
- Retry with feedback: same approach, verification output appended. Fixes most transient failures.
- Retry with a fresh context: summarize what was learned, discard the polluted transcript, start clean. Long failed transcripts poison future attempts; a compact "here is what did not work and why" note outperforms 60k tokens of flailing.
- Retry with a different strategy: the plan step must propose an approach materially different from the logged failures.
- Escalate to a human: file the issue, post the summary, stop. A loop that knows when to give up is a feature. Anthropic's multi-agent research system post makes a related point about long-running agents: durable state plus deliberate recovery beats blind restarts.
Cap each rung. Two or three attempts per strategy is plenty. Past that you are paying for noise.
Budget-bounded loops#
Every loop needs a hard ceiling on all three axes: iterations, tokens or dollars, and wall-clock time. Not one of them. All three, because they fail differently. An agent can burn its dollar budget in four iterations of huge context, or run 200 cheap iterations for six hours, or hang on a single tool call overnight.
// Illustrative budget guard
const budget = {
maxIterations: 25,
maxCostUsd: 10,
deadline: Date.now() + 2 * 60 * 60 * 1000,
};
The important design decision is what happens at the boundary. A loop that hits its budget should not just die, it should land: commit work-in-progress to a branch, write a handoff note describing state and next steps, and exit cleanly. Budget exhaustion is a planned exit path, the same as convergence, just with a different report. This is exactly why Claude Code's /goal takes an explicit budget and why every serious automation platform makes you set one. When I covered the $400 overnight bill failure mode, the root cause was always the same: a loop with a verifier but no ceiling.
Loop until dry#
My favorite composite pattern, and the one behind most of my recurring automations: run the same bounded task repeatedly until an iteration finds nothing to do.
Each iteration: find the single highest-value item of a specific type (a lint violation, a flaky test, a doc page that drifted from the code, an unlabeled inbox thread), fix it, verify, commit. Exit when a full pass finds zero items. The convergence criterion is diff-defined and count-defined at once, each iteration is small enough to verify cheaply, and a failed iteration only loses one item's worth of work.
// Illustrative loop-until-dry harness
let dryPasses = 0;
while (dryPasses < 1 && budget.remaining()) {
const item = await agent.findNext(criteria); // plan
if (!item) { dryPasses++; continue; }
const fix = await agent.resolve(item); // act
const check = await verify(fix); // verify
if (check.passed) await commit(fix);
else await recordFailure(item, check.feedback); // skip, do not thrash
}
Note recordFailure: items that fail verification get logged and skipped, not retried inline, so one stubborn case cannot eat the whole budget. The dry pass at the end is what makes this pattern self-terminating in a way "improve the codebase" never is. It is also exactly the shape Addy Osmani describes when he frames loop engineering as designing the iteration, not the prompt.
Self-pacing: let the loop set its own cadence#
For recurring loops (as opposed to run-to-completion goals), fixed intervals are usually wrong. Polling a deploy every five minutes is fine; triaging an inbox every five minutes is waste. The upgrade is letting the loop choose its next wake-up based on what it just observed: found ten items, check back in 15 minutes; found zero, back off to two hours; found something urgent, stay hot.
// Illustrative self-pacing
const next = itemsHandled === 0
? Math.min(interval * 2, MAX_INTERVAL) // decay when dry
: BASE_INTERVAL; // reset on activity
It is exponential backoff, applied to attention. Claude Code's /loop supports exactly this when you omit the interval, and it is trivial to add to your own schedulers. The budget rules still apply per-wake-up: a self-pacing loop without a per-run ceiling is just a slower money fire.
The checklist#
Before you leave any loop running, custom harness or /goal alike:
- One iteration has an explicit plan/act/verify shape.
- Verification is code, not the model's opinion.
- The exit condition is objective and includes stall detection.
- Retries escalate (feedback, fresh context, new strategy, human) with caps per rung.
- Budgets exist on iterations, cost, and wall-clock, and hitting one triggers a clean landing, not a crash.
- Recurring loops self-pace and back off when dry.
That is loop engineering. The prompt is maybe 20 percent of it. The loop is the product.
FAQ#
What is the difference between loop engineering and prompt engineering?#
Prompt engineering optimizes a single model call. Loop engineering designs the iteration around the calls: what one cycle does, how completion is verified, how failures escalate, and what budgets bound the whole run. A mediocre prompt inside a well-designed loop with real verification beats a brilliant prompt in a loop that trusts the model to grade itself.
How do I stop an agent loop from running forever?#
Three layers: an objective convergence criterion (tests pass, diff is empty, queue is zero), stall detection that exits when consecutive iterations produce identical failure feedback, and hard budgets on iterations, cost, and wall-clock time. Any one alone is insufficient; a loop can satisfy the budget while stuck, or make progress while over budget.
What is a plan/act/verify loop?#
A loop body where the agent first states its intended step, then executes tools, then an independent check (tests, typecheck, linter, schema validation) confirms the result before the next iteration. It descends from the ReAct pattern of interleaving reasoning and action, with verification made mechanical rather than left to the model.
Should verification be done by the same model?#
No. Verification should be code wherever possible: test suites, compilers, linters, validators. When no mechanical check exists, use a separate model call with a fixed rubric as a judge, and treat it as the weakest acceptable option. Asking the working agent whether its own work is done reliably produces false positives.
What is the loop-until-dry pattern?#
A self-terminating loop where each iteration finds and fixes one item of a specific type, verifies it, and commits, and the loop exits when a full pass finds nothing left to do. It converges because the exit is count-defined, and it fails gracefully because each iteration risks only one item.
Where can I see these ideas in the actual tools?#
Claude Code exposes them as /goal (run until an outcome, with a budget), /loop (recurring, optionally self-pacing), and hooks for mechanical verification. Codex has automations and exec for non-interactive runs; OpenAI's Codex agent loop post walks the internals. The definitive guide covers the commands side in depth.
Continue Reading#
Get the next deep dive like this in your inbox
One email a week on Loop Engineering and the rest of the AI dev stack. Free.
Read next on AI coding tools
The Definitive Guide to Loop Engineering in Claude Code and Codex
Goal, loop, routine. Three verbs, two tools, one hard part. A complete field guide to running agentic loops in Claude Code and Codex, the real commands, the patterns people actually run, and the two failure modes that burn money.
16 min readLoop Engineering in 9 Minutes: Stop Prompting, Start Building Loops
A companion guide to the Loop Engineering video: the shift from repeatedly prompting an LLM to building long-running loops, goals, and automations. Here is the core idea and where to go deeper.
6 min readClaude Code Loops: Recurring Prompts That Actually Run
Claude Code now has a native Loop feature for scheduling recurring prompts - from one-minute intervals to three-day windows. Fix builds on repeat, summarize Slack channels, email yourself pull request digests. All from the CLI.
6 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.








