Skip to content

Loop Nodes

DAG workflow nodes support a loop field that runs an AI prompt repeatedly until a completion condition is met. Each iteration is a full AI agent session that can read files, write code, run commands, and produce output.

Use loop nodes for autonomous multi-step work: implement N stories from a PRD, iterate on a design until validation passes, or refine output until quality criteria are met.

name: iterate-until-done
description: Implement stories one at a time
nodes:
- id: setup
bash: |
echo "Found 3 stories to implement"
- id: implement
depends_on: [setup]
loop:
prompt: |
Read the PRD and implement the next unfinished story.
Validate your changes before committing.
Setup context: $setup.output
User request: $USER_MESSAGE
When all stories are done, output: <promise>COMPLETE</promise>
until: COMPLETE
max_iterations: 10
fresh_context: true
- id: report
depends_on: [implement]
prompt: |
Summarize what was implemented: $implement.output

A loop node iterates its prompt until one of these conditions is met:

  1. LLM completion signal — the AI outputs <promise>SIGNAL</promise> where SIGNAL matches the until value
  2. Deterministic bash check — an until_bash script exits with code 0
  3. Max iterations reached — the node fails with a clear error

Each iteration is a full AI agent invocation with tool access. Between iterations, the executor checks for workflow cancellation.

- id: my-loop
loop:
prompt: "..." # Required. The prompt sent each iteration.
until: COMPLETE # Required. Completion signal string.
max_iterations: 10 # Required. Hard limit — node fails if exceeded.
fresh_context: true # Optional. Default: false.
until_bash: "..." # Optional. Bash script checked after each iteration.
interactive: true # Optional. Default: false. Pause after each non-completing
# iteration for user input via /workflow approve.
gate_message: "..." # Required when interactive: true. Message shown to the
# user at each pause with the run ID and approve command.

The prompt text sent to the AI each iteration. Supports all standard variable substitution:

VariableValue
$ARGUMENTS / $USER_MESSAGEOriginal user message
$ARTIFACTS_DIRWorkflow artifacts directory
$BASE_BRANCHRepository base branch
$DOCS_DIRDocumentation directory path (default: docs/)
$WORKFLOW_IDCurrent workflow run ID
$nodeId.outputOutput from upstream nodes
$LOOP_USER_INPUTUser feedback provided via /workflow approve <id> <text> at an interactive loop gate. Only populated on the first iteration of a resumed interactive loop; empty string on all other iterations.

$USER_MESSAGE is particularly important for fresh_context: true loops — the agent has no memory of prior iterations, so the prompt must include all context needed to continue the work.

The completion signal string. The executor checks each iteration’s output for:

  1. Tag format (recommended): <promise>COMPLETE</promise> — case-insensitive match (both tags and signal value), whitespace-tolerant. Prevents false positives from the AI mentioning the signal word in discussion.
  2. Plain signal (fallback): The signal at the very end of output (trailing whitespace and punctuation tolerated) or on its own line. More prone to false positives — prefer the tag format.

The <promise> tags are automatically stripped from output sent to the user and to downstream nodes.

Hard safety limit. If the loop reaches this count without a completion signal, the node fails (not succeeds). This prevents runaway loops from burning tokens indefinitely.

Choose based on the work scope:

  • Simple refinement loops: 3–5
  • Multi-story implementation: 10–15
  • Long-running autonomous agents: 15–20

Controls session continuity between iterations:

ValueBehaviorUse when
trueEach iteration starts a fresh AI session. No memory of prior iterations.Work state lives on disk (files, git). Prevents context window exhaustion on long loops.
false (default)Sessions thread — each iteration resumes the prior conversation.Iterative refinement where the agent needs to remember what it tried before.

The first iteration is always fresh regardless of this setting.

Optional bash script executed after each iteration. If it exits with code 0, the loop completes — even if the AI didn’t output the completion signal.

loop:
prompt: "Fix the failing tests"
until: ALL_PASS
max_iterations: 5
until_bash: "bun run test" # Loop ends when tests pass

This is useful for deterministic completion criteria: test suites, lint checks, build success. The bash script supports the same variable substitution as prompt ($ARTIFACTS_DIR, $nodeId.output, etc.). Note: $nodeId.output values are shell-escaped when substituted into until_bash.

