← Blog Systems & Memory 22 September 2026 9 min read

Ralph loops, and the loop guard that killed good runs

One of the most talked-about agent techniques of early 2026 is a loop that never stops on purpose. Luminair ships a guard that stops runs for repeating themselves. On 17 September 2026 that guard was wrong, several times in one morning. This is what it got wrong, and how it tells work from a loop now.

Fig 01  Two kinds of repetitionSchematic
RALPH · REPETITION ON PURPOSE while :; do cat PROMPT.md | claude-code ; done PROMPT.md same every pass Fresh agent run new context each time Files change code, tests, notes next pass reads them The input repeats. The world it acts on does not. Script as written by Geoffrey Huntley. A STUCK RUN · REPETITION BY ACCIDENT Inside one turn, one tool call after another Edit { file, old, new } call 1 Edit { file, old, new } call 2, identical Edit { file, old, new } call 3 Solace stops the run Nothing new came back, so a fourth try will not differ.
Left is Huntley's one-line script. Right is the rule Luminair's loop guard enforces today, with an example tool. The hard part, and the subject of this post, is everything between the two.

The short version

  1. The Ralph Wiggum technique feeds the same prompt to a coding agent in an endless loop. It works because each pass sees the files the last pass changed.
  2. Luminair's Solace has a loop guard that does the opposite: it stops a run that calls the same tool with the same input three times in a row.
  3. On 17 September 2026 the guard killed working Antigravity runs. It compared only the first 200 characters of each call, and Antigravity reports file reads without line numbers.
  4. Now the guard compares the full input, never stops a tool that only reads, and records every stop. The records show it still catches polling loops, which is a judgement call we have left open.
02Meet Ralph

A loop that never stops.

Geoffrey Huntley's own description is two sentences: “Ralph is a technique. In its purest form, Ralph is a Bash loop.” The loop is one line. It pipes a prompt file into a coding agent, waits for it to finish, and does it again, forever.

That sounds like a way to burn money, and Huntley is candid about the defects: “the technique is deterministically bad in an undeterministic world.” The trick is that the prompt stays the same while the repository does not. Each pass starts with a clean context, reads what the last pass left behind, and pushes a little further.

According to HumanLayer's brief history of Ralph, Huntley published it in July 2025, and it went viral in the final weeks of that year. By then Anthropic had released an official plugin that installs, in VentureBeat's words, “a 'Stop Hook' inside your Claude session.” In January 2026 The Register ran the headline that Ralph lets Claude “vibe-clone commercial software for $10 an hour”.

One line in Huntley's post matters most for this story: “Ralph can be done with any tool that does not cap tool calls and usage.” Luminair has a tool that caps tool calls. We built it on purpose, and then it capped the wrong ones.

03The opposite instinct

Stop the spin.

Every agent user has watched a run go in circles: the same failing command, the same edit that does not apply, the same search with nothing new. Each lap costs tokens and time, and nothing changes. On 29 August 2026 Luminair added a tool-loop honesty card: if a run called the same tool with the same input six times in a row, a Solace card said it might be stuck and suggested pressing Stop.

Later Solace gained a stronger switch, Self-healing loop intercept, on by default. At three identical calls in a row it stops the run itself, shows a card and records what it saw. The idea is sound. The definition of “identical” was not.

This is how the first version decided two calls were the same:

desktop/renderer.js · 29 Aug 2026the original identity
const sig = (p.name || '') + '|' + JSON.stringify(p.input || '').slice(0, 200);

Tool name, then the first 200 characters of the input. Short commands fit easily. Long ones do not.

0417 September

“Antigravity is not working.”

That was the report on the morning of 17 September, in capitals. Runs on Google's Antigravity engine were ending abruptly in the middle of real work. The engine was fine. The code comment written that day names two causes, both in the guard.

1 · Long commands that start the same way

Agents often write long one-off scripts: set up the PATH, then a node -e or python3 -c with a page of code. Three different scripts with the same setup lines look identical in their first 200 characters.

Fig 02  Where the old identity stopped readingAdapted from loop-guard.test.js
CALL 1 export PATH=… node -e 'const fs = require("fs"); … (" a.log") });' CALL 2 export PATH=… node -e 'const fs = require("fs"); … (" b.log") });' 200 characters Shaded: all the old guard compared, the same in both. Outlined: where the calls actually differ.
The prefix is the one used in the regression test, shortened here with “…”. Serialized, the two test inputs first differ at character 203, so the old identity saw one call repeated.

2 · A file read with no line numbers

The second cause was subtler. When Antigravity reads part of a file, its stream reports the call as view_file with only the file's path. The line range is not in the event; the comment records that this was checked against a live run. So reading lines 1 to 200, then 200 to 400, then 400 to 600 of one file arrives as three identical calls. To the guard, that was a loop. To the agent, it was reading.

