Skip to content

Commit ef3c1e5

Browse files
authored
Merge pull request #696 from tonybaloney/cookbook/ralph-loop-recipe
Add RALPH-loop recipes to Copilot SDK cookbook
2 parents 3efeef3 + 717c012 commit ef3c1e5

10 files changed

Lines changed: 1405 additions & 2 deletions

File tree

cookbook/copilot-sdk/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
66

77
### .NET (C#)
88

9+
- [Ralph Loop](dotnet/ralph-loop.md): Build autonomous AI coding loops with fresh context per iteration, planning/building modes, and backpressure.
910
- [Error Handling](dotnet/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
1011
- [Multiple Sessions](dotnet/multiple-sessions.md): Manage multiple independent conversations simultaneously.
1112
- [Managing Local Files](dotnet/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -14,6 +15,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
1415

1516
### Node.js / TypeScript
1617

18+
- [Ralph Loop](nodejs/ralph-loop.md): Build autonomous AI coding loops with fresh context per iteration, planning/building modes, and backpressure.
1719
- [Error Handling](nodejs/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
1820
- [Multiple Sessions](nodejs/multiple-sessions.md): Manage multiple independent conversations simultaneously.
1921
- [Managing Local Files](nodejs/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -22,6 +24,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
2224

2325
### Python
2426

27+
- [Ralph Loop](python/ralph-loop.md): Build autonomous AI coding loops with fresh context per iteration, planning/building modes, and backpressure.
2528
- [Error Handling](python/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
2629
- [Multiple Sessions](python/multiple-sessions.md): Manage multiple independent conversations simultaneously.
2730
- [Managing Local Files](python/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -30,6 +33,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
3033

3134
### Go
3235

36+
- [Ralph Loop](go/ralph-loop.md): Build autonomous AI coding loops with fresh context per iteration, planning/building modes, and backpressure.
3337
- [Error Handling](go/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
3438
- [Multiple Sessions](go/multiple-sessions.md): Manage multiple independent conversations simultaneously.
3539
- [Managing Local Files](go/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -83,4 +87,4 @@ go run <filename>.go
8387

8488
## Status
8589

86-
Cookbook structure is complete with 4 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples.
90+
Cookbook structure is complete with 6 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples.
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Ralph Loop: Autonomous AI Task Loops
2+
3+
Build autonomous coding loops where an AI agent picks tasks, implements them, validates against backpressure (tests, builds), commits, and repeats — each iteration in a fresh context window.
4+
5+
> **Runnable example:** [recipe/ralph-loop.cs](recipe/ralph-loop.cs)
6+
>
7+
> ```bash
8+
> cd dotnet
9+
> dotnet run recipe/ralph-loop.cs
10+
> ```
11+
12+
## What is a Ralph Loop?
13+
14+
A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflow where an AI agent iterates through tasks in isolated context windows. The key insight: **state lives on disk, not in the model's context**. Each iteration starts fresh, reads the current state from files, does one task, writes results back to disk, and exits.
15+
16+
```
17+
┌─────────────────────────────────────────────────┐
18+
│ loop.sh │
19+
│ while true: │
20+
│ ┌─────────────────────────────────────────┐ │
21+
│ │ Fresh session (isolated context) │ │
22+
│ │ │ │
23+
│ │ 1. Read PROMPT.md + AGENTS.md │ │
24+
│ │ 2. Study specs/* and code │ │
25+
│ │ 3. Pick next task from plan │ │
26+
│ │ 4. Implement + run tests │ │
27+
│ │ 5. Update plan, commit, exit │ │
28+
│ └─────────────────────────────────────────┘ │
29+
│ ↻ next iteration (fresh context) │
30+
└─────────────────────────────────────────────────┘
31+
```
32+
33+
**Core principles:**
34+
35+
- **Fresh context per iteration**: Each loop creates a new session — no context accumulation, always in the "smart zone"
36+
- **Disk as shared state**: `IMPLEMENTATION_PLAN.md` persists between iterations and acts as the coordination mechanism
37+
- **Backpressure steers quality**: Tests, builds, and lints reject bad work — the agent must fix issues before committing
38+
- **Two modes**: PLANNING (gap analysis → generate plan) and BUILDING (implement from plan)
39+
40+
## Simple Version
41+
42+
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
43+
44+
```csharp
45+
using GitHub.Copilot.SDK;
46+
47+
var client = new CopilotClient();
48+
await client.StartAsync();
49+
50+
try
51+
{
52+
var prompt = await File.ReadAllTextAsync("PROMPT.md");
53+
var maxIterations = 50;
54+
55+
for (var i = 1; i <= maxIterations; i++)
56+
{
57+
Console.WriteLine($"\n=== Iteration {i}/{maxIterations} ===");
58+
59+
// Fresh session each iteration — context isolation is the point
60+
var session = await client.CreateSessionAsync(
61+
new SessionConfig { Model = "gpt-5.1-codex-mini" });
62+
try
63+
{
64+
var done = new TaskCompletionSource<string>();
65+
session.On(evt =>
66+
{
67+
if (evt is AssistantMessageEvent msg)
68+
done.TrySetResult(msg.Data.Content);
69+
});
70+
71+
await session.SendAsync(new MessageOptions { Prompt = prompt });
72+
await done.Task;
73+
}
74+
finally
75+
{
76+
await session.DisposeAsync();
77+
}
78+
79+
Console.WriteLine($"Iteration {i} complete.");
80+
}
81+
}
82+
finally
83+
{
84+
await client.StopAsync();
85+
}
86+
```
87+
88+
This is all you need to get started. The prompt file tells the agent what to do; the agent reads project files, does work, commits, and exits. The loop restarts with a clean slate.
89+
90+
## Ideal Version
91+
92+
The full Ralph pattern with planning and building modes, matching the [Ralph Playbook](https://github.com/ClaytonFarr/ralph-playbook) architecture:
93+
94+
```csharp
95+
using GitHub.Copilot.SDK;
96+
97+
// Parse args: dotnet run [plan] [max_iterations]
98+
var mode = args.Contains("plan") ? "plan" : "build";
99+
var maxArg = args.FirstOrDefault(a => int.TryParse(a, out _));
100+
var maxIterations = maxArg != null ? int.Parse(maxArg) : 50;
101+
var promptFile = mode == "plan" ? "PROMPT_plan.md" : "PROMPT_build.md";
102+
103+
var client = new CopilotClient();
104+
await client.StartAsync();
105+
106+
Console.WriteLine(new string('', 40));
107+
Console.WriteLine($"Mode: {mode}");
108+
Console.WriteLine($"Prompt: {promptFile}");
109+
Console.WriteLine($"Max: {maxIterations} iterations");
110+
Console.WriteLine(new string('', 40));
111+
112+
try
113+
{
114+
var prompt = await File.ReadAllTextAsync(promptFile);
115+
116+
for (var i = 1; i <= maxIterations; i++)
117+
{
118+
Console.WriteLine($"\n=== Iteration {i}/{maxIterations} ===");
119+
120+
// Fresh session — each task gets full context budget
121+
var session = await client.CreateSessionAsync(
122+
new SessionConfig
123+
{
124+
Model = "gpt-5.1-codex-mini",
125+
// Pin the agent to the project directory
126+
WorkingDirectory = Environment.CurrentDirectory,
127+
// Auto-approve tool calls for unattended operation
128+
OnPermissionRequest = (_, _) => Task.FromResult(
129+
new PermissionRequestResult { Kind = "approved" }),
130+
});
131+
try
132+
{
133+
var done = new TaskCompletionSource<string>();
134+
session.On(evt =>
135+
{
136+
// Log tool usage for visibility
137+
if (evt is ToolExecutionStartEvent toolStart)
138+
Console.WriteLine($" ⚙ {toolStart.Data.ToolName}");
139+
else if (evt is AssistantMessageEvent msg)
140+
done.TrySetResult(msg.Data.Content);
141+
});
142+
143+
await session.SendAsync(new MessageOptions { Prompt = prompt });
144+
await done.Task;
145+
}
146+
finally
147+
{
148+
await session.DisposeAsync();
149+
}
150+
151+
Console.WriteLine($"\nIteration {i} complete.");
152+
}
153+
154+
Console.WriteLine($"\nReached max iterations: {maxIterations}");
155+
}
156+
finally
157+
{
158+
await client.StopAsync();
159+
}
160+
```
161+
162+
### Required Project Files
163+
164+
The ideal version expects this file structure in your project:
165+
166+
```
167+
project-root/
168+
├── PROMPT_plan.md # Planning mode instructions
169+
├── PROMPT_build.md # Building mode instructions
170+
├── AGENTS.md # Operational guide (build/test commands)
171+
├── IMPLEMENTATION_PLAN.md # Task list (generated by planning mode)
172+
├── specs/ # Requirement specs (one per topic)
173+
│ ├── auth.md
174+
│ └── data-pipeline.md
175+
└── src/ # Your source code
176+
```
177+
178+
### Example `PROMPT_plan.md`
179+
180+
```markdown
181+
0a. Study `specs/*` to learn the application specifications.
182+
0b. Study IMPLEMENTATION_PLAN.md (if present) to understand the plan so far.
183+
0c. Study `src/` to understand existing code and shared utilities.
184+
185+
1. Compare specs against code (gap analysis). Create or update
186+
IMPLEMENTATION_PLAN.md as a prioritized bullet-point list of tasks
187+
yet to be implemented. Do NOT implement anything.
188+
189+
IMPORTANT: Do NOT assume functionality is missing — search the
190+
codebase first to confirm. Prefer updating existing utilities over
191+
creating ad-hoc copies.
192+
```
193+
194+
### Example `PROMPT_build.md`
195+
196+
```markdown
197+
0a. Study `specs/*` to learn the application specifications.
198+
0b. Study IMPLEMENTATION_PLAN.md.
199+
0c. Study `src/` for reference.
200+
201+
1. Choose the most important item from IMPLEMENTATION_PLAN.md. Before
202+
making changes, search the codebase (don't assume not implemented).
203+
2. After implementing, run the tests. If functionality is missing, add it.
204+
3. When you discover issues, update IMPLEMENTATION_PLAN.md immediately.
205+
4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A`
206+
then `git commit` with a descriptive message.
207+
208+
5. When authoring documentation, capture the why.
209+
6. Implement completely. No placeholders or stubs.
210+
7. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it.
211+
```
212+
213+
### Example `AGENTS.md`
214+
215+
Keep this brief (~60 lines). It's loaded every iteration, so bloat wastes context.
216+
217+
```markdown
218+
## Build & Run
219+
220+
dotnet build
221+
222+
## Validation
223+
224+
- Tests: `dotnet test`
225+
- Build: `dotnet build --no-restore`
226+
```
227+
228+
## Best Practices
229+
230+
1. **Fresh context per iteration**: Never accumulate context across iterations — that's the whole point
231+
2. **Disk is your database**: `IMPLEMENTATION_PLAN.md` is shared state between isolated sessions
232+
3. **Backpressure is essential**: Tests, builds, lints in `AGENTS.md` — the agent must pass them before committing
233+
4. **Start with PLANNING mode**: Generate the plan first, then switch to BUILDING
234+
5. **Observe and tune**: Watch early iterations, add guardrails to prompts when the agent fails in specific ways
235+
6. **The plan is disposable**: If the agent goes off track, delete `IMPLEMENTATION_PLAN.md` and re-plan
236+
7. **Keep `AGENTS.md` brief**: It's loaded every iteration — operational info only, no progress notes
237+
8. **Use a sandbox**: The agent runs autonomously with full tool access — isolate it
238+
9. **Set `WorkingDirectory`**: Pin the session to your project root so tool operations resolve paths correctly
239+
10. **Auto-approve permissions**: Use `OnPermissionRequest` to allow tool calls without interrupting the loop
240+
241+
## When to Use a Ralph Loop
242+
243+
**Good for:**
244+
245+
- Implementing features from specs with test-driven validation
246+
- Large refactors broken into many small tasks
247+
- Unattended, long-running development with clear requirements
248+
- Any work where backpressure (tests/builds) can verify correctness
249+
250+
**Not good for:**
251+
252+
- Tasks requiring human judgment mid-loop
253+
- One-shot operations that don't benefit from iteration
254+
- Vague requirements without testable acceptance criteria
255+
- Exploratory prototyping where direction isn't clear
256+
257+
## See Also
258+
259+
- [Error Handling](error-handling.md) — timeout patterns and graceful shutdown for long-running sessions
260+
- [Persisting Sessions](persisting-sessions.md) — save and resume sessions across restarts
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#:package GitHub.Copilot.SDK@*
2+
3+
using GitHub.Copilot.SDK;
4+
5+
// Ralph loop: autonomous AI task loop with fresh context per iteration.
6+
//
7+
// Two modes:
8+
// - "plan": reads PROMPT_plan.md, generates/updates IMPLEMENTATION_PLAN.md
9+
// - "build": reads PROMPT_build.md, implements tasks, runs tests, commits
10+
//
11+
// Each iteration creates a fresh session so the agent always operates in
12+
// the "smart zone" of its context window. State is shared between
13+
// iterations via files on disk (IMPLEMENTATION_PLAN.md, AGENTS.md, specs/*).
14+
//
15+
// Usage:
16+
// dotnet run # build mode, 50 iterations
17+
// dotnet run plan # planning mode
18+
// dotnet run 20 # build mode, 20 iterations
19+
// dotnet run plan 5 # planning mode, 5 iterations
20+
21+
var mode = args.Contains("plan") ? "plan" : "build";
22+
var maxArg = args.FirstOrDefault(a => int.TryParse(a, out _));
23+
var maxIterations = maxArg != null ? int.Parse(maxArg) : 50;
24+
var promptFile = mode == "plan" ? "PROMPT_plan.md" : "PROMPT_build.md";
25+
26+
var client = new CopilotClient();
27+
await client.StartAsync();
28+
29+
Console.WriteLine(new string('━', 40));
30+
Console.WriteLine($"Mode: {mode}");
31+
Console.WriteLine($"Prompt: {promptFile}");
32+
Console.WriteLine($"Max: {maxIterations} iterations");
33+
Console.WriteLine(new string('━', 40));
34+
35+
try
36+
{
37+
var prompt = await File.ReadAllTextAsync(promptFile);
38+
39+
for (var i = 1; i <= maxIterations; i++)
40+
{
41+
Console.WriteLine($"\n=== Iteration {i}/{maxIterations} ===");
42+
43+
// Fresh session — each task gets full context budget
44+
var session = await client.CreateSessionAsync(
45+
new SessionConfig
46+
{
47+
Model = "gpt-5.1-codex-mini",
48+
// Pin the agent to the project directory
49+
WorkingDirectory = Environment.CurrentDirectory,
50+
// Auto-approve tool calls for unattended operation
51+
OnPermissionRequest = (_, _) => Task.FromResult(
52+
new PermissionRequestResult { Kind = "approved" }),
53+
});
54+
55+
try
56+
{
57+
var done = new TaskCompletionSource<string>();
58+
session.On(evt =>
59+
{
60+
// Log tool usage for visibility
61+
if (evt is ToolExecutionStartEvent toolStart)
62+
Console.WriteLine($" ⚙ {toolStart.Data.ToolName}");
63+
else if (evt is AssistantMessageEvent msg)
64+
done.TrySetResult(msg.Data.Content);
65+
});
66+
67+
await session.SendAsync(new MessageOptions { Prompt = prompt });
68+
await done.Task;
69+
}
70+
finally
71+
{
72+
await session.DisposeAsync();
73+
}
74+
75+
Console.WriteLine($"\nIteration {i} complete.");
76+
}
77+
78+
Console.WriteLine($"\nReached max iterations: {maxIterations}");
79+
}
80+
finally
81+
{
82+
await client.StopAsync();
83+
}

0 commit comments

Comments
 (0)