/ouroboros:ralph
MCP-owned Ralph loop around background evolve_step jobs. "The boulder never stops."
Usage
ooo ralph --lineage-id <lineage_id>
/ouroboros:ralph --lineage-id <lineage_id>
# For a plain natural-language request, run `ooo interview` + `ooo seed` first,
# then call the MCP tool with a fresh lineage_id and the validated Seed YAML.
Trigger keywords: "ralph", "don't stop", "must complete", "until it works", "keep going"
How It Works
Ralph is owned by the ouroboros_ralph MCP tool. In non-plugin runtimes, the
tool starts one background Ralph job, runs repeated evolve_step generations
inside that job, and stops only when QA passes, convergence is reached, a
terminal evolution action occurs, cancellation is requested, or
max_generations is reached. In OpenCode plugin mode, the MCP tool returns a
delegated_to_plugin envelope with job_id=None; the bridge plugin dispatches
a child Task session that owns the loop instead of creating a local JobManager
job.
The client skill should not reimplement the loop. Deterministic frontmatter
dispatch is limited to the router's named --lineage-id option so raw trailing
text is never treated as lineage identity. Raw natural-language
ooo ralph "<request>" input must flow through the validated Seed path before
any mutating Ralph loop starts. Until a lineage id and optional Seed YAML are
prepared, ouroboros_ralph returns structured input guidance instead of
starting a job. Once the inputs are prepared, start the MCP-owned Ralph surface
once, then follow either the returned job tools path or the OpenCode Task widget
path.
Instructions
When the user invokes this skill:
Load MCP Tools (Required first)
The Ouroboros MCP tools are often registered as deferred tools that must be
explicitly loaded before use. Do this before preparing input or calling Ralph:
- Use the active runtime's tool-discovery capability to find and load the Ralph/job MCP tools:
tool discovery query: "+ouroboros ralph job"
- The loaded tools may be exposed under plugin-prefixed names such as
mcp__plugin_ouroboros_ouroboros__ouroboros_ralph. Use the actual tool
names returned by runtime tool discovery; the bare names below are the canonical MCP
tool names for documentation.
- Confirm that
ouroboros_ralph and the job tools (ouroboros_job_wait,
ouroboros_job_status, ouroboros_job_result, and
ouroboros_cancel_job) are callable. If the tools are unavailable, stop and
tell the user that Ralph requires the Ouroboros MCP runtime.
Ralph Flow
Prepare lineage input:
- If the user provides an existing
lineage_id and explicitly wants to
continue it, reuse that lineage_id and omit seed_content unless they
explicitly provide an updated Seed.
- If the user provides Seed YAML for a new Ralph run, use it as
seed_content and generate a fresh lineage_id for this run. Keep
lineage_id separate from Seed, interview, and session IDs so separate
Ralph runs over the same Seed do not collide.
- If the user provides only a plain natural-language request, do not treat
it as a direct
ooo ralph "<request>" command, do not freehand Seed YAML,
and do not pass raw text as seed_content. Route through the authoritative
Seed path first: ooo interview to capture requirements, then ooo seed /
ouroboros_generate_seed to produce validated Seed YAML with the normal
ambiguity gate. After Seed generation, call the MCP tool with a fresh
lineage_id and that validated Seed YAML as seed_content; do not use the
raw request text. If an interview/seed session already exists in context,
reuse that validated Seed output instead of regenerating it.
Start Ralph by calling ouroboros_ralph with:
lineage_id: existing lineage id for an explicit continuation, otherwise a
freshly generated stable id for this Ralph run, such as
ralph-<short-slug>-<uuid>; do not use a Seed/interview id by itself
seed_content: valid Seed YAML for generation 1 when starting a new lineage
execute: default true
parallel: default true
skip_qa: default false
project_dir: explicit target project directory when known
max_generations: default 10 unless the user requests a tighter bound
Handle the start response:
If response.meta.job_id is present, report it concisely and retain the
job cursor from response.meta.cursor:
[Ralph] Started background loop: <job_id>
Lineage: <lineage_id>
Live view: <dashboard_url, or `ouroboros tui open`>
A read-only observer will post meaningful progress, attention, and terminal
events here. This conversation remains available for other safe work.
If response.meta.job_observer is unavailable, recover it from the final
<!-- ouroboros-job-observer-v1 base64 ... --> content sentinel. Fail
closed unless the bounded payload passes canonical v1 validation and its
job identity matches the visible start receipt. Use that ID only as an
identity anchor, never to reconstruct tools or arguments. Reject validation
failure or any mismatch between structured and inline surfaces.
If the structured or recovered job_observer is present and the host
supports an independent child session, spawn exactly one read-only
observer and pass that contract unchanged. The observer exclusively owns
job wait/result calls and the cursor. The main session retains only user
conversation, explicit on-demand status, and cancellation when the user
requests it. The main session must not poll the same job while the observer
is active. It may refine requirements, perform read-only review, or work in
an unrelated isolated worktree; check active-worker conflicts before
writing to Ralph's workspace.
On Codex, call spawn_agent exactly once with task_name="run_observer";
wait is not a spawn, and do not claim an observer until a live child
ID/path is returned. Once acknowledged, keep the parent turn open with
wait_agent calls of at most 60 seconds while the observer is active. Child
send_message calls only enqueue mailbox events and cannot revive an ended
parent turn. Relay meaningful updates and wait again until terminal.
On OMP, submit exactly one native Task child named RunObserver, require
its live agent/job ID, and use the host wait/inbox relay until terminal.
User input may interrupt the wait; handle it and resume
waiting while observation remains active unless the user asks to stop live
observation or replaces the active request. Then end only the relay loop,
keep the durable job running, and offer next-turn or explicit-status catch-
up. If the observer child fails, is cancelled, or exits before a terminal
summary, use that same fallback instead of waiting indefinitely. This relay
loop must not poll the Ouroboros job or take cursor ownership. If spawn
fails, do not promise live proactive relays: the detached worker continues
after the stdio turn, and the main session catches up from durable events
on the next interaction or explicit status request. Keep the main turn open
in the fallback polling loop only when the user asked for live watching.
If response.meta.status == "delegated_to_plugin" and
response.meta.job_id is None, report that OpenCode plugin mode delegated
the loop to a child Task session. Do not call ouroboros_job_wait,
ouroboros_job_result, or ouroboros_cancel_job without a job id; follow
the host Task widget/session lifecycle instead.
Monitor non-plugin job progress in the polling owner when a job_id exists.
The delegated observer is the default owner. Use the main-session loop below
only when no independent child session exists and the user explicitly asked
for live watching; otherwise catch up on the next parent turn. Never run both
loops:
ouroboros_job_wait(job_id, cursor, timeout_seconds=120, stream="linked", wait_for="attention_or_ac_change") for long polling;
after every wait/status response, update cursor = response.meta.cursor
ouroboros_job_status(job_id) for a quick status check
ouroboros_job_result(job_id) when the job is terminal
ouroboros_cancel_job(job_id) if the user says stop/cancel
Observer events are concise: relay phase/progress changes in 1-2 lines,
surface attention_required immediately, present terminal as the final
result, distinguish Synapse queued from runtime-proven applied, surface
rejected/uncertain delivery immediately, and suppress unchanged heartbeats or
raw tool output. Render every relay in the user's current conversation
language; preserve raw event codes only when exact diagnostics help.
Interpret structured subtypes: report run configuration, total ACs and
dependency/parallel levels, first scheduled ACs, bounded Discover targets,
current model/harness changes, level transitions, and verified AC completion.
Say "currently running with" because later generations may escalate or switch
harnesses. Never forward raw commands or model reasoning.
When a new generation starts, do not just report the generation number —
lineage.generation.started carries an ac_focus block
(active_ac_indices, frozen_ac_indices, active_ac_descriptions,
reason). Report WHAT the generation is redoing, e.g.
"Gen 7: 2/5 AC 재작업 — 'CSV export writes summary.csv', 'CLI exits 0 on
--help' (3 AC는 이전 PASS 증거로 frozen)". When ac_focus is absent or
every AC is active with reason "initial/full generation", say the full AC
graph is being executed. Never quote verify commands or expected outputs —
descriptions only.
When the user asks a live AC a read-only question or provides additive intent,
reload +ouroboros session signal, call
ouroboros_session_signal_targets for the observed execution, and select the
semantically relevant AC without asking for internal IDs. Use
mode="inform" for assurance/questions and omit fallback_mode in that
mode. For implementation refinement use
contract_effect="additive", source="user", mode="redirect", and explicit
fallback_mode="after_turn" with the exact discovered guards. Shared goal/AC/
constraint/non-goal changes require an approved shared successor.
On non-plugin job termination, the polling owner fetches
ouroboros_job_result(job_id) and
summarize the final job result and next step:
- Success / convergence: summarize the final generation output, QA verdict,
and any
worktree_path / worktree_branch returned in job metadata. Do not
present ooo evaluate as an automatic next step for Ralph results: the
Ralph job contract preserves the evolution lineage_id, but it does not
reliably preserve a separate execution session_id for the evaluate
workflow. If a valid execution session_id is explicitly available from a
separate run result, keep it distinct from the Ralph lineage_id and follow
the ooo evaluate <session_id> contract; otherwise state that formal
evaluation needs a real execution session and should not be invoked from the
Ralph lineage id alone.
- Max generations / failure: summarize the stop reason and suggest
ooo unstuck, ooo interview, or a narrower Ralph retry
- Cancelled: confirm cancellation and preserve the job id for later inspection
On OpenCode plugin delegation, rely on the child Task result as the
terminal surface. Summarize the Task completion/error state and lineage id; do
not claim a local Ralph job can be polled or cancelled.
Active Conductor decision policy
For attention_required, use at most one short-lived read-only verifier. If the
host has no verifier primitive, surface the evidence and do not mutate.
Otherwise VERIFY → DECIDE from recommended_host_actions → LOG selected with
ouroboros_record_conductor_decision → ACT only a menu-listed registered tool →
LOG exactly one completed, failed, or declined outcome. Ralph may apply a
conductor directive only to the first and sole bounded successor generation
(max_generations=1), and only when it is deterministic and non-relaxing. Never
silently retry or weaken the approved shared contract.
These are English canonical host instructions. Render them naturally in the
user's conversation language.
Tool Mapping
| Skill action |
MCP tool |
| Start Ralph loop |
ouroboros_ralph |
| Wait for progress |
ouroboros_job_wait |
| Fetch final result |
ouroboros_job_result |
| Cancel loop |
ouroboros_cancel_job |
| Inspect current status |
ouroboros_job_status |
The Boulder Never Stops
This is the key phrase. Ralph does not give up:
- Each failure is data for the next attempt.
- Verification drives the loop.
- Only success, convergence, terminal failure, cancellation, or max-generation
limits stop it.
RFC #1392 State Breadcrumb Footer
Your final response MUST end with exactly one breadcrumb footer line:
◆ <current state> → next: <recommended action>
Derive <current state> from live session state via ouroboros_session_status when that MCP projection is available; otherwise derive it from this skill's actual outcome. Never use a linear Step N of M footer because Ouroboros is an evolutionary loop. When the next action is genuinely a choice, list 2-3 honest options in the next: clause. The breadcrumb line must be the last line of the response.
1---2name: ralph3description: MCP-owned Ralph loop around background evolve_step jobs4---56# /ouroboros:ralph78MCP-owned Ralph loop around background `evolve_step` jobs. "The boulder never stops."910## Usage1112```13ooo ralph --lineage-id <lineage_id>14/ouroboros:ralph --lineage-id <lineage_id>1516# For a plain natural-language request, run `ooo interview` + `ooo seed` first,17# then call the MCP tool with a fresh lineage_id and the validated Seed YAML.18```1920**Trigger keywords:** "ralph", "don't stop", "must complete", "until it works", "keep going"2122## How It Works2324Ralph is owned by the `ouroboros_ralph` MCP tool. In non-plugin runtimes, the25tool starts one background Ralph job, runs repeated `evolve_step` generations26inside that job, and stops only when QA passes, convergence is reached, a27terminal evolution action occurs, cancellation is requested, or28`max_generations` is reached. In OpenCode plugin mode, the MCP tool returns a29`delegated_to_plugin` envelope with `job_id=None`; the bridge plugin dispatches30a child Task session that owns the loop instead of creating a local JobManager31job.3233The client skill should not reimplement the loop. Deterministic frontmatter34dispatch is limited to the router's named `--lineage-id` option so raw trailing35text is never treated as lineage identity. Raw natural-language36`ooo ralph "<request>"` input must flow through the validated Seed path before37any mutating Ralph loop starts. Until a lineage id and optional Seed YAML are38prepared, `ouroboros_ralph` returns structured input guidance instead of39starting a job. Once the inputs are prepared, start the MCP-owned Ralph surface40once, then follow either the returned job tools path or the OpenCode Task widget41path.4243## Instructions4445When the user invokes this skill:4647### Load MCP Tools (Required first)4849The Ouroboros MCP tools are often registered as deferred tools that must be50explicitly loaded before use. Do this before preparing input or calling Ralph:51521. Use the active runtime's tool-discovery capability to find and load the Ralph/job MCP tools:53 ```54 tool discovery query: "+ouroboros ralph job"55 ```562. The loaded tools may be exposed under plugin-prefixed names such as57 `mcp__plugin_ouroboros_ouroboros__ouroboros_ralph`. Use the actual tool58 names returned by runtime tool discovery; the bare names below are the canonical MCP59 tool names for documentation.603. Confirm that `ouroboros_ralph` and the job tools (`ouroboros_job_wait`,61 `ouroboros_job_status`, `ouroboros_job_result`, and62 `ouroboros_cancel_job`) are callable. If the tools are unavailable, stop and63 tell the user that Ralph requires the Ouroboros MCP runtime.6465### Ralph Flow66671. **Prepare lineage input**:68 - If the user provides an existing `lineage_id` and explicitly wants to69 continue it, reuse that `lineage_id` and omit `seed_content` unless they70 explicitly provide an updated Seed.71 - If the user provides Seed YAML for a new Ralph run, use it as72 `seed_content` and generate a fresh `lineage_id` for this run. Keep73 `lineage_id` separate from Seed, interview, and session IDs so separate74 Ralph runs over the same Seed do not collide.75 - If the user provides only a plain natural-language request, do not treat76 it as a direct `ooo ralph "<request>"` command, do not freehand Seed YAML,77 and do not pass raw text as `seed_content`. Route through the authoritative78 Seed path first: `ooo interview` to capture requirements, then `ooo seed` /79 `ouroboros_generate_seed` to produce validated Seed YAML with the normal80 ambiguity gate. After Seed generation, call the MCP tool with a fresh81 `lineage_id` and that validated Seed YAML as `seed_content`; do not use the82 raw request text. If an interview/seed session already exists in context,83 reuse that validated Seed output instead of regenerating it.84852. **Start Ralph** by calling `ouroboros_ralph` with:86 - `lineage_id`: existing lineage id for an explicit continuation, otherwise a87 freshly generated stable id for this Ralph run, such as88 `ralph-<short-slug>-<uuid>`; do not use a Seed/interview id by itself89 - `seed_content`: valid Seed YAML for generation 1 when starting a new lineage90 - `execute`: default `true`91 - `parallel`: default `true`92 - `skip_qa`: default `false`93 - `project_dir`: explicit target project directory when known94 - `max_generations`: default `10` unless the user requests a tighter bound95963. **Handle the start response**:97 - If `response.meta.job_id` is present, report it concisely and retain the98 job cursor from `response.meta.cursor`:99100 ```101 [Ralph] Started background loop: <job_id>102 Lineage: <lineage_id>103 Live view: <dashboard_url, or `ouroboros tui open`>104105 A read-only observer will post meaningful progress, attention, and terminal106 events here. This conversation remains available for other safe work.107 ```108109 - If `response.meta.job_observer` is unavailable, recover it from the final110 `<!-- ouroboros-job-observer-v1 base64 ... -->` content sentinel. Fail111 closed unless the bounded payload passes canonical v1 validation and its112 job identity matches the visible start receipt. Use that ID only as an113 identity anchor, never to reconstruct tools or arguments. Reject validation114 failure or any mismatch between structured and inline surfaces.115116 - If the structured or recovered `job_observer` is present and the host117 supports an independent child session, spawn exactly one read-only118 observer and pass that contract unchanged. The observer exclusively owns119 job wait/result calls and the cursor. The main session retains only user120 conversation, explicit on-demand status, and cancellation when the user121 requests it. The main session must not poll the same job while the observer122 is active. It may refine requirements, perform read-only review, or work in123 an unrelated isolated worktree; check active-worker conflicts before124 writing to Ralph's workspace.125 On Codex, call `spawn_agent` exactly once with `task_name="run_observer"`;126 `wait` is not a spawn, and do not claim an observer until a live child127 ID/path is returned. Once acknowledged, keep the parent turn open with128 `wait_agent` calls of at most 60 seconds while the observer is active. Child129 `send_message` calls only enqueue mailbox events and cannot revive an ended130 parent turn. Relay meaningful updates and wait again until terminal.131 On OMP, submit exactly one native Task child named `RunObserver`, require132 its live agent/job ID, and use the host wait/inbox relay until terminal.133 User input may interrupt the wait; handle it and resume134 waiting while observation remains active unless the user asks to stop live135 observation or replaces the active request. Then end only the relay loop,136 keep the durable job running, and offer next-turn or explicit-status catch-137 up. If the observer child fails, is cancelled, or exits before a terminal138 summary, use that same fallback instead of waiting indefinitely. This relay139 loop must not poll the Ouroboros job or take cursor ownership. If spawn140 fails, do not promise live proactive relays: the detached worker continues141 after the stdio turn, and the main session catches up from durable events142 on the next interaction or explicit status request. Keep the main turn open143 in the fallback polling loop only when the user asked for live watching.144145 - If `response.meta.status == "delegated_to_plugin"` and146 `response.meta.job_id is None`, report that OpenCode plugin mode delegated147 the loop to a child Task session. Do not call `ouroboros_job_wait`,148 `ouroboros_job_result`, or `ouroboros_cancel_job` without a job id; follow149 the host Task widget/session lifecycle instead.1501514. **Monitor non-plugin job progress in the polling owner** when a `job_id` exists.152153 The delegated observer is the default owner. Use the main-session loop below154 only when no independent child session exists and the user explicitly asked155 for live watching; otherwise catch up on the next parent turn. Never run both156 loops:157158 - `ouroboros_job_wait(job_id, cursor, timeout_seconds=120, stream="linked", wait_for="attention_or_ac_change")` for long polling;159 after every wait/status response, update `cursor = response.meta.cursor`160 - `ouroboros_job_status(job_id)` for a quick status check161 - `ouroboros_job_result(job_id)` when the job is terminal162 - `ouroboros_cancel_job(job_id)` if the user says stop/cancel163164 Observer events are concise: relay phase/progress changes in 1-2 lines,165 surface `attention_required` immediately, present `terminal` as the final166 result, distinguish Synapse `queued` from runtime-proven `applied`, surface167 rejected/uncertain delivery immediately, and suppress unchanged heartbeats or168 raw tool output. Render every relay in the user's current conversation169 language; preserve raw event codes only when exact diagnostics help.170 Interpret structured subtypes: report run configuration, total ACs and171 dependency/parallel levels, first scheduled ACs, bounded Discover targets,172 current model/harness changes, level transitions, and verified AC completion.173 Say "currently running with" because later generations may escalate or switch174 harnesses. Never forward raw commands or model reasoning.175176 When a new generation starts, do not just report the generation number —177 `lineage.generation.started` carries an `ac_focus` block178 (`active_ac_indices`, `frozen_ac_indices`, `active_ac_descriptions`,179 `reason`). Report WHAT the generation is redoing, e.g.180 "Gen 7: 2/5 AC 재작업 — 'CSV export writes summary.csv', 'CLI exits 0 on181 --help' (3 AC는 이전 PASS 증거로 frozen)". When `ac_focus` is absent or182 every AC is active with reason "initial/full generation", say the full AC183 graph is being executed. Never quote verify commands or expected outputs —184 descriptions only.185186 When the user asks a live AC a read-only question or provides additive intent,187 reload `+ouroboros session signal`, call188 `ouroboros_session_signal_targets` for the observed execution, and select the189 semantically relevant AC without asking for internal IDs. Use190 `mode="inform"` for assurance/questions and omit `fallback_mode` in that191 mode. For implementation refinement use192 `contract_effect="additive"`, `source="user"`, `mode="redirect"`, and explicit193 `fallback_mode="after_turn"` with the exact discovered guards. Shared goal/AC/194 constraint/non-goal changes require an approved shared successor.1951965. **On non-plugin job termination**, the polling owner fetches197 `ouroboros_job_result(job_id)` and198 summarize the final job result and next step:199 - Success / convergence: summarize the final generation output, QA verdict,200 and any `worktree_path` / `worktree_branch` returned in job metadata. Do not201 present `ooo evaluate` as an automatic next step for Ralph results: the202 Ralph job contract preserves the evolution `lineage_id`, but it does not203 reliably preserve a separate execution `session_id` for the evaluate204 workflow. If a valid execution `session_id` is explicitly available from a205 separate run result, keep it distinct from the Ralph `lineage_id` and follow206 the `ooo evaluate <session_id>` contract; otherwise state that formal207 evaluation needs a real execution session and should not be invoked from the208 Ralph lineage id alone.209 - Max generations / failure: summarize the stop reason and suggest210 `ooo unstuck`, `ooo interview`, or a narrower Ralph retry211 - Cancelled: confirm cancellation and preserve the job id for later inspection2122136. **On OpenCode plugin delegation**, rely on the child Task result as the214 terminal surface. Summarize the Task completion/error state and lineage id; do215 not claim a local Ralph job can be polled or cancelled.216217### Active Conductor decision policy218219For `attention_required`, use at most one short-lived read-only verifier. If the220host has no verifier primitive, surface the evidence and do not mutate.221Otherwise VERIFY → DECIDE from `recommended_host_actions` → LOG `selected` with222`ouroboros_record_conductor_decision` → ACT only a menu-listed registered tool →223LOG exactly one `completed`, `failed`, or `declined` outcome. Ralph may apply a224conductor directive only to the first and sole bounded successor generation225(`max_generations=1`), and only when it is deterministic and non-relaxing. Never226silently retry or weaken the approved shared contract.227228These are English canonical host instructions. Render them naturally in the229user's conversation language.230231## Tool Mapping232233| Skill action | MCP tool |234| --- | --- |235| Start Ralph loop | `ouroboros_ralph` |236| Wait for progress | `ouroboros_job_wait` |237| Fetch final result | `ouroboros_job_result` |238| Cancel loop | `ouroboros_cancel_job` |239| Inspect current status | `ouroboros_job_status` |240241## The Boulder Never Stops242243This is the key phrase. Ralph does not give up:244245- Each failure is data for the next attempt.246- Verification drives the loop.247- Only success, convergence, terminal failure, cancellation, or max-generation248 limits stop it.249250## RFC #1392 State Breadcrumb Footer251252Your final response MUST end with exactly one breadcrumb footer line:253254```255◆ <current state> → next: <recommended action>256```257258Derive `<current state>` from live session state via `ouroboros_session_status` when that MCP projection is available; otherwise derive it from this skill's actual outcome. Never use a linear `Step N of M` footer because Ouroboros is an evolutionary loop. When the next action is genuinely a choice, list 2-3 honest options in the `next:` clause. The breadcrumb line must be the last line of the response.