Skip to main content

Ralph Loops: Working Through a Task List

The Ralph pattern runs an agent over and over against a list of tasks: pick a task, attempt it, check the result, learn from failures, move on. In FYAI you build it from two workflows:

Orchestrator workflow Task workflow (one task)
───────────────────── ────────────────────────
Trigger Trigger
└─ Loop Start (For Each over tasks) └─ Loop Start (Count, up to 3 attempts)
└─ Mission ──── runs once per task ──► └─ Implement (Agent)
└─ Loop End └─ Check (Agent or Function)
└─ Summarise (Agent) └─ Loop End (break when PASS)

The task workflow decides what "done" means — its checks are yours to define. The orchestrator is generic: it hands each task to the task workflow and collects the outcomes.

Building blocks​

FeatureWhereWhat it gives you
For Each loopLoop Start → Loop TypeOne pass per list item, with ${current_item}, ${current_index} (from 0), ${items_remaining} and ${loop_total}.
Count loop with a break conditionLoop Start + Loop End Break ConditionRetry until a check passes, up to Max Iterations.
Track output across iterationsLoop Start${<name>} = the last pass's output, ${<name>_history} = every pass's output, as a list.
Mission nodePaletteRuns the task workflow as a sub-workflow for each task.
Output variable nameFunction and Mission nodesKeeps a result under a name; JSON results are parsed, with ${<name>_<key>} for each top-level key.
Run stateAPI / Platform MCPA key–value store per workflow that persists between runs.

See the node reference and Passing Data Between Steps for details.

1. Build the task workflow​

This workflow receives one task as its initial message and keeps trying until its check passes.

  1. Create a workflow named, say, "Implement one task". Remove the default attributes — sub-workflows don't receive attribute values.

  2. Add Trigger → Loop Start → Implement (Agent) → Check (Agent or Function) → Loop End, connected in that order.

  3. Loop Start: Loop Type Count, Max Iterations 3. Turn on Track output across iterations with Output variable name attempts.

  4. Implement — your implementing agent. Input Message Template:

    ${initial_message}

    Previous attempts and their check results (empty on the first try):
    ${attempts_history}
  5. Check — an agent (or a function) that runs your quality checks: tests, lint, a review. Its instructions must end with exactly one line, VERDICT: PASS or VERDICT: FAIL — <reason>. Input message:

    Check this work against the task's acceptance criteria and run the checks.

    Task:
    ${initial_message}

    Work:
    ${node_3_result}

    (Type $ and pick the Implement node rather than typing node_3, so the reference points at the right node.)

  6. Loop End: Break Condition — Source Check → result, operator contains, value VERDICT: PASS.

  7. Save. To test it, click ▶ on any node and type a sample task into Input message. This runs the whole task workflow with that text as its initial message — see Testing Workflows.

Each attempt's output (implementation plus verdict) is added to ${attempts_history}, so the next attempt sees why the last one failed.

2. Build the orchestrator​

  1. Create a workflow named, say, "Work through tasks".

  2. Replace the default attributes with one Text Input attribute named tasks, Number of Lines 10, Description "Tasks (JSON list)". Add a Button attribute with Trigger ticked if you want a labelled launch button.

  3. Add Trigger → Loop Start → Mission → Loop End → Summarise (Agent).

  4. Loop Start: Loop Type For Each, Collection Variable tasks, Max Iterations (safety cap) 50. Turn on Track output across iterations with Output variable name task_results.

  5. Mission: Workflow "Implement one task". Input Message Template:

    Task ${current_index} (${items_remaining} remaining after this one):
    ${current_item}
  6. Summarise: connected from Loop End. Input message:

    Below is the output for each task, in order. For each, report the task,
    whether its final verdict was PASS or FAIL, and the reason.

    ${task_results_history}
  7. Save.

3. Run it​

Go to Build → Missions, choose "Work through tasks", and paste the task list into Tasks (JSON list):

[
{"id": "1", "title": "Add a /health endpoint", "acceptance_criteria": "GET /health returns 200"},
{"id": "2", "title": "Add request logging", "acceptance_criteria": "Method, path, status and duration are logged"}
]

Launch. The orchestrator's canvas shows the loop working through the list; each task starts its own child mission, which you can open from mission history to watch its attempts.

How it behaves​

  • The list is read once, when the loop starts. Changing the variable inside the loop doesn't add or remove items.
  • A task that never passes uses up its attempts and returns its last VERDICT: FAIL; the orchestrator moves on to the next task.
  • A failed node stops everything. If an agent or function errors (as opposed to reporting FAIL), the whole run fails. Give flaky nodes Max Retries.
  • ${current_item} is inserted as text. Templates can't read a field such as current_item.title directly; pass the whole item and let the agent read it. Condition rules can read fields (for example ${current_item.priority}), but only rules written through the API.
  • The Mission node's output is a JSON object of every node result in the task workflow, keyed by node ID. That's why the check's verdict is a recognisable line you can search for with contains.
  • Costs add up. Every attempt of every task is a full agent run. Set Max Iterations deliberately and watch Avg Cost on the workflow list.

Keeping progress between runs​

Loop variables last for one run. To carry a progress log or learned patterns across runs, use the workflow's run state — a key–value store that persists between runs:

MethodPath
List all keysGET /workflows/{workflow_id}/run-state
Read a keyGET /workflows/{workflow_id}/run-state/{key}
Write a keyPUT /workflows/{workflow_id}/run-state/{key} with body {"value": ...} (any JSON)
Delete a keyDELETE /workflows/{workflow_id}/run-state/{key}
Clear everythingDELETE /workflows/{workflow_id}/run-state

The Platform MCP server exposes the same operations (get_workflow_run_state, get_workflow_run_state_key, set_workflow_run_state, delete_workflow_run_state_key, clear_workflow_run_state). There is no built-in node for run state; read and write it from outside the run, or from a function or agent that calls the API.