The same was true of Antigravity's edit tools, which report the target file but not the change, and of a task tool polled for its status. The records from that morning, which we come back to below, contain all three.

05The rules now

Only repeated actions count.

The fix, made on 17 September and tightened on the 18th, moved the guard into a small pure block in renderer.js that the test suite loads and runs directly. It changes both halves of the question: what counts as the same call, and which calls are allowed to repeat.

Fig 03  What the guard does with a repeated callFrom the LOOP-GUARD block
Repeated callVerdict
An event with no input at allthere is nothing to compare, so it never countsignored
A tool that only readsview_file, read, grep, glob, list_dir, web search and similar, at any countno card, never stopped
Antigravity's path-only edit toolsreplace_file_content and write_to_file, which do not report the changeno card, never stopped
Status and list actionson the task and subagent tools, and asking the user a questionno card, never stopped
Any other tool, same full input, 3 times in a rowwith Self-healing loop intercept on (the default)run stopped, recorded
The same, 6 times in a rowwith the intercept switched offone warning card
Real rules from toolRepeatSig, isReadOnlyTool and toolRepeatVerdict. Identity is now the tool name plus the full input, serialized. All six tests in test/core/loop-guard.test.js pass.

The comment explains the read-only rule in one line: “re-reading after each edit is normal work”. An agent that edits a file and reads it back, again and again, is doing exactly what you want. A read cannot burn anything but tokens, and it is how an agent learns that the world changed, which is the whole point of Ralph.

When the guard does stop a run, it drops the rest of the stream, stops the engine, shows a card that begins “Solace self-healed:” with the tool and the count, and writes a record: the tool, the count and the call's signature.

06What the records show

Twenty-seven stops.

Those records live in a file in the app's data folder. The one on our main development Mac holds 27, the first from the morning of 17 September. We sorted them by what today's rules would do with each.

Fig 04  Every self-heal record on one Mac, by dayReal data · 17 to 26 Sep 2026
17 SEP192223242526 9 now exempt 5 view_file · 3 status checks 1 replace_file_content from 18 Sep: only commands and edits a command or edit, still stopped today a read or status check, exempt since the fix
Every record in controller-selfheal.json on one development Mac, counted by tool and day. Days with no records are left out. One dot per stopped run.

Two things stand out. First, nine of the twelve stops on 17 September were exactly the false positives the fix removed: file reads, task status checks and a path-only edit. None of those tools appears again after that day.

Second, the stops that remain are not all loops. Several are an agent waiting on a slow build: the same short ps or sleep-then-check command run three times while a code-signing job finishes. Each call returns the same thing because the build is not done yet. By the guard's rule that is a loop. By Ralph's logic, the world just had not changed yet. We have not changed that rule; a Mac-side wait is usually better done with a proper background job, and the card makes the stop visible.

07Loop or work?

Repetition is not the signal.

Ralph and the loop guard look like opposites, but they agree on the underlying idea. Repetition is fine when something changes between laps. Ralph makes sure it does: fresh context, a changed repository. The guard should only step in when nothing can have changed: the same action, with the same input, from a tool whose result is already known.

That is why the fix did not just raise the threshold. A higher count would still have killed a long Antigravity read, only later. The real change was to stop treating reads as actions, and to stop trusting a summary of the input instead of the input itself.

What we would tell anyone building a guardCompare the whole call, never a prefix. Know what your engine leaves out of its events before you compare them. And keep a record of every stop, so the false positives show up as data instead of as an angry message in capitals.
08Find it in the app

See what the guard did.

  1. 1Open the Solace panel. Under Quality gates, Self-healing loop intercept is on by default. Switch it off and you get one warning card at six repeats instead of a stop at three.
  2. 2When a run stops with a card that begins Solace self-healed:, it names the tool and how many times it was called with the same input. If the run was doing real work, send it again with a nudge to vary its approach.
  3. 3For a long wait on a build, ask the agent to start it as a background job instead of polling it with the same command.
09What we checked

Checked, and not claimed.

6/6
Loop guard tests pass, run against the real block in renderer.js
200
Characters the original identity compared, from 29 August to 17 September 2026
9/12
Stops on 17 September that today's rules would not make
3
Identical calls in a row before a stop, with the intercept on

What this post does not claim

  • That every remaining stop is a real loop. Some are polling, as described.
  • How many runs the guard saved from burning tokens. A stopped run cannot tell us what it would have cost.
  • That the two long commands stopped early on 17 September were truly identical. Their records were cut at 200 characters, the very flaw that was fixed.
  • That the records on one Mac represent anyone else's.

Sources

Let the work repeat, not the mistake

Solace stops the spin and tells you exactly what it saw.

Download Luminair →