Agent Loops: Claude Code's Creator No Longer Prompts, He Writes Loops

Agent Loops: Claude Code's Creator No Longer Prompts, He Writes Loops


“I no longer prompt Claude. I have loops running, they are the ones prompting Claude and deciding what to do. My job is to write loops.” This sentence is from Boris Cherny, the creator of Claude Code, and the clip has garnered about 700,000 views in 24 hours on X. Since then, everyone is talking about “loop engineering,” and almost no one explains how to create one.

We’re going to fix that, starting from scratch. No prerequisites, other than having typed a question into Claude Code at least once. First, we’ll look at the loop that’s already running before your eyes without you knowing it, and then we’ll build a real one, in three levels of difficulty, with the files to copy and the exact place to put them.

Along the way, we’re going to clear up a very common misunderstanding: the /loop command in Claude Code is not an improvement loop. It’s a timer.

Five Vocabulary Words, and We’re Good

If you’re a beginner, half of the confusion comes from jargon. Five definitions and we’re done talking about it.

  • A tool: an action that Claude is allowed to execute on your machine. Read a file, write it, run a command in the terminal, search the web. He doesn’t execute them himself; he requests them, and the Claude Code program executes them for him.
  • A turn: a complete round trip. Claude speaks, tools run, results come back to him. A somewhat hefty question means five, ten, or thirty turns, and you only see one scrolling in the terminal.
  • The context: everything Claude has in front of him at any given moment. Your request, the files read, command outputs. It’s limited, it fills up, and when it’s full, Claude summarizes the old to make space. This summary is called compaction, and it loses details.
  • The file CLAUDE.md: a text file placed at the root of your project, which Claude reads with each request. It’s your permanent internal regulation. Unlike your first sentence of the conversation, which eventually disappears during compaction, it always stays there.
  • A sub-agent: a disposable Claude, launched by the main Claude for a specific mission, with its own brand new context. It works in its corner, and only its conclusion comes back. It’s a Markdown file in .claude/agents/, and you can write as many as you want.

Let’s add a sixth word, the one that will change everything below: a hook, it’s a little script of yours that Claude Code executes automatically at a certain moment, for example just before writing a file, or just before handing back control. A script, not an AI. It does what it’s told, always the same, and it doesn’t cost a single token.

The Loop That’s Already Running Without You Asking for Anything

Before building anything, you need to see the one that already exists. Anthropic’s documentation on the agent loop describes it in five steps, and that’s exactly what happens every time you hit Enter.

  1. You send your request.
  2. Claude reads it and responds, either in text or by asking for tools.
  3. Claude Code executes the requested tools.
  4. The results go back to Claude, added to the context.
  5. We start again at step 2, until Claude responds without asking for any tools. That message closes the loop.

The five steps of an agent turn, with the return arrow closing the cycle

Let’s get concrete. You type “fix the tests that fail in auth.ts.” Here’s what really happens, while you only see a wall of text scrolling:

TurnWhat Claude asksWhat he gets back
1runs npm testthree tests failed
2reads auth.ts and the test filethe content of both files
3modifies auth.ts, reruns npm testeverything is green
4nothing, it responds in textend, the loop stops

Four turns. You wrote only one sentence, and there were four back-and-forths, six tool calls, and one stop decision. That's the loop.

Remember the consequence, because that's the whole article: an agent IS already a loop. Everything you add on top, sub-agents, hooks, config files, slash commands, serves only one purpose: to decide when it is allowed to stop.

And therein lies the problem. By default, it is Claude himself who decides that he is done. In turn 4, he felt it was good. He did not check that the project still compiles fully, nor that no one has broken something else elsewhere. He just had the feeling that he was finished. Doing loop engineering means taking that decision and entrusting it to something less subjective than a feeling.

The four families of loops

The Claude Code team defines a loop as "an agent that repeats cycles of work until a stop condition is met." There are four kinds, and they do not serve the same purpose at all. This is the table to keep handy.

FamilyTriggerStop ConditionWhat for
By turnsyour promptClaude feels he is donedaily tasks, short tasks
By goal (/goal)your promptthe goal is reached, or the quota of turns is exhaustedanything with a verifiable criterion
By time (/loop)an intervalyou cancel, or the session closesmonitoring, external systems
Proactivean event, without youeach task exits when its goal is reachedbug sorting, migrations, upgrades

