> ## Documentation Index
> Fetch the complete documentation index at: https://graph.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Plan file schema

> Every field of a plan document, in one place

The complete field reference for plan YAML files. Concepts and worked examples live in the [Plans](/plans/overview) section; this page is the lookup table.

**Every field name in a plan file is snake\_case**, at every depth — top level, steps, control-step grammar, and an [`agent`](/plans/agent-step) step's `input` alike. That is the one spelling to author in, and what everything writing a plan emits: `graph plan show` (including `--json`), the [authoring commands](/reference/cli#authoring-plans), and the [workbench's](/workbench/plan-workbench) save.

The planner uses camelCase internally (`toolName`, `queryToAnswer`) because that is the schema the model is taught. You never see it: a plan it drafts is normalized to snake\_case before it reaches disk. The camelCase spellings stay accepted on the way in, so a plan authored before this settled still loads — and rewriting it with any authoring command normalizes it.

Optional fields you never set are omitted rather than written as `null`/`[]`.

## Top level

| Field              | Required | Type           | Notes                                                                                                                                  |
| ------------------ | -------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `identifier`       | yes      | string         | tool-name-safe (`[a-zA-Z0-9_-]+`); becomes `plan__<identifier>` in the catalog                                                         |
| `name`             | yes      | string         | display name                                                                                                                           |
| `description`      | yes      | string         | routing signal — what the agent reads when choosing tools                                                                              |
| `exemplars`        | no       | list of string | example user phrasings; folded into the tool description verbatim ([routing](/using/tool-selection))                                   |
| `requires_servers` | no       | list of string | MCP server dependencies; unconfigured → plan hidden from the catalog, loud error by name ([discovery](/plans/overview#plan-discovery)) |
| `input_schema`     | no       | JSON Schema    | validates inputs before any step runs; referenced as `{{input.*}}` ([inputs](/plans/inputs))                                           |
| `steps`            | yes      | list of step   | run sequentially                                                                                                                       |
| `solver`           | no       | object         | LLM-written finish ([finish modes](/plans/finish-modes))                                                                               |
| `output`           | no       | template map   | structured JSON finish, zero LLM                                                                                                       |

`solver` and `output` are mutually exclusive; declaring neither makes the plan **silent** (side effects only).

## `solver`

| Field             | Required | Notes                                                                                                              |
| ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `query_to_answer` | yes      | instructions for the solver; itself a [template](/plans/template-language), so sections can adapt it to the data   |
| `system_prompt`   | no       | extra system-level guidance for the solver call, on top of the built-in solver prompt                              |
| `data`            | yes      | template map of what the solver sees — whole results spliced by template; oversized payloads are truncated/sampled |

## Steps

| Field       | Required | Notes                                                                                                                                                                                      |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`        | yes      | identifier (letters, digits, `_`, not digit-leading), unique across the plan including body sub-steps; may not shadow the reserved roots `input`, `item`, `index`, `accumulator`, `length` |
| `tool_name` | yes      | a catalog tool (`server__*`, `user__*`, `builtin__*`, `plan__*`), `plan_and_execute`, or a control step below                                                                              |
| `input`     | yes      | the tool's arguments; string values are [templates](/plans/template-language) over `input` and earlier step results                                                                        |
| `reasoning` | no       | free text, shown in traces                                                                                                                                                                 |

## Control steps

Seven reserved `tool_name`s are executor-intercepted, never dispatched to a registry. Their `input` is the control grammar — snake\_case like everything else, and the only part of a step's `input` graph normalizes, since every other tool's arguments belong to that tool:

### `exit` — [end the plan early](/plans/exit-gates)

| Field     | Required | Notes                                                                                                                                         |
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `when`    | no       | logical gate (below); mutually exclusive with `infer`; **both omitted = unconditional exit**                                                  |
| `infer`   | no       | yes/no question judged by the `judge` role                                                                                                    |
| `model`   | no       | model for the `infer` verdict: a role name, `default`, or a [named model](/models/models-and-providers#named-models); ignored without `infer` |
| `status`  | yes      | `success` \| `error` — error maps to exit code 4 on `plan run`                                                                                |
| `message` | no       | shown as the plan's answer / error message; may use templates                                                                                 |
| `output`  | no       | template map emitted as the plan's structured output when the exit fires                                                                      |

### `decide` — [fork into one of two branches](/plans/branching)

| Field   | Required                    | Notes                                      |
| ------- | --------------------------- | ------------------------------------------ |
| `if`    | exactly one of `if`/`infer` | logical gate (same grammar as `when`)      |
| `infer` | 〃                           | yes/no question judged by the `judge` role |
| `model` | no                          | as on `exit`; may itself be a template     |
| `then`  | yes                         | body (below), run when the gate holds      |
| `else`  | no                          | body, run otherwise                        |

### `filter` — [partition a list](/plans/selection)

| Field         | Required                       | Notes                                                                                             |
| ------------- | ------------------------------ | ------------------------------------------------------------------------------------------------- |
| `over`        | yes                            | template that must render to an array                                                             |
| `where`       | exactly one of `where`/`infer` | logical gate (same grammar as `when`), evaluated per element with `{{item}}`/`{{index}}` in scope |
| `infer`       | 〃                              | yes/no question judged per element by the `judge` role                                            |
| `model`       | no                             | as on `exit`; applies to every `infer` verdict                                                    |
| `concurrency` | no                             | max `infer` verdicts in flight; default 1; ignored for `where`                                    |

Legal everywhere, including inside `decide` branches and `map`/`reduce` bodies — the one nestable control step (it makes no tool call). Inside a body its `{{item}}`/`{{index}}` shadow the enclosing body's within the gate.

### `map` / `reduce` — [run a body per item](/plans/iteration)

| Field         | Required           | Notes                                                                                                     |
| ------------- | ------------------ | --------------------------------------------------------------------------------------------------------- |
| `over`        | yes                | template that must render to an array                                                                     |
| `do`          | yes                | body (below), run once per element with `{{item}}`/`{{index}}` in scope (`reduce` adds `{{accumulator}}`) |
| `concurrency` | no (`map` only)    | max items in flight; default 1 (sequential); `results` stays in input order                               |
| `initial`     | no (`reduce` only) | starting accumulator; default `null`                                                                      |

### `agent` — [delegate an open-ended sub-task](/plans/agent-step)

| Field            | Required | Notes                                                                                                                                    |
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`         | yes      | the sub-task, as a self-contained instruction; may use templates (rendered when the loop starts)                                         |
| `output_schema`  | yes      | JSON Schema the result must satisfy; must be `"type": "object"` or declare `properties`                                                  |
| `tools`          | no       | glob patterns selecting which catalog tools the agent may call (e.g. `linear__*`); defaults to the full catalog minus `plan_and_execute` |
| `max_iterations` | no       | tool rounds before the loop gives up; default 8                                                                                          |
| `model`          | no       | model for the loop: a role name, `default`, or a [named model](/models/models-and-providers#named-models)                                |
| `system_prompt`  | no       | replaces the default agent system prompt; may use templates                                                                              |

Legal everywhere, including inside `decide` branches and `map`/`reduce` bodies.

### `ask` — [put a question to the user](/plans/ask-step)

| Field             | Required                        | Notes                                                                                                                                    |
| ----------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`          | yes                             | the question as the user sees it; may use templates                                                                                      |
| `output_schema`   | yes                             | JSON Schema for the answer; must be `"type": "object"` with **primitive-typed properties only** (string, number, integer, boolean, enum) |
| `when_unanswered` | no                              | `fail` (default) \| `default` — behaviour when nobody can be asked                                                                       |
| `default`         | with `when_unanswered: default` | fallback answer; must conform to `output_schema`; may use templates                                                                      |

Legal everywhere, including inside `decide` branches and `map`/`reduce` bodies. Whether a human is reachable depends on the host, not the plan, so `when_unanswered` is what keeps one plan runnable both interactively and in CI.

## Gate grammar (`when` / `if` / `where`)

A single comparison: `{ value, op, to }`, where `value` is usually a template ([typed splice](/plans/template-language#typed-splice) keeps numbers numeric).

| `op`                     | Meaning                                       |
| ------------------------ | --------------------------------------------- |
| `eq`, `ne`               | equality on any JSON value                    |
| `gt`, `lt`, `gte`, `lte` | numeric ordering                              |
| `empty`, `not_empty`     | arrays, strings, objects, null (`to` omitted) |
| `contains`               | substring, or array membership                |

## Body grammar (`then` / `else` / `do`)

Either form:

* **A single tool call** — `{tool_name, input}`, no id. Any catalog tool, including `plan__*` and `plan_and_execute`.
* **An inline step list** — normal steps with ids, run in order, with same-branch/same-iteration dataflow. Body ids must not reuse top-level ids and are invisible outside the body.

Bodies must not contain `decide`, `map`, or `reduce` (branches may contain `exit`; map/reduce bodies may not). `agent`, `ask`, and `filter` are legal in any body. For other nested control flow, call a `plan__*` sub-plan — cycles are detected and nesting is capped at 8.

## Step results of control steps

| Step               | Result shape                                                                                                                                                                    |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `exit` (not fired) | `{passed: true, verdict?, reason?}`                                                                                                                                             |
| `agent`            | `{output, iterations, tools_called, final}` — `output` conforms to `output_schema` when `final` is true; when the budget ran out first, `final` is `false` and `output` is `{}` |
| `ask`              | `{answer, answered, reason?}` — `answer` conforms to `output_schema`; `answered` is false when the `default` was used, and `reason` is then `"declined"` or `"unavailable"`     |
| `decide`           | `{branch, verdict, reason, result}` — `result` is the branch's single-call result or its last step's result                                                                     |
| `filter`           | `{count, items: […], dropped: […], dropped_count}` — both halves of the partition, input order                                                                                  |
| `map`              | `{count, results: […]}` in input order                                                                                                                                          |
| `reduce`           | `{count, result}` — the final accumulator                                                                                                                                       |


## Related topics

- [Ask steps](/plans/ask-step.md)
- [CLI reference](/reference/cli.md)
- [Agent steps](/plans/agent-step.md)
- [Built-ins](/tools/builtins.md)
- [CI checks](/cookbook/ci-checks.md)
