Skip to content
Like what we’re building? Star on GitHub

Backend selection — the seven wired backends ​

Read it when a node runs on the wrong backend or you wire a new one. The full per-backend reference is docs/backends.md; the stale-free wiring list is the generated map-packages.md.

Backend selection ​

Seven backends are wired:

  • claw (in-process; automatic or explicit, and the last-resort fallback) — recommended for direct provider calls and Iterion-native declared tools. Use any provider model claw supports, e.g. model: "openai/gpt-5.4-mini" or model: "anthropic/claude-opus-5". A model: pin alone does not select it; use backend: "claw" when the direct path is required.
  • claude_code — recommended coding-agent CLI for implementation work and rich tool/shell access (implementers, fixers). A structured output (output: with json fields) is materialized strict-safe for this CLI: the CLI's validator only tolerates the type-union schema that codex/OpenAI require for an any-valued field as a warning (and is reported to strip it on other builds), so at this backend a json field reaches the CLI as the semantically equivalent type-less any-schema (strictStructuredOutput, pkg/backend/delegate/claudesdk/strict_output_schema.go) while codex keeps the union.
  • pi — the pi coding agent driven through the generic CLI-agent seam (ADR-065 + ADR-085, pkg/backend/delegate/pi.go): backend: "pi", prompt on stdin (pi's argv parser drops a message starting with -/@), --append-system-prompt via a file, --provider/--model emitted together, --thinking for effort. Drives a long-lived --mode rpc session by default (ITERION_PI_MODE=print rolls back to one-shot): the only CLI backend where tool events reach the studio timeline, operator chat rides native steer, and accounting comes from get_session_stats. Reach for it to run a model the other backends cannot: ~36 first-class providers, plus a provider-computed USD cost (cost.AnnotateWithUSD) instead of an estimate. Its wire types are a pinned port of pi's own exported client surface in pkg/backend/delegate/pisdk/ (the claudesdk/ precedent). Capability gaps that are easy to miss: __ITERION_SECRET_*__ placeholders are not materialised (file secrets work); ultracode degrades to xhigh. iterion refuses the target repo's .pi/ extensions by default (they execute as TypeScript inside the agent process). An embedded TypeScript extension (pi-extension/, bundled into pkg/backend/delegate/piext/asset/) supplies what pi has no native surface for (RPC transport only): iterion's permission gate — resolving through the same permission.Policy as claude_code/claw — ask_user plus the async pair (ask_user_async/await_answers, ADR-081; answers delivered mid-run via native steer), and a hand-rolled MCP client on all three transports (streamable http, legacy sse, stdio), since pi ships none. That client is what makes board capabilities: and workflow mcp_server: blocks reach a pi node; tools are discovered via tools/list, never hardcoded. Bridging happens inside pi's session_start, which iterion's own RPC handshake waits on, so each server is bounded by ITERION_PI_MCP_CONNECT_TIMEOUT_MS (default 10s) and connects in parallel — an unreachable server costs its own tools, never the run. task pi-ext:check fails if the committed asset is stale. Cost gotcha: pi injects the repo's AGENTS.md+CLAUDE.md on every call — measured at 26,933 vs 448 input tokens for a one-word prompt when this repo's CLAUDE.md was 103 KB (ADR-085); ITERION_PI_NO_CONTEXT_FILES=1 turns it off.
  • codex — supported Codex CLI backend (pkg/backend/delegate/codex.go), selected explicitly per node/workflow or by adding it to ITERION_BACKEND_PREFERENCE. It uses the Codex CLI login, including ChatGPT OAuth, and supports images, reasoning effort, sessions and structured output. Its native tool set cannot currently be narrowed through Iterion's tools: list (AllowedTools/CanUseTool do not gate the built-in shell), and the pinned SDK cannot run inside Iterion's outer Docker/Kubernetes sandbox; document and test those capability boundaries when extending it.
  • kimi — Moonshot's kimi-code CLI driven through the generic CLI-agent seam (ADR-065, pkg/backend/delegate/kimi.go): backend: "kimi", prompt via -p, stream-json output, model alias passed through verbatim (e.g. model: "kimi-code/kimi-for-coding"); credentials are resolved by the CLI itself from its own env/config. Sessions are best-effort — resume/fork are not wired for CLI-agent backends, so each node runs fresh.
  • grok — xAI Grok Build CLI driven through the same CLI-agent seam (ADR-065, pkg/backend/delegate/grok.go): backend: "grok", prompt via -p, --output-format json, system: via --rules (append — never override the native agentic baseline), model via -m (optional xai/ prefix stripped), reasoning_effort via --reasoning-effort; headless tool approval forced with --permission-mode bypassPermissions --always-approve. Credentials come from the CLI itself (Grok Build login / ~/.grok). Distinct from the metered xAI HTTP API path (backend: claw + model: "xai/…").
  • opencode — the opencode agent CLI through the same CLI-agent seam (ADR-065, pkg/backend/delegate/opencode.go): backend: "opencode", headless via the run subcommand FIRST (2.x refuses a flag before it; ITERION_OPENCODE_ARGV=1 re-emits the 1.x flags-first order), prompt on stdin (its message is a variadic positional, which swallows a prompt starting with - — the same trap pi documents), --format json, model via -m provider/model passed through unchanged (its own format is iterion's spec shape), reasoning_effort folded into it as #variant (accepted names are per-model; iterion passes the level verbatim, and 2.x refuses an unknown one; a reference that already spells one keeps it and the dial is warned, not appended). Credentials come from the CLI itself. Capability gaps that are easy to miss: it cannot enforce iterion's permission gate in ANY mode — no PreToolUse hook — so a gated node is refused by C176 at compile and again at dispatch; interaction: async is refused by C267; session resume/fork is wired (--session <id> / --fork=true, after run; an unknown id is "Session not found" on 1.x and CREATED on 2.x) but session: persist is not — opencode keeps its sessions in one SQLite database, with no per-session file to pack; MCP forwarding and tools: narrowing are not wired; command: (C174) and provider: (C088) are inert. Security: opencode executes the workspace's .opencode/plugin[s]/*.ts inside the agent process, so iterion refuses a workspace carrying one unless ITERION_OPENCODE_TRUST_PROJECT=1, and pins OPENCODE_DISABLE_CLAUDE_CODE=1 so a node's prompt never depends on the operator's ~/.claude. Not baked into the stock sandbox image.