No, /loop does not improve anything

The confusion comes from its name. Look at what it really does:

/loop 5m checks if the deployment is complete

Every five minutes, Claude Code sends back the same prompt, word for word. Not "better" each time: identical. The documentation is clear, the command "launches a prompt repeatedly as long as the session remains open." It's a cron with a brain, not an upward spiral. Two useful details though: if you omit the interval, Claude self-paces between two iterations, and if you omit the prompt, it will read .claude/loop.md.

The command that really improves is /goal. It takes a condition in natural language, and Claude continues to work through the turns as long as that condition is false:

/goal raise the Lighthouse score of the homepage to 90 or more, stop after 5 attempts

Two things matter in this line, and if you remember only one paragraph from the entire article, take this one.

The "90 or more," first. A measurable condition can be verified without any possible discussion. Compare with "make the page faster": faster than what, judged by whom? A loop needs a number or a passing test, not an adjective. Writing a loop is first forcing yourself to transform a wish into a verifiable criterion, and that's frankly the hard part.

The "stop after 5 attempts," next. A loop without a counter is a bill. The model never gets tired, never gets bored, and has no idea what the cost of the thirtieth attempt is.

"Do my agents in .claude already create a loop?"

That's THE question I get, and the answer is no. Well, not quite, and the detail is worth it.

Let's take a classic setup, the one that many end up writing after a few weeks: a sub-agent architect who designs before we code, a sub-agent developer who implements, a sub-agent reviewer who reviews the work, and a CLAUDE.md that specifies the order in which to call them. It's clean, it's useful, it avoids a lot of mistakes. But as it is, it's a chain, not a loop.

Comparison between a chain of agents that ends and a loop of agents that closes on itself

The difference lies in a single pencil stroke. In the chain, the reviewer is the last box: they give their report, and the turn ends. In the loop, their report becomes an input for the developer again, and it restarts as long as the verdict is not good.

A loop needs three ingredients, and the third is the one we systematically forget:

  • a repeatable action, you already have that, it's your sequence of agents,
  • a measurable stop condition, not "when it's good" but "when the build passes and the reviewer finds no more blocking issues,"
  • a return edge, meaning a path that brings the output of the reviewer back into the hands of the developer.

Without the third, your reviewer produces a nice report at the end of the run and no one reads it. With it, the chain becomes a cycle and the program improves with each round. Good news: closing this edge takes about ten lines.

Level 1: your first loop takes one line

Before writing any file, know that 80% of the benefit comes from typing this into Claude Code:

/goal the build passes and the tests are green, stop after 5 attempts

Claude will code, run the build, see the error, correct it, rerun, and he will only give you back control when the condition is true or when he has exhausted his five attempts. You just did loop engineering. Really. Try this before anything else.

The limitation is that it only lasts as long as your command. Tomorrow, in a new session, you will have to retype it, and you will forget. Hence the next two levels, which make the rule permanent.

Level 2: the definition of "done," written once and for all

The CLAUDE.md is reviewed with each request, including after a context compaction. It is written in black and white in the SDK documentation: the persistent rules go there, not in your first message, which will eventually disappear. So this is where the stop condition lives.

Create the file at the root of your project, if it does not exist, and add this block to it:

## Definition of Done

A task is not finished unless all four points are true:

1. the build passes, with no new warnings
2. the sub-agent `reviewer` has returned VERDICT: PASS
3. HISTORY.md describes the what and why of the project
4. the version is incremented

As long as one point is false: correct it, then go back to point 1.

It's short, it's numbered, and especially the last line explicitly draws the return edge. From now on, Claude will review himself and go back on his own in most cases.

In most cases, yes. Not all. A model remains a model, it sometimes decides that it is finished when it is not, especially at the end of a long session. If you want it to be a law and not just advice, you need level 3.

Level 3: the Stop hook, the one that has the right to say no

Here is the complete structure we will obtain. Everything happens in a .claude/ folder at the root of the project, to be created if it does not exist.