Each iteration reads state from disk, does one unit of work, writes state back. The prompt tells the agent it has no memory and must bootstrap from files.

- id: implement
depends_on: [setup]
idle_timeout: 600000
loop:
prompt: |
You are in a FRESH session — no memory of previous iterations.
Read the PRD tracking file to find the next unfinished story.
Implement it, validate, commit, update tracking.
When all stories are done: <promise>COMPLETE</promise>
Project context: $setup.output
until: COMPLETE
max_iterations: 15
fresh_context: true

When to use: Multi-story implementation, long-running tasks where context window exhaustion is a risk. The agent reads .archon/ralph/*/prd.json or similar tracking files to know what’s done and what’s next.

The agent builds on its own prior work across iterations. Good for iterative refinement where remembering previous attempts matters.

- id: refine
loop:
prompt: |
Review the current implementation and improve it.
Run validation after each change.
When validation passes with zero issues: <promise>DONE</promise>
until: DONE
max_iterations: 5
fresh_context: false

When to use: Fix-iterate cycles, design refinement, test-driven development where the agent needs to remember what it already tried.

Combine LLM work with a deterministic completion check:

- id: fix-tests
loop:
prompt: |
Run the test suite. Read the failures. Fix them one at a time.
If all tests pass: <promise>TESTS_PASS</promise>
until: TESTS_PASS
max_iterations: 8
until_bash: "bun run test"
fresh_context: false

The loop ends either when the AI signals completion or when the bash check succeeds — whichever comes first. This prevents the AI from falsely claiming completion when tests still fail.

  • depends_on — upstream dependencies
  • when — conditional execution
  • trigger_rule — join semantics
  • idle_timeout — per-iteration timeout (default: 30 minutes)
  • $nodeId.output — downstream nodes receive the last iteration’s output

Set interactive: true to pause the loop between iterations and wait for human input. After each non-completing iteration the executor:

  1. Sends the gate_message to the user along with the run ID and a /workflow approve command
  2. Pauses the workflow run
  3. Waits — the workflow resumes when the user runs /workflow approve <id> <feedback>

The user’s feedback is injected into the next iteration’s prompt via $LOOP_USER_INPUT.

Note: Interactive loop nodes require interactive: true at the workflow level as well. If only the loop node has interactive: true, a loader warning is emitted and the workflow will not pause correctly in web background mode.

name: guided-refine
description: Refine output with human review between iterations.
interactive: true # Required at workflow level for interactive loops
nodes:
- id: refine
loop:
prompt: |
Review the current draft and improve it based on this feedback: $LOOP_USER_INPUT
When the output is satisfactory, output: <promise>DONE</promise>
until: DONE
max_iterations: 5
interactive: true
gate_message: Review the output above. Reply with your feedback or type DONE to finish.
  • retry — rejected at parse time. The loader fails the workflow if retry: is set on a loop node.
  • context: fresh — silently ignored. Session control is handled exclusively by fresh_context within the loop: config
  • hooks — per-node SDK hooks are not passed through to loop iterations
  • mcp — per-node MCP server configs are not loaded for loop nodes
  • skills — skill preloading is not applied to loop iterations
  • allowed_tools / denied_tools — tool restrictions are not enforced on loop iterations
  • output_format — structured JSON output is not supported for loop nodes
  • provider / model — accepted in YAML without error but silently ignored at runtime. Loop nodes always use the workflow-level provider and model.

These fields (except retry) are silently discarded at parse time with a loader warning — the workflow still loads but the fields have no effect. retry is the exception: it causes a hard load error.

The loop executor manages its own AI sessions independently from the standard node executor. If you need hooks, MCP, skills, or tool restrictions, consider using a command node that wraps the iterative logic in a command file.

A loop node’s output (available via $nodeId.output to downstream nodes) is the last iteration’s output only — not a concatenation of all iterations.

If you need to accumulate results across iterations, write them to files in $ARTIFACTS_DIR and have the downstream node read from there.

ScenarioBehavior
Iteration throws an errorNode fails immediately (no more iterations)
Max iterations exceededNode fails with descriptive error
Workflow cancelledDetected between iterations, node stops
Idle timeout per iterationIteration completes with whatever output was collected; loop continues to next iteration
retry configured on nodeRejected at parse time — workflow fails to load