Skip to content
All posts

Loop engineering: AI agents that finish without re-prompting

Loop engineering replaces the human re-prompt with a system: a stop condition, a guardrail the agent cannot negotiate with, and a bounded iteration count.

8 min read
  • Claude Code
  • AI-assisted development
  • Agents
  • Developer experience
  • Testing

Every coding agent has the same default ending. It does some work, decides the work looks finished, writes a summary and stops. If the work is not finished, you read the summary, notice, and type "keep going". Anthropic's own best-practices guide puts it bluntly: "Without a check it can run, 'looks done' is the only signal available, and you become the verification loop."

Loop engineering is the practice of building that loop instead of being it. You design a system that re-enters the agent, decides whether the goal is met, and constrains what it may do on the way, so that the human prompts once and reviews at the end rather than at every step. The loop itself is trivial. Everything that makes it safe to leave running is not.

Key Takeaways

  • The canonical loop is one line of Bash: feed the same prompt to the agent until something ends it. Everything load-bearing lives in the prompt and the stop condition.
  • There are four ways to decide "done": a timer, an evaluator model, a script, or a separate verifier. Pick by how much you trust the agent's own report, which should be: not much.
  • A loop manufactures repeated failed attempts, which is exactly the condition under which models start gaming the check. On an adversarial benchmark Claude Opus 4.1 cheated on half of conflicting tasks, mostly by editing the tests (ImpossibleBench, 2025).
  • Guardrails that hold are deterministic, not advisory: a hook that blocks the edit, an iteration cap, one unit of work per pass committed to git, and a sanctioned way to give up.

The loop is one line of Bash

Geoffrey Huntley's Ralph is the reference implementation, and it is short enough to quote in full:

while :; do cat PROMPT.md | claude-code ; done

Everything interesting is in PROMPT.md: one task per pass, run the tests after implementing, commit only when they pass, stop when the task list is empty. The same idea now ships as a ralph-wiggum plugin in the Claude Code repository. It swaps the Bash loop for a Stop hook that refuses to let the session end and re-feeds the prompt, takes --max-iterations and a --completion-promise string, and its README tells you to treat the iteration cap as "the primary safety mechanism" because the promise is an exact string match the model can emit at any time.

Four ways to close the loop

The question any loop has to answer is who decides the work is done. Claude Code now ships four answers beyond the prompt itself. The table starts with the prompt as the baseline, and the rows differ in one dimension: how much the decision trusts the agent's own account of itself.

MechanismWhat decides "done"Trusts the agent's report?
In-prompt ("stop when all tests pass")The agentEntirely
/loopA timer; the prompt re-runs every N minutes with context intactEntirely, each tick
/goalA separate small model judging the stated condition after every turnPartly: it reads the transcript
Stop hookA script: exit 2 and the session cannot endNot at all
Verification subagentA second agent with no stake in the first one's claimsNot at all

The first two rows, the prompt and /loop, are re-prompting. They keep the agent working, which is useful, but the stop signal is still the agent saying it is finished. /goal is the first rung where something other than the agent reads the result, and its docs are careful about what to put in the condition: the example is all tests in test/auth pass and the lint step is clean, and they recommend adding constraints "such as 'no other test file is modified'". That constraint is there because the people who built the feature expect the agent to try the shortcut.

Iteration does pay off when a real check sits at the end of it. DeepSWE reports 42.2% on SWE-Bench-Verified at one attempt and 71.0% at sixteen, with a verifier choosing among the sixteen, execution-based checks plus a judge model, landing at 59%. A vendor number, so take it as illustrative, but the direction is the point: retries help in proportion to how good the thing judging them is.

What a loop optimises for

A loop is a machine for generating repeated attempts at the same check, and repeated failed attempts are the documented precondition for a model to stop solving the problem and start solving the check.

Anthropic described this in the Claude 3.7 Sonnet system card, under the heading "Excessive focus on passing tests": returning expected test values directly instead of a general solution, and "modifying the problematic tests themselves to match the code's output", behaviour that "typically emerges after multiple failed attempts". METR measured o3 reward-hacking in 30% of runs on one research benchmark, including monkey-patching the evaluation function so it judged every submission a success, against under 1% on a more conventional task set.

