Flow Definition

API reference for flow.json.

A flow is defined by flow.json in flows/<name>/. It declares the DAG: entry node, node map, routing rules, and guardrails. See Flow for the conceptual overview.

Schema

{
  "name": "research",
  "description": "Parallel research pipeline. Use when investigating a topic that needs multi-perspective verified research.",
  "entry": "planner",
  "maxLoop": 1,
  "maxFanout": 2,
  "strictVerdict": true,
  "nodes": {
    "planner": {
      "agent": "planner",
      "input": "$task\n\nSplit this into exactly 2 orthogonal research sub-topics.",
      "outputs": { "topics": "array" },
      "onComplete": { "parallel": ["r1", "r2"] }
    },
    "r1": {
      "agent": "researcher",
      "input": "Investigate the FIRST sub-topic:\n$planner.slots.topics",
      "onComplete": "verify"
    },
    "r2": {
      "agent": "researcher",
      "input": "Investigate the SECOND sub-topic:\n$planner.slots.topics",
      "onComplete": "verify"
    },
    "verify": {
      "agent": "verifier",
      "input": "=== Track A ===\n$r1.output\n\n=== Track B ===\n$r2.output",
      "onComplete": "$return"
    }
  }
}

Flow Fields

| Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | name | string | Yes | — | Unique flow name; whitelists and mounts depend on it, keep stable | | description | string | Yes | — | What the flow does + when to use it; the only routing signal in the flow catalog | | entry | string | Yes | — | Entry node id | | nodes | map | Yes | — | Node id → node definition | | maxLoop | int | No | 10 | Max back-edge traversals per run (loop protection) | | maxFanout | int | No | 4 | Max parallel fan-out width | | strictVerdict | bool | No | false | When true, a switch node without a valid FlowReport verdict fails the node |

Node Fields

| Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | agent | string | Yes | — | Flow-local agent dir name, falling back to the global agent of that name | | input | string | Yes | — | Input template ($task, $node.output, $node.slots.field) | | onComplete | route | Yes | — | Success routing (see below) | | onError | string | No | stop | stop (abort) / restart (re-execute) / resume (warn + continue) | | maxRetries | int | No | 0 | Retry count for onError: "restart" | | outputs | map | No | — | Structured output contract: slot name → "string" | "array" |

Routes

onComplete (and each switch case value) accepts:

"nodeId"                                    // go to node
"$return"                                   // finish flow, return output
{ "parallel": ["r1", "r2"], "onFail": "collect" }   // fan out; onFail: abort (default) | collect
{
  "switch": "$reviewer.verdict",            // switch expression (informational; routing reads the verdict)
  "cases": { "pass": "$return", "fix": "fixer" },
  "default": "$return",                     // conservative route when no case matches
  "lenient": true                           // opt-in legacy verdict guessing; migrated flows only
}

FlowReport Contract

Node agents report results via the FlowReport tool:

| Parameter | Type | Description | |-----------|------|-------------| | verdict | string | Switch node: exactly one declared case key. Sequential node: done. Generic binary: ok / error | | output | string | Work output, passed downstream as $<nodeId>.output | | slots | object | Fields matching the node's outputs declaration — exact keys, "string" slots take strings, "array" slots take JSON arrays |

When the node carries a contract, violations return a tool error so the agent self-corrects within the same turn.

Verdict Matching

Engine order: exact (case-insensitive) → same-familysubstring (warns).

| Family | Values | |--------|--------| | ok | ok, pass, passed, success, succeeded, successful, true, yes, done, complete, completed, clean, fixed | | error | error, fail, failed, failure, false, no, reject, rejected, abort, aborted, revise |

Same-family matching applies only when the cases contain exactly one key of that family.

Load-time Validation

Rejected at load:

  • Fewer than 2 nodes (use an agent instead)
  • No termination path to $return (pure cycle)
  • A parallel branch reaching $return before converging at a join
  • A barrier join (in-degree ≥ 2) mixing parallel and serial arrivals