# bmad-plus swarm

> Deal, brief, check, file and tally a swarm of sub-agents within a budget: panel, jury or fanout. Flags, run tree, approval at exit 2, honesty rules, real output and errors.

Canonical: https://bmad-plus.rochetta.fr/docs/reference/cli-swarm/
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/) › bmad-plus swarm

A swarm is many sub-agents doing the same kind of work once each: thirty simulated buyers reading a pitch, three jurors holding one lens each, one writer per page. `bmad-plus swarm` is the bookkeeping around it. It deals the units, writes one brief per sub-agent, checks every answer file, files a reply a sub-agent only printed, and counts the valid answers. It never calls a model, never opens a network connection and never starts a process: your assistant (the host) launches the sub-agents, and the command only reads and writes files under `.bmad-plus/swarm/`. Use it when a panel, a jury or a fan-out of independent units is requested. 0.23.0 ships no swarm definition in any pack; you or a pack supply the definition file.

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

**In your project terminal**

```sh
npx bmad-plus swarm init --definition FILE [--profile FILE | --units FILE] [--context NAME=FILE]... [--n N] [--seed N] [--approved]
npx bmad-plus swarm briefs RUN [--wave N]
npx bmad-plus swarm check RUN
npx bmad-plus swarm save RUN UNIT --input FILE
npx bmad-plus swarm tally RUN [--out FILE]
npx bmad-plus swarm inspect RUN
npx bmad-plus swarm plan RUN [--out FILE]
```

**Flags of bmad-plus swarm**

| Flag | Argument | Default | What it does |
| --- | --- | --- | --- |
| `<action>` | `init`, `briefs`, `check`, `save`, `tally`, `inspect`, `plan` | required | Which step to run. |
| `[run]`, `[unit]` | run id, unit id | none | Run id such as `buyer-panel-20261018-oqu7` (every action except `init`); unit id such as `B002` (`save` only). |
| `-d, --directory <path>` | path | current directory | Project directory. |
| `--definition <file>` | path | none | Swarm definition, project-relative JSON. `init` only; required. |
| `--profile <file>` | path | none | Panel profile, project-relative JSON. `init`; required for a panel, refused for other kinds. |
| `--units <file>` | path | none | Fanout units, project-relative JSON. `init`; required for a fanout, refused for other kinds. |
| `--context <name=file>` | NAME=FILE | none | Context file included verbatim in every brief. Repeatable. Every name the definition declares is required; an undeclared name is refused. |
| `--n <count>` | integer 1 to 1000 | none | Number of units to deal for a panel. Refused for a jury or a fanout. |
| `--seed <number>` | integer 0 to 2147483647 | drawn once, then recorded | Dealing seed. Panel only. Same profile, same `--n`, same seed: same cards. |
| `--approved` | none | off | Records that a human agreed to exceed the budget. Never pass it on your own. |
| `--wave <count>` | integer 1 to 1000 | configured wave (10) | Sub-agents launched together. `briefs` only. |
| `--input <file>` | path | none | Reply a sub-agent printed. `save` only; required; outside `.bmad-plus/`; at most 1 MiB. |
| `--out <file>` | path | inside the run | Output file for `tally` and `plan`. Must be under `.bmad-plus/swarm/` (see Reads, writes and limits). |
| `--json` | none | off | Machine-readable output with `schemaVersion: 1` and a `status` field. |

<a id="kinds"></a>
## The three kinds of swarm

**Kinds, set by the definition file**

| Kind | Units come from | Typical use | Tally |
| --- | --- | --- | --- |
| `panel` | A profile: dimensions with shares, dealt by exact quota and seeded (`--profile`, `--n`, `--seed`). | Simulated buyers, readers or users reading a pitch. | Rate by group, counts, quotes, numbers. |
| `jury` | The definition itself: units listed one by one, each with a `lens`. | An adversarial read of a pitch, a page or a plan. | Counts and quotes per lens. |
| `fanout` | A units file (`--units`): each unit has its own disjoint write `scope`. | Pages to write, modules to document: independent work in parallel. | Rate and numbers; can be exported as a durable Nexus plan with `plan`. |

