A comprehensive guide to running autonomous AI coding loops using Geoff Huntley's Ralph methodology. View as formatted guide below π
1,032
stars
55
commits
HTML
primary language
Mar 6, 2026
updated
December 2025 boiled Ralph's powerful yet dumb little face to the top of most AI-related timelines.
I try to pay attention to the crazy-smart insights @GeoffreyHuntley shares, but I can't say Ralph really clicked for me this summer. Now, all of the recent hubbub has made it hard to ignore.
@mattpocockuk and @ryancarson's overviews helped a lot - right until Geoff came in and said 'nah'.
Many folks seem to be getting good results with various shapes - but I wanted to read the tea leaves as closely as possible from the person who not only captured this approach but also has had the most ass-time in the seat putting it through its paces.
So I dug in to really RTFM on recent videos and Geoff's original post to try and untangle for myself what works best.
Below is the result - a (likely OCD-fueled) Ralph Playbook that organizes the miscellaneous details for putting this all into practice w/o hopefully neutering it in the process.
Digging into all of this has also brought to mind some possibly valuable additional enhancements to the core approach that aim to stay aligned with the guidelines that make Ralph work so well.
[!TIP] View as π Formatted Guide β
Hope this helps you out - @ClaytonFarr
A picture is worth a thousand tweets and an hour-long video. Geoff's overview here (sign up to his newsletter to see full article) really helped clarify the workflow details for moving from 1) idea β 2) individual JTBD-aligned specs β 3) comprehensive implementation plan β 4) Ralph work loops.