your-project/
  .claude/
    settings.json          which hooks to launch, and when
    agents/
      reviewer.md          the reviewing sub-agent
    hooks/
      done-or-continue.sh  the script that accepts or refuses the end
  CLAUDE.md                the permanent rules (level 2)

1. A reviewer that gives a verdict readable by a machine

The trap of the reviewing sub-agent is that it produces prose. Very well written, often correct, but impossible to derive an automatic decision from: no script knows how to read "overall it's correct but I would have a doubt about error handling." So we impose a final standardized line, and everything becomes possible.

File .claude/agents/reviewer.md. The part between the two dashed lines is called the frontmatter, it is the agent's identity card. The rest is its instructions.

---
name: reviewer
description: Review the work after each completed implementation.
model: opus
tools: Read, Glob, Grep, Bash
---

You only review what has just changed (the diff, plus untracked files).
Class each remark as CRITICAL, WARNING, or INFO.
You never write code, you never commit.

Write your report in .claude/last-review.md and end it with a single line:
VERDICT: PASS   if there are no CRITICAL left
VERDICT: FAIL   otherwise

Note the tools: which contains neither Edit nor Write. This is not vanity: a reviewer who can correct starts defending their own corrections. We separate the one who does from the one who judges, otherwise the judgment is worthless.

2. The script that refuses the end of the round

The hook Stop triggers right when Claude wants to hand control back to you. He has the right to refuse. File .claude/hooks/done-or-continue.sh, to be made executable with chmod +x :

#!/usr/bin/env bash
# Refuse the end of the round as long as the Definition of Done is false.
set -uo pipefail

[ -f .claude/loop.off ] && exit 0          # emergency switch

STATE=.claude/loop-count
COUNT=$(cat "$STATE" 2>/dev/null || echo 0)
if [ "$COUNT" -ge 5 ]; then                 # circuit breaker
  rm -f "$STATE"
  exit 0
fi

if ! npm run build >/tmp/build.log 2>&1; then
  echo $((COUNT + 1)) > "$STATE"
  jq -n '{decision:"block",
          reason:"The build fails. Read /tmp/build.log, fix it, then continue."}'
  exit 0
fi

if ! grep -q "VERDICT: PASS" .claude/last-review.md 2>/dev/null; then
  echo $((COUNT + 1)) > "$STATE"
  jq -n '{decision:"block",
          reason:"Green build but no VERDICT: PASS. Restart the sub-agent
                  reviewer, apply the CRITICAL, then continue."}'
  exit 0
fi

rm -f "$STATE"
exit 0

If you do not read bash fluently, here is the same thing in English, in order:

  • If the file .claude/loop.off exists, we do nothing and let Claude stop. It's your switch: a touch .claude/loop.off and the loop is neutralized, without killing anything.
  • We read a counter stored in a small file. If it is already at 5, we give up and let it pass. It's the circuit breaker, it prevents the infinite loop.
  • We launch the build. If it breaks, we increment the counter and respond block with a reason. Claude cannot stop and receives this reason as a new instruction.
  • If the build passes, we look for the line VERDICT: PASS in the reviewer's report. If absent, we block again, specifying exactly what to do.
  • If everything is good, we clear the counter and exit silently. Claude has the right to finish.

Three fields are enough to control all this, and there are no others to know to get started. decision: "block" prevents Claude from stopping and transmits your reason as a new instruction. continue: false cuts everything, no matter what happens. And hookSpecificOutput.additionalContext injects a remark without blocking, when you want to guide rather than force. Note for those who do not have jq installed: a simple exit 2 with a message on the error output produces the same blocking, in a more brutal way.

3. Declare the hook

Now we need to tell Claude Code when to run this script. File .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/done-or-continue.sh" }
        ]
      }
    ]
  }
}

4. Check that it works

The simplest test, and do it, otherwise you will doubt for three days. Intentionally break a line of code, ask Claude for any small modification, and let him try to finish. He should restart on his own while correcting, with your reason visible on the screen. If nothing happens, check in order: the script is executable, the path in settings.json is correct, and running the script manually in a terminal produces no errors.

Result: the developer codes, the reviewer judges, the hook refuses the output until the verdict is good, and it loops up to five times before giving up cleanly. There, yes, you have a loop.

Three loops to try tonight

Harmless examples to get your hands dirty, from the simplest to the least simple.

