← Documentation index · ← Iterion
.bot DSLWorkflows are written in a declarative, indentation-significant language. The formal grammar is in grammar/iterion_v1.ebnf.
Workflow source files use the
.botextension. Packaged workflows use.botz.
Define typed variables at the top level. These can be set at runtime with --var key=value:
vars:
pr_title: string
max_retries: int = 3
verbose: bool = false
config: json = { "key": "value" }
tags: string[] = ["security", "performance"]
Supported types: string, bool, int, float, json, string[].
Reusable prompt templates with `` interpolation:
prompt review_system:
You are a code reviewer specializing in .
Focus on:
prompt review_user:
Review this code:
Previous feedback:
Typed data contracts for structured agent I/O:
schema review_result:
approved: bool
summary: string
issues: string[]
confidence: string [enum: "low", "medium", "high"]
score: float
metadata: json
Supported field types: string, bool, int, float, json, string[]. Strings support [enum: ...] constraints.
The primary execution unit. Calls an LLM with prompts, optionally uses tools, and returns structured output:
agent reviewer:
model: "claude-sonnet-4-20250514"
input: review_request
output: review_result
system: review_system
user: review_user
session: fresh
tools: [git_diff, read_file, search_codebase]
tool_max_steps: 10
publish: review_artifact
reasoning_effort: high
readonly: true
| Property | Description |
|---|---|
model |
LLM model identifier (supports ${ENV_VAR}) |
backend |
Execution backend: claw (default, in-process LLM), claude_code (recommended for tool use), codex (discouraged, see Delegation) |
input / output |
Schema references for structured I/O |
publish |
Persist output as a named artifact |
system / user |
Prompt references |
session |
Context mode: fresh (default), inherit, inherit_if_available, fork, or artifacts_only. inherit_if_available (v0.6.0+) inherits the parent session when _session_id resolves on the input and falls back to fresh otherwise — useful inside loops where the first iteration has no parent. |
tools |
List of allowed tool names |
tool_max_steps |
Max tool-use iterations (0 = unlimited) |
reasoning_effort |
Extended thinking: low, medium, high, xhigh, max |
readonly |
If true, prevents tool side effects (workspace safety) |
Structurally identical to agents, but semantically intended for evaluation — typically no tools:
judge compliance_check:
model: "claude-sonnet-4-20250514"
input: plan_schema
output: verdict_schema
system: compliance_system
user: compliance_user
Branches execution into parallel or conditional paths. Four modes are available:
router dispatch:
mode: fan_out_all # Send to ALL outgoing edges in parallel
router branch:
mode: condition # Route based on `when` clauses on edges
router alternate:
mode: round_robin # Cycle through targets one per iteration
router smart:
mode: llm # LLM decides which target(s) to route to
model: "claude-sonnet-4-20250514"
system: routing_prompt
multi: true # Allow selecting multiple targets
For a deep dive on routing modes, edge rules, and convergence patterns, see
routers.md.
awaitParallel branches converge at a real downstream node (agent, judge, human, tool, or compute) that declares how it waits for multiple incoming edges:
agent merge:
model: "claude-sonnet-4-20250514"
input: branch_results
output: merged_result
user: merge_prompt
await: wait_all # or: best_effort
workflow example:
fan -> branch_a
fan -> branch_b
branch_a -> merge
branch_b -> merge
await: wait_all — waits for every incoming branchawait: best_effort — proceeds with successful branches and tolerates failures on othersDedicated join node declarations are no longer supported; put await: on the node that consumes the parallel branch outputs.
Pauses the workflow for human input, or lets an LLM handle it (full guide, form widgets, and the interaction-mode decision tree: human-in-the-loop.md):
## Always pause for human answers
human approval:
input: approval_request
output: approval_response
instructions: approval_prompt
interaction: human
min_answers: 1
## LLM auto-answers (never pauses)
human auto_review:
interaction: llm
model: "claude-sonnet-4-20250514"
system: auto_review_prompt
output: review_decision
## LLM decides whether to pause or auto-answer
human conditional:
interaction: llm_or_human
model: "claude-sonnet-4-20250514"
system: decision_guidance
instructions: review_questions
output: review_decision
Resume a paused run with iterion resume --run-id <id> --file <file> --answer key=value.
Direct shell command execution — no LLM involved:
tool run_tests:
command: "make test"
output: test_result
publish: test_result_artifact # optional — see below
Supports ${ENV_VAR} in the command string. Like agent/judge/human nodes,
a tool — or a compute — node may add publish: <name> to persist its
output as a versioned artifact (surfaced in the studio Artifact tab and
iterion report, and referenceable downstream as ``).
This is deterministic and adds no LLM cost — publish: only redirects
the node’s already-computed output into the store.
Every workflow must end at done (success) or fail (failure). These are built-in — you don’t declare them.
A workflow ties nodes together with an entry point, optional budget, and edges:
workflow pr_review:
entry: context_builder
budget:
max_duration: "30m"
max_cost_usd: 10
max_tokens: 400000
context_builder -> reviewer
reviewer -> done when approved
reviewer -> context_builder when not approved as retry(3)
Edge syntax:
src -> dst # Unconditional
src -> dst when approved # Conditional (bool field from src output)
src -> dst when not approved # Negated condition
src -> dst as loop_name(5) # Bounded loop (max 5 iterations)
src -> dst as loop_name(unbounded 200) # Opt-in unbounded loop, fuel ceiling 200
src -> dst with { # Data mapping
context: "",
config: ""
}
Edge rules:
when X + when not X) or have an unconditional fallbackas name(N) — undeclared cycles are a compile erroras name(unbounded [<fuel>]) — no static iteration cap; termination is relocated to a runtime fuel ceiling + a liveness monitor. This is the DSL’s opt-in Turing-completeness; see dsl-totality-and-tc.md and ADR-050. Diagnostics C097 (fuel required) / C098 (no exit edge)Templates use `` interpolation:
| Reference | Description |
|---|---|
| `` | Workflow variable |
| `` | Current node’s input field |
| `` | Full output of a previously executed node |
| `` | Specific field from a node’s output |
| `` | Array of all outputs across loop iterations |
| `` | Published artifact by name |
| `` | Declared attachment by name; resolves to the host path (same as .path) |
| `` | Attachment host path |
| `` | Presigned attachment URL |
| `` | Attachment MIME type |
| `` | Attachment size in bytes |
| `` | Attachment SHA-256 digest |
| `` | Current 0-based iteration count for a declared loop |
| `` | Effective loop iteration cap |
| `` | Previous iteration output snapshot; append subfields to drill in |
| `` | Current run identifier |
Environment variables are supported with ${ENV_VAR} syntax (resolved at compile time).
You can declare MCP (Model Context Protocol) servers directly in your .bot files:
mcp_server code_tools:
transport: stdio
command: "npx"
args: ["-y", "@anthropic-ai/claude-code-mcp"]
mcp_server api_server:
transport: http
url: "http://localhost:3000/mcp"
Agents can then reference these servers:
agent worker:
model: "claude-sonnet-4-20250514"
mcp:
servers: [code_tools, api_server]
Supported transports: stdio (requires command and forbids url), plus http and sse (both require url and must not set command or args; they share the streamable transport path at runtime).
Control costs and prevent runaway execution:
budget:
max_parallel_branches: 4 # Concurrent branch limit
max_duration: "30m" # Global timeout
max_cost_usd: 10.0 # Cost cap in USD
max_tokens: 500000 # Total token budget
max_iterations: 50 # Loop iteration limit
The budget is shared across all branches. When a limit is hit, the engine emits a budget_exceeded event and stops the run.
Two top-level workflow directives let a run isolate itself from the host:
workflow safe_pr_fix:
worktree: auto # Run inside a fresh git worktree; persist commits to a branch on success
sandbox: auto # Run all tool/agent calls inside a Docker/Podman container
entry: planner
...
worktree: auto — the engine creates <store-dir>/worktrees/<run-id>, executes the workflow there, then on a clean exit creates a persistent branch (default iterion/run/<friendly-name>) and fast-forwards the user’s currently-checked-out branch to that HEAD. Override with --merge-into, --branch-name, --merge-strategy, or --auto-merge=false. See resume.md.sandbox: auto — reads .devcontainer/devcontainer.json (or falls back to the default iterion sandbox image) and runs each agent/tool node inside an isolated container with the worktree bind-mounted at /workspace, plus an HTTP CONNECT proxy enforcing a network allowlist. claw backend calls run through the hidden iterion __claw-runner subprocess inside the container, so custom images must provide iterion on PATH or allow the host binary to be bind-mounted. Use iterion sandbox doctor to verify host capabilities. See sandbox.md.grammar/iterion_v1.ebnf — formal EBNF grammarreferences/dsl-grammar.md — readable grammar referencereferences/diagnostics.md — all validation diagnostic codes (C001–C086, sparse)references/patterns.md — 10 reusable workflow patternsattachments.md — attachment handlingworkflow_authoring_pitfalls.md — required reading before authoring workflows that commit code