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.
Quick Start
Section titled “Quick Start”name: iterate-until-donedescription: Implement stories one at a timenodes: - 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.outputHow It Works
Section titled “How It Works”A loop node iterates its prompt until one of these conditions is met:
- LLM completion signal — the AI outputs
<promise>SIGNAL</promise>where SIGNAL matches theuntilvalue - Deterministic bash check — an
until_bashscript exits with code 0 - 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.
Configuration Fields
Section titled “Configuration Fields”- 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.prompt
Section titled “prompt”The prompt text sent to the AI each iteration. Supports all standard variable substitution:
| Variable | Value |
|---|---|
$ARGUMENTS / $USER_MESSAGE | Original user message |
$ARTIFACTS_DIR | Workflow artifacts directory |
$BASE_BRANCH | Repository base branch |
$DOCS_DIR | Documentation directory path (default: docs/) |
$WORKFLOW_ID | Current workflow run ID |
$nodeId.output | Output from upstream nodes |
$LOOP_USER_INPUT | User 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:
- 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. - 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.
max_iterations
Section titled “max_iterations”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
fresh_context
Section titled “fresh_context”Controls session continuity between iterations:
| Value | Behavior | Use when |
|---|---|---|
true | Each 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.
until_bash
Section titled “until_bash”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 passThis 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.
Patterns
Section titled “Patterns”Stateless agent (Ralph pattern)
Section titled “Stateless agent (Ralph pattern)”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: trueWhen 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.
Accumulating context
Section titled “Accumulating context”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: falseWhen to use: Fix-iterate cycles, design refinement, test-driven development where the agent needs to remember what it already tried.
Deterministic exit with until_bash
Section titled “Deterministic exit with until_bash”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: falseThe 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.
Node Features
Section titled “Node Features”What works on loop nodes
Section titled “What works on loop nodes”depends_on— upstream dependencieswhen— conditional executiontrigger_rule— join semanticsidle_timeout— per-iteration timeout (default: 30 minutes)$nodeId.output— downstream nodes receive the last iteration’s output
interactive and gate_message
Section titled “interactive and gate_message”Set interactive: true to pause the loop between iterations and wait for human input.
After each non-completing iteration the executor:
- Sends the
gate_messageto the user along with the run ID and a/workflow approvecommand - Pauses the workflow run
- 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: trueat the workflow level as well. If only the loop node hasinteractive: true, a loader warning is emitted and the workflow will not pause correctly in web background mode.
name: guided-refinedescription: Refine output with human review between iterations.interactive: true # Required at workflow level for interactive loopsnodes: - 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.What is NOT supported on loop nodes
Section titled “What is NOT supported on loop nodes”retry— rejected at parse time. The loader fails the workflow ifretry:is set on a loop node.context: fresh— silently ignored. Session control is handled exclusively byfresh_contextwithin theloop:confighooks— per-node SDK hooks are not passed through to loop iterationsmcp— per-node MCP server configs are not loaded for loop nodesskills— skill preloading is not applied to loop iterationsallowed_tools/denied_tools— tool restrictions are not enforced on loop iterationsoutput_format— structured JSON output is not supported for loop nodesprovider/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.
Output
Section titled “Output”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.
Error Handling
Section titled “Error Handling”| Scenario | Behavior |
|---|---|
| Iteration throws an error | Node fails immediately (no more iterations) |
| Max iterations exceeded | Node fails with descriptive error |
| Workflow cancelled | Detected between iterations, node stops |
| Idle timeout per iteration | Iteration completes with whatever output was collected; loop continues to next iteration |
retry configured on node | Rejected at parse time — workflow fails to load |
See Also
Section titled “See Also”- Authoring Workflows — full workflow reference
- Per-Node Hooks — SDK hooks for command/prompt nodes
- Per-Node MCP Servers — external tool integration
- Per-Node Skills — skill preloading