---
title: "The Ralph loop: how a bash one-liner moved into the model"
description: Coding agents used to stop before the work was done, so people wrapped them in a bash loop that restarted them on the same prompt. That trick, the Ralph loop, then moved into the tools as a goal command, and then into the models. How it works, and how Claude Code and Codex handle it today.
date: 2026-11-11
tags: AI coding agents, Ralph loop, loop engineering, Claude Code, Codex
---

"Earlier models didn't understand the idea of what done meant," said Raj, from OpenAI's applied AI team, at DevDay 2026. They made good progress, then ended their turn early. The fix moved three times: first a bash loop around the agent, called the Ralph loop, then a goal command inside the tool, and now persistence trained into the model.

If you run coding agents, it's worth knowing which of the three layers does the job today. Otherwise you build a loop that your model or your tool already runs for you, and you pay for it twice.

The talk is [What Comes After Loops?](https://www.youtube.com/watch?v=nAubbmlhPfI), 24 minutes, by Raj and Eric from OpenAI. It's OpenAI telling the story of its own product, Codex, so I checked the other side too: how Anthropic's Claude Code handles the same problem.

## The first fix was typing "continue"

Before the tools had an answer, the answer was you. The agent stopped, you typed "continue", and it went on for a while.

Raj showed a post by Peter Steinberger, who created OpenClaw. To keep his agent working, he queued a stack of "continue" messages, one after the other. Everyone was doing a version of it.

In the talk's words, this is an "outer loop": something outside the agent that keeps nudging it until it finishes. The model doesn't know when to stop, so a person, or a script, decides for it.

<figure class="fd-fig kf" aria-labelledby="lp1-cap" data-desc="The outer loop by hand, in three steps. One, work: the agent makes good progress on the task. Two, in red, stop: it ends its turn before the work is done. Three, nudge: you type continue, or queue a stack of continue messages. Then back to step one, until the work is really done.">
<p class="kf-kick">Long tasks, before the tools helped</p>
<p class="kf-title">The agent stopped early, <em class="is-alert">so you typed continue</em></p>
<ol class="kf-steps">
<li><b>Work</b><span>The agent makes good progress on the task.</span></li>
<li class="is-alert"><b>Stop</b><span>It ends its turn before the work is done.</span></li>
<li><b>Nudge</b><span>You type "continue", or queue a stack of them.</span></li>
</ol>
<p class="kf-loop">Then back to step 1, until the work is really done</p>
<figcaption id="lp1-cap">The outer loop as the talk describes it. The drawing is mine.</figcaption>
</figure>

## A bash loop that never gives up

The Ralph loop is the same nudge, written as a script. Geoffrey Huntley made it popular in July 2025, in a post called [Ralph Wiggum as a "software engineer"](https://ghuntley.com/ralph/). Ralph Wiggum is the child in The Simpsons who never gets it right and never stops trying.

In its purest form, it's one line of bash:

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

The loop reads a prompt file and gives it to a coding agent. The agent works until it stops. Then the loop starts it again, with the same prompt, forever. There's no exit condition in that line.

Four ideas make it work, and they're more interesting than the loop itself:

- Fresh memory every run. Each run starts with an empty context window, the text the model can see at once. No long chat history fills it up or pushes the goal out.
- The files remember. Progress lives on disk: the code, a folder of specifications, one per file, and a plan file Huntley calls `fix_plan.md`. Each run reads them to find out where things stand.
- One thing per loop. Huntley's rule: "ask Ralph to do one thing per loop. Only one thing."
- Tests push back. After each change, the agent runs the tests for that part of the code. A failing test rejects a bad change before the next run builds on it. Huntley calls this backpressure.

He's honest about what it is. He calls the technique "deterministically bad in an undeterministic world": it fails in ways you can predict, so you can fix them in the prompt. It also needs "a great deal of faith", because the code converges slowly, run after run.

<figure class="fd-fig kf" aria-labelledby="lp2-cap" data-desc="One run of a Ralph loop, in four steps. One, read: the prompt, the plan and the specifications, with an empty context. Two, do one thing: a single task from the plan. Three, test: the tests reject a bad change before the next run builds on it. Four, highlighted, write it down: commit the code and update the plan file. Then the bash loop starts the agent again, with empty memory.">
<p class="kf-kick">One run of a Ralph loop</p>
<p class="kf-title">Each run starts empty, <em>the files remember</em></p>
<ol class="kf-steps">
<li><b>Read</b><span>The prompt, the plan and the specifications. The context is empty.</span></li>
<li><b>Do one thing</b><span>A single task from the plan.</span></li>
<li><b>Test</b><span>A failing test rejects a bad change before the next run builds on it.</span></li>
<li class="is-key"><b>Write it down</b><span>Commit the code, update the plan file.</span></li>
</ol>
<p class="kf-loop">Then the loop starts the agent again, with empty memory</p>
<figcaption id="lp2-cap">From Geoffrey Huntley's post of July 2025. The four steps are my summary of his prompt.</figcaption>
</figure>

## The goal moved into the tool

The next step was to build the loop into the tool. OpenAI added a `/goal` command to Codex, which shipped in Codex CLI 0.128 at the end of April. Raj called it "loop engineering" brought into the product.

To see why it helps, you need one more word: compaction. When the context window fills up, the tool asks the model to condense the history into a short summary, and the work goes on from there. That keeps a long task alive, but every summary can drop a detail. After three or four compactions, the original instructions may be gone.

A goal avoids that. You give Codex an objective, and the tool stores it outside the conversation, where compaction can't touch it. The tool tracks its status and checks after each turn whether the agent is done. If not, it nudges the agent to keep going.

The agent can also say it's stuck. When it hits a real blocker, it reports it and stops, instead of looping on a problem it can't solve. The bash loop has no way to do that.

<figure class="fd-fig kf" aria-labelledby="lp3-cap" data-desc="The bash loop against a goal in the tool, in four rows. Memory: the bash loop starts empty every run; a goal keeps one session, compacted as it grows. Where the goal lives: the bash loop reads a prompt file each run; a goal is stored by the tool, outside the context, so compaction can't erase it. Who decides it's done: for the bash loop, you, by stopping it; for a goal, a check after every turn. When it's stuck: the bash loop keeps looping; a goal reports the blocker and stops.">
<p class="kf-kick">The bash loop against a goal command</p>
<p class="kf-title">A goal keeps the objective <em>where compaction can't reach</em></p>
<div class="kf-swap is-3">
<p class="kf-swap-h"><small></small><span>The bash loop</span><span>A goal in the tool</span></p>
<p><small>Memory</small><span>Empty every run</span><em>One session, compacted as it grows</em></p>
<p><small>The goal</small><span>A prompt file, read each run</span><em>Stored by the tool, outside the context</em></p>
<p><small>Done when</small><span>You stop it</span><em>A check after every turn says so</em></p>
<p><small>When stuck</small><span>It keeps looping</span><em>It reports the blocker and stops</em></p>
</div>
<figcaption id="lp3-cap">Codex's goal as described in the talk, against Huntley's one-line loop.</figcaption>
</figure>

## Then persistence moved into the model

The last step, OpenAI says, removed the need for the loop. With GPT-5.6, it trained persistence into the model itself. The model checks its own work and keeps going until the job is done, with no goal and no "continue".

Raj quoted two users. Tobi Lütke, who runs Shopify, called it the first model that no longer needed `/goal`. Ethan Mollick, a Wharton professor, let Codex use his computer and play the game Slay the Spire 2 for about five hours straight. These are OpenAI's examples, on OpenAI's stage.

The pattern behind it is the interesting part. OpenAI watches what people bolt onto the harness, the code around the model that runs its tools and manages its context. Then it trains that behaviour into the next model. Compaction went the same way: first a call the tool makes, then a skill the model learned, so it knows how to resume from its own summary.

Anthropic tells a similar story from its side. When it launched Claude Sonnet 4.5 in September 2025, it said it had seen the model keep its focus for more than 30 hours on complex tasks. In November 2025, its engineers wrote up [how they keep Claude working across many context windows](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents). They name the same failure: "Claude declares victory on the entire project too early."

Their fix looks a lot like Ralph. A first agent writes a list of over 200 features, all marked as failing, plus a progress file. Each session after that works on one feature, commits, and writes a summary for the next one. The agents may only change a feature's status, never delete it, so nobody can declare a missing feature done.

<figure class="fd-fig kf" aria-labelledby="lp4-cap" data-desc="Where keep going lives, as three layers from the outside in. Your script: a bash loop restarts the agent on the same prompt, the Ralph loop of July 2025. The tool: a goal command stores the objective and checks after each turn, as in Codex and Claude Code. Highlighted, the model: persistence trained in, so the model checks its own work and keeps going, which OpenAI claims for GPT-5.6. Under the layers: each layer took over the job of the one outside it.">
<p class="kf-kick">Where "keep going" lives</p>
<p class="kf-title">The loop moved inward, <em>from your script to the model</em></p>
<ol class="kf-stack is-nest">
<li><small>Your script</small><span>A bash loop restarts the agent on the same prompt. The Ralph loop, July 2025.</span></li>
<li><small>The tool</small><span>A goal command stores the objective and checks after each turn. Codex and Claude Code.</span></li>
<li class="is-key"><small>The model</small><span>Persistence trained in: it checks its own work and keeps going. OpenAI's claim for GPT-5.6.</span></li>
</ol>
<p class="kf-note">Each layer took over the job of the one outside it.</p>
<figcaption id="lp4-cap">My summary of the talk's story. The GPT-5.6 claim is OpenAI's, not an independent test.</figcaption>
</figure>

## How Claude Code and Codex handle it today

Both tools now have all three layers, and they built the middle one in different ways.

Start with the bash loop. Anthropic publishes an official [Ralph Loop plugin](https://claude.com/plugins/ralph-loop) for Claude Code. You run `/ralph-loop "your prompt" --max-iterations 10 --completion-promise "DONE"`. A stop hook, a small script that runs each time Claude tries to end, catches the exit and feeds the same prompt back. It stops when Claude writes the word you chose, or after the iteration cap.

There's one real difference from the original. The plugin keeps the same session going, while Huntley's loop starts each run with empty memory. Huntley himself links a video on his page called "why the claude code plugin isn't it". With Codex, people still write the loop by hand, with `codex exec`, its non-interactive mode, inside the `while`.

Then the goal. Both tools now ship a `/goal` command, and they check it differently:

- Codex. The goal is state the tool keeps for the chat. The app shows its progress above the input box, with buttons to pause, resume, edit or clear it. The model itself reports when it's blocked.
- Claude Code. You write a condition, up to 4,000 characters. After each turn, a separate model reads the conversation and answers: not met, met, or impossible. The docs say this checker "doesn't run commands or read files" on its own.

That last detail changes how you write a goal for Claude Code. The checker only sees what the conversation shows. "The tests pass" is weak. "`npm test` passes and its output is shown" is a condition it can actually check. The docs also suggest a way out, written into the condition itself: "or stop after 20 turns".

Claude Code adds one thing Codex doesn't have under that name: `/loop`. It runs a prompt again on a schedule, every five minutes for example, or at a pace Claude picks, between one minute and one hour. That's for watching something, like a deploy, not for finishing a task.

<figure class="fd-fig kf" aria-labelledby="lp5-cap" data-desc="Claude Code and Codex compared on four rows, neither side highlighted. The bash loop: Claude Code has an official Ralph Loop plugin, where a stop hook feeds the prompt back; for Codex, you write it yourself, with codex exec in a while loop. A goal: in Claude Code, a condition that a separate model checks after every turn; in Codex, an objective the tool keeps, with pause, resume, edit and clear. On a schedule: Claude Code has the loop command, every few minutes or self-paced; for Codex, not covered here. Helpers: Claude Code has subagents that report back, and agent teams that message each other, still experimental; Codex has subagents, on by default, with the subagents command to switch between them.">
<p class="kf-kick">Claude Code and Codex, November 2026</p>
<p class="kf-title">Same layers, <em>different checks</em></p>
<div class="kf-swap is-3 is-even">
<p class="kf-swap-h"><small></small><span>Claude Code</span><span>Codex</span></p>
<p><small>The bash loop</small><span>Official Ralph Loop plugin: a stop hook feeds the prompt back</span><span>Write it yourself: codex exec in a while loop</span></p>
<p><small>A goal</small><span>A condition a separate model checks after every turn</span><span>An objective the tool keeps: pause, resume, edit, clear</span></p>
<p><small>On a schedule</small><span>/loop: every few minutes, or self-paced</span><span>Not covered here</span></p>
<p><small>Helpers</small><span>Subagents report back. Agent teams message each other, still experimental</span><span>Subagents, on by default, and /subagents to switch</span></p>
</div>
<figcaption id="lp5-cap">From the Claude Code and Codex docs, read on November 11. Codex also has scheduled tasks, which I didn't look at.</figcaption>
</figure>

## Add agents only when the work asks

Once one agent keeps going on its own, the next question is speed: why not ten agents? Eric's answer, in the second half of the talk, was to wait. Start with one agent, give it a clear task and a way to check its work. For many tasks, that's enough.

Add more only when the work splits on its own. Reviewing five pull requests is easy to split: one helper agent per pull request, each with its own context. A tricky race condition is less obvious, but a couple of helpers can look in different places at once.

The cost is real. Every agent you add can raise the token bill a lot, without making the work faster or better. Both vendors say it in their docs: Codex subagents "consume more tokens", and Claude Code's agent teams use "significantly more tokens than a single session".

Eric also named a trap he calls the middle management tax. Every rule you add about how agents should work together, they start to wait for approval through hierarchies that don't exist. So keep the setup simple. If you can't say why an agent is there, take it out.

At the far end of the scale, Raj said OpenAI ran 10,000 agents on a famous open problem in mathematics, the Navier-Stokes equations. He gave 2.7 million messages and about 130 billion output tokens. That's OpenAI's report of its own internal model, and nobody outside has checked it yet. For a team at work, the useful lesson is the one before it: one agent first.

<figure class="fd-fig kf" aria-labelledby="lp6-cap" data-desc="Three ways to staff a task, as cards. Highlighted, one agent: a clear task and a way to check its work, where most tasks stop. Helpers: independent pieces, such as one helper per pull request to review, each reporting back. A team: pieces that must talk, with agents messaging each other, and the most tokens.">
<p class="kf-kick">How many agents for a task</p>
<p class="kf-title">Start with <em>one agent and a way to check</em></p>
<div class="kf-cards">
<div class="is-key"><small>One agent</small><b>A task and a check</b><span>A clear task and a way to verify the work. Most tasks stop here.</span></div>
<div><small>Helpers</small><b>Pieces that split</b><span>One helper per pull request to review, each reporting back.</span></div>
<div><small>A team</small><b>Pieces that talk</b><span>Agents message each other while they work. The most tokens.</span></div>
</div>
<figcaption id="lp6-cap">Eric's advice from the talk, with the two kinds of helpers both tools offer.</figcaption>
</figure>

## A short work log keeps the goal

Eric's last tip is the one I'd try first, because it works with any tool. As you watch an agent work, you keep adding small instructions you forgot in the first prompt. After a few compactions, the agent may drift from that first prompt, and it held most of the context about the goal.

His fix: ask the agent to keep a short work log. It holds three things. The overall goal. The work in progress. And how your latest instruction fits with both.

Keep it short on purpose. A log that records every failed test becomes a diary nobody reads, and it fills the context again. It's the same idea as Ralph's plan file, kept by the agent inside one long session.

He closed with a warning that fits this whole note: best practices have a shelf life, so don't get attached to your workflow. The bash loop was the best practice a year ago.

<figure class="fd-fig kf" aria-labelledby="lp7-cap" data-desc="The work log Eric suggests, in three parts. Highlighted, the overall goal: what the whole task is for. The running work: what is in progress now. Your last steer: how your latest instruction fits with both. Under the parts: short on purpose, not a diary of every failed test.">
<p class="kf-kick">A work log, in three parts</p>
<p class="kf-title">Write it down, so <em>the goal survives compaction</em></p>
<ol class="kf-stack">
<li class="is-key"><small>The overall goal</small><span>What the whole task is for.</span></li>
<li><small>The running work</small><span>What is in progress now.</span></li>
<li><small>Your last steer</small><span>How your latest instruction fits with both.</span></li>
</ol>
<p class="kf-note">Short on purpose: not a diary of every failed test.</p>
<figcaption id="lp7-cap">From the talk. The three parts are Eric's.</figcaption>
</figure>

## Try it

The Ralph loop comes up at about 7:10 in the video, `/goal` just after, and Eric's advice on agents from about 11:00.

- OpenAI, [What Comes After Loops?](https://www.youtube.com/watch?v=nAubbmlhPfI), DevDay 2026, 24 minutes.
- Geoffrey Huntley, [Ralph Wiggum as a "software engineer"](https://ghuntley.com/ralph/), the original post.
- Anthropic, [Effective harnesses for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents), the feature list and progress file.
- Claude Code docs: [/goal](https://code.claude.com/docs/en/goal), [/loop](https://code.claude.com/docs/en/scheduled-tasks), [agent teams](https://code.claude.com/docs/en/agent-teams), and the [Ralph Loop plugin](https://claude.com/plugins/ralph-loop).
- Codex docs: [slash commands, with /goal](https://learn.chatgpt.com/codex/reference/slash-commands), and [subagents](https://learn.chatgpt.com/codex/agent-configuration/subagents).
- [Fork your agent's harness, keep the winner](/notes/fork-your-agents-harness.html), on improving the code around the model.
- [TokenOps: why a 40-turn agent costs 16×, not 4×](/notes/tokenops-agent-cost-curve.html), on what long runs cost.

Before you write your own loop, check what your tool and model already do. Give one task a goal with a condition the checker can see, like a test command and its output. Then tell me where it stopped, and why.
