skip to content

The ralph pattern

Ralph loop: repeat one standing prompt until the agent writes the loop_done channel or the iteration ceiling is hit. Use for open-ended maintenance driven by a single standing instruction, with the filesystem and git as the loop's memory.

ralph — the standing-prompt loop (--ralph)

Repeat one standing prompt: execute, gate, one fix on red, re-gate, scoped revert on still-red, else commit. There is no goal and no goal-inversion — the filesystem and git are the loop's memory, and the durable counters are declared state.

The loop is closed by a four-rule ending block: the agent signals completion through the loop_done channel, an empty-iteration streak reaches the quiescence threshold, a red streak fails the run, or the iteration ceiling is hit.

ralph
[header]
name = "ralph"
version = "1.1"
description = """
Ralph loop: repeat one standing prompt until the agent writes the loop_done channel or the iteration
ceiling is hit. Use for open-ended maintenance driven by a single standing instruction, with the
filesystem and git as the loop's memory.
"""
entry = "main"

The parameter interface

The standing prompt itself, plus the two loop bounds and an optional harness effort hint.

ralph
[[header.parameters]]
name = "prompt"
kind = "file"
declared_plan_source = true
description = "The standing prompt repeated each iteration."

[[header.parameters]]
name = "max_iters"
kind = "string"
default = "100"
description = "The iteration ceiling before the loop stops resumably."

[[header.parameters]]
name = "quiesce_after"
kind = "string"
default = "2"
description = "The empty-iteration threshold retained for fixed-flag binding parity."

[[header.parameters]]
name = "effort"
kind = "string"
default = ""
description = "The harness effort hint applied to each iteration agent when supplied."

[[header.levels]]
label = "iteration"

The completion sentinel: the executor writes this file to end the loop successfully.

ralph
[[channels]]
name = "loop_done"
path = "state/loop-done.loop-done"
producer = "execute"
values = ["done"]

The systemic red streak: consecutive iterations that ended in a scoped revert. It increments on a red gate verdict and resets on a green one, and survives resume, so a run that stops mid-streak does not forget how deep it already was.

ralph
[[counters]]
name = "red_streak"
reset = "consecutive"
survives_resume = true
increment_on = "gate-red"

Consecutive iterations that reached the commit step with no project changes. Its threshold is a calm resumable stop: the standing instruction may still be useful later, but the worktree has gone quiet.

ralph
[[counters]]
name = "empty_iterations"
reset = "consecutive"
survives_resume = true
increment_on = "no-project-changes"

main

The standing loop — no work list, so its bounds are the ending rules alone.

ralph
[[blocks.main]]
type = "loop"
body = "iterate"
rules = "ending"

iterate

Execute the standing prompt — fall-through, since the gate that follows renders the verdict — then gate and branch: green commits, red escalates into the repair block.

ralph
[[blocks.iterate]]
type = "agent"
role = "build"
prompt = "execute"
failure = "fall-through"
standing_loop_iteration = true
standing_instruction = "prompt"
standing_effort = "effort"
standing_done_channel = "loop_done"

[[blocks.iterate]]
type = "gate"
if = "red"
then = "repair"
else = "commit"

repair

One fix, re-gate, and branch again: a green re-gate commits the repaired work, a still-red one reverts it.

ralph
[[blocks.repair]]
type = "agent"
role = "build"
prompt = "fix"
stage = "fix"
failure = "fall-through"

[[blocks.repair]]
type = "gate"
if = "red"
then = "revert"
else = "commit"

revert

Reset the project tree to the iteration's baseline, preserving .gantry/, then validate that the restored baseline still passes. The validation gate's verdict is not counted, so it cannot clear the red streak the iteration just earned; a still-red baseline is an infrastructure stop — the interpreter halts — never a counted red.

ralph
[[blocks.revert]]
type = "git"
op = "reset"
scope = { kind = "project-only" }

[[blocks.revert]]
type = "validation-gate"

Commit the iteration's work; an empty commit is what advances empty_iterations.

ralph
[[blocks.commit]]
type = "git"
op = "commit"

ending

First-match-wins: a systemic red streak fails the run; the loop_done channel succeeds; an empty-iteration streak quiesces resumably; the iteration ceiling is a calm resumable stop.

ralph
[[rule_blocks.ending]]
condition = { kind = "counter-threshold", counter = "red_streak", threshold = 3 }
outcome = { kind = "terminate", disposition = "failure", message = "ralph loop stopped: a systemic red streak reverted three consecutive iterations" }

[[rule_blocks.ending]]
condition = { kind = "channel-value", channel = "loop_done", value = "done" }
outcome = { kind = "terminate", disposition = "success", message = "ralph loop reported itself finished (done-file sentinel)" }

[[rule_blocks.ending]]
condition = { kind = "counter-threshold", counter = "empty_iterations", threshold = "{{quiesce_after}}" }
outcome = { kind = "terminate", disposition = "resumable-stop", message = "ralph loop quiesced after {{quiesce_after}} consecutive empty iteration(s)" }

[[rule_blocks.ending]]
condition = { kind = "iteration", number = "{{max_iters}}" }
outcome = { kind = "terminate", disposition = "resumable-stop", message = "ralph loop reached its iteration ceiling - a calm resumable stop" }