Loop engineering means you stop prompting the agent turn by turn and build a small system that prompts it for you. You set the goal and a check for what done looks like; the loop keeps feeding the agent work, checking the result and trying again until the check passes. This works the same whether you write code all day or you’re a PM who ships with agents.
What it is
Most of us went through two steps already. First you typed the code, then you prompted an agent to type it. The third step is writing the loop that prompts the agent, so your job moves from doing the work to defining it and checking it.
Inner loop vs outer loop
This is the part that makes everything else click. There are two loops, and they run at very different speeds.
The inner loop belongs to the agent. It writes something, runs it, checks it against your done condition and fixes what failed, over and over, in minutes. Nobody needs to watch it, because a machine can tell whether it passed.
The outer loop belongs to you. You decide what’s worth building, write the done condition, review what the inner loop hands back, ship it and learn from real users. It runs over days or weeks, and a person has to judge every turn of it.
The usual way to get burned is mixing the two up: trying to automate the outer loop (“build me the right product”), or babysitting the inner loop by hand and approving every edit. Automate the inner loop hard, and keep the outer loop yours.
What’s inside a good inner loop
A trigger starts it, a maker agent does one unit of work, and a separate checker agent proves it’s done. Failures go back to the maker, passes come to you as a short report. Memory lives in files, not in the chat, because the agent forgets between runs and the repo doesn’t.
The simplest inner loop: Ralph
Geoffrey Huntley’s Ralph Wiggum technique is the easiest way to see this. It’s a bash loop that feeds the same PROMPT.md to a fresh agent every pass. The agent reads the plan from disk, does one task, writes progress back, commits and exits, and the loop starts it again. When it keeps failing the same way, you don’t jump in and drive; you fix the prompt and let it run.
Set up your first loop today
You can have this running in under an hour. Pick something small with an obvious check, like a ticket with clear acceptance criteria or a set of failing tests.
1. Write the plan and the prompt
Create two files in your repo. The plan is the task list the loop works through; the prompt tells every fresh agent how to behave.
# plan.md
## Goal
Users can reset their password from the login screen.
## Done when
- [ ] npm test passes
- [ ] "Forgot password" link visible on /login
- [ ] Reset email sends in the local dev mailer
## Tasks
- [ ] Add reset token table + migration
- [ ] POST /auth/reset-request endpoint
- [ ] Reset form UI
- [ ] Tests for expired and reused tokens
## Notes from previous passes
# PROMPT.md
Read plan.md. Pick the single most important unchecked task.
Do only that task. Then run npm test and npm run lint and fix
anything they catch. Check off the task, add one line under
"Notes from previous passes" about what you learned, and commit.
If every box under "Done when" is checked, write STATUS: DONE
at the top of plan.md. Do not start a second task.
Not everything has a test suite. If your loop writes posts, reports or specs, “Done when” still needs checks a machine can run:
| Check | Example |
|---|---|
| Count or limit | Under 200 words, five bullets max |
| Contains | Mentions the price and links the source |
| Format | Valid JSON, matches the template’s headings |
| Rubric | A separate reviewer scores it 1–5 against your criteria |
| You | The fallback for taste and judgment, so keep these few |
2. Run it with a cap
Run it on a branch so a bad pass is easy to throw away. Every pass is a fresh claude -p call, so the agent starts clean and only knows what’s in the files. The caps matter: 20 passes, 40 turns and $3 per pass, because a loop with no ceiling is a token bill with no ceiling.
# ralph.sh (run on a branch)
for i in $(seq 1 20); do
claude -p "$(cat PROMPT.md)" \
--allowedTools "Read" "Edit" "Write" "Bash(npm test *)" "Bash(npm run lint *)" "Bash(git *)" \
--max-turns 40 --max-budget-usd 3
grep -q "STATUS: DONE" plan.md && echo "Done after $i passes" && break
done
Inside Claude Code you can skip the script, with one difference: these run inside a single session, so context builds up instead of resetting each pass. The Ralph plugin uses a stop hook to feed the same prompt back until the agent outputs your completion phrase. /goal keeps going until a condition is true; a separate small model judges it after each turn, but only from what shows up in the conversation, so write the condition as something Claude can prove by running a command. Use auto mode if you want either one to run without stopping for approvals.
/ralph-loop "Follow PROMPT.md. When plan.md says STATUS: DONE, output <promise>DONE</promise>" --max-iterations 20 --completion-promise "DONE"
/goal npm test exits 0 and npm run lint is clean, or stop after 20 turns
3. Add a checker
Once the basic loop works, split the grading out. Right now the same agent writes the code and checks its own boxes, which is fine for a first run but not for anything you’ll walk away from. Make a second agent whose only job is to verify against “Done when.” In Claude Code that’s a subagent file with no edit tools.
# .claude/agents/checker.md
---
name: checker
description: Verifies finished work against "Done when" in plan.md. Use before marking anything done.
tools: Read, Grep, Glob, Bash
---
You verify, you never fix. Read "Done when" in plan.md, run the
checks it names, and report PASS or FAIL per item with the evidence.
Do not edit, create or delete files.
It has Bash so it can run tests, which means “never fix” is an instruction, not a hard lock. If you want it truly read-only, drop Bash and have it read the test output the maker saved.
4. Put it on a schedule
For recurring work, /loop reruns a prompt on an interval while your session stays open (recurring loops expire after seven days). For anything that should survive a closed laptop, use a routine, which runs in the cloud on a schedule of an hour or longer. Good first schedules are boring ones: a morning digest, a PR babysitter, a nightly test fixer. Not in a terminal? Scheduled tasks in the Claude app give you the same idea without any code.
/loop 30m check open PRs, rebase any that are behind main, fix failing CI, report what changed
5. Run the outer loop
Read the report, not the diff stream. When the loop fails the same way twice, don’t fix it in chat; add a line to PROMPT.md, CLAUDE.md or a skill so every future pass knows. That habit is how the loop gets better every week instead of making the same mistake forever.
What to put in a loop
Before anything gets a schedule, ask one question: who verifies the result, and what does that check cost? Hard work with a cheap check goes in the loop first. Easy work with an expensive check stays a draft that you decide on.
How I run mine
- Signal loop. A content-intelligence pipeline scrapes competitor posts and an LLM step writes a digest. It’s read-only, so the worst case is a stale summary.
- Spec loop. A pack of product skills takes an idea through PRD, spec and tickets, one skill per step. The skills draft and push back; I approve the PRD and the spec.
- Build loop. Approved tickets go to my agent software factory, one ticket per pass with a checker that runs the app. I merge the PR, and I’m wiring an orchestrator on a home machine so I can kick these off from my phone.
- Mistake loop. Repeat failures become skill edits. My last skill audit fixed five skills, mostly YAML errors, script bugs and skills contradicting each other.
What I’d recommend
- Start with one inner loop this week. A ticket with clear acceptance criteria and a test suite is the perfect first target.
- Write “Done when” before you write the prompt. If you can’t say how a machine would check it, it isn’t ready for a loop. PMs already have this muscle from writing acceptance criteria.
- Run it by hand for a week before you schedule it. You’ll find the checks you forgot while you’re still watching.
- Make your first scheduled loop read-only. Digests and monitors can’t break anything, and they teach you what good loop output looks like.
- Keep the maker and the checker apart. Different instructions, read-only tools for the checker, and a stronger model where it’s worth paying for.
- Anything irreversible needs your yes. Sending, posting, paying, deleting or contacting someone always stops for approval, no matter how good the loop gets.
- Cap every loop. Iterations, a dollar budget (
--max-budget-usdin headless runs) and a branch or worktree you can delete. - Protect the outer loop. Read what shipped, talk to users and decide what’s next yourself. The faster the inner loop runs, the more that judgment is the whole job.
Try it yourself tonight
Paste this into Claude Code, or into the Claude app if you don’t write code. It interviews you, turns your idea of “done” into real checks, writes the loop files and runs one pass while you watch. After that first run, you’re the outer loop: read the report, fix whatever it got wrong in the prompt and run it again.
I want to set up my first agent loop. Act as my loop engineer and walk me through it.
STEP 1 — Ask me these one at a time. Wait for each answer.
1. What task do I want done, either until it's finished or on a repeat?
2. Is it code (a repo with tests) or non-code (writing, research, reports)?
3. What does "done" look like? Push back until it's something a machine could check.
4. What must never happen without my approval? (sending, posting, paying, deleting, contacting anyone)
STEP 2 — Turn "done" into 3 to 6 checks.
- Code: real commands, like "npm test exits 0".
- Non-code: checks like a word limit, must contain X, matches a format, or scores 4+ out of 5 on a rubric graded by a separate reviewer.
- Mark any check only I can judge.
Show me the checks and wait for my OK before moving on.
STEP 3 — Create three files:
- plan.md: Goal, Done when (the checks), Tasks (small enough for one per pass), Notes from previous passes.
- PROMPT.md: read plan.md, do only the most important unchecked task, run the checks, add one line of what you learned, then stop. Never take an action on my approval list.
- checker.md: instructions for a separate reviewer that only verifies "Done when", reports PASS or FAIL per check with evidence, and never edits anything.
STEP 4 — Run one pass yourself while I watch. Then run the checker and show me its report.
STEP 5 — Give me the exact way to run it without me:
- Code: a capped loop for Claude Code (max 10 passes, a budget cap, on a new branch).
- Non-code: how to put it on a schedule.
Then tell me the one thing I should look at after the first unattended run.
Keep every step short and plain.
Start with something small and safe, like a daily digest or one ticket with tests. If the first run goes sideways, that’s the loop doing its job: it shows you which check you forgot.