A panel and a jury run as session sub-agents: the host launches them, waits, then reads their files. A fanout can run the same way, or be exported with `plan` when the work must survive the session, needs an independent check per unit or edits the repository (see `bmad-plus nexus`). The definition file (JSON) names the kind, the brief template, the answer fields (`boolean`, `integer`, `number`, `string`, `enum`, `list`), the tally and a one-sentence `disclosure` of 1 to 500 characters.

<a id="budget"></a>
## Budget, waves and approval

Three settings in the `swarm` block of `_bmad/config.yaml` bound a run. An absent file or block means the defaults below; any other value is refused. `init` compares the number of units with the budget: a larger run is created in state `needs-approval`, writes no brief and exits with code 2. The assistant then has to ask you, in one sentence with the number, and only after a yes re-run `init` with `--approved` and the seed the refusal printed.

**The swarm block of _bmad/config.yaml**

| Key | Default | Allowed | Effect |
| --- | --- | --- | --- |
| `budget` | 26 | integer 1 to 1000 | Sub-agents one run may use before the host must ask you. |
| `wave` | 10 | integer 1 to 1000 | Sub-agents launched together; `briefs` groups the pending units in waves of this size (override with `--wave`). |
| `media_credits` | `ask` | exactly `ask` | Paid image, video or audio generation is never covered by a budget: the host asks before each such run. |

> **Warning · Approval belongs to a person** `--approved` is stored in the run as `approval: user`. The swarm skill allows it only after a human yes, whatever the number of units. A run is never approved by the assistant’s own judgement.

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

**What each action reads and writes**

| Action | Reads | Writes | Exit |
| --- | --- | --- | --- |
| `init` | Definition, template, profile or units, contexts, `_bmad/config.yaml`. | The run tree (frozen inputs and `run.json`). | 0 ready, 2 needs approval, 1 refused. |
| `briefs RUN` | The run tree and the answers. | A brief for every unit without a valid answer; the `answers/` directory. | 0, or 2 if the run needs approval. |
| `check RUN` | The run tree and the answers. | Nothing. | 0 complete, 1 incomplete. |
| `save RUN UNIT --input F` | The run tree and the reply file. | `answers/<unit>.json`. | 0 saved, 1 refused. |
| `tally RUN [--out F]` | The run tree and the answers. | `tally.json` and `tally.md` (or `--out`). | 0, or 2 if the run needs approval. |
| `inspect RUN` | The run tree, answers, briefs. | Nothing. | 0. |
| `plan RUN [--out F]` | The run tree (fanout only). | All briefs, `checks/`, `nexus-plan.json` (or `--out`). | 0 planned, 2 needs approval, 1 not a fanout. |

Typical order: `init`, then `briefs`, the host launches one sub-agent per brief with exactly the instruction "Read the brief and follow it exactly; it is your whole instruction", then `check`. Run `briefs` once more to give missing units a second chance (it only renders units without a valid answer), file any printed reply with `save`, then `tally`. A unit still missing after the second wave stays missing and is named in the tally. `inspect` summarises a run at any time without writing.

<a id="run-tree"></a>
## The run tree

**Created under your project by init**

```text
.bmad-plus/swarm/runs/<definition-id>-<yyyymmdd>-<4 chars>/
  run.json            inputs hashes, n, seed, wave, budget, approval, status
  definition.json     the normalized definition, frozen
  template.md         the brief template, frozen
  cards.json          the dealt or listed units
  context/<name>.md   each context file, frozen
  briefs/<unit>.md    written by briefs (and plan)
  answers/<unit>.json written by the sub-agent, or filed by save
  tally.json, tally.md
  checks/answer.cjs, checks/swarm-validate.cjs, nexus-plan.json   (fanout, written by plan)
```

