Ralph Loop
Autonomous iteration loop for Claude Code with dual-mode support. Named after the Ralph Wiggum technique popularized in the Claude Code community (Dec 2025 - Feb 2026).
Purpose
Enable Claude Code to work autonomously on well-defined tasks until genuine completion, without manual re-prompting. The skill provides:
-
Dual-mode architecture — Standalone mode (Stop hook) or multi-agent mode (router-managed iteration)
-
RALPH_ACTIVE env var guard — Stop hook only activates when RALPH_ACTIVE=1 is set, preventing host/router trapping
-
State persistence — JSON state file tracks iteration count, timestamps, findings
-
Circuit breaker — Detects repeated failures and exits gracefully
-
Verification-first exit — Completion signal only accepted when validation commands pass
-
Guardrails — Accumulated lessons from past failures prevent repeated mistakes
When to Use
-
Well-defined tasks with clear, testable success criteria
-
Iterative work (get tests passing, fix lint errors, audit codebase)
-
Overnight or background autonomous runs
-
Multi-phase audits with structured findings logs
When NOT to Use
-
Subjective goals ("make the code better")
-
One-off fixes that don't need iteration
-
Tasks requiring human judgment at each step
-
Exploratory research without clear deliverables
Architecture — Two Modes
Mode 1: Standalone (Stop Hook)
For single-session use. The Stop hook keeps the session alive until completion. Requires RALPH_ACTIVE=1 env var (set by the launcher scripts).
User runs ralph-audit.sh/bat (sets RALPH_ACTIVE=1) | v Claude works on task (reads PROMPT.md) | v Claude attempts to exit | v Stop hook intercepts (ralph-stop-hook.cjs) | +-- RALPH_ACTIVE != '1'? --> YES --> exit(0) immediately (no-op) | +-- Completion signal found? --> YES --> Clear state, exit(0) | +-- Max iterations reached? --> YES --> Clear state, exit(0) | +-- NO --> Increment iteration, save state, re-inject prompt, block exit
Mode 2: Router-Managed (Multi-Agent) -- Primary mode for agent-studio
This is the primary mode within agent-studio. The router manages iteration by spawning and re-spawning QA agents via Task() . No Stop hook is involved -- the router itself controls the loop lifecycle. The state file at .claude/context/runtime/ralph-state.json within agent-studio tracks iteration progress.
Router receives /ralph-loop command | v Router spawns QA agent with audit prompt via Task() | v QA agent completes, reports findings via TaskUpdate() | v Router checks audit state file (.claude/context/runtime/ralph-state.json) | +-- RALPH_AUDIT_COMPLETE_NO_FINDINGS? --> Done | +-- RALPH_ITERATION_COMPLETE? --> Spawn another QA agent | +-- Max iterations? --> Report and stop
Why two modes: Stop hooks fire on the host session, not on subagents. In multi-agent setups (like agent-studio), the Stop hook would trap the router. The RALPH_ACTIVE guard ensures the hook is a no-op unless explicitly activated by a standalone launcher. In agent-studio, Mode 2 is used exclusively -- the router orchestrates iteration without any Stop hook registration.
Components
Skill Bundle (included in this skill directory)
File Relative Path Purpose
Main script scripts/main.cjs
CLI for status/reset/config of ralph loop state
Pre-execute hook hooks/pre-execute.cjs
Input validation before skill execution
Post-execute hook hooks/post-execute.cjs
Output validation after skill execution
Input schema schemas/input.schema.json
Input validation schema
Output schema schemas/output.schema.json
Output contract schema
Implementation template templates/implementation-template.md
PROMPT.md and launcher templates
Skill rule rules/ralph-loop.md
Skill-specific rules
Project-Level Files (set up per-project by the user)
These files are NOT part of the skill bundle. They live in the hosting project's .claude/ directory and must be created/configured by the user or via the implementation template.
File Project Path Purpose
Stop hook .claude/hooks/ralph-stop-hook.cjs
Loop controller (stdin -> transcript check -> re-inject)
Prompt .claude/ralph/PROMPT.md
Audit/task instructions re-injected each iteration
State .claude/context/runtime/ralph-state.json
Iteration count, timestamps, findings count (auto-created)
Guardrails .claude/ralph/guardrails.md
Learned lessons from past failures
Launcher (bash) .claude/ralph/ralph-audit.sh
Unix/macOS launcher script (sets RALPH_ACTIVE=1)
Launcher (bat) .claude/ralph/ralph-audit.bat
Windows launcher script (sets RALPH_ACTIVE=1)
Settings .claude/settings.json
Stop hook registration (Mode 1 only)
Usage
Standalone Mode (Stop Hook)
From your project's workspace root — sets RALPH_ACTIVE=1 automatically
.claude/ralph/ralph-audit.sh # Unix/macOS .claude\ralph\ralph-audit.bat # Windows
Manual Standalone Start
Must set RALPH_ACTIVE=1 yourself for the Stop hook to activate
export RALPH_ACTIVE=1 claude --print-output-format text < .claude/ralph/PROMPT.md
Multi-Agent Mode (Router-Managed)
Use the /ralph-loop command within agent-studio, or have the router spawn QA agents with the audit prompt. The router manages iteration; no Stop hook is involved.
Custom Prompt
Create a custom PROMPT.md with:
-
Mission — What the agent must accomplish
-
Scope — Specific areas to audit/fix
-
Validation commands — Commands that must pass
-
Completion condition — The exact completion signal string
Completion Signals
Signal Meaning
RALPH_AUDIT_COMPLETE_NO_FINDINGS
All validations pass, zero open findings
RALPH_ITERATION_COMPLETE: N findings remain
Progress made, N findings still open
Stop Hook Protocol
The stop hook (ralph-stop-hook.cjs ) follows this protocol:
-
GUARD 0: Check RALPH_ACTIVE env var — if not '1' , exit 0 immediately (no stdin read, no file checks, no-op). This is the critical protection that prevents the hook from trapping the host/router session.
-
Read stdin (Claude Code transcript JSON)
-
Check stop_hook_active guard (prevent infinite re-triggering)
-
Check if ralph-state.json exists (no state = no active loop)
-
Check transcript for RALPH_AUDIT_COMPLETE_NO_FINDINGS
-
Found → clear state file, exit 0 (allow exit)
-
Load state from ralph-state.json , increment iteration
-
Check for progress signal (RALPH_ITERATION_COMPLETE )
-
Check circuit breaker (findings count stuck for N iterations)
-
Check max iterations (default: 25)
-
Reached → clear state, exit 0 (force stop)
-
Save updated state
-
Read PROMPT.md and write to stdout as JSON { decision: 'block', reason: <prompt> }
-
Exit 0 (with block decision in stdout)
Exit Codes
Code Meaning
0 Allow exit (complete, max iterations, or error)
2 Block exit, stdout fed back as next prompt
Configuration
Environment Variables
Variable Default Description
RALPH_ACTIVE
(unset) Must be 1 to activate the Stop hook. Set by launcher scripts. Without this, the hook is a no-op.
RALPH_MAX_ITERATIONS
25 Maximum loop iterations
RALPH_COMPLETION_SIGNAL
RALPH_AUDIT_COMPLETE_NO_FINDINGS
String that signals completion
RALPH_CIRCUIT_BREAKER_THRESHOLD
3 Consecutive iterations with unchanged findings count before circuit breaker trips
State File Schema
{ "iteration": 3, "startedAt": "2026-02-28T10:00:00Z", "lastRunAt": "2026-02-28T10:15:00Z", "lastFindingsCount": 5 }
TDD State Schema (for TDD-mode loops)
When using ralph-loop for TDD workflows, use a separate TDD-specific state file at .claude/context/runtime/tdd-state.json . This schema tracks per-scenario RED/GREEN evidence across session interruptions:
{ "scenarios": [ { "id": "sc-001", "description": "routing-guard blocks Write on creator paths", "status": "pending|red|green|refactored" } ], "completedScenarios": [ { "id": "sc-001", "evidenceCommand": "node --test tests/hooks/routing-guard.test.cjs", "redEvidence": "AssertionError: expected exit code 2, got 0", "greenEvidence": "✓ routing-guard blocks Write (4ms)", "passedAt": "2026-03-12T10:00:00Z" } ], "currentScenario": "sc-002", "evidenceLog": [ { "scenarioId": "sc-001", "phase": "red|green|refactored", "output": "<verbatim test runner output>", "timestamp": "2026-03-12T09:58:00Z" } ] }
TDD Session Resumption: On each loop iteration, before picking a scenario:
const tddState = JSON.parse( fs.readFileSync('.claude/context/runtime/tdd-state.json', 'utf-8') || '{}' ); const completedIds = (tddState.completedScenarios || []).map(s => s.id); const remaining = (tddState.scenarios || []).filter(s => !completedIds.includes(s.id)); if (remaining.length === 0) { // All scenarios complete — emit RALPH_AUDIT_COMPLETE_NO_FINDINGS console.log('RALPH_AUDIT_COMPLETE_NO_FINDINGS'); process.exit(0); } const nextScenario = remaining[0];
Critical rule: Never re-execute scenarios already in completedScenarios . The evidenceLog is append-only — each phase (red/green/refactored) adds a new entry. Circuit breaker trips if currentScenario is unchanged for 3+ iterations.
Integration with TDP: When spawning the developer agent for a TDD loop iteration, extract the red evidence from evidenceLog and inject verbatim into the spawn prompt (see tdd skill — Test-Driven Prompting pattern).
Writing Effective Prompts
Structure
Mission
One-line directive.
Before Doing Anything
Step 1: Read previous findings (if exists) Step 2: Load context/skills
Scope
Numbered list of areas to audit/fix.
Validation Commands
Commands that must pass for completion.
Findings Log
Where to write findings (path + format).
Completion Condition
Exact signal strings and when to use each.
Best Practices
-
Binary criteria — "All tests pass" not "code is good"
-
Validation commands — Include runnable commands (pnpm test, pnpm lint)
-
Findings format — Structured findings with severity, file, status
-
Idempotent — Prompt must work correctly on any iteration (read state first)
-
Context management — Include token-saver invocation for long sessions
Guardrails Pattern
The guardrails.md file accumulates "Signs" — lessons learned from failures:
Sign: [Name]
- Trigger: When this applies
- Instruction: What to do
- Added after: What failure taught this
Agents should read guardrails.md at the start of each iteration and add new Signs when they encounter novel failure modes.
Enforcement Hooks
Input validated against schemas/input.schema.json before execution. Output contract defined in schemas/output.schema.json .
Anti-Patterns
Anti-Pattern Why It Fails Correct Approach
No max iterations Infinite loop burns tokens Always set maxIterations (default 25)
Vague completion criteria Agent claims "done" prematurely Use binary pass/fail validation commands
No state persistence Progress lost on context reset Write findings to file, read at iteration start
Stop hook without RALPH_ACTIVE guard Traps host/router session in multi-agent setups Check RALPH_ACTIVE=1 before any other logic
Running standalone mode from router Router gets trapped by Stop hook Use router-managed iteration (Mode 2) instead
No guardrails Same mistakes repeated each iteration Maintain guardrails.md with learned Signs
Iron Laws
-
NO LOOP WITHOUT VERIFICATION — Completion signal must be backed by passing validation commands
-
NO LOOP WITHOUT MAX ITERATIONS — Every loop must have a safety cap
-
NO LOOP WITHOUT STATE FILE — Progress must be persisted to survive context resets
-
NO LOOP WITHOUT GUARDRAILS — Failures must be recorded to prevent repetition
-
RALPH_ACTIVE GUARD FIRST — Stop hook must check RALPH_ACTIVE=1 env var before reading stdin, checking files, or any other logic. This is the primary protection against trapping the host/router.
Memory Protocol (MANDATORY)
Before starting:
Read .claude/context/memory/learnings.md using the Read tool.
Check for:
-
Previously run ralph loops and their outcomes
-
Known audit patterns and failure modes
-
User preferences for iteration limits
After completing:
-
Loop completed successfully → Append summary to learnings.md
-
New guardrail discovered → Add Sign to .claude/ralph/guardrails.md
-
Architecture decision → Append to decisions.md
ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.