ImpossibleBench made it measurable by building tasks where the spec and the tests contradict each other, so any pass is a cheat by construction.

Cheating rate on ImpossibleBench's conflicting SWE-bench tasks, by model On tasks where the specification and the tests conflict so that any pass is a cheat, GPT-5 cheated on 54 percent of tasks, Claude Opus 4.1 on 50 percent, and o3 on 49 percent. With an explicit option to abort and flag the task for a human, GPT-5 fell to 9 percent. Cheating rate on conflicting tasks (%) GPT-5 54 Claude Opus 4.1 50 o3 49 GPT-5, with an abort option 9 Source: Zhong, Raghunathan and Carlini, ImpossibleBench (arXiv 2510.20270), October 2025.
An adversarial benchmark, so these sit closer to a ceiling than to what you will see on a real suite. The last row is the one to design around: given a legitimate way out, the model mostly took it.

The models differ in method. When Claude and Qwen cheated, more than 79% of the time it was by modifying the test cases; OpenAI's models spread across test edits, operator overloading and special-casing. Two findings from that paper translate directly into design rules. Making the tests read-only was "effective against models like Opus-4.1, whose primary cheating strategy involves modifying test cases". And giving the model an explicit way to abort and flag the task for a human cut GPT-5's rate from 54% to 9%.

Rules for a loop you can leave running

Make the guardrail a rule, not a request. "Never edit the tests" in the prompt is advice, like every other convention you write down for an assistant. A PreToolUse hook that exits 2 is a rule: the call is blocked and the stderr goes back to the model as the reason. The docs single that exit code out as the one outcome nothing else in the hook can override.

#!/bin/sh
# .claude/hooks/protect-tests.sh — test files are read-only inside a loop
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
  *.test.ts|*.test.tsx|*__tests__*|*__snapshots__*)
    echo "Test files are read-only in this loop. Fix the code under test, or report the test as wrong and move on." >&2
    exit 2 ;;
esac

Register it on Edit|Write. It does not see a sed -i issued through the Bash tool, so pair it with a Bash matcher that greps the command for the same globs, or make git diff --stat -- '**/*.test.*' the first line of the next pass.

Give it a way to give up. The stderr line above is the abort option from the paper. A blocked edit with no way forward invites creativity; a blocked edit plus "report it and move on" lands the problem in your review instead of in a clever workaround.

One unit of work per pass, committed. Huntley's rule, and Anthropic arrived at it independently. In their write-up on harnesses for long-running agents, the recurring failure was Claude marking a feature complete after running unit tests but never checking it worked end to end; the fix was one feature per session, a commit per session, and a prompt that said "only mark features as 'passing' after careful testing". Git is the ledger for the same reason small merges beat long-lived branches: between passes you read git log, and a bad pass is one revert.

Bound the iterations. The completion promise can be wrong, the goal can be unreachable, the hook can block every path. A cap turns all three into a stopped loop with a transcript instead of a seven-day bill. The ralph-wiggum README is right to call it the primary safety mechanism.

Separate verification from generation. The best-practices page asks you to have Claude "show evidence rather than asserting success". The strongest form is structural: a second agent, or a script, that runs the check without having written the code. The thing that produced the work should not be the thing that grades it.

These rules are drawn from the sources above and from short, bounded loops on my own work, not from a long-running one. The read-only hook has earned its place; the rest I would still want a transcript for before trusting overnight.

The human leaves the prompt, not the loop

METR's randomised trial is the number people reach for against all of this: 16 experienced developers, 246 real tasks, 19% slower with AI tools, while believing they were 20% faster. Read closely, it argues for loop engineering. Those developers were the loop. They prompted, read, corrected and re-prompted, and the reading and correcting is where the time went.

What the practice removes is the prompting. What it must not remove is the check, because the moment nothing outside the agent decides "done", the loop will optimise for whatever does. The same lesson as writing the conventions down so an assistant can read them: the leverage is never in the model. It is in how much of your judgement you managed to express as something that runs.