Recursive Agent Learning & Planning Harness — agentic task execution pipeline
Version: 1.0 | Last Updated: February 2026
Ralph (Recursive Agent Learning & Planning Harness) is the core agentic execution engine. A Ralph Loop takes a PRD (Product Requirements Document), decomposes it into tasks, and executes each task using agents, workflows, pipelines, or human reviewers. Each iteration produces learnings that feed back into the system.
| Component | Purpose |
|---|---|
Ai::RalphLoop |
The loop container — holds tasks, iterations, configuration |
Ai::RalphTask |
Individual task within a loop — with dependencies and executor routing |
Ai::RalphIteration |
Single execution attempt of a task — records output, tokens, learnings |
ExecutionService |
Orchestrates loop lifecycle and iteration execution |
TaskExecutor |
Routes tasks to the appropriate executor (agent, workflow, pipeline, etc.) |
AgenticLoop |
Tool-calling agent loop with git operations and MCP tool support |
PRD (JSON)
│
▼
┌──────────────────────────┐
│ RalphLoop │ status: pending → running → completed
│ parse_prd() │
└────────────┬─────────────┘
│
┌────────┴────────┐
▼ ▼
RalphTask #1 RalphTask #2 ...
(depends: []) (depends: [#1])
│
▼
┌──────────────────────────┐
│ TaskExecutor │
│ execute() │
└────────────┬─────────────┘
│
┌────────┼────────┬──────────┬──────────┐
▼ ▼ ▼ ▼ ▼
Agent Workflow Pipeline A2A Task Human
│
▼
┌──────────────────────────┐
│ AgenticLoop │ max 15 tool rounds
│ execute(messages) │
└────────────┬─────────────┘
│
┌────────┴────────┐
▼ ▼
GitToolExecutor MCP Tools
(file ops, git) (platform.*)
The container for a set of tasks derived from a PRD.
STATUSES = %w[pending running paused completed failed cancelled]
SCHEDULING_MODES = %w[manual scheduled continuous event_triggered]
belongs_to :account
belongs_to :default_agent, class_name: "Ai::Agent", optional: true
belongs_to :container_instance, class_name: "Devops::ContainerInstance", optional: true
belongs_to :risk_contract, class_name: "Ai::CodeFactory::RiskContract", optional: true
belongs_to :mission, class_name: "Ai::Mission", optional: true
has_many :ralph_tasks, dependent: :destroy
has_many :ralph_iterations, dependent: :destroyScheduling modes:
manual— triggered by user or API callscheduled— runs on a cron schedulecontinuous— re-runs automatically on completionevent_triggered— runs in response to external events
Scopes: pending, running, paused, active, due_for_execution, scheduled, event_triggered
Individual task within a loop, with dependency tracking and executor routing.
STATUSES = %w[pending in_progress passed failed blocked skipped]
EXECUTION_TYPES = %w[agent workflow pipeline a2a_task container human community]
CAPABILITY_STRATEGIES = %w[all any weighted]
belongs_to :ralph_loop
belongs_to :executor, polymorphic: true, optional: true
has_many :ralph_iterations, dependent: :nullifyKey methods:
dependencies_satisfied?— checks if all dependent tasks have passedblocking_dependencies— returns list of unresolved dependency task keysfind_matching_executor— routes byexecution_typeto find the right agent/workflow/pipelinerecord_execution_attempt!(executor)— increments attempt counterhas_fallback?— checks if fallback configuration exists
State machine: start! → pass!(iteration_number:) | fail!(error_message:) | block!(reason:) | skip!(reason:) | reset!()
Records a single execution attempt of a task.
STATUSES = %w[pending running completed failed skipped]
belongs_to :ralph_loop
belongs_to :ralph_task, optional: trueKey methods:
start!— sets status to runningcomplete!(output:, checks_passed:, commit_sha:, learning:)— records success with optional learningfail!(error_message:, error_code:, error_details:)— records failurerecord_token_usage(input:, output:, cost:)— tracks LLM token usage
Orchestrates the lifecycle of a Ralph Loop — starting, iterating, and completing.
service = Ai::Ralph::ExecutionService.new(ralph_loop: loop, account: account, user: user)Included modules:
LoopLifecycle— start, pause, resume, cancel, completeIterationExecution— execute individual iterations, handle success/failurePrdAndBroadcasting— PRD parsing, WebSocket broadcasts
Code Factory integration:
code_factory_preflight_check(changed_files:)— runs preflight gate viaPreflightGateServicecode_factory_evidence_satisfied?— checks if review state evidence requirements are met
Routes tasks to the appropriate executor based on execution_type.
executor = Ai::Ralph::TaskExecutor.new(task: task, ralph_loop: loop, account: account)
result = executor.executeExecution routes:
| Type | Executor | Description |
|---|---|---|
agent |
AgenticLoop |
Tool-calling agent with git + MCP tools |
workflow |
WorkflowRun |
Creates and enqueues workflow execution |
pipeline |
PipelineExecution |
Triggers CI/CD pipeline |
a2a_task |
A2A::Service |
Agent-to-agent task submission |
container |
ContainerOrchestrationService |
Container-based execution |
human |
Notification | Creates notification for human review |
community |
A2A external | External agent federation |
Executor resolution priority:
- Explicit executor on the task
- Loop's default agent
- Capability-based matching (
find_matching_executor)
Prompt construction: The executor builds prompts including:
- Task description and acceptance criteria
- PRD overview (title, description)
- Repository context (structure, recent commits)
- Git instructions (worktree, branch, commit conventions)
The core tool-calling loop that executes agent tasks with iterative tool use.
loop = Ai::Ralph::AgenticLoop.new(
client: provider_client,
provider_type: "anthropic",
account: account,
git_tool_executor: git_executor,
mcp_tools: mcp_tool_definitions
)
result = loop.execute(messages, options)
# => { success: true, content: "...", file_changes: [...], last_commit_sha: "...", tool_calls_log: [...] }Behavior:
- Calls
client.send_messageiteratively (max 15 rounds) - Extracts tool calls from response
- Routes git tools to
GitToolExecutor, MCP tools toMcp::SyncExecutionService - Accumulates text content and tool results
- Returns aggregated result with file changes
Handles repository operations within a worktree.
Available tools (3 categories):
| Category | Tools |
|---|---|
file_ops |
read_file, write_file, delete_file, list_files |
code_intel |
search_code, get_file_info |
repo_context |
get_repo_info, list_branches, get_branch_diff, list_commits |
Provider-aware formatting:
- Anthropic:
{ name:, description:, input_schema: } - OpenAI/Ollama:
{ type: "function", function: { name:, description:, parameters: } }
File operations commit automatically — write_file and delete_file create git commits with the provided message.
Controller: Api::V1::Ai::RalphLoopsController
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/v1/ai/ralph_loops |
ai.workflows.read |
List loops (filter by status/agent) |
POST |
/api/v1/ai/ralph_loops |
ai.workflows.create |
Create loop (optionally parse PRD) |
GET |
/api/v1/ai/ralph_loops/:id |
ai.workflows.read |
Show loop details |
PATCH |
/api/v1/ai/ralph_loops/:id |
ai.workflows.update |
Update loop |
DELETE |
/api/v1/ai/ralph_loops/:id |
ai.workflows.delete |
Delete (terminal/pending only) |
| Method | Path | Permission | Description |
|---|---|---|---|
POST |
/api/v1/ai/ralph_loops/:id/start |
ai.workflows.execute |
Start loop |
POST |
/api/v1/ai/ralph_loops/:id/pause |
ai.workflows.execute |
Pause loop |
POST |
/api/v1/ai/ralph_loops/:id/resume |
ai.workflows.execute |
Resume loop |
POST |
/api/v1/ai/ralph_loops/:id/cancel |
ai.workflows.execute |
Cancel loop |
POST |
/api/v1/ai/ralph_loops/:id/reset |
ai.workflows.execute |
Reset loop |
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/v1/ai/ralph_loops/:id/tasks |
ai.workflows.read |
List tasks (filter by status) |
GET |
/api/v1/ai/ralph_loops/:id/tasks/:key |
ai.workflows.read |
Get task by key or ID |
PATCH |
/api/v1/ai/ralph_loops/:id/tasks/:key |
ai.workflows.update |
Update task |
GET |
/api/v1/ai/ralph_loops/:id/iterations |
ai.workflows.read |
List iterations |
GET |
/api/v1/ai/ralph_loops/:id/iterations/:num |
ai.workflows.read |
Get iteration |
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/v1/ai/ralph_loops/:id/progress |
ai.workflows.read |
Progress + recent commits |
GET |
/api/v1/ai/ralph_loops/:id/learnings |
ai.workflows.read |
Extracted learnings |
GET |
/api/v1/ai/ralph_loops/:id/statistics |
ai.workflows.read |
Aggregate stats |
Ralph Loops are typically created from a PRD JSON structure:
{
"title": "Add User Profile Page",
"description": "Create a user profile page with avatar upload and settings",
"tasks": [
{
"key": "task_1",
"name": "Create User Profile Model",
"description": "Add profile fields to User model with migration",
"priority": 1,
"acceptance_criteria": "Migration runs, model validates presence of display_name",
"dependencies": [],
"execution_type": "agent"
},
{
"key": "task_2",
"name": "Create Profile API Endpoint",
"description": "Add GET/PATCH /api/v1/profile endpoint",
"priority": 2,
"acceptance_criteria": "Returns profile data, updates display_name and bio",
"dependencies": ["task_1"],
"execution_type": "agent"
}
]
}| File | Path |
|---|---|
| Models | server/app/models/ai/ralph_loop.rb, ralph_task.rb, ralph_iteration.rb |
| Execution | server/app/services/ai/ralph/execution_service.rb |
| Task Routing | server/app/services/ai/ralph/task_executor.rb |
| Agentic Loop | server/app/services/ai/ralph/agentic_loop.rb |
| Git Tools | server/app/services/ai/ralph/git_tool_executor.rb, git_tool_definitions.rb |
| Controller | server/app/controllers/api/v1/ai/ralph_loops_controller.rb |