2026-08-28 · Updated 2026-08-28 · 10 min read
One canonical multi-harness Ralph workflow
Run the same Ralph-style loop with Claude, Codex, Cursor, and Pi: the four-role loop contract that stays harness-agnostic, what each harness needs before its first pass, and the evidence a mixed run records automatically.
By Juno AI INC · ralph-loop · multi-harness · yylo
Most teams that run a Ralph loop with more than one agent treat the second harness as a rewrite: copy the loop script, swap the binary, relearn where the state lives, and maintain two drivers that drift apart within a week. The origin essay predicted the opposite fate — "Ralph can be done with any tool that does not cap tool calls and usage" — because the loop was never really about the agent inside it. Verified against the essay and YYLO's committed source on 2026-08-28, this page is the canonical way to run Ralph-style loops across Claude, Codex, Cursor, and Pi: the loop contract that stays identical when the worker changes, the wiring each harness needs before its first pass, and the record a mixed run leaves behind without anyone keeping notes.
If the technique itself is new, start one level up: the Ralph loop reference covers where the technique came from, the words people use for it, what one pass actually does, and where it breaks — this page assumes all four. And if what you want is several different agents contributing different *steps* to one workflow — a triage step, a challenge step, a brief step — that is a different job, owned by the multi-harness workflow guide. A Ralph loop repeats; it does not choreograph.
The loop contract has four roles, and the harness fills one
Strip a Ralph loop of its folklore and four responsibilities remain. Something must hold the standing work: the original used a prompt file plus a plan file the agent edited. Something must reconstruct context on every pass: each cycle starts a fresh process that re-derives its position from files rather than memory. Something must drive repetition: the bash while that relaunches the worker. And something must decide when to stop: in the raw form, a human with an interrupt reflex.
The harness you dispatch — Claude, Codex, Cursor, Pi — answers only the second responsibility. It is the worker that consumes a prompt and produces a pass. The other three roles belong to the structure around the worker, which is exactly why the essay's portability claim holds: any capable agent can occupy the worker seat because the seat makes no demands the agent cannot meet. Teams still get multi-harness loops wrong by letting each harness bring its own surrounding structure — its own prompt file convention, its own history, its own idea of done. The fix is to keep three roles fixed in the control plane and vary only the fourth:
- Work source. YYLO replaces the prompt file and plan file with the task board: each task carries intent, response, and commit reference. The worker reads the board the way the raw loop read
PROMPT.md— except the board is not model-editable prose, a boundary that Ledger-backed task truth draws in full. - Context reconstruction. Every dispatch is a fresh agent invocation with its own iteration cap; the skill that steers it states the contract in one sentence — "Execute exactly one explicitly assigned Kanban task to a validated queued commit. Use only when the user explicitly requests ralph-loop." One task per pass, chosen from the board, never from the agent's own sense of what is next.
- Repetition driver. A runner, not a hand-rolled loop, relaunches the worker — and checks the board before every relaunch, so the loop ends when the work does rather than when someone wakes up.
- Stop condition. Semantic staleness detection and numeric caps, both described in their own section below, neither of which knows or cares which harness was running.
Hold those four roles in your head and everything below is wiring.
One driver, four dispatch flags
The repetition driver is run_until_completion.sh. Its loop is deliberately shaped for this contract: before every pass it lists open tasks (backlog, todo, in_progress); if the board is empty, the agent is not invoked at all; if work remains, it dispatches one bounded run and comes back. Every argument you pass beyond the runner's own flags is forwarded to yylo itself, so the worker is a flag:
Read the middle command as the whole thesis. Nothing else changed — not the board, not the tasks, not the evidence directory, not the stop semantics — because none of those belong to Codex. Switching harnesses mid-board costs nothing beyond the flag on the next pass, because nothing else belonged to the old worker. The same inversion works interactively: yylo loop repeats a short list of shell steps for a bounded count (-n), and the README's own example mixes workers inside one iteration — a yy pi step that implements, then a yy cc step that continues and inspects — with per-step failure policy and a loop ID reported at the end. Use the queue runner when the board is the unit; use yylo loop when a fixed sequence of steps is the unit.
One dispatch surface matters for lanes as well as loops. The parallel runner fans a task list out to concurrent worker processes, and it invokes each lane with a hard-coded single-iteration cap — many bounded passes in parallel, not many unbounded loops. Its -s/--service flag accepts exactly claude, codex, and pi, so mixed Claude/Codex/Pi fan-out is native. Cursor is deliberately absent from that list, and the next section explains where Cursor belongs instead.
What each harness needs before its first pass
The four workers are not interchangeable out of the box, and pretending otherwise is how multi-harness guides go stale. Here is what each one requires, verified against the committed templates on 2026-08-28:
- Claude. Shipped service script, no wiring. The skill installer provisions the steering skills — including
ralph-loop— into.claude/skills/, and the parallel runner accepts it natively. It is also the committed default service when no-sflag selects a worker. - Codex. Shipped service script, no wiring, parallel-runner native. Skills land in
.agents/skills/with the same four-skill set, so the one-task-per-pass contract reads identically. Its own model shorthands (:codex,:mini) resolve inside the Codex family. - Pi. Shipped service script, parallel-runner native, skills in
.pi/skills/. Pi adds the one mode no other worker has:--liveruns Pi's interactive TUI under YYLO's hooks and auto-exits on a non-aborted completion — the canonical form isyylo pi --live -p '/skill:ralph-loop' -i 1, oryplfor short.--liveis validated as Pi-only; passing it to another service is rejected before dispatch. - Cursor. The honest caveat of the four.
cursoris a selectable service in YYLO's dispatch validation, but the shipped services directory contains wrappers for Claude, Codex, Gemini, and Pi only — no Cursor wrapper — so a-s cursordispatch resolves to a script you provide at~/.yylo/services/cursor.py(or fails closed, printing the paths it checked). No skills directory is provisioned for Cursor, so the steering contract travels in the prompt and the task body instead. The Cursor integration guide owns the wrapper pattern and its boundary rules; for this workflow, route Cursor passes through the CLI and workflow surfaces above, never through aparallel_runner.sh -s cursorlane that the committed runner would reject.
That last bullet is not a deficit to apologize for. It is the same seam this whole page teaches: Cursor fills the worker role through your adapter, and the other three roles stay exactly where they were — which means a Cursor pass is bounded, recorded, and stoppable by the same machinery as every other pass.
The evidence a mixed run leaves behind
Run one board through three harnesses and a question appears that single-harness loops never face: a month later, which worker produced this pass? YYLO answers it from recorded state, not from anyone's memory of that week.
Every dispatch — whatever the service — adds one entry to session_history.json naming the subagent and model that ran, and yylo session list (which filters by subagent) plus yylo session info <id> re-open those entries afterwards. In a mixed run the recorded subagent field is the attribution: Claude passes, Codex passes, and Cursor passes through your wrapper all land in the same history with their worker named. Around the loop surface, yylo loop stamps every step with one-based metadata — YYLO_LOOP_ID, YYLO_ITERATION, YYLO_ITERATION_COUNT, YYLO_STEP, YYLO_STEP_COUNT — so hooks and logs can cite the exact cycle that produced them. Parallel runs add their structured artifacts: a parallel_runner_status.json, one JSON record per task lane, and aggregation files that preserve each lane's final response, session ID, and commit.
None of that evidence distinguishes harnesses in quality — it is uniform by design, which is what makes the mixing safe. When a pass needs inspection, the auditable agent workflows guide walks run directories, manifests, and recovery; when the question is whether in-flight work survives a worker change, the harness-switching guide owns that answer — with its evidence-dated support matrix and what a switch does and does not carry.
Stops that do not depend on the worker
An unbounded loop is a bet that you will interrupt it in time; the raw technique inherits that bet from its while : skeleton. YYLO's runner replaces the bet with two mechanisms that are part of the loop contract, not of any harness.
The numeric bound lives in two layers. Inside a single invocation, -i/--max-iterations caps the agent's turns — the committed default is one, and every example on this page keeps it there. Around the whole workflow, -n/--iterations bounds how many times yylo loop repeats its steps. The semantic bound is staleness detection: between passes, the runner snapshots the open-task state — status counts and task IDs — and if nothing changes for a configurable number of consecutive passes (default 3), it runs the ON_STALE hook and then stops with a diagnosis instead of looping on a board the worker cannot move. Staleness is measured on the board, which the worker cannot fake: a Claude pass, a Codex pass, and a Cursor pass all face the identical tripwire, because the tripwire watches the work, not the worker.
This is the property that makes multi-harness repetition honest. The weaker worker is not carried forever by a loop with no opinion; it produces a stalled board, the tripwire fires, and the operator reads which tasks did not move. The design vocabulary behind these choices — blast radius, paired numeric and semantic stops, machine-detectable staleness — belongs to bounded failure design; scaling the same discipline to many concurrent lanes is safe parallel execution.
Run one board through two harnesses this week
The cheapest honest test of this workflow is a board too small to matter, run through two workers on purpose:
Expect three observations, in order: the second command needs no re-setup, because no setup belonged to Claude in the first place; the session list names which worker produced which pass; and if either pass stalls, the runner says so within three idle cycles instead of running until morning. Your first solo loop — one worker, one task, one bound — is the shape the bounded-loop guide walks step by step; this page is what stays true after the second harness arrives. Install YYLO with npm and start one bounded, evidence-producing task — then change the worker flag and prove to yourself that the loop did not notice.