Parallel Loops¶
Ralph supports running multiple orchestration loops in parallel using git worktrees for filesystem isolation. This enables working on multiple tasks simultaneously without conflicts.
How It Works¶
When you start a Ralph loop:
- First loop acquires
.ralph/loop.lockand runs in-place (the primary loop) - Additional loops automatically spawn into
.worktrees/<loop-id>/ - Each loop has isolated events, tasks, and scratchpad
- Memories are shared — symlinked back to the main repo's
.agent/memories.md - On completion, worktree loops automatically spawn a merge-ralph to integrate changes
┌─────────────────────────────────────────────────────────────────────┐
│ Terminal 1 │ Terminal 2 │
│ ralph run -p "Add auth" │ ralph run -p "Add logging" │
│ [acquires lock, runs in-place]│ [spawns to worktree] │
│ ↓ │ ↓ │
│ Primary loop │ .worktrees/ralph-20250124-a3f2/ │
│ ↓ │ ↓ │
│ LOOP_COMPLETE │ LOOP_COMPLETE → auto-merge │
└─────────────────────────────────────────────────────────────────────┘
Usage¶
# First loop acquires lock, runs in-place
ralph run -p "Add authentication"
# In another terminal — automatically spawns to worktree
ralph run -p "Add logging"
# Check running loops
ralph loops
# View logs from a specific loop
ralph loops logs <loop-id>
ralph loops logs <loop-id> --follow # Real-time streaming
# Force sequential execution (wait for lock)
ralph run --exclusive -p "Task that needs main workspace"
# Skip auto-merge (keep worktree for manual handling)
ralph run --no-auto-merge -p "Experimental feature"
Loop States¶
| State | Description |
|---|---|
running | Loop is actively executing |
queued | Completed, waiting for merge |
merging | Merge operation in progress |
merged | Successfully merged to main |
needs-review | Merge failed, requires manual resolution |
crashed | Process died unexpectedly |
orphan | Worktree exists but not tracked |
discarded | Explicitly abandoned by user |
File Structure¶
project/
├── .ralph/
│ ├── loop.lock # Primary loop indicator
│ ├── loops.json # Loop registry
│ ├── merge-queue.jsonl # Merge event log
│ └── events.jsonl # Primary loop events
├── .agent/
│ └── memories.md # Shared across all loops
└── .worktrees/
└── ralph-20250124-a3f2/
├── .ralph/events.jsonl # Loop-isolated
├── .agent/
│ ├── memories.md → ../../.agent/memories.md # Symlink
│ └── scratchpad.md # Loop-isolated
└── [project files]
Managing Loops¶
# List all loops with status
ralph loops list
# View loop output
ralph loops logs <id> # Full output
ralph loops logs <id> --follow # Stream real-time
# View event history
ralph loops history <id> # Formatted table
ralph loops history <id> --json # Raw JSONL
# Show changes from merge-base
ralph loops diff <id> # Full diff
ralph loops diff <id> --stat # Summary only
# Open shell in worktree
ralph loops attach <id>
# Re-run merge for failed loop
ralph loops retry <id>
# Stop a running loop
ralph loops stop <id> # SIGTERM
ralph loops stop <id> --force # SIGKILL
# Abandon loop and cleanup
ralph loops discard <id> # With confirmation
ralph loops discard <id> -y # Skip confirmation
# Clean up stale loops (crashed processes)
ralph loops prune
Auto-Merge Workflow¶
When a worktree loop completes, it queues itself for merge. The primary loop processes this queue when it finishes:
┌──────────────────────────────────────────────────────────────────────┐
│ Worktree Loop Primary Loop │
│ ───────────── ───────────── │
│ LOOP_COMPLETE │
│ ↓ │
│ Queue for merge ─────────────────────→ [continues working] │
│ ↓ ↓ │
│ Exit cleanly LOOP_COMPLETE │
│ ↓ │
│ Process merge queue │
│ ↓ │
│ Spawn merge-ralph │
└──────────────────────────────────────────────────────────────────────┘
The merge-ralph process uses a hat collection with specialized roles:
| Hat | Trigger | Purpose |
|---|---|---|
merger | merge.start | Performs git merge, runs tests |
resolver | conflict.detected | Resolves merge conflicts by understanding intent |
tester | conflict.resolved | Verifies tests pass after conflict resolution |
cleaner | merge.done | Removes worktree and branch |
failure_handler | *failed, unresolvable | Marks loop for manual review |
The workflow handles conflicts intelligently: 1. No conflicts: Merge → Run tests → Clean up → Done 2. With conflicts: Detect → AI resolves → Run tests → Clean up → Done 3. Unresolvable: Abort → Mark for review → Keep worktree for manual fix
Conflict Resolution¶
When merge conflicts occur, the AI resolver:
- Examines conflict markers (
<<<<<<<,=======,>>>>>>>) - Understands the intent of both sides (not just the code)
- Resolves by preserving both intents when possible
- Prefers the loop's changes when directly contradictory (newer work)
Conflicts marked needs-review: - Major architectural changes on both sides - Complex refactoring that can't be automatically reconciled - Business logic contradictions requiring human judgment
To manually resolve:
# Enter the worktree
ralph loops attach <loop-id>
# Fix the issue, commit
git add . && git commit -m "Manual conflict resolution"
# Retry the merge
ralph loops retry <loop-id>
# Or discard if unneeded
ralph loops discard <loop-id>
Best Practices¶
When to use parallel loops: - Independent features with minimal file overlap - Bug fixes while feature work continues - Documentation updates parallel to code changes - Test additions that don't conflict with active development
When to use --exclusive (sequential): - Large refactoring touching many files - Database migrations or schema changes - Tasks that modify shared configuration files - Work that depends on changes from another in-progress loop
Tips for reducing conflicts: - Keep loops focused on distinct areas of the codebase - Use separate files when adding new features - Avoid modifying the same functions in parallel loops - Let one loop complete before starting conflicting work
Troubleshooting¶
Loop stuck in queued state¶
# Check if primary loop is still running
ralph loops
# If primary finished but merge didn't start, manually trigger
ralph loops retry <loop-id>
Merge keeps failing¶
# View merge-ralph logs
ralph loops logs <loop-id>
# Check what changes conflict
ralph loops diff <loop-id>
# Manually resolve in worktree
ralph loops attach <loop-id>
Orphaned worktrees¶
# List and clean up orphans
ralph loops prune
# Force cleanup of specific worktree
git worktree remove .worktrees/<loop-id> --force
git branch -D ralph/<loop-id>
Lock file issues¶
# Check who holds the lock
cat .ralph/loop.lock
# If process is dead, remove stale lock
rm .ralph/loop.lock
Environment Variables¶
| Variable | Description |
|---|---|
RALPH_MERGE_LOOP_ID | Set by auto-merge to identify which loop to merge |
RALPH_DIAGNOSTICS=1 | Enable detailed diagnostic logging |
RALPH_VERBOSE=1 | Verbose output mode |