The 4-character suffix is random and an existing directory is never reused. Inputs are frozen at `init`: editing a context or the definition afterwards changes nothing for that run. Statuses: `needs-approval` (terminal), `ready`, `briefed`, `tallied`. A tally can be re-run, and `briefs` can be re-run after a tally. Reports requested with `--out` go under `.bmad-plus/swarm/reports/<run id>/`.

<a id="honesty"></a>
## Honesty rules

- Simulated units are not people. The tally opens with the definition’s disclosure sentence, before any figure. Rates from a panel are an upper bound to confirm with real people, never a measurement.
- Quotes are simulated: present them as what a simulated unit wrote, never as a testimonial or an interview.
- The moderator never answers for a unit: no hand-written answer file, no "fixing" an invalid one. `save` only extracts and validates what a sub-agent printed.
- Briefs are never edited. A change belongs in the template or the definition, then a new run.
- Same seed, same cards: a before/after comparison of two pitches re-runs the same profile with the same `--n` and `--seed`, so only the context changed.
- The runtime filters nothing: card values, pool entries and context files reach the sub-agent word for word. Read a context before passing it; it is copied into every brief, secrets and instructions alike.
- A valid answer is a well-formed one. Nothing proves that a sub-agent read its brief or told the truth.

<a id="reads-writes"></a>
## Reads, writes and limits

- Writes only under `.bmad-plus/swarm/`. `--out` for `tally` and `plan` must stay in `.bmad-plus/swarm/reports/<run id>/` or in the run’s own tree, never over `answers/`, `briefs/`, `context/`, `checks/`, `run.json`, `cards.json`, `definition.json`, `template.md`, `nexus-plan.json`, `tally.json` or `tally.md`.
- Every path you name (`--definition`, `--profile`, `--units`, `--context`, `--input`) is project-relative with `/`; absolute paths, backslashes, `..`, `.git` and anything through a symlink are refused. Those files may not come from `.bmad-plus/`.
- Limits: 1 to 1000 units per run; budget and wave 1 to 1000; seed 0 to 2147483647; inputs at most 16 MiB; a printed reply for `save` at most 1 MiB; at most 50 answer fields; at most 20 contexts per definition; at most 5 `groupBy` fields.
- Unit ids are letters, digits, `_` or `-` (up to 64 characters; fanout ids are lowercase). Dealt panel ids are the prefix plus the position padded to at least three digits (`B001`).
- Files are written through a temporary file and renamed into place. No network, no child process, no `eval`.

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

**Exit codes of bmad-plus swarm**

| Code | Meaning |
| --- | --- |
| 0 | The action succeeded; for `check`, every unit has a valid answer. |
| 1 | `check` found units missing or invalid (status `incomplete`), or any error (printed as `Swarm: <message>`). |
| 2 | Status `needs-approval`: the run has more units than the budget and no `--approved`. Ask a person, then re-run `init --approved --seed <seed>`. `briefs`, `save`, `tally` and `plan` on such a run also exit 2 and write nothing. |

<a id="sample-files"></a>
## The sample files

The transcripts and the examples below use the files printed here. They live in the repository under `tests/fixtures/swarm/` and are not part of the npm package, so they are given in full: create them under `swarm/` in your project and the commands reproduce the transcripts, except for the run ids (a date and a random suffix). The first six belong to the buyer panel; the last three belong to the fan-out example at the end of the page.

**swarm/buyer-panel.json, the definition of the panel**

