factory-ralph-loop
Iterative task execution using the Ralph Loop pattern (named after Ralph Wiggum). Use when you need to repeatedly run an agent until a condition is met—fixing all lint errors, passing all tests, or exhausting PRD tasks. The filesystem serves as memory between iterations.
Best use case
factory-ralph-loop is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Iterative task execution using the Ralph Loop pattern (named after Ralph Wiggum). Use when you need to repeatedly run an agent until a condition is met—fixing all lint errors, passing all tests, or exhausting PRD tasks. The filesystem serves as memory between iterations.
Teams using factory-ralph-loop should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/factory-ralph-loop/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How factory-ralph-loop Compares
| Feature / Agent | factory-ralph-loop | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Iterative task execution using the Ralph Loop pattern (named after Ralph Wiggum). Use when you need to repeatedly run an agent until a condition is met—fixing all lint errors, passing all tests, or exhausting PRD tasks. The filesystem serves as memory between iterations.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Ralph Loop Pattern
The Ralph Loop (named after Ralph Wiggum) is an agentic pattern where you run an AI agent in a continuous loop until a task is complete. Each iteration starts relatively fresh, with the filesystem serving as persistent memory.
## Core Characteristics
1. **Same systemPrompt repeated** — The agent receives consistent instructions each iteration
2. **Filesystem as memory** — Code changes persist on disk between iterations
3. **Fresh context** — Each iteration reduces context pollution vs. single long conversation
4. **Exit condition** — Loop ends when tests pass, lint is clean, or work is exhausted
5. **Simple orchestrator** — Just `while (!done) { run agent }`
## Basic Structure
```typescript
const maxIterations = 10;
let iteration = 0;
let done = false;
while (!done && iteration < maxIterations) {
iteration++;
factory.observe.log("info", `Iteration ${iteration}`, { maxIterations });
const result = await factory.spawn({
agent: "worker",
systemPrompt: "You are fixing issues iteratively",
prompt: "Fix the next issue",
model: "anthropic/claude-sonnet-4-6",
step: iteration,
});
// Check exit condition
done = result.exitCode === 0 && result.text.includes("all clean");
if (result.exitCode !== 0) {
factory.observe.log("error", "Agent failed", { iteration, error: result.errorMessage });
break;
}
}
```
## Pattern 1: Fix All Lint Errors
Repeatedly run an agent until lint is clean:
```typescript
import { spawnSync } from "node:child_process";
const maxIterations = 20;
let iteration = 0;
while (iteration < maxIterations) {
iteration++;
const lintResult = spawnSync("npm", ["run", "lint"], {
cwd: process.cwd(),
encoding: "utf-8",
});
if (lintResult.status === 0) {
factory.observe.log("info", "Lint clean!", { iterations: iteration });
break;
}
factory.observe.log("info", `Iteration ${iteration}`, {
exitCode: lintResult.status,
errorCount: (lintResult.stdout.match(/error/gi) || []).length,
});
const result = await factory.spawn({
agent: "linter",
systemPrompt: `You fix lint errors iteratively.
Run 'npm run lint' to see current errors.
Fix one or more errors, focusing on the most common patterns.
Make minimal, focused changes.`,
prompt: `Fix lint errors. Current output:\n\n${lintResult.stdout}\n${lintResult.stderr}`,
model: "mistral/devstral-2512",
step: iteration,
});
if (result.exitCode !== 0) {
factory.observe.log("error", "Agent failed", { iteration });
break;
}
}
```
## Pattern 2: With Progress Tracking
Accumulate state across iterations to show progress:
```typescript
import { spawnSync } from "node:child_process";
interface ProgressState {
fixedIssues: string[];
lastErrorCount: number;
stagnantIterations: number;
}
const maxIterations = 20;
let iteration = 0;
const progress: ProgressState = {
fixedIssues: [],
lastErrorCount: Infinity,
stagnantIterations: 0,
};
while (iteration < maxIterations) {
iteration++;
const lintResult = spawnSync("npm", ["run", "lint"], {
cwd: process.cwd(),
encoding: "utf-8",
});
const errorCount = (lintResult.stdout.match(/error/gi) || []).length;
if (lintResult.status === 0) {
factory.observe.log("info", "All issues fixed!", {
iterations: iteration,
fixedIssues: progress.fixedIssues,
});
break;
}
// Track progress
if (errorCount >= progress.lastErrorCount) {
progress.stagnantIterations++;
} else {
progress.stagnantIterations = 0;
}
// Exit if stagnant
if (progress.stagnantIterations >= 3) {
factory.observe.log("warning", "No progress for 3 iterations", { errorCount });
break;
}
factory.observe.log("info", `Iteration ${iteration}`, {
errorCount,
lastErrorCount: progress.lastErrorCount,
fixed: progress.fixedIssues.length,
});
progress.lastErrorCount = errorCount;
const result = await factory.spawn({
agent: "fixer",
systemPrompt: `You fix lint errors iteratively.
Track your progress and avoid repeating unsuccessful approaches.
Previous fixes: ${progress.fixedIssues.join(", ") || "none yet"}
Error count: ${errorCount} (was ${progress.lastErrorCount === Infinity ? "unknown" : progress.lastErrorCount})`,
prompt: `Fix lint errors:\n\n${lintResult.stdout}\n${lintResult.stderr}`,
model: "anthropic/claude-sonnet-4-6",
step: iteration,
});
if (result.exitCode === 0) {
const fixMatch = result.text.match(/fixed?:?\s*(.+)/i);
if (fixMatch) {
progress.fixedIssues.push(fixMatch[1]);
}
}
}
```
## Pattern 3: Loop Until Tests Pass
Run agent repeatedly until test suite passes:
```typescript
import { spawnSync } from "node:child_process";
const testCommand = "npm test";
const [cmd, ...args] = testCommand.split(" ");
const maxIterations = 10;
let iteration = 0;
while (iteration < maxIterations) {
iteration++;
const testResult = spawnSync(cmd, args, {
cwd: process.cwd(),
encoding: "utf-8",
timeout: 60000,
});
if (testResult.status === 0) {
factory.observe.log("info", "Tests passing!", { iterations: iteration });
break;
}
factory.observe.log("info", `Iteration ${iteration}`, {
exitCode: testResult.status,
timeout: testResult.signal === "SIGTERM",
});
const failureOutput = [testResult.stdout, testResult.stderr]
.filter(Boolean)
.join("\n")
.slice(-5000); // Last 5KB to avoid huge prompt payloads
const result = await factory.spawn({
agent: "test-fixer",
systemPrompt: `You fix failing tests iteratively.
Analyze test output, identify the root cause, and make minimal fixes.
Run the tests again to verify your changes.
Focus on one failure at a time if there are multiple.`,
prompt: `Fix failing tests. Output from '${testCommand}':\n\n${failureOutput}`,
model: "anthropic/claude-opus-4-6",
step: iteration,
});
if (result.exitCode !== 0) {
factory.observe.log("error", "Agent failed", { iteration });
break;
}
}
```
## Pattern 4: Exhaustive PRD Implementation
Work through Product Requirements Document tasks until all are complete:
```typescript
import fs from "node:fs";
interface PRDTask {
id: string;
description: string;
completed: boolean;
}
const prdPath = "./PRD.md";
const tasksPath = "./tasks.json";
const maxIterations = 50;
// Load or initialize tasks
let tasks: PRDTask[];
if (fs.existsSync(tasksPath)) {
tasks = JSON.parse(fs.readFileSync(tasksPath, "utf-8"));
} else {
const prdContent = fs.readFileSync(prdPath, "utf-8");
tasks = parsePRD(prdContent);
fs.writeFileSync(tasksPath, JSON.stringify(tasks, null, 2));
}
let iteration = 0;
while (iteration < maxIterations) {
const nextTask = tasks.find(t => !t.completed);
if (!nextTask) {
factory.observe.log("info", "All tasks completed!", { iterations: iteration });
break;
}
iteration++;
factory.observe.log("info", `Iteration ${iteration}: ${nextTask.id}`, {
remaining: tasks.filter(t => !t.completed).length,
});
const result = await factory.spawn({
agent: "implementer",
systemPrompt: `You implement PRD tasks iteratively.
Read the PRD at ${prdPath}.
Complete tasks one at a time.
Mark tasks complete by updating ${tasksPath}.`,
prompt: `Implement: ${nextTask.id} - ${nextTask.description}\n\nCompleted so far:\n${
tasks.filter(t => t.completed).map(t => `+ ${t.id}`).join("\n")
}`,
model: "openai-codex/gpt-5.3-codex",
step: iteration,
});
if (result.exitCode !== 0) {
factory.observe.log("error", "Agent failed", { iteration, task: nextTask.id });
break;
}
// Reload tasks (agent may have updated them)
if (fs.existsSync(tasksPath)) {
tasks = JSON.parse(fs.readFileSync(tasksPath, "utf-8"));
}
}
function parsePRD(content: string): PRDTask[] {
const matches = content.matchAll(/^[-*]\s*\[\s*\]\s*(.+)$/gm);
const tasks: PRDTask[] = [];
let id = 1;
for (const match of matches) {
tasks.push({
id: `TASK-${id++}`,
description: match[1].trim(),
completed: false,
});
}
return tasks;
}
```
## Pattern 5: Combined Safety Checks
Comprehensive safety and exit logic:
```typescript
import { spawnSync } from "node:child_process";
const maxIterations = 20;
const maxStagnantIterations = 3;
const maxFailedIterations = 2;
const checkCommand = "npm run lint";
let iteration = 0;
let stagnantCount = 0;
let failedCount = 0;
let lastCheckOutput = "";
while (iteration < maxIterations) {
iteration++;
// Periodic check
const [cmd, ...args] = checkCommand.split(" ");
const checkResult = spawnSync(cmd, args, {
cwd: process.cwd(),
encoding: "utf-8",
});
if (checkResult.status === 0) {
factory.observe.log("info", "Check passed!", { iterations: iteration });
break;
}
// Track stagnation
const currentOutput = checkResult.stdout + checkResult.stderr;
if (currentOutput === lastCheckOutput) {
stagnantCount++;
factory.observe.log("warning", "No change detected", { stagnantCount });
} else {
stagnantCount = 0;
}
lastCheckOutput = currentOutput;
if (stagnantCount >= maxStagnantIterations) {
factory.observe.log("error", "Stagnant iterations exceeded", { stagnantCount });
break;
}
factory.observe.log("info", `Iteration ${iteration}`, {
stagnantCount,
failedCount,
max: maxIterations,
});
const result = await factory.spawn({
agent: "worker",
systemPrompt: "You are fixing issues iteratively",
prompt: "Continue fixing issues",
model: "anthropic/claude-sonnet-4-6",
step: iteration,
});
if (result.exitCode !== 0) {
failedCount++;
factory.observe.log("error", "Agent failed", { iteration, failedCount });
if (failedCount >= maxFailedIterations) {
factory.observe.log("error", "Failed iterations exceeded", { failedCount });
break;
}
} else {
failedCount = 0;
}
}
```
## Best Practices
### 1. **Set max iterations**
Always have an upper bound to prevent infinite loops:
```typescript
const maxIterations = 20; // Sensible default
```
### 2. **Detect stagnation**
Track if the agent is making progress:
```typescript
if (currentState === lastState) {
stagnantCount++;
if (stagnantCount >= 3) break;
}
```
### 3. **Use bash exit conditions**
Shell out to authoritative checks (tests, lint, build):
```typescript
const result = spawnSync("npm", ["test"], { encoding: "utf-8" });
if (result.status === 0) break;
```
### 4. **Provide context to agent**
Include iteration number, progress, previous attempts:
```typescript
prompt: `Iteration ${iteration}/${maxIterations}
Fixed so far: ${fixed.join(", ")}
Current errors: ${errorCount}
...`
```
### 5. **Log everything**
Observability is critical for debugging loops:
```typescript
factory.observe.log("info", "Loop state", {
iteration,
errorCount,
stagnantCount,
lastChange
});
```
### 6. **Limit context size**
Truncate large outputs to avoid prompt bloat:
```typescript
const recentOutput = fullOutput.slice(-5000); // Last 5KB
```
### 7. **Allow early exit**
If the goal is achieved, return immediately:
```typescript
if (testsPassing) break;
```
## When to Use Ralph Loop
Good for:
- Fixing lint/type errors iteratively
- Making tests pass one by one
- Implementing PRD tasks sequentially
- Refactoring with incremental validation
- Code generation with iterative refinement
Not ideal for:
- Tasks requiring deep context across iterations
- Complex multi-step reasoning within a single problem
- When the agent needs to remember detailed discussions
- Parallel work (use `Promise.all` with `factory.spawn` instead)
## Advanced: Nested Loops
You can nest Ralph Loops for hierarchical work:
```typescript
const modules = ["src/auth", "src/api", "src/db"];
for (const module of modules) {
factory.observe.log("info", `Processing module: ${module}`);
let iteration = 0;
while (iteration < 10) {
iteration++;
const result = await factory.spawn({
agent: "module-fixer",
systemPrompt: `Fix issues in ${module}`,
prompt: "Run checks and fix issues",
model: "mistral/devstral-2512",
step: iteration,
});
const check = spawnSync("npm", ["run", "lint", module], {
cwd: process.cwd(),
encoding: "utf-8",
});
if (check.status === 0) break;
}
}
```
## Summary
The Ralph Loop is a simple but powerful pattern:
- **While loop** around `await factory.spawn()`
- **Filesystem persistence** between iterations
- **Bash exit conditions** for authoritative checks
- **Progress tracking** to detect stagnation
- **Max iterations** for safety
It works because the agent sees fresh context each iteration, making progress incrementally while the filesystem accumulates changes. Perfect for iterative tasks where "run it again" is a valid strategy.Related Skills
ai-factory
Set up Claude Code context for a project. Analyzes tech stack, installs relevant skills from skills.sh, generates custom skills, and configures MCP servers. Use when starting new project, setting up AI context, or asking "set up project", "configure AI", "what skills do I need".
ai-factory.fix
Fix a specific bug or problem in the codebase. Analyzes code to find and fix issues without creating plans. Use when user reports a bug, error, or something not working. Always suggests test coverage and adds logging.
ai-ad-code-factory
No description provided.
agent-factory
Claude Code agent generation system that creates custom agents and sub-agents with enhanced YAML frontmatter, tool access patterns, and MCP integration support following proven production patterns
acc-create-factory
Generates DDD Factory for PHP 8.5. Creates factories for complex domain object instantiation with validation and encapsulated creation logic. Includes unit tests.
ralph
Agent-agnostic autonomous loop creator. Use when asked to 'use ralph', 'ralph this', 'reverse ralph', 'decompose a feature', or '/ralph decompose'. Forward mode implements features end-to-end; decompose mode breaks existing features into atomic user stories for reimplementation.
prompt-factory
World-class prompt powerhouse that generates production-ready mega-prompts for any role, industry, and task through intelligent 7-question flow, 69 comprehensive presets across 15 professional domains (technical, business, creative, legal, finance, HR, design, customer, executive, manufacturing, R&D, regulatory, specialized-technical, research, creative-media), multiple output formats (XML/Claude/ChatGPT/Gemini), quality validation gates, and contextual best practices from OpenAI/Anthropic/Google. Supports both core and advanced modes with testing scenarios and prompt variations.
Async Feedback Loop
Enables mid-stream course correction by monitoring a FEEDBACK.md file for user interventions. Allows the agent to incorporate new instructions without restarting the task.
ai-orchestration-feedback-loop
Multi-AI engineering loop orchestrating Claude, Codex, and Gemini for comprehensive validation. USE WHEN (1) mission-critical features requiring multi-perspective validation, (2) complex architectural decisions needing diverse AI viewpoints, (3) security-sensitive code requiring deep analysis, (4) user explicitly requests multi-AI review or triple-AI loop. DO NOT USE for simple features or single-file changes. MODES - Triple-AI (full coverage), Dual-AI Codex-Claude (security/logic), Dual-AI Gemini-Claude (UX/creativity).
bgo
Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.
moai-lang-r
R 4.4+ best practices with testthat 3.2, lintr 3.2, and data analysis patterns.
moai-lang-python
Python 3.13+ development specialist covering FastAPI, Django, async patterns, data science, testing with pytest, and modern Python features. Use when developing Python APIs, web applications, data pipelines, or writing tests.