/goal all tests pass, stop after 5 attempts

The classic. Works on any project that has a test command.

/goal no more linter warnings in src/, stop after 3 attempts

Excellent first try, because the criterion is binary and the fixes are safe.

/loop 10m watch new files in ./inbox, summarize each in ./resumes

This one is a monitoring loop, not an improvement, and it illustrates the difference well. It does not progress, it watches.

The self-improving loop

There is one more level. So far, the loop improves the program. It can also improve itself, and this is the pattern popularized by Andrej Karpathy in March 2026 with his repository autoresearch, which surpassed 50,000 stars on GitHub in a few weeks. The principle is summed up in one sentence from his README: we give an agent a real little model training, it modifies the code, it trains for five minutes, it checks if the metric has improved, it keeps or discards, and it starts again. All night long.

What makes the difference with a simple /loop is the memory. A file that the agent rereads at the beginning of each pass and updates at the end, with what it has just learned. Without this file, iteration 200 is as foolish as iteration 1, it makes the same mistakes with the same enthusiasm.

Transposed to a normal project, it adds a few more lines in the CLAUDE.md:

## Self-improvement

Before handing over:

- if I had to correct you, add the corresponding rule in LESSONS.md,
  a dry sentence, in the imperative, never an explanation
- if a step has been redone twice, note why in NOTES.md
- if a manual check comes back with each project, turn it
  into a script

LESSONS.md is reviewed at the beginning of each session, before any action.

After a month, the LESSONS.md is worth more than the code. It is the record of the mistakes that the loop will not repeat. And it is the file that everyone forgets to fill out, even though it costs nothing.

Beginner mistakes that are costly

The patterns that recur among everyone running loops in production are not very glamorous, but they are what keeps the house standing.

  • A vague stopping condition. "When the code is clean" is not a condition, it's an opinion. If you can't verify it with a command that answers yes or no, your loop will never stop at the right time.
  • No ceiling. The SDK exposes max_turns and max_budget_usd, and when the limit is reached, the result comes back with an explicit subtype (error_max_turns, error_max_budget_usd) instead of going into a silent spiral. Use this, and always start with a low ceiling.
  • The same agent does and checks. In the same context, it always reviews itself kindly. A sub-agent starts with a fresh context, which provides a true outside perspective, and it costs less since only its conclusion is sent back.
  • Two loops in the same folder. They will overwrite each other's files. Git worktrees solve this for almost nothing.
  • No switch. Always plan for the witness file that you can create manually to stop everything without killing the process. You will need it sooner than you think.
  • Run everything on the largest model. A loop running every five minutes means 288 executions per day. The choice of model and effort level is the first cost lever, far ahead of the rest.

On cost, specifically: a poorly bounded loop is the only known way to spend a monthly budget during lunch. Start with a narrow goal, a ceiling of five turns, and see what it consumes before opening the floodgates.

Running this without you

Once the loop is closed, it can be connected to something other than your keyboard. The composition recommended by Anthropic simply stacks a clock and a goal:

/schedule every hour: check #project-feedback for bug reports.
/goal: don't stop until every report found this run is triaged,
actioned, and responded to.

The first decides when, the second decides how far. This is exactly what Cherny describes when he talks about hundreds of instances reading GitHub issues, Slack feedback, and X threads to come up with the next thing to do. No one at the keyboard, yet work is progressing. But don't go there before you've run levels 1 to 3 for a few days, under your eyes.

What it changes concretely

The shift from prompt to loop is not a vocabulary trend. Writing a prompt is asking for a result. Writing a loop is describing what a good result is, and letting the machine bang against it until it gets there. The work shifts: less formulation, more acceptance criteria, automatable checks, and safeguards.

Personally, the part I find most underestimated is the line VERDICT: PASS. Three words imposed on a sub-agent, and a prose report becomes a stopping condition exploitable by a ten-line shell script. That’s what loop engineering really is: making measurable what wasn’t before. The rest is plumbing.

Sources

claude-codeiaagentsloop-engineeringanthropicautomatisationdeveloppement

No comments yet.

Une erreur s'est produite. Cette application peut ne plus répondre jusqu'à ce qu'elle soit rechargée.Veuillez contacter l'auteur. Reload 🗙