```json
{
  "schemaVersion": 1,
  "id": "buyer-panel",
  "kind": "panel",
  "pack": "marketfit",
  "title": "Simulated buyers see the pitch",
  "units": { "source": "profile", "idPrefix": "B" },
  "brief": { "template": "briefs/buyer.md", "context": ["pitch"] },
  "answer": {
    "required": ["id", "buys", "reason_code", "reason"],
    "fields": {
      "buys": { "type": "boolean" },
      "reason_code": { "type": "enum", "values": ["price", "habit", "need", "trust", "other"] },
      "reason": { "type": "string", "max": 600 },
      "price_floor": { "type": "number", "min": 0 },
      "visits_per_week": { "type": "integer", "min": 0, "max": 14 },
      "would_tell": { "type": "list", "maxItems": 3 }
    }
  },
  "tally": {
    "rate": "buys",
    "groupBy": ["segment", "behaviour"],
    "counts": ["reason_code"],
    "quotes": { "reason": 2 },
    "numbers": ["price_floor"]
  },
  "disclosure": "Simulated units are not people. Read rates as an upper bound and confirm with real ones."
}
```

**swarm/profile.json, the profile the 30 buyer cards are dealt from**

```json
{
  "schemaVersion": 1,
  "subject": "Orbit espresso bar",
  "target": "Urban professionals who buy coffee most days",
  "dimensions": [
    {
      "name": "segment",
      "values": [
        { "value": "early career", "share": 0.45, "ranges": { "income": [38000, 85000] } },
        { "value": "mid career", "share": 0.4, "ranges": { "income": [85000, 150000] } },
        { "value": "high earner", "share": 0.15, "ranges": { "income": [150000, 210000] } }
      ]
    },
    {
      "name": "behaviour",
      "values": [
        { "value": "daily regular", "share": 0.2, "note": "same drink, same place, most mornings" },
        { "value": "explorer", "share": 0.5 },
        { "value": "occasional", "share": 0.3, "note": "a treat, not a routine" }
      ]
    }
  ],
  "ranges": { "age": [24, 38], "walk_minutes": [1.5, 12] },
  "pools": {
    "objection": ["too steep for a drink", "my place is closer", "I do not know the brand"],
    "habit": ["you pass the street on the way to work", "you order the same thing every day"]
  }
}
```

**swarm/pitch.md, the text the buyers read**

```md
Orbit opens at 7, two streets from the station. A flat white is 4.20, the loyalty card gives
the tenth one free, and the bar keeps your usual order under your name.
```

**swarm/briefs/buyer.md, the brief template (each placeholder in double braces is filled per unit)**

```md
# You are unit {{unit.id}} of run {{run.id}}

You are one simulated buyer. Stay inside this card for the whole task.

- Segment: {{card.segment}}, income {{card.income}}
- Behaviour: {{card.behaviour}} ({{card.behaviour_note}})
- Age {{card.age}}, {{card.walk_minutes}} minutes on foot from the bar
- Your first objection: {{card.objection}}
- Your habit: {{card.habit}}

## The pitch

{{context.pitch}}

## Your answer

Write a JSON object to `{{answer.path}}` with the fields `id` (exactly "{{unit.id}}"), `buys`,
`reason_code` (price, habit, need, trust or other), `reason` (your own words, 600 characters at
most) and, when you have one, `price_floor`.

{{disclosure}}
```

**swarm/reply-ok.json, a valid reply for unit B001**

```json
{ "id": "B001", "buys": true, "reason_code": "habit", "reason": "The loyalty card and my usual order under my name suit my mornings." }
```

**swarm/reply-bad.txt, a reply that the validator refuses (buys must be true or false)**

```json
{ "id": "B002", "buys": "maybe", "reason_code": "price", "reason": "Too steep for a drink." }
```

**swarm/units.json, the three pages of the fan-out example**

```json
{
  "schemaVersion": 1,
  "units": [
    { "id": "about", "scope": "site/about", "title": "About us", "tone": "warm" },
    { "id": "pricing", "scope": ["site/pricing", "site/data/prices.json"], "title": "Pricing", "tone": "plain" },
    { "id": "contact", "scope": "site/contact", "title": "Contact", "tone": "short" }
  ]
}
```

**swarm/page-fanout.json, the fan-out definition**

