# nexus

> Durable task runs: a plan of tasks with disjoint write scopes, attempts you can recover after an interruption, independent verification, and a separate acceptance step.

Canonical: https://bmad-plus.rochetta.fr/docs/reference/cli-nexus/
Language: en
BMAD+ version: 0.23.1
Documentation reviewed: 2026-10-19
Author: Laurent Rochetta
License: MIT

BMAD+ documentation › [Reference](https://bmad-plus.rochetta.fr/docs/reference/) › nexus

When several tasks must be done and you want proof that each one was done, the Nexus agent can keep them in a durable run. The `nexus` command is that run’s ledger. You describe the tasks in a JSON plan; each task owns a folder or file it may write (its scope), and a list of check commands that decide whether its result is acceptable. The ledger records who tried what (an attempt), keeps the state across crashes, runs the checks itself, and only then lets you accept the task. A worker saying “done” is never evidence: the checks are. This page is for people who drive Nexus by hand, from CI, or who want to know what the agent does under the hood.

<a id="synopsis"></a>
## Synopsis

**In your project terminal**

```sh
npx bmad-plus nexus create --plan <file> [--json] [-d <path>]
npx bmad-plus nexus inspect [run] [--json] [-d <path>]
npx bmad-plus nexus recover-lock --input <file> [--json] [-d <path>]
npx bmad-plus nexus <start|launch|collect|record|reconcile|verify|retry|cancel|accept> <run> <task> [--input <file>] [--json] [-d <path>]
```

**Options of nexus**

| Flag | Argument | Default | What it does |
| --- | --- | --- | --- |
| `-d, --directory <path>` | folder | current folder | The project that holds the run. |
| `--plan <file>` | JSON file | none | The task plan. Only `create` takes it. |
| `--input <file>` | JSON file | none | Host identity, observation or retry request for an action that mutates a task, or the lock observation for `recover-lock`. Regular file, at most 1 MiB, JSON object. |
| `--json` | - | off | Print the durable state and evidence as JSON instead of the short summary. |

<a id="actions"></a>
## The actions

**Nexus actions**

| Action | Arguments | Input file | What it does |
| --- | --- | --- | --- |
| `create` | `--plan FILE` | no (it uses `--plan`) | Validates the plan and creates the run. Refuses an existing run id. |
| `inspect` | `[RUN]` | none allowed | Read-only. Without a run id, lists the run ids and the lock state. With one, shows tasks, attempts, evidence and observations. |
| `recover-lock` | - | required | Explicitly removes an abandoned writer lock, after you confirm its owner stopped. |
| `start` | `RUN TASK` | required | Begins an attempt for a host-owned task, bound to an exact host handle and session. |
| `launch` | `RUN TASK` | optional | Starts the task’s process backend in the foreground and waits. With `{"retry":true}` creates a new attempt. |
| `collect` | `RUN TASK` | required | Takes the receipt of a launched process attempt (exact attempt id and backend). |
| `record` | `RUN TASK` | required | A host reports the outcome of its attempt: `completed`, `failed`, `blocked` or `ambiguous`. |
| `reconcile` | `RUN TASK` | required | Settles an interrupted or cancelled attempt as `completed`, `failed` or `cancelled`, once you have checked its effects. |
| `verify` | `RUN TASK` | none allowed | Runs the plan’s check commands and records real receipts. |
| `retry` | `RUN TASK` | required | Opens a new attempt after a reconciled failure, within the attempt budget. |
| `cancel` | `RUN TASK` | required | Records a cancellation request with a reason. Signals no process of the host. |
| `accept` | `RUN TASK` | none allowed | Accepts the current workspace for the task, after rechecking evidence. |

<a id="three-outcomes"></a>
## Execution, verification, acceptance

Nexus keeps three questions apart. Execution: did the worker finish? A host reports it with `record`, or the foreground supervisor collects the real exit of the process it started. Verification: does the work pass the plan’s checks? Nexus runs them itself, with literal arguments and no shell, and compares the declared artifacts and the whole write scope before and after. Acceptance: is the current workspace still the one that passed? `accept` rechecks artifact hashes, protected resources and the accepted state of every dependency. A failed check prevents acceptance even if the worker succeeded. Nexus never commits, merges, copies files or deploys.

<a id="plan"></a>
## The plan file

**A minimal plan with one task**

```json
{
  "id": "example",
  "maxParallel": 1,
  "config": { "language": "English" },
  "tasks": [
    {
      "id": "result",
      "objective": "Produce the requested result in work/result.txt.",
      "acceptance": ["The result satisfies the independent check."],
      "dependsOn": [],
      "scope": ["work"],
      "artifacts": ["work/result.txt"],
      "resources": ["checks/check-result.cjs"],
      "checks": [
        {
          "id": "result-content",
          "command": "node",
          "args": ["checks/check-result.cjs"],
          "cwd": ".",
          "timeoutMs": 30000
        }
      ],
      "maxAttempts": 2,
      "deadlineMs": 3600000,
      "idempotent": true,
      "authorizations": ["Edit work/ and run the listed local check."]
    }
  ]
}
```

**Plan fields and their rules**

| Field | Rule |
| --- | --- |
| `id` | Run id: 1 to 64 lowercase letters, digits, `_` or `-`. Same rule for task ids. |
| `maxParallel` | Between 1 and 32 attempts active at once. |
| `config` | Non-secret primitive settings only. Secret-looking keys are refused. |
| `tasks` | 1 to 200 tasks. Ids unique; dependencies must exist and must not form a cycle. |
| `objective`, `acceptance` | What the task must produce and the statements the result must satisfy. |
| `dependsOn` | Task ids that must be accepted first. |
| `scope` | Files or folders the worker may write. Scopes of different tasks must be disjoint, even when one task depends on the other. Never `.git` or Nexus’s own state. |
| `artifacts` | Files that must exist inside the task’s scope when it is verified. |
| `resources` | Protected files: the verifier and every instruction file given to the worker. They are hashed and must lie outside every scope. |
| `checks` | 1 to 30 per task. `command` is `node` or an absolute executable path; `args` is a literal array; every file argument must be a declared resource; `timeoutMs` from 10 to 300000. |
| `maxAttempts` | 1 to 10. Verification rounds are bounded by it too. |
| `deadlineMs` | 100 to 86400000. A deadline is an observation, not proof that the work stopped. |
| `idempotent` | Boolean. A non-idempotent task needs an explicit `retrySafe: true` reconciliation before a retry. |
| `authorizations` | Statements of what was authorised. Workers cannot edit them. |
| `execution` | Optional. Selects a process backend instead of a host (see below). |

<a id="backends"></a>
## Backends: host or process

By default a task is host-owned: your assistant does the work with its own tools, and tells Nexus the outcome with `record`. Nexus then verifies. Adding an `execution` object to a task makes it a process task instead: `launch` starts the program in the foreground, waits for it, and stores a checksummed receipt of what really happened. There are two adapters.

**Execution adapters**

| Adapter | Fields | Notes |
| --- | --- | --- |
| `command` | `command`, `args`, `cwd`, `input`, `resources`, `blockedExitCodes`, `outputLimitBytes` | You supply the command; it is never inferred from task text. The executable is pinned by SHA-256. Zero exit means completed; a code listed in `blockedExitCodes` means blocked. No shell, no environment override. |
| `codex-exec` | `command` (absolute path to the installed `codex`), `input`, `instructions`, `sandbox`, optional `model` | Runs the installed Codex CLI non-interactively. `sandbox` is `read-only` (default) or `workspace-write`. Completion needs exit zero plus the thread-started and turn-completed events; only an explicit `approval_required` error becomes blocked. |

> **Warning · Not a sandbox** Workers share your project and your operating-system user. They can ignore their declared scope; checksums detect accidental change but are not signatures. There is no daemon, no scheduler and no process-tree supervision: each `launch` is one foreground command. `workspace-write` is a request to the installed Codex host, not a Nexus sandbox. Use an isolated project or host policy when the worker is not trusted.

<a id="limits"></a>
## Limits

**Supported bounds**

| Bound | Value |
| --- | --- |
| Tasks per run | 200 |
| Active attempts per run | 32 |
| Attempts per task | 10 |
| Checks per task | 30 |
| Time per check | 10 to 300000 ms |
| Captured output per check | 64 KiB |
| Files in one scope snapshot | 10000, each at most 16 MiB |
| Run state file | 16 MiB |
| Input and plan files | 1 MiB, regular file, no symbolic link |

<a id="reads-writes"></a>
## What it reads and writes

- State: `.bmad-plus/nexus/runs/<run-id>.json`, a checksummed envelope written through a flushed temporary file and an atomic rename.
- Locks: `.bmad-plus/nexus/writer.lock` (one mutation at a time) and, during an exceptional recovery, `.bmad-plus/nexus/recovery.lock`. A removed lock is archived under `.bmad-plus/nexus/lock-recovery/`.
- `inspect` writes nothing and keeps working while a verification runs. All other actions except `create` change an existing run; `create` makes it.
- Reads your plan and input files, the declared resources and artifacts, and snapshots the write scopes. Verification runs your check commands; `launch` runs the task’s process. Those are the only processes Nexus starts, and only when you invoke them.
- Check commands get a minimal environment (`PATH`, `SYSTEMROOT`, `WINDIR`, `TEMP`, `TMP`, `TMPDIR` plus fixed locale `C` and timezone `UTC`). Credentials and `NODE_OPTIONS` are not forwarded. Put no secret in arguments or in output.

<a id="exit-codes"></a>
## Exit codes

**Exit codes of nexus**

| Code | Meaning |
| --- | --- |
| 0 | The action succeeded. |
| 1 | `verify` did not pass; `launch` did not end `completed`; `collect` did not end `completed`; or any error. A non-zero code on `launch` or `collect` says what the process did, never that the task is acceptable. |

<a id="real-output"></a>
## Reading the output

The shortest complete run needs three small files next to `nexus-plan.json`. The verifier is the one the plan declares as a resource; `host.json` is the identity your assistant gives to `start`; `rec.json` is the outcome it reports to `record`, with the attempt id that `start` printed.

**checks/check-result.cjs, the verifier**

```text
const fs = require("node:fs");
const text = fs.readFileSync("work/result.txt", "utf8");
if (!text.includes("done")) {
  console.error("work/result.txt must contain done");
  process.exit(1);
}
```

**host.json, given to start**

```json
{ "backend": { "kind": "host", "id": "my-assistant", "sessionId": "session-1" } }
```

**rec.json, given to record (the attempt id is the one start printed)**

```json
{
  "attemptId": "c3bbafd1-62ef-4b8b-ac72-0d9b09932427",
  "backend": { "kind": "host", "id": "my-assistant", "sessionId": "session-1" },
  "outcome": "completed",
  "summary": "Wrote work/result.txt."
}
```

**Create, start, record, verify and accept a run**

```text
$ npx bmad-plus nexus create --plan nexus-plan.json
Nexus run example — durable task coordination
result: not started; verification=pending; acceptance=pending
Use inspect --json for recorded evidence and reconciliation observations.
$ npx bmad-plus nexus start example result --input host.json
Nexus run example — durable task coordination
result: running; verification=pending; acceptance=pending
  Attempt: c3bbafd1-62ef-4b8b-ac72-0d9b09932427; host: my-assistant/session-1
Use inspect --json for recorded evidence and reconciliation observations.
$ npx bmad-plus nexus record example result --input rec.json
Nexus run example — durable task coordination
result: completed; verification=pending; acceptance=pending
  Attempt: c3bbafd1-62ef-4b8b-ac72-0d9b09932427; host: my-assistant/session-1
Use inspect --json for recorded evidence and reconciliation observations.
$ npx bmad-plus nexus verify example result
Nexus run example — durable task coordination
result: completed; verification=passed; acceptance=pending
  Attempt: c3bbafd1-62ef-4b8b-ac72-0d9b09932427; host: my-assistant/session-1
Use inspect --json for recorded evidence and reconciliation observations.
$ npx bmad-plus nexus accept example result
Nexus run example — durable task coordination
result: completed; verification=passed; acceptance=accepted
  Attempt: c3bbafd1-62ef-4b8b-ac72-0d9b09932427; host: my-assistant/session-1
Use inspect --json for recorded evidence and reconciliation observations.
$ npx bmad-plus nexus inspect example
Nexus run example — durable task coordination
result: completed; verification=passed; acceptance=accepted
  Attempt: c3bbafd1-62ef-4b8b-ac72-0d9b09932427; host: my-assistant/session-1
  Accept only after rechecking current artifacts and resources.
Use inspect --json for recorded evidence and reconciliation observations.
# The words after “acceptance=” and the extra hint lines depend on the run state; read them with inspect --json.
```

Output captured from a real run. Source: Replayed with the 0.23.0 package in a throwaway Git project; the attempt id changes on every run

Running `verify` right after `create` is refused, because nothing has been recorded yet: `Nexus: Nexus: Verification requires completed, unaccepted, reconciled work.` Start and record first, as above. Note the doubled prefix: the runtime’s own messages already begin with `Nexus:`, and the command adds its own.

Each task line reads `<task>: <execution>; verification=<status>; acceptance=<state>`, followed by the last attempt with its backend. `inspect --json` adds, per task, `resourcesCurrent`, `evidenceEligible`, `stale`, `staleReason` and `overdue`, so an old accepted record cannot claim that files edited later still pass. In JSON mode, errors are printed as `{"schemaVersion":1,"status":"error","error":"..."}`.

<a id="recovery"></a>
## Interruptions, cancellation, retries

- An interrupted or disconnected attempt keeps its capacity until `reconcile` settles it. The observation needs `ownerStopped: true`, a statement from you that you checked, not telemetry. For a task that is not `idempotent`, a retry also needs `retrySafe: true`.
- `cancel` only records a request. For a host task, the host stops its own work; for a process task, only the original foreground supervisor may signal the child it created. Nexus never kills a saved PID.
- `retry` (or `launch` with `{"retry":true}`) opens a new attempt and consumes `maxAttempts`. A new backend session does not reset the limit.
- A verification that times out is recorded as ambiguous, because descendants of the check could still have effects. Inspect before retrying.
- `recover-lock` takes the exact lock hash from `inspect`, `ownerStopped: true` and a non-empty `note`. A live local writer blocks it. Age alone never authorises removal.

<a id="errors"></a>
## Errors and what to do

Errors are printed as `Nexus: <message>` on the error stream. The runtime’s own messages already start with `Nexus:`, so most errors show the prefix twice (`Nexus: Nexus: Run ID must use ...`).

**nexus errors**

| Message | Cause | What to do |
| --- | --- | --- |
| `Use nexus create --plan FILE, inspect RUN, or start\|launch\|collect\|record\|reconcile\|verify\|retry\|cancel\|accept RUN TASK.` | Unknown action or missing run/task ids. | Check the synopsis. |
| `--plan requires a JSON file.` / `--input requires a JSON file.` | The option is missing for an action that needs it. | Add the file; see the actions table. |
| `--input must name a regular JSON file no larger than 1 MiB.` | Not a regular file, a symbolic link, or too big. | Use a plain JSON file. |
| `verify executes its recorded contract and takes no input file.` | `--input` given to `verify` or `accept`. | Remove `--input`. |
| `Overlapping exclusive write scopes: <a> and <b>` | Two tasks share a path in their scopes. | Make the scopes disjoint. |
| `Verifier/resource lies in a worker write scope: <file>` | A protected file is inside a task’s scope. | Move the verifier outside every scope. |
| `Run already exists; inspect it instead.` | `create` with an existing run id. | Use `inspect`, or a new plan id. |
| `Stale or missing attempt identity.` / `Backend handle/session does not own this attempt.` | The input names another attempt, handle or session. | Copy the exact attempt id and backend returned by `start` or `launch`. |
| `Protected verifier or agent resource changed since the plan was created.` | A declared resource was edited after `create`. | Restore it, or create a new run from an updated plan. |
| `Dependency has not been accepted: <task>` | A dependency is not accepted yet. | Verify and accept the dependency first. |
| `Run capacity is occupied until active work is reconciled.` | Active or interrupted attempts fill `maxParallel`. | Collect, record or reconcile them. |
| `Another mutation owns writer.lock; inspect before explicit recovery.` | Another command is running, or one was killed. | Wait; if it was killed, use `inspect` then `recover-lock`. |
| `Corrupt or foreign run state; original bytes were preserved.` | The run file does not match its checksum. | Restore the file from a backup; nothing is auto-repaired. |
| `Current attempt has no eligible passed verification.` | `accept` before a passing `verify`. | Run `verify`; fix the work if it fails. |
| `Stale evidence: checked artifacts or write scope changed.` | Files changed after the verification. | Verify again. |

<a id="examples"></a>
## Examples

<a id="example-cli-nexus-split-and-prove"></a>
### Split work and prove each part

Three independent changes touch different folders. You want each one verified by a check you wrote, not by the worker’s word.

**In your AI assistant**

```text
Nexus, split the approved work into independent tasks, each with its own write folder and a check command I can run. Create a durable run with `bmad-plus nexus create`, start one attempt per task, and do not accept anything until `bmad-plus nexus verify` has passed for it.
```

**What you should get** Nexus writes a plan with disjoint scopes and runs `create`, which refuses overlapping scopes or a verifier inside a scope. For each task the host does the work and records the outcome; `verify` runs your check and stores its receipt; `accept` succeeds only when the evidence is current. A task whose check fails stays unaccepted and can be retried within `maxAttempts`.

<a id="example-cli-nexus-recover-after-crash"></a>
### Recover after a crash

The machine restarted while a task was running. You are not sure whether the worker still holds files open.

**In your AI assistant**

```text
Nexus, inspect the run `release-prep` with `bmad-plus nexus inspect release-prep --json`. Tell me which attempts are unresolved and what I must check before you reconcile them. Do not retry anything on your own.
```

**What you should get** `inspect` is read-only and reports each attempt with its backend, the recorded outcome and the observations (`stale`, `overdue`). Unresolved attempts keep their capacity. Once you have checked the effects and confirm the owner stopped, `reconcile` records `completed`, `failed` or `cancelled`; only then can a retry open a new attempt. If a writer lock was left behind, `inspect` without a run id shows its hash for `recover-lock`.

**See also**

- [CLI overview and exit codes](https://bmad-plus.rochetta.fr/docs/reference/cli-overview/)
- [swarm: export a fanout as a Nexus plan](https://bmad-plus.rochetta.fr/docs/reference/cli-swarm/)
- [uat: the acceptance gate used as a check](https://bmad-plus.rochetta.fr/docs/reference/cli-uat/)
- [Project layout](https://bmad-plus.rochetta.fr/docs/reference/project-layout/)
- [Troubleshooting](https://bmad-plus.rochetta.fr/docs/faq/troubleshooting/)

[BMAD+ documentation](https://bmad-plus.rochetta.fr/docs/) · [Reference](https://bmad-plus.rochetta.fr/docs/reference/)

[Source on GitHub](https://github.com/lrochetta/BMAD-PLUS)
