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.
[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.
[[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.
[[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.
[[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.
[[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.
[[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.
[[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.
[[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.
[[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.
[[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.
[[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" }
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_donechannel, an empty-iteration streak reaches the quiescence threshold, a red streak fails the run, or the iteration ceiling is hit.