```json
{
  "schemaVersion": 1,
  "id": "site-pages",
  "kind": "fanout",
  "title": "One sub-agent per page of the site",
  "units": { "source": "input" },
  "brief": { "template": "briefs/page.md", "context": [] },
  "answer": {
    "required": ["id", "done", "words"],
    "fields": {
      "done": { "type": "boolean" },
      "words": { "type": "integer", "min": 0 },
      "note": { "type": "string", "max": 200 }
    }
  },
  "tally": { "rate": "done", "numbers": ["words"] },
  "disclosure": "Each page was written by a separate sub-agent from the same brief; read them together before publishing."
}
```

**swarm/briefs/page.md, the brief template of the fan-out**

```md
# Page {{unit.id}}

Write the page "{{card.title}}" under `{{card.scope}}`, {{card.tone}} in tone. Touch nothing
outside that folder.

When the page is written, write a JSON object to `{{answer.path}}` with `id` ("{{unit.id}}"),
`done`, `words` (the word count) and an optional `note`.

{{disclosure}}
```

<a id="real-output"></a>
## Real output

These transcripts come from the human acceptance run of 0.23.0: a panel of 30 simulated buyers in a demo app, against the default budget of 26. The `npm notice` lines and the shell prompt are removed. The definition (`swarm/buyer-panel.json`) and profile are the repository’s test fixtures, copied into the demo app.

**30 units against a budget of 26: refused (exit code 2)**

```text
$ npx bmad-plus swarm init --definition swarm/buyer-panel.json --profile swarm/profile.json --context pitch=swarm/pitch.md --n 30 --seed 7 --directory .\demo-app
buyer-panel-20261018-ungp: needs approval. The run needs 30 sub-agents and the budget is 26: ask the user, then re-run init with --approved --seed 7.
```

Output captured from a real run. Source: Captured during the 0.23.0 acceptance run, 2026-10-18 (step 6)

**After a human yes: --approved with the same seed**

```text
$ npx bmad-plus swarm init --definition swarm/buyer-panel.json --profile swarm/profile.json --context pitch=swarm/pitch.md --n 30 --seed 7 --approved --directory .\demo-app
buyer-panel-20261018-oqu7: panel of 30 units, budget 26, approval user.
Seed 7 (same seed, same cards).
Next: bmad-plus swarm briefs buyer-panel-20261018-oqu7
```

Output captured from a real run. Source: Captured during the 0.23.0 acceptance run, 2026-10-18 (step 6)

**Briefs, in waves of 10**

```text
$ npx bmad-plus swarm briefs buyer-panel-20261018-oqu7 --directory .\demo-app
buyer-panel-20261018-oqu7: 30 of 30 units need a sub-agent, in waves of 10.
Wave 1:
  B001  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B001.md
  B002  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B002.md
  B003  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B003.md
  B004  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B004.md
  B005  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B005.md
  B006  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B006.md
  B007  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B007.md
  B008  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B008.md
  B009  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B009.md
  B010  C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\briefs\B010.md
# Waves 2 (B011 to B020) and 3 (B021 to B030) follow in the same format; they are left out here.
Each sub-agent: Read the brief and follow it exactly; it is your whole instruction.
Disclosure: Simulated units are not people. Read rates as an upper bound and confirm with real ones.
```

Output captured from a real run. Source: Captured during the 0.23.0 acceptance run, 2026-10-18 (step 6; waves 2 and 3 shortened)

**One valid answer, one refused reply, the tally**

```text
$ npx bmad-plus swarm save buyer-panel-20261018-oqu7 B001 --input swarm/reply-ok.json --directory .\demo-app
buyer-panel-20261018-oqu7: answer of B001 filed at C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\answers\B001.json
$ npx bmad-plus swarm check buyer-panel-20261018-oqu7 --directory .\demo-app
buyer-panel-20261018-oqu7: 1 of 30 valid answers.
  B002  missing: no answer file
  B003  missing: no answer file
# B004 to B030 are listed the same way (27 more lines).
$ npx bmad-plus swarm save buyer-panel-20261018-oqu7 B002 --input swarm/reply-bad.txt --directory .\demo-app
Swarm: Reply of unit B002 refused: field "buys" must be true or false.
$ npx bmad-plus swarm tally buyer-panel-20261018-oqu7 --directory .\demo-app
C:\Users\you\demo-app\.bmad-plus\swarm\runs\buyer-panel-20261018-oqu7\tally.md
1 of 30 units counted; 29 skipped.
Disclosure: Simulated units are not people. Read rates as an upper bound and confirm with real ones.
```

