# Execution
> Running an approved plan as parallel workstreams — the graph schema, how targeting is pinned, how write contracts are enforced, and the three ways reality is allowed to diverge.
Source: https://ivar.run/docs/guide/execution

import { Callout } from 'fumadocs-ui/components/callout';
import { Step, Steps } from 'fumadocs-ui/components/steps';

```text
/ivar-execute <plan-path>
```

Execution takes an approved plan and runs it as several workstreams at once,
each with its own agent and its own slice of the filesystem.

## Before it will run

- A **feature session** — `IVAR_FEATURE` and `IVAR_SESSION_ID` both set, and
  `$IVAR_SESSION_PATH/state.json` readable.
- The feature exists in the hall.
- Every planning gate passed: Requirements, Analysis and Plan approved.

## The graph schema

```json title="execution-graph.json"
{
  "workstreams": [
    {
      "id": "api-contract",
      "title": "Define API contracts",
      "operations": ["OP-API-CONTRACT", "OP-AUTH"],
      "depends_on": [],
      "write_contract": ["ecbert/apps/ecbert/src/**"],
      "provider": "opencode"
    },
    {
      "id": "frontend",
      "title": "Update frontend components",
      "operations": ["OP-UI"],
      "depends_on": ["api-contract"],
      "write_contract": ["lagertha/src/**"]
    }
  ]
}
```

| Field | Meaning |
| --- | --- |
| `id` | the workstream id — matches `### <id>` in the plan byte for byte |
| `title` | what it is doing |
| `operations` | the `OP-*` ids from the plan it owns |
| `depends_on` | workstream ids that must finish first |
| `write_contract` | the paths it may write, relative to the view directory |
| `provider` · `model` · `agent` | optional targeting overrides |

<Callout type="warn" title="That is the whole schema">
  The parser denies unknown fields. `version`, `plan_path`, `repos` and `prompt`
  are all refused — there is no graph schema version, and no per-workstream
  prompt to author. The executor's prompt is rendered from the plan itself, the
  workstream's operations and its write contract.
</Callout>

## Write contracts

The write contract is what keeps two parallel workstreams off the same file.
Paths are relative to the session view directory, where repos sit at the root:

```
api/src/**          ✓
repos/api/src/**    ✗ matches nothing
```

<Callout type="warn" title="A repos/ prefix silently matches nothing">
  There is no `repos/` level in the view directory. A glob written that way does
  not error — it never matches, and the guard then refuses every write the
  workstream tries to make.
</Callout>

Enforcement happens at two layers:

| Layer | Catches |
| --- | --- |
| Provider guard | a `Write` / `Edit` outside the contract, as it happens |
| Post-run audit | writes a shell command made without asking the guard |

The audit compares the worktrees against the commit the run started from, so
committing does not hide a stray write. It reads the difference both ways, which
also catches a run that threw away an uncommitted edit it inherited — a
`git checkout --` or `git reset --hard` is as much a contract violation as a
write.

## A run, start to finish

<Steps>
  <Step>
    **Decompose.** With no board yet, the plan is broken into a candidate graph
    — workstreams, their operations, their dependencies and their contracts.
  </Step>
  <Step>
    **Confirm who runs what.** The session's `provider` is the default every
    untargeted workstream inherits. The candidate's targeting is shown one line
    per workstream, and you are asked whether to change any of it — every time,
    even when everything is defaulted.

    ```
    api-contract  provider=opencode     model=—              agent=—
    frontend      provider=opencode     model=—              agent=—
    ```
  </Step>
  <Step>
    **Prepare.** The resolved `provider`, `model` and `agent` are written into
    each `### <workstream>` block in `plan.md` so the plan and the board agree,
    then the board is created.

    ```sh
    ivar feature execute prepare <feature> --graph-json <path> --session "$IVAR_SESSION_ID"
    ```
  </Step>
  <Step>
    **Approve.** The board sits at `AwaitingApproval` with the graph — including
    each workstream's provider, model and agent — for you to read.

    ```sh
    ivar feature execute approve <feature>
    ```
  </Step>
  <Step>
    **Tick.** Launches every workstream that is pending and whose dependencies
    have all succeeded, then blocks until the ones it launched terminate. When
    it returns, a wave has landed; if that made others ready, tick again.

    ```sh
    ivar feature execute tick <feature>
    ```
  </Step>
  <Step>
    **Reply.** When a workstream blocks on a question, answer it.

    ```sh
    ivar feature execute reply <answer> --feature <feature> --session <session>
    ```
  </Step>
</Steps>

<Callout title="tick is the wait, not a poll">
  There is nothing to poll while `tick` runs — the call itself blocks. It is
  also idempotent: run it as many times as you like, it only ever launches
  workstreams whose dependencies have already succeeded.
</Callout>

## Targeting is pinned at prepare

There is no command that changes a prepared workstream's provider, model or
agent. Retargeting means deleting `board.json` and re-authoring the graph.

That is why the question comes before `prepare` and why the answer belongs in
the candidate JSON — a question skipped at the cheap moment costs the whole
setup later. By approval time the resolved provider is explicit in both
`plan.md` and `board.json`, so no approved workstream is left relying on the
hall default.

## When reality diverges

Three situations, three paths, and exactly one applies:

| Situation | Path |
| --- | --- |
| Outside the approved scope, and isolatable | `ivar feature create <child> --parent <current>` |
| A structural correction to the approved plan | `ivar feature execute replan <feature> --plan <plan-path>` |
| Implementation-only local divergence inside an operation | `ivar feature execute reconcile <feature> --workstream <id> --description <text>` |

A **replan** folds the revised plan into a live board. Behaviour-changing
revisions pause the affected workstreams until each acknowledges:

```sh
ivar feature execute ack-revision <feature> --workstream <id>
```

Execution resumes only once every affected workstream has acknowledged.

A **reconcile** records the deviation in the execution journal and leaves the
plan standing — it is for the case where the implementation drifted, not the
design.

## Coordinator and executor

The two roles have different authority, and that split is what keeps a parallel
run from corrupting shared state.

**The coordinator** owns the feature and its tree. It is the one that creates a
child when an isolatable out-of-scope request arrives — without asking
permission, because the alternative is a queue of blocked workstreams waiting on
a question that has one right answer.

**The executor** never mutates shared feature state: it does not create,
reparent, promote, integrate, close or delete anything. It stops and reports an
isolatable request, and the coordinator creates the child.

<Callout type="warn" title="Never hand-edit the board">
  `graph.json`, `status.json` and the journal are written by the CLI. Editing
  them by hand desynchronises the board from the runs it is tracking.
</Callout>