This diagram clarified for me that Ralph isn't just "a loop that codes." It's a funnel with 3 Phases, 2 Prompts, and 1 Loop.
specs/FILENAME.md for each topicPROMPT.md as needed)Same loop mechanism, different prompts for different objectives:
| Mode | When to use | Prompt focus |
|---|---|---|
| PLANNING | No plan exists, or plan is stale/wrong | Generate/update IMPLEMENTATION_PLAN.md only |
| BUILDING | Plan exists | Implement from plan, commit, update plan as side effect |
Prompt differences per mode:
Why use the loop for both modes?
Context loaded each iteration: PROMPT.md + AGENTS.md
PLANNING mode loop lifecycle:
specs/* and existing /srcIMPLEMENTATION_PLAN.md with prioritized tasksBUILDING mode loop lifecycle:
specs/* (requirements)IMPLEMENTATION_PLAN.md/src ("don't assume not implemented")IMPLEMENTATION_PLAN.md β mark task done, note discoveries/bugsAGENTS.md β if operational learnings| Term | Definition |
|---|---|
| Job to be Done (JTBD) | High-level user need or outcome |
| Topic of Concern | A distinct aspect/component within a JTBD |
| Spec | Requirements doc for one topic of concern (specs/FILENAME.md) |
| Task | Unit of work derived from comparing specs to code |
Relationships:
Example:
Topic Scope Test: "One Sentence Without 'And'"
This informs and drives everything else:
Creating the right signals & gates to steer Ralph's successful output is critical. You can steer from two directions:
PROMPT.md + AGENTS.md)AGENTS.md specifies actual commands to make backpressure project-specificRalph's effectiveness comes from how much you trust it do the right thing (eventually) and engender its ability to do so.
--dangerously-skip-permissions - asking for approval on every tool call would break the loop. This bypasses Claude's permission system entirely - so a sandbox becomes your only security boundary.git reset --hard reverts uncommitted changes; regenerate plan if trajectory goes wrongTo get the most out of Ralph, you need to get out of his way. Ralph should be doing all of the work, including decided which planned work to implement next and how to implement it. Your job is now to sit on the loop, not in it - to engineer the setup and environment that will allow Ralph to succeed.
Observe and course correct β especially early on, sit and watch. What patterns emerge? Where does Ralph go wrong? What signs does he need? The prompts you start with won't be the prompts you end with - they evolve through observed failure patterns.
Tune it like a guitar β instead of prescribing everything upfront, observe and adjust reactively. When Ralph fails a specific way, add a sign to help him next time.
But signs aren't just prompt text. They're anything Ralph can discover:
AGENTS.md - operational learnings about how to build/test[!TIP]
- try starting with nothing in
AGENTS.md(empty file; no best practices, etc.)- spot-test desired actions, find missteps (walkthrough example from Geoff)
- watch initial loops, see where gaps occur
- tune behavior only as needed, via AGENTS updates and/or code patterns (shared utilities, etc.)
And remember, the plan is disposable:
loop.sh acts in effect as an 'outer loop' where each loop = a single task (in separate sessions). When the task is completed, loop.sh kicks off a fresh session to select the next task, if any remaining tasks are available.
Geoff's initial minimal form of loop.sh script:
while :; do cat PROMPT.md | claude ; done
Note: The same approach can be used with other CLIs; e.g. amp, codex, opencode, etc.
What controls task continuation?
The continuation mechanism is elegantly simple:
PROMPT.md to claudeKey insight: The IMPLEMENTATION_PLAN.md file persists on disk between iterations and acts as shared state between otherwise isolated loop executions. Each iteration deterministically loads the same files (PROMPT.md + AGENTS.md + specs/*) and reads the current state from disk.
No sophisticated orchestration needed - just a dumb bash loop that keeps restarting the agent, and the agent figures out what to do next by reading the plan file each time.
Each task is prompted to keep doing its work against backpressure (tests, etc) until it passes - creating a pseudo inner 'loop' (in single session).
This inner loop is just internal self-correction / iterative reasoning within one long model response, powered by backpressure prompts, tool use, and subagents. It's not a loop in the programming sense.
A single task execution has no hard technical limit. Control relies on:
Ralph can go in circles, ignore instructions, or take wrong directions - this is expected and part of the tuning process. When Ralph "tests you" by failing in specific ways, you add guardrails to the prompt or adjust backpressure mechanisms. The nondeterminism is manageable through observation and iteration.
loop.sh ExampleWraps core loop with mode selection (plan/build), with max-iterations for max number of tasks to complete, and git push after each iteration.
This enhancement uses two saved prompt files:
PROMPT_plan.md - Planning mode (gap analysis, generates/updates plan)PROMPT_build.md - Building mode (implements from plan)#!/bin/bash
# Usage: ./loop.sh [plan|build] [max_iterations]
# Examples:
# ./loop.sh # Build mode, unlimited tasks
# ./loop.sh 20 # Build mode, max 20 tasks
# ./loop.sh build 20 # Build mode, max 20 tasks
# ./loop.sh plan # Plan mode, unlimited tasks
# ./loop.sh plan 5 # Plan mode, max 5 tasks
# Parse arguments
if [ "$1" = "plan" ]; then
# Plan mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "build" ]; then
# Explicit build mode (with optional max iterations)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=${2:-0}
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max tasks (bare number)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=$1
else
# Build mode, unlimited (no arguments or invalid input)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=0
fi
ITERATION=0
CURRENT_BRANCH=$(git branch --show-current)
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: $MODE"
echo "Prompt: $PROMPT_FILE"
echo "Branch: $CURRENT_BRANCH"
[ $MAX_ITERATIONS -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations (number of tasks)"
echo "ββββββββββββββββββββββββββββββββββββββββ"
# Verify prompt file exists
if [ ! -f "$PROMPT_FILE" ]; then
echo "Error: $PROMPT_FILE not found"
exit 1
fi
while true; do
if [ $MAX_ITERATIONS -gt 0 ] && [ $ITERATION -ge $MAX_ITERATIONS ]; then
echo "Reached max iterations (number of tasks): $MAX_ITERATIONS"
break
fi
# Run Ralph iteration with selected prompt
# -p: Headless mode (non-interactive, reads from stdin)
# --dangerously-skip-permissions: Auto-approve all tool calls (YOLO mode)
# --output-format=stream-json: Structured output for logging/monitoring
# --model opus: Primary agent uses Opus for complex reasoning (task selection, prioritization)
# Can use 'sonnet' in build mode for speed if plan is clear and tasks well-defined
# --verbose: Detailed execution logging
cat "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
# Push changes after each iteration
git push origin "$CURRENT_BRANCH" || {
echo "Failed to push. Creating remote branch..."
git push -u origin "$CURRENT_BRANCH"
}
ITERATION=$((ITERATION + 1))
echo -e "\n\n======================== LOOP $ITERATION ========================\n"
done
Mode selection:
PROMPT_build.md for building (implementation)plan keyword β Uses PROMPT_plan.md for planning (gap analysis, plan generation)Max-iterations:
./loop.sh runs unlimited (manual stop with Ctrl+C)./loop.sh 20 runs max 20 iterations then stopsClaude CLI flags:
-p (headless mode): Enables non-interactive operation, reads prompt from stdin--dangerously-skip-permissions: Bypasses all permission prompts for fully automated runs--output-format=stream-json: Outputs structured JSON for logging/monitoring/visualization--model opus: Primary agent uses Opus for task selection, prioritization, and coordination (can use sonnet for speed if tasks are clear)--verbose: Provides detailed execution loggingAn alternative loop_streamed.sh that pipes Claude's raw JSON output through parse_stream.js for readable, color-coded terminal display showing tool calls, results, and execution stats.
Differences from base loop.sh:
-p "$FULL_PROMPT") instead of stdin pipe--include-partial-messages for real-time streamingparse_stream.js (Node.js, no dependencies)Files: loop_streamed.sh Β· parse_stream.js
β contributed by @terry-xyz Β· @blackrosesxyz
This repository is available under the MIT License.
Third-party screenshots and externally sourced images are excluded unless explicitly noted otherwise. See NOTICE for details.
project-root/
βββ loop.sh # Ralph loop script
βββ PROMPT_build.md # Build mode instructions
βββ PROMPT_plan.md # Plan mode instructions
βββ AGENTS.md # Operational guide loaded each iteration
βββ IMPLEMENTATION_PLAN.md # Prioritized task list (generated/updated by Ralph)
βββ specs/ # Requirement specs (one per JTBD topic)
β βββ [jtbd-topic-a].md
β βββ [jtbd-topic-b].md
βββ src/ # Application source code
βββ src/lib/ # Shared utilities & components
loop.shThe primary loop script that orchestrates Ralph iterations.
See Loop Mechanics section for detailed implementation examples and configuration options.
Setup: Make the script executable before first use:
chmod +x loop.sh
Core function: Continuously feeds prompt file to Claude, manages iteration limits, and pushes changes after each task completion.
The instruction set for each loop iteration. Swap between PLANNING and BUILDING versions as needed.
Prompt Structure:
| Section | Purpose |
|---|---|
| Phase 0 (0a, 0b, 0c) | Orient: study specs, source location, current plan |
| Phase 1-4 | Main instructions: task, validation, commit |
| 999... numbering | Guardrails/invariants (higher number = more critical) |
Key Language Patterns (Geoff's specific phrasing):
PROMPT_plan.md TemplateNotes:
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0c. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0d. For reference, the application source code is in `src/*`.
1. Study @IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `src/*` and compare it against `specs/*`. Use an Opus subagent to analyze findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented. Ultrathink. Consider searching for TODO, minimal implementations, placeholders, skipped/flaky tests, and inconsistent patterns. Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve [project-specific goal]. Consider missing elements and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
PROMPT_build.md TemplateNote: Current subagents names presume using Claude.
0a. Study `specs/*` with up to 500 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md.
0c. For reference, the application source code is in `src/*`.
1. Your task is to implement functionality per the specifications using parallel subagents. Follow @IMPLEMENTATION_PLAN.md and choose the most important item to address. Before making changes, search the codebase (don't assume not implemented) using Sonnet subagents. You may use up to 500 parallel Sonnet subagents for searches/reads and only 1 Sonnet subagent for build/tests. Use Opus subagents when complex reasoning is needed (debugging, architectural decisions).
2. After implementing functionality or resolving problems, run the tests for that unit of code that was improved. If functionality is missing then it's your job to add it as per the application specifications. Ultrathink.
3. When you discover issues, immediately update @IMPLEMENTATION_PLAN.md with your findings using a subagent. When resolved, update and remove the item.
4. When the tests pass, update @IMPLEMENTATION_PLAN.md, then `git add -A` then `git commit` with a message describing the changes. After the commit, `git push`.
99999. Important: When authoring documentation, capture the why β tests and implementation importance.
999999. Important: Single sources of truth, no migrations/adapters. If tests unrelated to your work fail, resolve them as part of the increment.
9999999. As soon as there are no build or test errors create a git tag. If there are no git tags start at 0.0.0 and increment patch by 1 for example 0.0.1 if 0.0.0 does not exist.
99999999. You may add extra logging if required to debug issues.
999999999. Keep @IMPLEMENTATION_PLAN.md current with learnings using a subagent β future work depends on this to avoid duplicating efforts. Update especially after finishing your turn.
9999999999. When you learn something new about how to run the application, update @AGENTS.md using a subagent but keep it brief. For example if you run commands multiple times before learning the correct command then that file should be updated.
99999999999. For any bugs you notice, resolve them or document them in @IMPLEMENTATION_PLAN.md using a subagent even if it is unrelated to the current piece of work.
999999999999. Implement functionality completely. Placeholders and stubs waste efforts and time redoing the same work.
9999999999999. When @IMPLEMENTATION_PLAN.md becomes large periodically clean out the items that are completed from the file using a subagent.
99999999999999. If you find inconsistencies in the specs/* then use an Opus 4.6 subagent with 'ultrathink' requested to update the specs.
999999999999999. IMPORTANT: Keep @AGENTS.md operational only β status updates and progress notes belong in `IMPLEMENTATION_PLAN.md`. A bloated AGENTS.md pollutes every future loop's context.
AGENTS.mdSingle, canonical "heart of the loop" - a concise, operational "how to run/build" guide.
Status, progress, and planning belong in IMPLEMENTATION_PLAN.md, not here.
Loopback / Immediate Self-Evaluation:
AGENTS.md should contain the project-specific commands that enable loopback - the ability for Ralph to immediately evaluate his work within the same loop. This includes:
The BUILDING prompt says "run tests" generically; AGENTS.md specifies the actual commands. This is how backpressure gets wired in per-project.
## Build & Run
Succinct rules for how to BUILD the project:
## Validation
Run these after implementing to get immediate feedback:
- Tests: `[test command]`
- Typecheck: `[typecheck command]`
- Lint: `[lint command]`
## Operational Notes
Succinct learnings about how to RUN the project:
...
### Codebase Patterns
...
IMPLEMENTATION_PLAN.mdPrioritized bullet-point list of tasks derived from gap analysis (specs vs code) - generated by Ralph.
The circularity is intentional: eventual consistency through iteration.
No pre-specified template - let Ralph/LLM dictate and manage format that works best for it.
specs/*One markdown file per topic of concern. These are the source of truth for what should be built.
No pre-specified template - let Ralph/LLM dictate and manage format that works best for it.
src/ and src/lib/Application source code and shared utilities/components.
Referenced in PROMPT.md templates for orientation steps.
I'm still determining the value/viability of these, but the opportunities sound promising:
During Phase 1 (Define Requirements), use Claude's built-in AskUserQuestionTool to systematically explore JTBD, topics of concern, edge cases, and acceptance criteria through structured interview before writing specs.
When to use: Minimal/vague initial requirements, need to clarify constraints, or multiple valid approaches exist.
Invoke: "Interview me using AskUserQuestion to understand [JTBD/topic/acceptance criteria/...]"
Claude will ask targeted questions to clarify requirements and ensure alignment before producing specs/*.md files.
Flow:
No code or prompt changes needed - this simply enhances Phase 1 using existing Claude Code capabilities.
Inspiration - Thariq's X post:
Geoff's Ralph implicitly connects specs β implementation β tests through emergent iteration. This enhancement would make that connection explicit by deriving test requirements during planning, creating a direct line from "what success looks like" to "what verifies it."
This enhancement connects acceptance criteria (in specs) directly to test requirements (in implementation plan), improving backpressure quality by:
| Principle | Maintained? | How |
|---|---|---|
| Monolithic operation | β Yes | One agent, one task, one loop at a time |
| Backpressure critical | β Yes | Tests are the mechanism, just derived explicitly now |
| Context efficiency | β Yes | Planning decides tests once vs building rediscovering |
| Deterministic setup | β Yes | Test requirements in plan (known state) not emergent |
| Let Ralph Ralph | β Yes | Ralph still prioritizes and chooses implementation approach |
| Plan is disposable | β Yes | Wrong test requirements? Regenerate plan |
| "Capture the why" | β Yes | Test intent documented in plan before implementation |
| No cheating | β Yes | Required tests prevent placeholder implementations |
The critical distinction:
Acceptance criteria (in specs) = Behavioral outcomes, observable results, what success looks like
Test requirements (in implementation plan) = Verification points derived from acceptance criteria
Implementation approach (up to Ralph) = Technical decisions about how to achieve it
The key: Specify WHAT to verify (outcomes), not HOW to implement (approach)
This maintains "Let Ralph Ralph" principle - Ralph decides implementation details while having clear success signals.
Phase 1: Requirements Definition
specs/*.md + Acceptance Criteria
β
Phase 2: Planning (derives test requirements)
IMPLEMENTATION_PLAN.md + Required Tests
β
Phase 3: Building (implements with tests)
Implementation + Tests β Backpressure
During the human + LLM conversation that produces specs:
Modify PROMPT_plan.md instruction 1 to include test derivation. Add after the first sentence:
For each task in the plan, derive required tests from acceptance criteria in specs - what specific outcomes need verification (behavior, performance, edge cases). Tests verify WHAT works, not HOW it's implemented. Include as part of task definition.
Modify PROMPT_build.md instructions:
Instruction 1: Add after "choose the most important item to address":
Tasks include required tests - implement tests as part of task scope.
Instruction 2: Replace "run the tests for that unit of code" with:
run all required tests specified in the task definition. All required tests must exist and pass before the task is considered complete.
Prepend new guardrail (in the 9s sequence):
999. Required tests derived from acceptance criteria must exist and pass before committing. Tests are part of implementation scope, not optional. Test-driven development approach: tests can be written first or alongside implementation.
Some acceptance criteria resist programmatic validation:
These require human-like judgment but need backpressure to meet acceptance criteria during building loop.
Solution: Add LLM-as-Judge tests as backpressure with binary pass/fail.
LLM reviews are non-deterministic (same artifact may receive different judgments across runs). This aligns with Ralph philosophy: "deterministically bad in an undeterministic world." The loop provides eventual consistency through iterationβreviews run until pass, accepting natural variance.
Create two files in src/lib/:
src/lib/
llm-review.ts # Core fixture - single function, clean API
llm-review.test.ts # Reference examples showing the pattern (Ralph learns from these)
llm-review.ts - Binary pass/fail API Ralph discovers:interface ReviewResult {
pass: boolean;
feedback?: string; // Only present when pass=false
}
function createReview(config: {
criteria: string; // What to evaluate (behavioral, observable)
artifact: string; // Text content OR screenshot path
intelligence?: "fast" | "smart"; // Optional, defaults to 'fast'
}): Promise<ReviewResult>;
Multimodal support: Both intelligence levels would use multimodal model (text + vision). Artifact type detection is automatic:
artifact: "Your content here" β Routes as text inputartifact: "./tmp/screenshot.png" β Routes as vision input (detects .png, .jpg, .jpeg extensions)Intelligence levels (quality of judgment, not capability type):
fast (default): Quick, cost-effective models for straightforward evaluations
smart: Higher-quality models for nuanced aesthetic/creative judgment
The fixture implementation selects appropriate models. (Examples are current options, not requirements.)
llm-review.test.ts - Shows Ralph how to use it (text and vision examples):import { createReview } from "@/lib/llm-review";
// Example 1: Text evaluation
test("welcome message tone", async () => {
const message = generateWelcomeMessage();
const result = await createReview({
criteria:
"Message uses warm, conversational tone appropriate for design professionals while clearly conveying value proposition",
artifact: message, // Text content
});
expect(result.pass).toBe(true);
});
// Example 2: Vision evaluation (screenshot path)
test("dashboard visual hierarchy", async () => {
await page.screenshot({ path: "./tmp/dashboard.png" });
const result = await createReview({
criteria:
"Layout demonstrates clear visual hierarchy with obvious primary action",
artifact: "./tmp/dashboard.png", // Screenshot path
});
expect(result.pass).toBe(true);
});
// Example 3: Smart intelligence for complex judgment
test("brand visual consistency", async () => {
await page.screenshot({ path: "./tmp/homepage.png" });
const result = await createReview({
criteria:
"Visual design maintains professional brand identity suitable for financial services while avoiding corporate sterility",
artifact: "./tmp/homepage.png",
intelligence: "smart", // Complex aesthetic judgment
});
expect(result.pass).toBe(true);
});
Ralph learns from these examples: Both text and screenshots work as artifacts. Choose based on what needs evaluation. The fixture handles the rest internally.
Future extensibility: Current design uses single artifact: string for simplicity. Can expand to artifact: string | string[] if clear patterns emerge requiring multiple artifacts (before/after comparisons, consistency across items, multi-perspective evaluation). Composite screenshots or concatenated text could handle most multi-item needs.
Planning Phase - Update PROMPT_plan.md:
After:
...Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
Insert this:
When deriving test requirements from acceptance criteria, identify whether verification requires programmatic validation (measurable, inspectable) or human-like judgment (perceptual quality, tone, aesthetics). Both types are equally valid backpressure mechanisms. For subjective criteria that resist programmatic validation, explore src/lib for non-deterministic evaluation patterns.
Building Phase - Update PROMPT_build.md:
Prepend new guardrail (in the 9s sequence):
9999. Create tests to verify implementation meets acceptance criteria and include both conventional tests (behavior, performance, correctness) and perceptual quality tests (for subjective criteria, see src/lib patterns).
Discovery, not documentation: Ralph learns LLM review patterns from llm-review.test.ts examples during src/lib exploration (Phase 0c). No AGENTS.md updates needed - the code examples are the documentation.
| Principle | Maintained? | How |
|---|---|---|
| Backpressure critical | β Yes | Extends backpressure to non-programmatic acceptance |
| Deterministic setup | β οΈ Partial | Criteria in plan (deterministic), evaluation non-deterministic but converges through iteration. Intentional tradeoff for subjective quality. |
| Context efficiency | β Yes | Fixture reused via src/lib, small test definitions |
| Let Ralph Ralph | β Yes | Ralph discovers pattern, chooses when to use, writes criteria |
| Plan is disposable | β Yes | Review requirements part of plan, regenerate if wrong |
| Simplicity wins | β Yes | Single function, binary result, no scoring complexity |
| Add signs for Ralph | β Yes | Light prompt additions, learning from code exploration |
The Critical Principle: Geoff's Ralph works from a single, disposable plan where Ralph picks "most important." To use branches with Ralph while maintaining this pattern, you must scope at plan creation, not at task selection.
Why this matters:
Solution: Add a plan-work mode to create a work-scoped IMPLEMENTATION_PLAN.md on the current branch. User creates work branch, then runs plan-work with a natural language description of the work focus. The LLM uses this description to scope the plan. Post planning, Ralph builds from this already-scoped plan with zero semantic filtering - just picks "most important" as always.
Terminology: "Work" is intentionally broad - it can describe features, topics of concern, refactoring efforts, infrastructure changes, bug fixes, or any coherent body of related changes. The work description you pass to plan-work is natural language for the LLM - it can be prose, not constrained by git branch naming rules.
1. Full Planning (on main branch)
./loop.sh plan
# Generate full IMPLEMENTATION_PLAN.md for entire project
2. Create Work Branch
User performs:
git checkout -b ralph/user-auth-oauth
# Create branch with whatever naming convention you prefer
# Suggestion: ralph/* prefix for work branches
3. Scoped Planning (on work branch)
./loop.sh plan-work "user authentication system with OAuth and session management"
# Pass natural language description - LLM uses this to scope the plan
# Creates focused IMPLEMENTATION_PLAN.md with only tasks for this work
4. Build from Plan (on work branch)
./loop.sh
# Ralph builds from scoped plan (no filtering needed)
# Picks most important task from already-scoped plan
5. PR Creation (when work complete)
User performs:
gh pr create --base main --head ralph/user-auth-oauth --fill
Extends the base enhanced loop script to add work branch support with scoped planning:
#!/bin/bash
set -euo pipefail
# Usage:
# ./loop.sh [plan|build] [max_iterations] # Plan/build on current branch
# ./loop.sh plan-work "work description" # Create scoped plan on current branch
# Examples:
# ./loop.sh # Build mode, unlimited
# ./loop.sh 20 # Build mode, max 20
# ./loop.sh build 20 # Build mode, max 20
# ./loop.sh plan 5 # Full planning, max 5
# ./loop.sh plan-work "user auth" # Scoped planning
# Parse arguments
MODE="build"
PROMPT_FILE="PROMPT_build.md"
if [ "$1" = "plan" ]; then
# Full planning mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "build" ]; then
# Explicit build mode (with optional max iterations)
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "plan-work" ]; then
# Scoped planning mode
if [ -z "$2" ]; then
echo "Error: plan-work requires a work description"
echo "Usage: ./loop.sh plan-work \"description of the work\""
exit 1
fi
MODE="plan-work"
WORK_DESCRIPTION="$2"
PROMPT_FILE="PROMPT_plan_work.md"
MAX_ITERATIONS=${3:-5} # Default 5 for work planning
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max iterations (bare number)
MAX_ITERATIONS=$1
else
# Build mode, unlimited
MAX_ITERATIONS=0
fi
ITERATION=0
CURRENT_BRANCH=$(git branch --show-current)
# Validate branch for plan-work mode
if [ "$MODE" = "plan-work" ]; then
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
echo "Error: plan-work should be run on a work branch, not main/master"
echo "Create a work branch first: git checkout -b ralph/your-work"
exit 1
fi
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: plan-work"
echo "Branch: $CURRENT_BRANCH"
echo "Work: $WORK_DESCRIPTION"
echo "Prompt: $PROMPT_FILE"
echo "Plan: Will create scoped IMPLEMENTATION_PLAN.md"
[ "$MAX_ITERATIONS" -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations"
echo "ββββββββββββββββββββββββββββββββββββββββ"
# Warn about uncommitted changes to IMPLEMENTATION_PLAN.md
if [ -f "IMPLEMENTATION_PLAN.md" ] && ! git diff --quiet IMPLEMENTATION_PLAN.md 2>/dev/null; then
echo "Warning: IMPLEMENTATION_PLAN.md has uncommitted changes that will be overwritten"
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
# Export work description for PROMPT_plan_work.md
export WORK_SCOPE="$WORK_DESCRIPTION"
else
# Normal plan/build mode
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: $MODE"
echo "Branch: $CURRENT_BRANCH"
echo "Prompt: $PROMPT_FILE"
echo "Plan: IMPLEMENTATION_PLAN.md"
[ "$MAX_ITERATIONS" -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations"
echo "ββββββββββββββββββββββββββββββββββββββββ"
fi
# Verify prompt file exists
if [ ! -f "$PROMPT_FILE" ]; then
echo "Error: $PROMPT_FILE not found"
exit 1
fi
# Main loop
while true; do
if [ "$MAX_ITERATIONS" -gt 0 ] && [ "$ITERATION" -ge "$MAX_ITERATIONS" ]; then
echo "Reached max iterations: $MAX_ITERATIONS"
if [ "$MODE" = "plan-work" ]; then
echo ""
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Scoped plan created: $WORK_DESCRIPTION"
echo "To build, run:"
echo " ./loop.sh 20"
echo "ββββββββββββββββββββββββββββββββββββββββ"
fi
break
fi
# Run Ralph iteration with selected prompt
# -p: Headless mode (non-interactive, reads from stdin)
# --dangerously-skip-permissions: Auto-approve all tool calls (YOLO mode)
# --output-format=stream-json: Structured output for logging/monitoring
# --model opus: Primary agent uses Opus for complex reasoning (task selection, prioritization)
# Can use 'sonnet' for speed if plan is clear and tasks well-defined
# --verbose: Detailed execution logging
# For plan-work mode, substitute ${WORK_SCOPE} in prompt before piping
if [ "$MODE" = "plan-work" ]; then
envsubst < "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
else
cat "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
fi
# Push to current branch
CURRENT_BRANCH=$(git branch --show-current)
git push origin "$CURRENT_BRANCH" || {
echo "Failed to push. Creating remote branch..."
git push -u origin "$CURRENT_BRANCH"
}
ITERATION=$((ITERATION + 1))
echo -e "\n\n======================== LOOP $ITERATION ========================\n"
done
PROMPT_plan_work.md TemplateNote: Identical to PROMPT_plan.md but with scoping instructions and WORK_SCOPE env var substituted (automatically by the loop script).
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0c. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0d. For reference, the application source code is in `src/*`.
1. You are creating a SCOPED implementation plan for work: "${WORK_SCOPE}". Study @IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `src/*` and compare it against `specs/*`. Use an Opus subagent to analyze findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented. Ultrathink. Consider searching for TODO, minimal implementations, placeholders, skipped/flaky tests, and inconsistent patterns. Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
IMPORTANT: This is SCOPED PLANNING for "${WORK_SCOPE}" only. Create a plan containing ONLY tasks directly related to this work scope. Be conservative - if uncertain whether a task belongs to this work, exclude it. The plan can be regenerated if too narrow. Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve the scoped work "${WORK_SCOPE}". Consider missing elements related to this work and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
| Principle | Maintained? | How |
|---|---|---|
| Monolithic operation | β Yes | Ralph still operates as single process within branch |
| One task per loop | β Yes | Unchanged |
| Fresh context | β Yes | Unchanged |
| Deterministic | β Yes | Scoping at plan creation (deterministic), not runtime (prob.) |
| Simple | β Yes | Optional enhancement, main workflow still works |
| Plan-driven | β Yes | One IMPLEMENTATION_PLAN.md per branch |
| Single source of truth | β Yes | One plan per branch - scoped plan replaces full plan on branch |
| Plan is disposable | β Yes | Regenerate scoped plan anytime: ./loop.sh plan-work "work description" |
| Markdown over JSON | β Yes | Still markdown plans |
| Let Ralph Ralph | β Yes | Ralph picks "most important" from already-scoped plan - no filter |
Geoff's suggested workflow already aligns planning with Jobs-to-be-Done β breaking JTBDs into topics of concern, which in turn become specs. I love this and I think there's an opportunity to lean further into the product benefits this approach affords by reframing topics of concern as activities.
Activities are verbs in a journey ("upload photo", "extract colors") rather than capabilities ("color extraction system"). They're naturally scoped by user intent.
Topics: "color extraction", "layout engine" β capability-oriented Activities: "upload photo", "see extracted colors", "arrange layout" β journey-oriented
Activities β and their constituent steps β sequence naturally into a user flow, creating a journey structure that makes gaps and dependencies visible. A User Story Map organizes activities as columns (the journey backbone) with capability depths as rows β the full space of what could be built:
UPLOAD β EXTRACT β ARRANGE β SHARE
basic auto manual export
bulk palette templates collab
batch AI themes auto-layout embed
Horizontal slices through the map become candidate releases. Not every activity needs new capability in every release β some cells stay empty, and that's fine if the slice is still coherent:
UPLOAD β EXTRACT β ARRANGE β SHARE
Release 1: basic auto export
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Release 2: palette manual
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Release 3: batch AI themes templates embed
The story map gives you structure for slicing. Jason Cohen's Simple, Lovable, Complete (SLC) gives you criteria for what makes a slice good:
Why SLC over MVP? MVPs optimize for learning at the customer's expense β "minimum" often means broken or frustrating. SLC flips this: learn in-market while delivering real value. If it succeeds, you have optionality. If it fails, you still treated users well.
Each slice can become a release with a clear value and identity:
UPLOAD β EXTRACT β ARRANGE β SHARE
Palette Picker: basic auto export
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Mood Board: palette manual
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Design Studio: batch AI themes templates embed
The concepts above β activities, story maps, SLC releases β are the thinking tools. How do we translate them into Ralph's workflow?
Default Ralph approach:
specs/*.mdIMPLEMENTATION_PLAN.mdThis works well for capability-focused work (features, refactors, infrastructure). But it doesn't naturally produce valuable (SLC) product releases - it produces "whatever the specs describe".
Activities β SLC Release approach:
To get SLC releases, we need to ground activities in audience context. Audience defines WHO has the JTBDs, which in turn informs WHAT activities matter and what "lovable" means.
Audience (who)
βββ has JTBDs (desired outcomes)
βββ fulfilled by Activities (means to achieve outcomes)
I. Requirements Phase (2 steps):
Still performed in LLM conversations with the human, similar to the default Ralph approach.
Define audience and their JTBDs β WHO are we building for and what OUTCOMES do they want?
AUDIENCE_JTBD.mdDefine activities β WHAT do users do to accomplish their JTBDs?
AUDIENCE_JTBD.mdspecs/*.md (one per activity)The discrete steps within activities are implicit and LLM can infer them during planning.
II. Planning Phase:
Performed in Ralph loop with updated planning prompt.
AUDIENCE_JTBD.md (who, desired outcomes)specs/* (what could be built)IMPLEMENTATION_PLAN.mdIII. Building Phase:
Performed in Ralph loop with standard building prompt.
Variant of PROMPT_plan.md that adds audience context and SLC-oriented slice recommendation.
Notes:
[project-specific goal] placeholder β the goal is implicit: recommend the most valuable next release for the audience.0a. Study @AUDIENCE_JTBD.md to understand who we're building for and their Jobs to Be Done.
0b. Study `specs/*` with up to 250 parallel Sonnet subagents to learn JTBD activities.
0c. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0d. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0e. For reference, the application source code is in `src/*`.
1. Sequence the activities in `specs/*` into a user journey map for the audience in @AUDIENCE_JTBD.md. Consider how activities flow into each other and what dependencies exist.
2. Determine the next SLC release. Use up to 500 Sonnet subagents to compare `src/*` against `specs/*`. Use an Opus subagent to analyze findings. Ultrathink. Given what's already implemented recommend which activities (at what capability depths) form the most valuable next release. Prefer thin horizontal slices - the narrowest scope that still delivers real value. A good slice is Simple (narrow, achievable), Lovable (people want to use it), and Complete (fully accomplishes a meaningful job, not a broken preview).
3. Use an Opus subagent (ultrathink) to analyze and synthesize the findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented for the recommended SLC release. Begin plan with a summary of the recommended SLC release (what's included and why), then list prioritized tasks for that scope. Consider TODOs, placeholders, minimal implementations, skipped tests - but scoped to the release. Note discoveries outside scope as future work.
IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve the most valuable next release for the audience in @AUDIENCE_JTBD.md. Consider missing elements and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
Why AUDIENCE_JTBD.md as a separate artifact:
Cardinalities:
A dedicated loop mode for generating and maintaining spec files with enforced quality rules. Ensures specs stay focused on behavioral outcomes (not implementation details), properly scoped topics ("one sentence without 'and'"), and consistent file naming conventions.
When to use: After writing or updating specs, run specs mode to enforce consistency and hygiene across all spec files.
What it does:
specs/* filesspecs/README.md<int>-filename.md (e.g., 01-range-optimization.md)Usage: Add a specs argument to your loop script that selects PROMPT_specs.md:
./loop.sh specs # Specs mode, unlimited iterations
./loop.sh specs 3 # Specs mode, max 3 iterations
To add specs mode to loop.sh: insert a new elif branch in the argument parsing:
# Parse arguments
if [ "$1" = "plan" ]; then
# Plan mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "specs" ]; then # β add this block
# Specs mode
MODE="specs"
PROMPT_FILE="PROMPT_specs.md"
MAX_ITERATIONS=${2:-0}
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max iterations
...
To add specs mode to loop_streamed.sh: same change β add the elif block in the same position. The rest of the script (streaming, parse_stream.js piping) works unchanged.
Files: PROMPT_specs.md
PROMPT_specs.md TemplateNotes:
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
1. Identify Jobs to Be Done (JTBD) β Break individual JTBD into topic(s) of concern β Use subagents to load info from URLs into context β LLM understands JTBD topic of concern: subagent writes specs/FILENAME.md for each topic.
## RULES (don't apply to `specs/README.md`)
999. NEVER add code blocks or suggest how a variable should be named. This will be decided by Ralph.
9999.
- Acceptance criteria (in specs) = Behavioral outcomes, observable results
for example:
β "Extracts 5-10 dominant colors from any uploaded image"
β "Processes images <5MB in <100ms"
β "Handles edge cases: grayscale, single-color, transparent backgrounds"
- Test requirements (in plan) = Verification points derived from acceptance criteria
for example:
β "Required tests: Extract 5-10 colors, Performance <100ms"
- Implementation approach (up to Ralph) = Technical decisions
example TO AVOID:
β "Use K-means clustering with 3 iterations"
99999. Topic Scope Test: "One Sentence Without 'And'"
Can you describe the topic of concern in one sentence without conjoining unrelated capabilities?
example to follow:
β "The color extraction system analyzes images to identify dominant colors"
example to avoid:
β "The user system handles authentication, profiles, and billing" β 3 topics
If you need "and" to describe what it does, it's probably multiple topics
99999999. The key: Specify WHAT to verify (outcomes), not HOW to implement (approach). This maintains "Let Ralph Ralph" principle - Ralph decides implementation details while having clear success signals.
99999999999. Apply all rules to all existing files with up to 100 parallel Sonnet subagents in @specs (except README.md) and create new files if determined its needed based on `specs/README.md`. The names of the files should follow this name convention: <int>-filename.md, for example 01-range-optimization.md, 02-adaptive-behavior.md etc.
β contributed by @terry-xyz Β· @blackrosesxyz
It's easy to start working with specs in Greenfield, but when you're working in Brownfield, you have to take another approach. That's why you need to reverse engineer the implementations of the code back into specs to begin using the Ralph playbook.
When to use: You inherited or joined a codebase with no specs. You want to use Ralph on a project that wasn't built with Ralph. You need to add features to an existing brownfield project.
Invoke: "Reverse-engineer specs for [topic/area] using PROMPT_reverse_engineer_specs.md"
Flow:
PROMPT_reverse_engineer_specs.md βspecs/ βYou can use an agent orchestration pattern where the sub-agent is the reverse engineer and the orchestrator knows about the Topic of Concern Philosophy:
No modifications to existing prompt files needed β this is purely additive. The generated specs are the same format Ralph already consumes in planning and building phases.
| Principle | Maintained? | How |
|---|---|---|
| Deterministic setup | β Yes | Specs are written artifacts (known state), not ad-hoc context, contains all flaws in code. |
| Context efficiency | β οΈ Partial | Must be adopted throughout your entire team culture |
| Capture the why | β οΈ Partial | Not all implemented code contains the why behind things, only captures comments if they express the why intention. |
| Let Ralph Ralph | β Yes | Topics of concern are still chosen by Ralph. |
| Plan is disposable | β Yes | Specs provide stable baseline; plans regenerate against documented reality |
| Simplicity wins | β Yes | Provides a Hawkeye view of your entire specifications. |
PROMPT_reverse_engineer_specs.md TemplateNotes:
Files: PROMPT_reverse_engineer_specs.md
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn existing specifications.
0b. Study `src/*` to understand the codebase. Use up to 500 parallel Sonnet subagents for reads/searches. Treat `src/lib` as the project's standard library for shared utilities and components.
1. For each topic assigned (or discovered), reverse-engineer the source code and produce a specification in `specs/`. Use Opus subagents for complex tracing. Ultrathink. Before writing a spec, search to confirm one doesn't already exist for that topic.
2. One topic per spec. Must pass the "one sentence without 'and'" test. Split if "and" joins unrelated capabilities.
3. **Two-phase process:** Phase 1 (Investigation) β trace every entry point, branch, code path to terminal. Map data flow, side effects, state mutations, error handling, concurrency, config-driven paths, implicit behavior. Phase 2 (Output) β zero implementation details. No function/class/variable names, file paths, library/framework references. A different team on a different stack must be able to reimplement from the spec alone.
4. **Document reality, not intent.** Bugs are features. Never add behaviors the code doesn't implement. Never suggest improvements. If a source comment contradicts the code, document the code's behavior and ignore the comment.
5. **Scope boundaries:** When tracing leaves the topic, stop. Document what crosses the boundary (sent/received) only. Test: "Could this change without changing my topic's outcomes?" If yes, it's across the boundary.
6. **Shared behavior:** Inline fully in every spec (self-contained). Note shared topics for cross-spec tracking. Shared behavior also gets its own canonical spec.
7. **Spec format:** Markdown in `specs/`. Each spec includes: topic statement, scope (in-scope and boundaries), data contracts, behaviors (in execution order), and state transitions. Mark notable/surprising behavior, unreachable paths, and shared cross-topic behavior inline. Capture rationale from source comments (strip implementation references). File naming: `specs/NN-kebab-case.md` (e.g., `01-session-management.md`).
8. When specs are complete and validated, `git add -A` then `git commit` with a message describing which specs were added/updated. After the commit, `git push`.
99999. **Exhaustive checklist before finalizing:** Every entry point documented. Every branch traced to terminal. Every data contract. Every side effect in execution order. Every error path (caught/propagated/ignored). Every config-driven path. Concurrency outcomes. Unreachable paths marked. Notable/surprising behavior marked. Zero implementation details in output. If any item is missing, trace again.
999999. The code is the source of truth. If specs are inconsistent with the code, update the spec using an Opus 4.6 subagent.
9999999. Single sources of truth, no duplicated specs. Update existing specs rather than creating new ones.
99999999. When you learn something new about the project, update @AGENTS.md using a subagent but keep it brief and operational only β no status updates or progress notes.
999999999. Source comments explaining why behavior must be preserved (regulatory, compatibility, intentional) β capture rationale, strip implementation references. Stale comments are not spec.
9999999999. Document all configuration-driven paths, not just the currently active one.
99999999999. If you find inconsistencies in `specs/*` then use an Opus 4.6 subagent with 'ultrathink' to update the specs.
β contributed by Jake Cukjati Β· @Byte0fCode Β· @jackstine
HTML
91.8%
JavaScript
5.0%
Shell
3.2%
A comprehensive guide to running autonomous AI coding loops using Geoff Huntley's Ralph methodology. View as formatted guide below π
1,032
stars
55
commits
HTML
primary language
Mar 6, 2026
updated
December 2025 boiled Ralph's powerful yet dumb little face to the top of most AI-related timelines.
I try to pay attention to the crazy-smart insights @GeoffreyHuntley shares, but I can't say Ralph really clicked for me this summer. Now, all of the recent hubbub has made it hard to ignore.
@mattpocockuk and @ryancarson's overviews helped a lot - right until Geoff came in and said 'nah'.
Many folks seem to be getting good results with various shapes - but I wanted to read the tea leaves as closely as possible from the person who not only captured this approach but also has had the most ass-time in the seat putting it through its paces.
So I dug in to really RTFM on recent videos and Geoff's original post to try and untangle for myself what works best.
Below is the result - a (likely OCD-fueled) Ralph Playbook that organizes the miscellaneous details for putting this all into practice w/o hopefully neutering it in the process.
Digging into all of this has also brought to mind some possibly valuable additional enhancements to the core approach that aim to stay aligned with the guidelines that make Ralph work so well.
[!TIP] View as π Formatted Guide β
Hope this helps you out - @ClaytonFarr
A picture is worth a thousand tweets and an hour-long video. Geoff's overview here (sign up to his newsletter to see full article) really helped clarify the workflow details for moving from 1) idea β 2) individual JTBD-aligned specs β 3) comprehensive implementation plan β 4) Ralph work loops.

This diagram clarified for me that Ralph isn't just "a loop that codes." It's a funnel with 3 Phases, 2 Prompts, and 1 Loop.
specs/FILENAME.md for each topicPROMPT.md as needed)Same loop mechanism, different prompts for different objectives:
| Mode | When to use | Prompt focus |
|---|---|---|
| PLANNING | No plan exists, or plan is stale/wrong | Generate/update IMPLEMENTATION_PLAN.md only |
| BUILDING | Plan exists | Implement from plan, commit, update plan as side effect |
Prompt differences per mode:
Why use the loop for both modes?
Context loaded each iteration: PROMPT.md + AGENTS.md
PLANNING mode loop lifecycle:
specs/* and existing /srcIMPLEMENTATION_PLAN.md with prioritized tasksBUILDING mode loop lifecycle:
specs/* (requirements)IMPLEMENTATION_PLAN.md/src ("don't assume not implemented")IMPLEMENTATION_PLAN.md β mark task done, note discoveries/bugsAGENTS.md β if operational learnings| Term | Definition |
|---|---|
| Job to be Done (JTBD) | High-level user need or outcome |
| Topic of Concern | A distinct aspect/component within a JTBD |
| Spec | Requirements doc for one topic of concern (specs/FILENAME.md) |
| Task | Unit of work derived from comparing specs to code |
Relationships:
Example:
Topic Scope Test: "One Sentence Without 'And'"
This informs and drives everything else:
Creating the right signals & gates to steer Ralph's successful output is critical. You can steer from two directions:
PROMPT.md + AGENTS.md)AGENTS.md specifies actual commands to make backpressure project-specificRalph's effectiveness comes from how much you trust it do the right thing (eventually) and engender its ability to do so.
--dangerously-skip-permissions - asking for approval on every tool call would break the loop. This bypasses Claude's permission system entirely - so a sandbox becomes your only security boundary.git reset --hard reverts uncommitted changes; regenerate plan if trajectory goes wrongTo get the most out of Ralph, you need to get out of his way. Ralph should be doing all of the work, including decided which planned work to implement next and how to implement it. Your job is now to sit on the loop, not in it - to engineer the setup and environment that will allow Ralph to succeed.
Observe and course correct β especially early on, sit and watch. What patterns emerge? Where does Ralph go wrong? What signs does he need? The prompts you start with won't be the prompts you end with - they evolve through observed failure patterns.
Tune it like a guitar β instead of prescribing everything upfront, observe and adjust reactively. When Ralph fails a specific way, add a sign to help him next time.
But signs aren't just prompt text. They're anything Ralph can discover:
AGENTS.md - operational learnings about how to build/test[!TIP]
- try starting with nothing in
AGENTS.md(empty file; no best practices, etc.)- spot-test desired actions, find missteps (walkthrough example from Geoff)
- watch initial loops, see where gaps occur
- tune behavior only as needed, via AGENTS updates and/or code patterns (shared utilities, etc.)
And remember, the plan is disposable:
loop.sh acts in effect as an 'outer loop' where each loop = a single task (in separate sessions). When the task is completed, loop.sh kicks off a fresh session to select the next task, if any remaining tasks are available.
Geoff's initial minimal form of loop.sh script:
while :; do cat PROMPT.md | claude ; done
Note: The same approach can be used with other CLIs; e.g. amp, codex, opencode, etc.
What controls task continuation?
The continuation mechanism is elegantly simple:
PROMPT.md to claudeKey insight: The IMPLEMENTATION_PLAN.md file persists on disk between iterations and acts as shared state between otherwise isolated loop executions. Each iteration deterministically loads the same files (PROMPT.md + AGENTS.md + specs/*) and reads the current state from disk.
No sophisticated orchestration needed - just a dumb bash loop that keeps restarting the agent, and the agent figures out what to do next by reading the plan file each time.
Each task is prompted to keep doing its work against backpressure (tests, etc) until it passes - creating a pseudo inner 'loop' (in single session).
This inner loop is just internal self-correction / iterative reasoning within one long model response, powered by backpressure prompts, tool use, and subagents. It's not a loop in the programming sense.
A single task execution has no hard technical limit. Control relies on:
Ralph can go in circles, ignore instructions, or take wrong directions - this is expected and part of the tuning process. When Ralph "tests you" by failing in specific ways, you add guardrails to the prompt or adjust backpressure mechanisms. The nondeterminism is manageable through observation and iteration.
loop.sh ExampleWraps core loop with mode selection (plan/build), with max-iterations for max number of tasks to complete, and git push after each iteration.
This enhancement uses two saved prompt files:
PROMPT_plan.md - Planning mode (gap analysis, generates/updates plan)PROMPT_build.md - Building mode (implements from plan)#!/bin/bash
# Usage: ./loop.sh [plan|build] [max_iterations]
# Examples:
# ./loop.sh # Build mode, unlimited tasks
# ./loop.sh 20 # Build mode, max 20 tasks
# ./loop.sh build 20 # Build mode, max 20 tasks
# ./loop.sh plan # Plan mode, unlimited tasks
# ./loop.sh plan 5 # Plan mode, max 5 tasks
# Parse arguments
if [ "$1" = "plan" ]; then
# Plan mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "build" ]; then
# Explicit build mode (with optional max iterations)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=${2:-0}
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max tasks (bare number)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=$1
else
# Build mode, unlimited (no arguments or invalid input)
MODE="build"
PROMPT_FILE="PROMPT_build.md"
MAX_ITERATIONS=0
fi
ITERATION=0
CURRENT_BRANCH=$(git branch --show-current)
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: $MODE"
echo "Prompt: $PROMPT_FILE"
echo "Branch: $CURRENT_BRANCH"
[ $MAX_ITERATIONS -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations (number of tasks)"
echo "ββββββββββββββββββββββββββββββββββββββββ"
# Verify prompt file exists
if [ ! -f "$PROMPT_FILE" ]; then
echo "Error: $PROMPT_FILE not found"
exit 1
fi
while true; do
if [ $MAX_ITERATIONS -gt 0 ] && [ $ITERATION -ge $MAX_ITERATIONS ]; then
echo "Reached max iterations (number of tasks): $MAX_ITERATIONS"
break
fi
# Run Ralph iteration with selected prompt
# -p: Headless mode (non-interactive, reads from stdin)
# --dangerously-skip-permissions: Auto-approve all tool calls (YOLO mode)
# --output-format=stream-json: Structured output for logging/monitoring
# --model opus: Primary agent uses Opus for complex reasoning (task selection, prioritization)
# Can use 'sonnet' in build mode for speed if plan is clear and tasks well-defined
# --verbose: Detailed execution logging
cat "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
# Push changes after each iteration
git push origin "$CURRENT_BRANCH" || {
echo "Failed to push. Creating remote branch..."
git push -u origin "$CURRENT_BRANCH"
}
ITERATION=$((ITERATION + 1))
echo -e "\n\n======================== LOOP $ITERATION ========================\n"
done
Mode selection:
PROMPT_build.md for building (implementation)plan keyword β Uses PROMPT_plan.md for planning (gap analysis, plan generation)Max-iterations:
./loop.sh runs unlimited (manual stop with Ctrl+C)./loop.sh 20 runs max 20 iterations then stopsClaude CLI flags:
-p (headless mode): Enables non-interactive operation, reads prompt from stdin--dangerously-skip-permissions: Bypasses all permission prompts for fully automated runs--output-format=stream-json: Outputs structured JSON for logging/monitoring/visualization--model opus: Primary agent uses Opus for task selection, prioritization, and coordination (can use sonnet for speed if tasks are clear)--verbose: Provides detailed execution loggingAn alternative loop_streamed.sh that pipes Claude's raw JSON output through parse_stream.js for readable, color-coded terminal display showing tool calls, results, and execution stats.
Differences from base loop.sh:
-p "$FULL_PROMPT") instead of stdin pipe--include-partial-messages for real-time streamingparse_stream.js (Node.js, no dependencies)Files: loop_streamed.sh Β· parse_stream.js
β contributed by @terry-xyz Β· @blackrosesxyz
This repository is available under the MIT License.
Third-party screenshots and externally sourced images are excluded unless explicitly noted otherwise. See NOTICE for details.
project-root/
βββ loop.sh # Ralph loop script
βββ PROMPT_build.md # Build mode instructions
βββ PROMPT_plan.md # Plan mode instructions
βββ AGENTS.md # Operational guide loaded each iteration
βββ IMPLEMENTATION_PLAN.md # Prioritized task list (generated/updated by Ralph)
βββ specs/ # Requirement specs (one per JTBD topic)
β βββ [jtbd-topic-a].md
β βββ [jtbd-topic-b].md
βββ src/ # Application source code
βββ src/lib/ # Shared utilities & components
loop.shThe primary loop script that orchestrates Ralph iterations.
See Loop Mechanics section for detailed implementation examples and configuration options.
Setup: Make the script executable before first use:
chmod +x loop.sh
Core function: Continuously feeds prompt file to Claude, manages iteration limits, and pushes changes after each task completion.
The instruction set for each loop iteration. Swap between PLANNING and BUILDING versions as needed.
Prompt Structure:
| Section | Purpose |
|---|---|
| Phase 0 (0a, 0b, 0c) | Orient: study specs, source location, current plan |
| Phase 1-4 | Main instructions: task, validation, commit |
| 999... numbering | Guardrails/invariants (higher number = more critical) |
Key Language Patterns (Geoff's specific phrasing):
PROMPT_plan.md TemplateNotes:
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0c. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0d. For reference, the application source code is in `src/*`.
1. Study @IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `src/*` and compare it against `specs/*`. Use an Opus subagent to analyze findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented. Ultrathink. Consider searching for TODO, minimal implementations, placeholders, skipped/flaky tests, and inconsistent patterns. Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve [project-specific goal]. Consider missing elements and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
PROMPT_build.md TemplateNote: Current subagents names presume using Claude.
0a. Study `specs/*` with up to 500 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md.
0c. For reference, the application source code is in `src/*`.
1. Your task is to implement functionality per the specifications using parallel subagents. Follow @IMPLEMENTATION_PLAN.md and choose the most important item to address. Before making changes, search the codebase (don't assume not implemented) using Sonnet subagents. You may use up to 500 parallel Sonnet subagents for searches/reads and only 1 Sonnet subagent for build/tests. Use Opus subagents when complex reasoning is needed (debugging, architectural decisions).
2. After implementing functionality or resolving problems, run the tests for that unit of code that was improved. If functionality is missing then it's your job to add it as per the application specifications. Ultrathink.
3. When you discover issues, immediately update @IMPLEMENTATION_PLAN.md with your findings using a subagent. When resolved, update and remove the item.
4. When the tests pass, update @IMPLEMENTATION_PLAN.md, then `git add -A` then `git commit` with a message describing the changes. After the commit, `git push`.
99999. Important: When authoring documentation, capture the why β tests and implementation importance.
999999. Important: Single sources of truth, no migrations/adapters. If tests unrelated to your work fail, resolve them as part of the increment.
9999999. As soon as there are no build or test errors create a git tag. If there are no git tags start at 0.0.0 and increment patch by 1 for example 0.0.1 if 0.0.0 does not exist.
99999999. You may add extra logging if required to debug issues.
999999999. Keep @IMPLEMENTATION_PLAN.md current with learnings using a subagent β future work depends on this to avoid duplicating efforts. Update especially after finishing your turn.
9999999999. When you learn something new about how to run the application, update @AGENTS.md using a subagent but keep it brief. For example if you run commands multiple times before learning the correct command then that file should be updated.
99999999999. For any bugs you notice, resolve them or document them in @IMPLEMENTATION_PLAN.md using a subagent even if it is unrelated to the current piece of work.
999999999999. Implement functionality completely. Placeholders and stubs waste efforts and time redoing the same work.
9999999999999. When @IMPLEMENTATION_PLAN.md becomes large periodically clean out the items that are completed from the file using a subagent.
99999999999999. If you find inconsistencies in the specs/* then use an Opus 4.6 subagent with 'ultrathink' requested to update the specs.
999999999999999. IMPORTANT: Keep @AGENTS.md operational only β status updates and progress notes belong in `IMPLEMENTATION_PLAN.md`. A bloated AGENTS.md pollutes every future loop's context.
AGENTS.mdSingle, canonical "heart of the loop" - a concise, operational "how to run/build" guide.
Status, progress, and planning belong in IMPLEMENTATION_PLAN.md, not here.
Loopback / Immediate Self-Evaluation:
AGENTS.md should contain the project-specific commands that enable loopback - the ability for Ralph to immediately evaluate his work within the same loop. This includes:
The BUILDING prompt says "run tests" generically; AGENTS.md specifies the actual commands. This is how backpressure gets wired in per-project.
## Build & Run
Succinct rules for how to BUILD the project:
## Validation
Run these after implementing to get immediate feedback:
- Tests: `[test command]`
- Typecheck: `[typecheck command]`
- Lint: `[lint command]`
## Operational Notes
Succinct learnings about how to RUN the project:
...
### Codebase Patterns
...
IMPLEMENTATION_PLAN.mdPrioritized bullet-point list of tasks derived from gap analysis (specs vs code) - generated by Ralph.
The circularity is intentional: eventual consistency through iteration.
No pre-specified template - let Ralph/LLM dictate and manage format that works best for it.
specs/*One markdown file per topic of concern. These are the source of truth for what should be built.
No pre-specified template - let Ralph/LLM dictate and manage format that works best for it.
src/ and src/lib/Application source code and shared utilities/components.
Referenced in PROMPT.md templates for orientation steps.
I'm still determining the value/viability of these, but the opportunities sound promising:
During Phase 1 (Define Requirements), use Claude's built-in AskUserQuestionTool to systematically explore JTBD, topics of concern, edge cases, and acceptance criteria through structured interview before writing specs.
When to use: Minimal/vague initial requirements, need to clarify constraints, or multiple valid approaches exist.
Invoke: "Interview me using AskUserQuestion to understand [JTBD/topic/acceptance criteria/...]"
Claude will ask targeted questions to clarify requirements and ensure alignment before producing specs/*.md files.
Flow:
No code or prompt changes needed - this simply enhances Phase 1 using existing Claude Code capabilities.
Inspiration - Thariq's X post:
Geoff's Ralph implicitly connects specs β implementation β tests through emergent iteration. This enhancement would make that connection explicit by deriving test requirements during planning, creating a direct line from "what success looks like" to "what verifies it."
This enhancement connects acceptance criteria (in specs) directly to test requirements (in implementation plan), improving backpressure quality by:
| Principle | Maintained? | How |
|---|---|---|
| Monolithic operation | β Yes | One agent, one task, one loop at a time |
| Backpressure critical | β Yes | Tests are the mechanism, just derived explicitly now |
| Context efficiency | β Yes | Planning decides tests once vs building rediscovering |
| Deterministic setup | β Yes | Test requirements in plan (known state) not emergent |
| Let Ralph Ralph | β Yes | Ralph still prioritizes and chooses implementation approach |
| Plan is disposable | β Yes | Wrong test requirements? Regenerate plan |
| "Capture the why" | β Yes | Test intent documented in plan before implementation |
| No cheating | β Yes | Required tests prevent placeholder implementations |
The critical distinction:
Acceptance criteria (in specs) = Behavioral outcomes, observable results, what success looks like
Test requirements (in implementation plan) = Verification points derived from acceptance criteria
Implementation approach (up to Ralph) = Technical decisions about how to achieve it
The key: Specify WHAT to verify (outcomes), not HOW to implement (approach)
This maintains "Let Ralph Ralph" principle - Ralph decides implementation details while having clear success signals.
Phase 1: Requirements Definition
specs/*.md + Acceptance Criteria
β
Phase 2: Planning (derives test requirements)
IMPLEMENTATION_PLAN.md + Required Tests
β
Phase 3: Building (implements with tests)
Implementation + Tests β Backpressure
During the human + LLM conversation that produces specs:
Modify PROMPT_plan.md instruction 1 to include test derivation. Add after the first sentence:
For each task in the plan, derive required tests from acceptance criteria in specs - what specific outcomes need verification (behavior, performance, edge cases). Tests verify WHAT works, not HOW it's implemented. Include as part of task definition.
Modify PROMPT_build.md instructions:
Instruction 1: Add after "choose the most important item to address":
Tasks include required tests - implement tests as part of task scope.
Instruction 2: Replace "run the tests for that unit of code" with:
run all required tests specified in the task definition. All required tests must exist and pass before the task is considered complete.
Prepend new guardrail (in the 9s sequence):
999. Required tests derived from acceptance criteria must exist and pass before committing. Tests are part of implementation scope, not optional. Test-driven development approach: tests can be written first or alongside implementation.
Some acceptance criteria resist programmatic validation:
These require human-like judgment but need backpressure to meet acceptance criteria during building loop.
Solution: Add LLM-as-Judge tests as backpressure with binary pass/fail.
LLM reviews are non-deterministic (same artifact may receive different judgments across runs). This aligns with Ralph philosophy: "deterministically bad in an undeterministic world." The loop provides eventual consistency through iterationβreviews run until pass, accepting natural variance.
Create two files in src/lib/:
src/lib/
llm-review.ts # Core fixture - single function, clean API
llm-review.test.ts # Reference examples showing the pattern (Ralph learns from these)
llm-review.ts - Binary pass/fail API Ralph discovers:interface ReviewResult {
pass: boolean;
feedback?: string; // Only present when pass=false
}
function createReview(config: {
criteria: string; // What to evaluate (behavioral, observable)
artifact: string; // Text content OR screenshot path
intelligence?: "fast" | "smart"; // Optional, defaults to 'fast'
}): Promise<ReviewResult>;
Multimodal support: Both intelligence levels would use multimodal model (text + vision). Artifact type detection is automatic:
artifact: "Your content here" β Routes as text inputartifact: "./tmp/screenshot.png" β Routes as vision input (detects .png, .jpg, .jpeg extensions)Intelligence levels (quality of judgment, not capability type):
fast (default): Quick, cost-effective models for straightforward evaluations
smart: Higher-quality models for nuanced aesthetic/creative judgment
The fixture implementation selects appropriate models. (Examples are current options, not requirements.)
llm-review.test.ts - Shows Ralph how to use it (text and vision examples):import { createReview } from "@/lib/llm-review";
// Example 1: Text evaluation
test("welcome message tone", async () => {
const message = generateWelcomeMessage();
const result = await createReview({
criteria:
"Message uses warm, conversational tone appropriate for design professionals while clearly conveying value proposition",
artifact: message, // Text content
});
expect(result.pass).toBe(true);
});
// Example 2: Vision evaluation (screenshot path)
test("dashboard visual hierarchy", async () => {
await page.screenshot({ path: "./tmp/dashboard.png" });
const result = await createReview({
criteria:
"Layout demonstrates clear visual hierarchy with obvious primary action",
artifact: "./tmp/dashboard.png", // Screenshot path
});
expect(result.pass).toBe(true);
});
// Example 3: Smart intelligence for complex judgment
test("brand visual consistency", async () => {
await page.screenshot({ path: "./tmp/homepage.png" });
const result = await createReview({
criteria:
"Visual design maintains professional brand identity suitable for financial services while avoiding corporate sterility",
artifact: "./tmp/homepage.png",
intelligence: "smart", // Complex aesthetic judgment
});
expect(result.pass).toBe(true);
});
Ralph learns from these examples: Both text and screenshots work as artifacts. Choose based on what needs evaluation. The fixture handles the rest internally.
Future extensibility: Current design uses single artifact: string for simplicity. Can expand to artifact: string | string[] if clear patterns emerge requiring multiple artifacts (before/after comparisons, consistency across items, multi-perspective evaluation). Composite screenshots or concatenated text could handle most multi-item needs.
Planning Phase - Update PROMPT_plan.md:
After:
...Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
Insert this:
When deriving test requirements from acceptance criteria, identify whether verification requires programmatic validation (measurable, inspectable) or human-like judgment (perceptual quality, tone, aesthetics). Both types are equally valid backpressure mechanisms. For subjective criteria that resist programmatic validation, explore src/lib for non-deterministic evaluation patterns.
Building Phase - Update PROMPT_build.md:
Prepend new guardrail (in the 9s sequence):
9999. Create tests to verify implementation meets acceptance criteria and include both conventional tests (behavior, performance, correctness) and perceptual quality tests (for subjective criteria, see src/lib patterns).
Discovery, not documentation: Ralph learns LLM review patterns from llm-review.test.ts examples during src/lib exploration (Phase 0c). No AGENTS.md updates needed - the code examples are the documentation.
| Principle | Maintained? | How |
|---|---|---|
| Backpressure critical | β Yes | Extends backpressure to non-programmatic acceptance |
| Deterministic setup | β οΈ Partial | Criteria in plan (deterministic), evaluation non-deterministic but converges through iteration. Intentional tradeoff for subjective quality. |
| Context efficiency | β Yes | Fixture reused via src/lib, small test definitions |
| Let Ralph Ralph | β Yes | Ralph discovers pattern, chooses when to use, writes criteria |
| Plan is disposable | β Yes | Review requirements part of plan, regenerate if wrong |
| Simplicity wins | β Yes | Single function, binary result, no scoring complexity |
| Add signs for Ralph | β Yes | Light prompt additions, learning from code exploration |
The Critical Principle: Geoff's Ralph works from a single, disposable plan where Ralph picks "most important." To use branches with Ralph while maintaining this pattern, you must scope at plan creation, not at task selection.
Why this matters:
Solution: Add a plan-work mode to create a work-scoped IMPLEMENTATION_PLAN.md on the current branch. User creates work branch, then runs plan-work with a natural language description of the work focus. The LLM uses this description to scope the plan. Post planning, Ralph builds from this already-scoped plan with zero semantic filtering - just picks "most important" as always.
Terminology: "Work" is intentionally broad - it can describe features, topics of concern, refactoring efforts, infrastructure changes, bug fixes, or any coherent body of related changes. The work description you pass to plan-work is natural language for the LLM - it can be prose, not constrained by git branch naming rules.
1. Full Planning (on main branch)
./loop.sh plan
# Generate full IMPLEMENTATION_PLAN.md for entire project
2. Create Work Branch
User performs:
git checkout -b ralph/user-auth-oauth
# Create branch with whatever naming convention you prefer
# Suggestion: ralph/* prefix for work branches
3. Scoped Planning (on work branch)
./loop.sh plan-work "user authentication system with OAuth and session management"
# Pass natural language description - LLM uses this to scope the plan
# Creates focused IMPLEMENTATION_PLAN.md with only tasks for this work
4. Build from Plan (on work branch)
./loop.sh
# Ralph builds from scoped plan (no filtering needed)
# Picks most important task from already-scoped plan
5. PR Creation (when work complete)
User performs:
gh pr create --base main --head ralph/user-auth-oauth --fill
Extends the base enhanced loop script to add work branch support with scoped planning:
#!/bin/bash
set -euo pipefail
# Usage:
# ./loop.sh [plan|build] [max_iterations] # Plan/build on current branch
# ./loop.sh plan-work "work description" # Create scoped plan on current branch
# Examples:
# ./loop.sh # Build mode, unlimited
# ./loop.sh 20 # Build mode, max 20
# ./loop.sh build 20 # Build mode, max 20
# ./loop.sh plan 5 # Full planning, max 5
# ./loop.sh plan-work "user auth" # Scoped planning
# Parse arguments
MODE="build"
PROMPT_FILE="PROMPT_build.md"
if [ "$1" = "plan" ]; then
# Full planning mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "build" ]; then
# Explicit build mode (with optional max iterations)
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "plan-work" ]; then
# Scoped planning mode
if [ -z "$2" ]; then
echo "Error: plan-work requires a work description"
echo "Usage: ./loop.sh plan-work \"description of the work\""
exit 1
fi
MODE="plan-work"
WORK_DESCRIPTION="$2"
PROMPT_FILE="PROMPT_plan_work.md"
MAX_ITERATIONS=${3:-5} # Default 5 for work planning
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max iterations (bare number)
MAX_ITERATIONS=$1
else
# Build mode, unlimited
MAX_ITERATIONS=0
fi
ITERATION=0
CURRENT_BRANCH=$(git branch --show-current)
# Validate branch for plan-work mode
if [ "$MODE" = "plan-work" ]; then
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
echo "Error: plan-work should be run on a work branch, not main/master"
echo "Create a work branch first: git checkout -b ralph/your-work"
exit 1
fi
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: plan-work"
echo "Branch: $CURRENT_BRANCH"
echo "Work: $WORK_DESCRIPTION"
echo "Prompt: $PROMPT_FILE"
echo "Plan: Will create scoped IMPLEMENTATION_PLAN.md"
[ "$MAX_ITERATIONS" -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations"
echo "ββββββββββββββββββββββββββββββββββββββββ"
# Warn about uncommitted changes to IMPLEMENTATION_PLAN.md
if [ -f "IMPLEMENTATION_PLAN.md" ] && ! git diff --quiet IMPLEMENTATION_PLAN.md 2>/dev/null; then
echo "Warning: IMPLEMENTATION_PLAN.md has uncommitted changes that will be overwritten"
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
# Export work description for PROMPT_plan_work.md
export WORK_SCOPE="$WORK_DESCRIPTION"
else
# Normal plan/build mode
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Mode: $MODE"
echo "Branch: $CURRENT_BRANCH"
echo "Prompt: $PROMPT_FILE"
echo "Plan: IMPLEMENTATION_PLAN.md"
[ "$MAX_ITERATIONS" -gt 0 ] && echo "Max: $MAX_ITERATIONS iterations"
echo "ββββββββββββββββββββββββββββββββββββββββ"
fi
# Verify prompt file exists
if [ ! -f "$PROMPT_FILE" ]; then
echo "Error: $PROMPT_FILE not found"
exit 1
fi
# Main loop
while true; do
if [ "$MAX_ITERATIONS" -gt 0 ] && [ "$ITERATION" -ge "$MAX_ITERATIONS" ]; then
echo "Reached max iterations: $MAX_ITERATIONS"
if [ "$MODE" = "plan-work" ]; then
echo ""
echo "ββββββββββββββββββββββββββββββββββββββββ"
echo "Scoped plan created: $WORK_DESCRIPTION"
echo "To build, run:"
echo " ./loop.sh 20"
echo "ββββββββββββββββββββββββββββββββββββββββ"
fi
break
fi
# Run Ralph iteration with selected prompt
# -p: Headless mode (non-interactive, reads from stdin)
# --dangerously-skip-permissions: Auto-approve all tool calls (YOLO mode)
# --output-format=stream-json: Structured output for logging/monitoring
# --model opus: Primary agent uses Opus for complex reasoning (task selection, prioritization)
# Can use 'sonnet' for speed if plan is clear and tasks well-defined
# --verbose: Detailed execution logging
# For plan-work mode, substitute ${WORK_SCOPE} in prompt before piping
if [ "$MODE" = "plan-work" ]; then
envsubst < "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
else
cat "$PROMPT_FILE" | claude -p \
--dangerously-skip-permissions \
--output-format=stream-json \
--model opus \
--verbose
fi
# Push to current branch
CURRENT_BRANCH=$(git branch --show-current)
git push origin "$CURRENT_BRANCH" || {
echo "Failed to push. Creating remote branch..."
git push -u origin "$CURRENT_BRANCH"
}
ITERATION=$((ITERATION + 1))
echo -e "\n\n======================== LOOP $ITERATION ========================\n"
done
PROMPT_plan_work.md TemplateNote: Identical to PROMPT_plan.md but with scoping instructions and WORK_SCOPE env var substituted (automatically by the loop script).
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
0b. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0c. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0d. For reference, the application source code is in `src/*`.
1. You are creating a SCOPED implementation plan for work: "${WORK_SCOPE}". Study @IMPLEMENTATION_PLAN.md (if present; it may be incorrect) and use up to 500 Sonnet subagents to study existing source code in `src/*` and compare it against `specs/*`. Use an Opus subagent to analyze findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented. Ultrathink. Consider searching for TODO, minimal implementations, placeholders, skipped/flaky tests, and inconsistent patterns. Study @IMPLEMENTATION_PLAN.md to determine starting point for research and keep it up to date with items considered complete/incomplete using subagents.
IMPORTANT: This is SCOPED PLANNING for "${WORK_SCOPE}" only. Create a plan containing ONLY tasks directly related to this work scope. Be conservative - if uncertain whether a task belongs to this work, exclude it. The plan can be regenerated if too narrow. Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve the scoped work "${WORK_SCOPE}". Consider missing elements related to this work and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
| Principle | Maintained? | How |
|---|---|---|
| Monolithic operation | β Yes | Ralph still operates as single process within branch |
| One task per loop | β Yes | Unchanged |
| Fresh context | β Yes | Unchanged |
| Deterministic | β Yes | Scoping at plan creation (deterministic), not runtime (prob.) |
| Simple | β Yes | Optional enhancement, main workflow still works |
| Plan-driven | β Yes | One IMPLEMENTATION_PLAN.md per branch |
| Single source of truth | β Yes | One plan per branch - scoped plan replaces full plan on branch |
| Plan is disposable | β Yes | Regenerate scoped plan anytime: ./loop.sh plan-work "work description" |
| Markdown over JSON | β Yes | Still markdown plans |
| Let Ralph Ralph | β Yes | Ralph picks "most important" from already-scoped plan - no filter |
Geoff's suggested workflow already aligns planning with Jobs-to-be-Done β breaking JTBDs into topics of concern, which in turn become specs. I love this and I think there's an opportunity to lean further into the product benefits this approach affords by reframing topics of concern as activities.
Activities are verbs in a journey ("upload photo", "extract colors") rather than capabilities ("color extraction system"). They're naturally scoped by user intent.
Topics: "color extraction", "layout engine" β capability-oriented Activities: "upload photo", "see extracted colors", "arrange layout" β journey-oriented
Activities β and their constituent steps β sequence naturally into a user flow, creating a journey structure that makes gaps and dependencies visible. A User Story Map organizes activities as columns (the journey backbone) with capability depths as rows β the full space of what could be built:
UPLOAD β EXTRACT β ARRANGE β SHARE
basic auto manual export
bulk palette templates collab
batch AI themes auto-layout embed
Horizontal slices through the map become candidate releases. Not every activity needs new capability in every release β some cells stay empty, and that's fine if the slice is still coherent:
UPLOAD β EXTRACT β ARRANGE β SHARE
Release 1: basic auto export
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Release 2: palette manual
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Release 3: batch AI themes templates embed
The story map gives you structure for slicing. Jason Cohen's Simple, Lovable, Complete (SLC) gives you criteria for what makes a slice good:
Why SLC over MVP? MVPs optimize for learning at the customer's expense β "minimum" often means broken or frustrating. SLC flips this: learn in-market while delivering real value. If it succeeds, you have optionality. If it fails, you still treated users well.
Each slice can become a release with a clear value and identity:
UPLOAD β EXTRACT β ARRANGE β SHARE
Palette Picker: basic auto export
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Mood Board: palette manual
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Design Studio: batch AI themes templates embed
The concepts above β activities, story maps, SLC releases β are the thinking tools. How do we translate them into Ralph's workflow?
Default Ralph approach:
specs/*.mdIMPLEMENTATION_PLAN.mdThis works well for capability-focused work (features, refactors, infrastructure). But it doesn't naturally produce valuable (SLC) product releases - it produces "whatever the specs describe".
Activities β SLC Release approach:
To get SLC releases, we need to ground activities in audience context. Audience defines WHO has the JTBDs, which in turn informs WHAT activities matter and what "lovable" means.
Audience (who)
βββ has JTBDs (desired outcomes)
βββ fulfilled by Activities (means to achieve outcomes)
I. Requirements Phase (2 steps):
Still performed in LLM conversations with the human, similar to the default Ralph approach.
Define audience and their JTBDs β WHO are we building for and what OUTCOMES do they want?
AUDIENCE_JTBD.mdDefine activities β WHAT do users do to accomplish their JTBDs?
AUDIENCE_JTBD.mdspecs/*.md (one per activity)The discrete steps within activities are implicit and LLM can infer them during planning.
II. Planning Phase:
Performed in Ralph loop with updated planning prompt.
AUDIENCE_JTBD.md (who, desired outcomes)specs/* (what could be built)IMPLEMENTATION_PLAN.mdIII. Building Phase:
Performed in Ralph loop with standard building prompt.
Variant of PROMPT_plan.md that adds audience context and SLC-oriented slice recommendation.
Notes:
[project-specific goal] placeholder β the goal is implicit: recommend the most valuable next release for the audience.0a. Study @AUDIENCE_JTBD.md to understand who we're building for and their Jobs to Be Done.
0b. Study `specs/*` with up to 250 parallel Sonnet subagents to learn JTBD activities.
0c. Study @IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
0d. Study `src/lib/*` with up to 250 parallel Sonnet subagents to understand shared utilities & components.
0e. For reference, the application source code is in `src/*`.
1. Sequence the activities in `specs/*` into a user journey map for the audience in @AUDIENCE_JTBD.md. Consider how activities flow into each other and what dependencies exist.
2. Determine the next SLC release. Use up to 500 Sonnet subagents to compare `src/*` against `specs/*`. Use an Opus subagent to analyze findings. Ultrathink. Given what's already implemented recommend which activities (at what capability depths) form the most valuable next release. Prefer thin horizontal slices - the narrowest scope that still delivers real value. A good slice is Simple (narrow, achievable), Lovable (people want to use it), and Complete (fully accomplishes a meaningful job, not a broken preview).
3. Use an Opus subagent (ultrathink) to analyze and synthesize the findings, prioritize tasks, and create/update @IMPLEMENTATION_PLAN.md as a bullet point list sorted in priority of items yet to be implemented for the recommended SLC release. Begin plan with a summary of the recommended SLC release (what's included and why), then list prioritized tasks for that scope. Consider TODOs, placeholders, minimal implementations, skipped tests - but scoped to the release. Note discoveries outside scope as future work.
IMPORTANT: Plan only. Do NOT implement anything. Do NOT assume functionality is missing; confirm with code search first. Treat `src/lib` as the project's standard library for shared utilities and components. Prefer consolidated, idiomatic implementations there over ad-hoc copies.
ULTIMATE GOAL: We want to achieve the most valuable next release for the audience in @AUDIENCE_JTBD.md. Consider missing elements and plan accordingly. If an element is missing, search first to confirm it doesn't exist, then if needed author the specification at specs/FILENAME.md. If you create a new element then document the plan to implement it in @IMPLEMENTATION_PLAN.md using a subagent.
Why AUDIENCE_JTBD.md as a separate artifact:
Cardinalities:
A dedicated loop mode for generating and maintaining spec files with enforced quality rules. Ensures specs stay focused on behavioral outcomes (not implementation details), properly scoped topics ("one sentence without 'and'"), and consistent file naming conventions.
When to use: After writing or updating specs, run specs mode to enforce consistency and hygiene across all spec files.
What it does:
specs/* filesspecs/README.md<int>-filename.md (e.g., 01-range-optimization.md)Usage: Add a specs argument to your loop script that selects PROMPT_specs.md:
./loop.sh specs # Specs mode, unlimited iterations
./loop.sh specs 3 # Specs mode, max 3 iterations
To add specs mode to loop.sh: insert a new elif branch in the argument parsing:
# Parse arguments
if [ "$1" = "plan" ]; then
# Plan mode
MODE="plan"
PROMPT_FILE="PROMPT_plan.md"
MAX_ITERATIONS=${2:-0}
elif [ "$1" = "specs" ]; then # β add this block
# Specs mode
MODE="specs"
PROMPT_FILE="PROMPT_specs.md"
MAX_ITERATIONS=${2:-0}
elif [[ "$1" =~ ^[0-9]+$ ]]; then
# Build mode with max iterations
...
To add specs mode to loop_streamed.sh: same change β add the elif block in the same position. The rest of the script (streaming, parse_stream.js piping) works unchanged.
Files: PROMPT_specs.md
PROMPT_specs.md TemplateNotes:
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn the application specifications.
1. Identify Jobs to Be Done (JTBD) β Break individual JTBD into topic(s) of concern β Use subagents to load info from URLs into context β LLM understands JTBD topic of concern: subagent writes specs/FILENAME.md for each topic.
## RULES (don't apply to `specs/README.md`)
999. NEVER add code blocks or suggest how a variable should be named. This will be decided by Ralph.
9999.
- Acceptance criteria (in specs) = Behavioral outcomes, observable results
for example:
β "Extracts 5-10 dominant colors from any uploaded image"
β "Processes images <5MB in <100ms"
β "Handles edge cases: grayscale, single-color, transparent backgrounds"
- Test requirements (in plan) = Verification points derived from acceptance criteria
for example:
β "Required tests: Extract 5-10 colors, Performance <100ms"
- Implementation approach (up to Ralph) = Technical decisions
example TO AVOID:
β "Use K-means clustering with 3 iterations"
99999. Topic Scope Test: "One Sentence Without 'And'"
Can you describe the topic of concern in one sentence without conjoining unrelated capabilities?
example to follow:
β "The color extraction system analyzes images to identify dominant colors"
example to avoid:
β "The user system handles authentication, profiles, and billing" β 3 topics
If you need "and" to describe what it does, it's probably multiple topics
99999999. The key: Specify WHAT to verify (outcomes), not HOW to implement (approach). This maintains "Let Ralph Ralph" principle - Ralph decides implementation details while having clear success signals.
99999999999. Apply all rules to all existing files with up to 100 parallel Sonnet subagents in @specs (except README.md) and create new files if determined its needed based on `specs/README.md`. The names of the files should follow this name convention: <int>-filename.md, for example 01-range-optimization.md, 02-adaptive-behavior.md etc.
β contributed by @terry-xyz Β· @blackrosesxyz
It's easy to start working with specs in Greenfield, but when you're working in Brownfield, you have to take another approach. That's why you need to reverse engineer the implementations of the code back into specs to begin using the Ralph playbook.
When to use: You inherited or joined a codebase with no specs. You want to use Ralph on a project that wasn't built with Ralph. You need to add features to an existing brownfield project.
Invoke: "Reverse-engineer specs for [topic/area] using PROMPT_reverse_engineer_specs.md"
Flow:
PROMPT_reverse_engineer_specs.md βspecs/ βYou can use an agent orchestration pattern where the sub-agent is the reverse engineer and the orchestrator knows about the Topic of Concern Philosophy:
No modifications to existing prompt files needed β this is purely additive. The generated specs are the same format Ralph already consumes in planning and building phases.
| Principle | Maintained? | How |
|---|---|---|
| Deterministic setup | β Yes | Specs are written artifacts (known state), not ad-hoc context, contains all flaws in code. |
| Context efficiency | β οΈ Partial | Must be adopted throughout your entire team culture |
| Capture the why | β οΈ Partial | Not all implemented code contains the why behind things, only captures comments if they express the why intention. |
| Let Ralph Ralph | β Yes | Topics of concern are still chosen by Ralph. |
| Plan is disposable | β Yes | Specs provide stable baseline; plans regenerate against documented reality |
| Simplicity wins | β Yes | Provides a Hawkeye view of your entire specifications. |
PROMPT_reverse_engineer_specs.md TemplateNotes:
Files: PROMPT_reverse_engineer_specs.md
0a. Study `specs/*` with up to 250 parallel Sonnet subagents to learn existing specifications.
0b. Study `src/*` to understand the codebase. Use up to 500 parallel Sonnet subagents for reads/searches. Treat `src/lib` as the project's standard library for shared utilities and components.
1. For each topic assigned (or discovered), reverse-engineer the source code and produce a specification in `specs/`. Use Opus subagents for complex tracing. Ultrathink. Before writing a spec, search to confirm one doesn't already exist for that topic.
2. One topic per spec. Must pass the "one sentence without 'and'" test. Split if "and" joins unrelated capabilities.
3. **Two-phase process:** Phase 1 (Investigation) β trace every entry point, branch, code path to terminal. Map data flow, side effects, state mutations, error handling, concurrency, config-driven paths, implicit behavior. Phase 2 (Output) β zero implementation details. No function/class/variable names, file paths, library/framework references. A different team on a different stack must be able to reimplement from the spec alone.
4. **Document reality, not intent.** Bugs are features. Never add behaviors the code doesn't implement. Never suggest improvements. If a source comment contradicts the code, document the code's behavior and ignore the comment.
5. **Scope boundaries:** When tracing leaves the topic, stop. Document what crosses the boundary (sent/received) only. Test: "Could this change without changing my topic's outcomes?" If yes, it's across the boundary.
6. **Shared behavior:** Inline fully in every spec (self-contained). Note shared topics for cross-spec tracking. Shared behavior also gets its own canonical spec.
7. **Spec format:** Markdown in `specs/`. Each spec includes: topic statement, scope (in-scope and boundaries), data contracts, behaviors (in execution order), and state transitions. Mark notable/surprising behavior, unreachable paths, and shared cross-topic behavior inline. Capture rationale from source comments (strip implementation references). File naming: `specs/NN-kebab-case.md` (e.g., `01-session-management.md`).
8. When specs are complete and validated, `git add -A` then `git commit` with a message describing which specs were added/updated. After the commit, `git push`.
99999. **Exhaustive checklist before finalizing:** Every entry point documented. Every branch traced to terminal. Every data contract. Every side effect in execution order. Every error path (caught/propagated/ignored). Every config-driven path. Concurrency outcomes. Unreachable paths marked. Notable/surprising behavior marked. Zero implementation details in output. If any item is missing, trace again.
999999. The code is the source of truth. If specs are inconsistent with the code, update the spec using an Opus 4.6 subagent.
9999999. Single sources of truth, no duplicated specs. Update existing specs rather than creating new ones.
99999999. When you learn something new about the project, update @AGENTS.md using a subagent but keep it brief and operational only β no status updates or progress notes.
999999999. Source comments explaining why behavior must be preserved (regulatory, compatibility, intentional) β capture rationale, strip implementation references. Stale comments are not spec.
9999999999. Document all configuration-driven paths, not just the currently active one.
99999999999. If you find inconsistencies in `specs/*` then use an Opus 4.6 subagent with 'ultrathink' to update the specs.
β contributed by Jake Cukjati Β· @Byte0fCode Β· @jackstine
HTML
91.8%
JavaScript
5.0%
Shell
3.2%