Output captured from a real run. Source: Captured during the 0.23.0 acceptance run, 2026-10-18 (step 6; the 27 further missing lines of check are shortened; the first command (filing B001) was replayed afterwards with the same package; the Disclosure sentence is the one the same run printed from briefs)

How to read it: the first `init` created a refused run (`...-ungp`) and nothing else; the approved one is a different run (`...-oqu7`) with its own random suffix. Only B001 had answered, so the honest tally counts 1 unit and names 29 skipped; the disclosure sentence comes first, and it is also the first line of `tally.md`. The refused reply wrote nothing for B002.

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

**Messages printed by bmad-plus swarm**

| Message | Cause | What to do |
| --- | --- | --- |
| `<run>: needs approval. The run needs N sub-agents and the budget is B: ask the user, then re-run init with --approved --seed S.` (exit 2) | More units than `swarm.budget` and no `--approved`. | Ask a person in one sentence with the number. After a yes, re-run `init` with `--approved --seed S`. Or lower `--n`, or raise `swarm.budget` in `_bmad/config.yaml`. |
| `Swarm: Use swarm init --definition FILE [--profile FILE \| --units FILE] …` | Unknown action, or a run id or unit id missing or in the wrong place. | Check the synopsis: `save` takes a run and a unit, the other actions after `init` take a run only. |
| `Swarm: init needs --definition FILE.` | `init` without a definition. | Pass `--definition`. |
| `Swarm: A panel needs --profile FILE.` / `A panel needs --n, the number of units to deal.` / `A panel takes --profile, not --units.` | Panel started without its inputs. | Add `--profile` and `--n`; do not pass `--units`. |
| `Swarm: Only a panel takes --profile.` / `A jury has as many units as it lists; --n does not apply.` / `A seed only applies to a dealt panel.` / `A fanout needs --units FILE.` | Flags that do not fit the kind named in the definition. | Remove the flag, or use the right definition. |
| `Swarm: The definition needs --context NAME=FILE.` / `Context "x" is not declared by the definition.` / `A context is written NAME=FILE: …` | A declared context is missing, an extra one was passed, or the form is wrong. | Pass exactly the contexts the definition declares, as `--context name=path`. |
| `Swarm: _bmad/config.yaml: swarm.media_credits must be "ask".` / `…swarm.budget must be an integer from 1 to 1000.` / `…swarm has an unknown key "x".` | The `swarm` block holds a value or a key the runtime refuses. | Fix the block, or delete it to get the defaults (26, 10, `ask`). |
| `Swarm: Definition does not exist: …` / `…is not valid JSON: …` / `…exceeds N bytes` / `…is not a regular file` | The input file is missing, malformed, too large or not a file. | Fix the path or the JSON. |
| `Swarm: … may not reach .git` / `… may not come from the BMAD+ state tree` / `… refused: … (…)` | The path points into `.git`, `.bmad-plus/`, outside the project or through a link. | Use a project-relative path with `/`, outside `.bmad-plus/`. |
| `Swarm: Tally groupBy field "f" is absent from unit U.` | A card lacks a field the tally groups by. | Add the field to the profile, the list or the units file, or drop it from `tally.groupBy`. |
| `Swarm: A run id reads <definition>-<yyyymmdd>-<4 characters>.` / `Run record is not usable: …` | Wrong run id, or a damaged run tree. | Copy the id printed by `init`; if the tree was edited, start a new run. |
| `Swarm: Reply of unit U refused: …` / `No JSON object found in the reply filed for unit U.` | The printed reply has no JSON object, or it fails the answer contract (wrong type, missing field, wrong id). | Nothing was written. Ask the sub-agent again (re-run `briefs`) or leave the unit missing. Do not write the answer yourself. |
| `Swarm: Unit U already has a valid answer; it is kept.` / `Unknown unit U in run R.` | `save` on a unit that is answered, or not in the run. | Use `check` to see which units are open. |
| `Swarm: save needs --input FILE, the reply the sub-agent printed.` | `save` without `--input`. | Save the reply to a project file outside `.bmad-plus/` and pass it. |
| `Swarm: --out must stay under … or …` / `--out may not write inside …` / `--out may not replace the run file …` | `--out` points where `tally` or `plan` may not write. | Use a path under `.bmad-plus/swarm/reports/<run id>/`. |
| `Swarm: Only a fanout exports a Nexus plan; run R is a panel.` | `plan` on a panel or a jury. | Use `plan` only with a fanout. |

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

<a id="example-cli-swarm-buyer-panel"></a>
### A panel of simulated buyers within a budget

You have a swarm definition, a profile and a pitch file. The default budget is 26 sub-agents; you ask for 30.

**In your AI assistant**

```text
Run the buyer panel from swarm/buyer-panel.json with the profile swarm/profile.json and my pitch in swarm/pitch.md. Use 30 simulated buyers and seed 7.
```

**What you should get** The assistant runs `swarm init`. Thirty units exceed the budget of 26, so the command exits with code 2 and writes no brief; the assistant tells you "30 sub-agents against a budget of 26" and asks. After your yes it re-runs `init` with `--approved --seed 7`, gets a run id, runs `briefs` (three waves of 10), launches one sub-agent per brief with the fixed instruction, then runs `check`, gives missing units one second chance and finishes with `tally`. Its report opens with the disclosure sentence, states how many units were counted and how many were skipped, and presents rates as an upper bound.

<a id="example-cli-swarm-before-after"></a>
### Compare two versions of a pitch

The same panel, run twice with the same seed, differs only by the pitch file.

**In your AI assistant**

```text
Run the buyer panel on swarm/pitch-v1.md, then again on swarm/pitch-v2.md, with the same profile, 24 buyers and seed 7. Compare the two tallies.
```

**What you should get** Twenty-four units fit the default budget of 26, so neither `init` needs approval. Because profile, `--n` and `--seed` are identical, both runs deal the same 24 cards; only `{{context.pitch}}` differs in the briefs. Each run has its own id and `tally.md`. The comparison is reported as a difference between two simulated upper bounds, with the disclosure first, not as a measured conversion change.

<a id="example-cli-swarm-fanout-plan"></a>
### One writer per page, verified by Nexus

A fanout definition and a units file give each page its own write scope. The work must survive the session, so it is exported as a durable plan.

**In your AI assistant**

```text
Write the three pages listed in swarm/units.json with one sub-agent per page, using swarm/page-fanout.json. Export it as a Nexus plan so each page is verified before I accept it.
```

**What you should get** The assistant runs `swarm init` with `--definition` and `--units`, then `swarm plan RUN`. The plan has one task per unit: its scope plus its answer file, its brief and the frozen files as resources, one check `node checks/answer.cjs --unit=<unit>`, `maxAttempts` 2, and `maxParallel` equal to the configured wave capped at 32. `plan` prints the plan path and `Next: bmad-plus nexus create --plan <path>`. From there Nexus starts, verifies and accepts each task; a unit whose answer file is malformed fails its check and is retried, not accepted.

**See also**

- [bmad-plus nexus: run an exported fanout plan](https://bmad-plus.rochetta.fr/docs/reference/cli-nexus/)
- [bmad-plus intake: the questions asked before a swarm](https://bmad-plus.rochetta.fr/docs/reference/cli-intake/)
- [config.yaml: the swarm block](https://bmad-plus.rochetta.fr/docs/reference/config-yaml/#swarm-block)
- [Project layout: .bmad-plus/swarm/](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)
