Quand lancer un panel, un jury ou un fanout de sous-agents, les quatre fichiers qui les définissent, la distribution à graine, le budget de 26, des décomptes honnêtes et un exemple réel.
Un essaim (swarm) est un ensemble de sous-agents qui font chacun une fois le même genre de travail : trente acheteurs simulés qui lisent un argumentaire, trois jurés qui le lisent chacun sous un angle, un rédacteur par page d’un site. Le modérateur, c’est votre assistant ; il ne fait jamais le travail d’une unité. bmad-plus swarm tient les comptes : il valide les fichiers, distribue les unités, écrit un brief par sous-agent, contrôle chaque fichier de réponse et ne compte que les valides. Il n’appelle jamais de modèle, n’ouvre aucune connexion réseau et ne lance aucun processus. Cette page s’adresse à qui veut éprouver un message, une page ou un plan auprès de nombreux lecteurs, ou répartir un travail répétitif entre sous-agents dans un budget.
Demander d’abord, puis l’essaimLes questions de l’intake, le budget de 26 et l’étape d’approbation, les vagues de sous-agents, et un décompte qui rappelle que des unités simulées ne sont pas des personnes.Transcription
Avant d’agir, un pack pose ses questions. L’intake pose quelques questions à la fois et marque les indispensables. Sans réponse de votre part, il propose une valeur par défaut, ou note la question comme inconnue, avec son risque. Les réponses deviennent un brief, et rien n’y est inventé. Vient l’essaim : par défaut, 26 sous-agents au plus avant que l’assistant ne vous demande l’accord. Demandez davantage, et l’exécution attend, au statut needs-approval, qu’une personne dise oui. Une fois l’accord donné, les sous-agents travaillent par vagues de dix par défaut, chacun lisant un brief et écrivant une réponse. Le runtime valide chaque fichier de réponse et ne compte que les réponses valides. Ce décompte est une démonstration, et il s’ouvre sur son avertissement : des unités simulées ne sont pas des personnes. Lisez les taux comme une borne haute, à confirmer auprès de vraies personnes.
Attention· Les unités simulées ne sont pas des personnes
Un panel vous donne la lecture d’un modèle qui joue des acheteurs. Considérez ses taux comme une borne haute à confirmer auprès de vraies personnes, et ses citations comme ce qu’une unité simulée a écrit, jamais comme des témoignages ou des entretiens d’utilisateurs. Chaque décompte commence par cette phrase.
Un profil : les unités sont distribuées par quotas exacts, à partir d’une graine.
Des acheteurs, lecteurs ou utilisateurs simulés qui lisent un argumentaire, un prix, une page.
Un taux par groupe, des comptes, des citations textuelles, des nombres.
jury
Une liste dans la définition : une unité par entrée, chacune avec un lens.
Une lecture contradictoire d’un argumentaire, d’une page ou d’un plan, un angle par juré.
Des comptes et des citations par angle.
fanout
Un fichier d’unités que vous écrivez à l’init : une unité par entrée, chacune avec un scope d’écriture disjoint.
Des pages à écrire, des modules à documenter, des scènes à préparer, un sous-agent chacun.
Un taux et des nombres ; exportable en plan Nexus durable.
Un panel et un jury s’exécutent comme sous-agents de session : votre assistant les lance, attend et lit leurs fichiers. Un fanout peut s’exécuter de la même façon, ou être exporté avec swarm plan en plan Nexus durable quand le travail doit survivre à la session, exige un contrôle indépendant par unité ou modifie le dépôt.
Quand vous pouvez interroger de vraies personnes. Un essaim répète ; il ne mesure pas.
Pour deux ou trois tâches. Demandez directement à l’agent, ou utilisez parallel de Nexus.
Pour un travail où un résultat dépend d’un autre. Les unités sont indépendantes par construction.
Pour générer des médias payants. La génération d’images, de vidéo et d’audio n’est jamais couverte par le budget : l’assistant demande à chaque fois.
En 0.23.0, aucun pack ne livre de définition d’essaim : le runtime et la compétence de modération sont dans Core, et les définitions utilisées dans cette page sont des fixtures de test du dépôt BMAD+ (tests/fixtures/swarm/). C’est un pack, ou vous, qui fournit la définition. Ces fichiers ne sont pas dans le paquet npm : cette page affiche chacun en entier, vous pouvez donc les créer tels quels.
definition + profile|list|units + context
|
init ---- over budget? exit 2 "needs approval": ask a human, re-run with --approved
|
cards.json (frozen)
|
briefs ---- one brief per unit, in waves
|
one sub-agent per brief -> answers/<unit>.json
|
check ---- missing / invalid, with reasons
| (second chance once; save files a printed reply)
tally ---- tally.json + tally.md, disclosure first
01
init
Valide la définition, le profil ou le fichier d’unités, les fichiers de contexte et le gabarit de brief ; distribue ou liste les fiches ; applique le budget. Un panel exige --n (1 à 1000) ; la graine est tirée et enregistrée si vous n’en donnez pas. Une exécution plus grande que le budget s’arrête ici avec le code de sortie 2.
Écrit un brief par unité sans réponse valide, à partir du gabarit figé, de la fiche et des contextes figés, et affiche les vagues (10 unités par vague sauf --wave) avec le chemin du brief de chaque unité.
Dans le terminal du projet
npx bmad-plus swarm briefs RUN
03
Lancer une vague
Votre assistant lance un sous-agent par brief avec une seule instruction : lire le brief et le suivre à la lettre, c’est toute son instruction. Il ne résume pas le brief, n’ajoute pas de contexte et ne fusionne pas deux unités dans un sous-agent. Il attend la vague entière avant la suivante.
04
check
Liste toutes les unités dont la réponse manque ou est invalide, avec la raison. Code de sortie 0 si toutes les unités sont valides, 1 sinon.
Dans le terminal du projet
npx bmad-plus swarm check RUN
05
Une seconde chance, une fois
Relancez briefs (il n’écrit que les unités sans réponse valide) et relancez ces unités. Une unité qui a affiché sa réponse au lieu de l’écrire est classée avec save RUN UNIT --input FILE, le fichier contenant la réponse de cette unité telle quelle. Une unité encore absente après la seconde vague reste absente.
Dans le terminal du projet
npx bmad-plus swarm save RUN B005 --input swarm-inbox/B005.txt
06
tally
Ne compte que les réponses valides et écrit tally.json et tally.md. Les unités sans réponse valide sont listées comme ignorées et comptées dans la ligne du dénominateur. La mention de transparence est la première ligne de tally.md.
Dans le terminal du projet
npx bmad-plus swarm tally RUN
07
plan (fanout uniquement)
Au lieu d’exécuter les sous-agents dans la session, exporte le fanout sous forme de plan Nexus. Voir la page Nexus.
Dans le terminal du projet
npx bmad-plus swarm plan RUN --out .bmad-plus/swarm/reports/RUN/plan.json
swarm inspect RUN affiche à tout moment l’état d’une exécution (unités, avec brief, valides, invalides, manquantes) sans rien écrire. Codes de sortie : 0 fait, 2 needs-approval (un humain doit d’abord dire oui), 1 incomplet ou erreur. Avec --json, chaque réponse porte schemaVersion: 1 et un status.
Trois clés du bloc swarm de _bmad/config.yaml fixent les limites. L’installateur écrit le bloc avec ses valeurs par défaut ; un projet mis à jour depuis une version antérieure n’a pas de bloc et reçoit les mêmes valeurs par défaut.
Le bloc swarm
Clé
Défaut
Sens
budget
26
Sous-agents qu’une exécution peut utiliser avant que l’assistant doive vous demander. Entier de 1 à 1000.
wave
10
Sous-agents lancés ensemble. L’assistant attend une vague avant la suivante. Entier de 1 à 1000.
media_credits
ask
La génération payante (images, vidéo, audio) n’est jamais approuvée par un budget. La seule valeur acceptée est ask.
Quand n dépasse le budget et que --approved est absent, init crée l’exécution avec le statut needs-approval, sort avec 2 et n’écrit aucun brief. L’assistant vous pose une phrase avec le nombre (« Ce panel demande 30 sous-agents ; le budget est de 26. On lance ? »). Ce n’est qu’après votre oui qu’il relance init avec --approved et la même graine. Il ne passe jamais --approved de son propre chef. L’exécution refusée est terminale : les commandes suivantes sur elle répondent needs-approval et n’écrivent rien.
Refusé, puis approuvé : un panel de 30 pour un budget de 26
$ npx bmad-plus swarm init --definition swarm/buyer-panel.json --profile swarm/profile.json --context pitch=swarm/pitch.md --n 30 --seed 7buyer-panel-20261019-g1i9: needs approval. The run needs 30 sub-agents and the budget is 26: ask the user, then re-run init with --approved --seed 7.# Code de sortie 2. Aucun brief n’a été écrit.$ npx bmad-plus swarm init --definition swarm/buyer-panel.json --profile swarm/profile.json --context pitch=swarm/pitch.md --n 30 --seed 7 --approvedbuyer-panel-20261019-vqkf: panel of 30 units, budget 26, approval user.Seed 7 (same seed, same cards).Next: bmad-plus swarm briefs buyer-panel-20261019-vqkf
Le runtime refuse un champ inconnu, un mauvais type et un dépassement de borne ; il ne devine jamais. Chaque chemin est relatif au projet, avec des séparateurs /. Le chemin du gabarit de brief est relatif au fichier de définition.
1 à 64 lettres minuscules, chiffres ou -. Ouvre l’id de l’exécution.
kind et units.source
Le couple doit correspondre : panel avec profile, jury avec list, fanout avec input.
units.idPrefix
1 à 4 lettres. Obligatoire pour un panel ; facultatif pour un jury dont les entrées n’ont pas d’id ; absent pour un fanout.
units.list
Jury uniquement : 1 à 1000 entrées, chacune avec un texte lens et d’autres champs texte, nombre ou booléen ; un id facultatif.
pack
Id de pack facultatif, enregistré dans l’exécution.
title
Titre du décompte.
brief.template
Chemin relatif simple vers un fichier Markdown, sans partie . ni ...
brief.context
Noms des fichiers de contexte que le modérateur doit passer à l’init (--context NAME=FILE). Chacun est obligatoire ; 20 au plus.
answer.required
Champs obligatoires. id est implicite et toujours requis.
answer.fields
50 champs au plus, chacun typé (voir le tableau suivant). Un sous-agent ne peut pas écrire un champ non déclaré.
tally
rate (un champ booléen), groupBy (jusqu’à 5 champs de fiche que chaque fiche porte), counts (champs enum), quotes (champ texte vers nombre de citations gardées par groupe, 1 à 10), numbers (champs entiers ou nombres).
disclosure
1 à 500 caractères, affichés en premier dans chaque décompte et dans la sortie de briefs.
Types des champs de réponse
Type
Options
Valeur acceptée
boolean
aucune
true ou false.
integer
min, max
Un entier dans les bornes.
number
min, max
Un nombre quelconque dans les bornes.
string
min, max (longueur)
Du texte ; par défaut 0 à 4000 caractères, plafond 20000 ; min est vérifié sur le texte sans espaces aux extrémités.
enum
values (1 à 50, distinctes)
L’une des valeurs listées.
list
maxItems (défaut 50, plafond 200)
Une liste de textes non vides, chacun jusqu’à 1000 caractères.
Ce qui est jugé et qui juge. Documentation pour les lecteurs du fichier.
dimensions
1 à 10 dimensions, chacune avec un name et 1 à 50 values. Les parts d’une dimension totalisent 1.
values[].value, share
Le libellé d’une valeur et sa part du panel.
values[].ranges
Intervalles numériques facultatifs tirés quand cette valeur est distribuée, comme "income": [38000, 85000]. Chaque valeur d’une dimension déclare les mêmes noms d’intervalle.
values[].note
Phrase facultative. Elle devient le champ de fiche <dimension>_note (vide pour les valeurs qui n’en ont pas).
ranges (niveau supérieur)
Intervalles tirés une fois par fiche. Des bornes entières donnent des entiers ; les autres donnent des nombres à deux décimales.
pools
Listes nommées de phrases. Une entrée de chaque liste est distribuée par fiche.
Les champs de fiche sont les noms de dimension, leurs noms d’intervalle, les noms d’intervalle de premier niveau, les noms de liste et les notes. Les noms doivent être distincts, et id, scope et lens sont réservés. Les noms de personnes ne sont pas distribués : un brief qui en veut un le tire d’une liste fournie par le pack.
id : 1 à 64 lettres minuscules, chiffres, _ ou -. Il devient un id de tâche Nexus.
scope : un chemin ou une liste de 20 au plus, portables et relatifs au projet, jamais .git ni .bmad-plus. Les scopes sont deux à deux disjoints : aucun ne peut en contenir un autre.
Tout autre champ doit être un texte, un nombre ou un booléen ; il devient un champ de fiche utilisable comme {{card.field}} dans le brief.
La définition qui accompagne ce fichier d’unités n’a pas de profil ; init reçoit les unités avec --units, par exemple npx bmad-plus swarm init --definition swarm/page-fanout.json --units swarm/units.json. Voici la définition du fanout site-pages utilisé dans l’exemple à la fin de cette page, puis son gabarit de brief.
swarm/page-fanout.json (la définition)
{
"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 (le gabarit)
# 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}}
4. Le gabarit de brief (Markdown) et les contextes#
Marqueurs : rien d’autre n’est permis entre doubles accolades
Marqueur
Produit
{{card.<field>}}
Le champ de fiche ; une liste est rendue séparée par des virgules.
{{context.<name>}}
Le fichier de contexte passé à l’init, tel quel et figé dans l’exécution.
{{answer.path}}
Le chemin absolu du fichier de réponse que le sous-agent doit écrire.
{{run.id}}, {{unit.id}}
L’id de l’exécution et l’id de l’unité.
{{disclosure}}
La mention de transparence de la définition.
Attention· Le runtime ne filtre rien
Les valeurs de fiche, les entrées de liste, les champs d’unités listées et les fichiers de contexte arrivent au sous-agent mot pour mot. Une entrée de liste qui ressemble à une consigne arrive dans le brief comme une consigne. Le pack est responsable de ses listes, de ses champs d’unités et de ses gabarits ; vous êtes responsable des contextes que vous passez. Lisez un contexte avant de le passer, et ne passez jamais un fichier que vous n’avez pas lu : il est inclus dans chaque brief, secrets et consignes compris. Un marqueur inconnu, un champ de fiche qui manque à une fiche, un contexte non déclaré ou un {{ littéral dans le gabarit fait échouer init.
Le même profil, le même --n et la même --seed donnent toujours les mêmes fiches. Chaque dimension est répartie par quota exact (plus fort reste, égalités départagées par l’ordre de déclaration) : avec des parts de 0,45, 0,40 et 0,15, vingt fiches contiennent 9, 8 et 3 unités de ces valeurs, quelle que soit la graine. La colonne de valeurs est ensuite mélangée par un générateur fondé sur SHA-256, et la fiche k prend la position k de chaque colonne ; les intervalles et les entrées de liste suivent, dans l’ordre. La graine (0 à 2147483647) est enregistrée dans run.json.
C’est ce qui rend honnête une comparaison avant et après. Pour tester deux versions d’un argumentaire, relancez le même profil avec le même --n et la même --seed en ne changeant que le fichier de contexte : les acheteurs sont les mêmes, la seule chose qui a changé est l’argumentaire. Les ids d’unité sont le préfixe suivi de la position, complétée à au moins trois chiffres (B001, B030, B1000).
Une réponse ne compte que si answers/<unit>.json est un fichier ordinaire, se lit comme un objet JSON, porte un id égal à l’id de l’unité et tous les champs obligatoires, ne déclare aucun champ inconnu et respecte chaque type et chaque borne. check signale une unité missing (pas de fichier) ou invalid (avec les raisons). save applique les mêmes règles au premier objet JSON équilibré trouvé dans une réponse affichée, et refuse quand il n’y en a pas, quand la validation échoue (un mauvais id compris), quand l’unité a déjà une réponse valide et sur une exécution qui attend encore l’approbation. Une réponse refusée ne change rien sur le disque.
Une réponse est classée, une autre refusée
$ npx bmad-plus swarm save buyer-panel-20261019-2ftt B005 --input swarm-inbox/B005.txtbuyer-panel-20261019-2ftt: answer of B005 filed at /work/demo/app/.bmad-plus/swarm/runs/buyer-panel-20261019-2ftt/answers/B005.json$ npx bmad-plus swarm save buyer-panel-20261019-2ftt B006 --input swarm-inbox/B006.txtSwarm: Reply of unit B006 refused: field "buys" must be true or false.# Le premier fichier contient une phrase suivie d’un objet JSON valide ; le second, un objet dont le champ buys vaut le texte « maybe ». Codes de sortie 0 et 1.
npx bmad-plus swarm save buyer-panel-20261019-2ftt B005 --input swarm-inbox/B005.txt
npx bmad-plus swarm save buyer-panel-20261019-2ftt B006 --input swarm-inbox/B006.txt
Sortie capturée lors d’une exécution réelle. Source: bmad-plus 0.23.0, 2026-10-19
Ne jamais répondre à la place d’une unité : ni en écrivant un fichier de réponse, ni en « corrigeant » une réponse invalide, ni en classant avec save quelque chose qu’un sous-agent n’a pas affiché.
Ne jamais modifier un brief. Un changement relève du gabarit ou de la définition, puis d’une nouvelle exécution.
La mention de transparence vient une fois et en premier : le décompte commence par elle, et l’assistant la répète une fois dans son compte rendu, avant tout chiffre.
Les taux sont une borne haute à confirmer auprès de vraies personnes. Les citations sont ce qu’une unité simulée a écrit.
La graine voyage avec le résultat : même graine, mêmes fiches, donc une comparaison est une comparaison.
Une réponse valide est une réponse bien formée. Rien ne prouve qu’un sous-agent a lu son brief, est resté dans sa fiche ou a dit vrai ; la qualité, c’est votre lecture du décompte.
Les réponses sont classées depuis l’extérieur de l’arbre d’état : save --input prend un fichier du projet écrit par l’hôte (par exemple dans swarm-inbox/), jamais un chemin sous .bmad-plus/.
Le scénario : un bar à café nommé Orbit veut savoir comment un cadre urbain réagit à son argumentaire. Les fichiers ci-dessous sont les fixtures de test du dépôt BMAD+, utilisées ici sans changement : la définition, le profil et le gabarit de brief. L’argumentaire lui-même tient en deux phrases.
{
"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/briefs/buyer.md (le gabarit)
# 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/pitch.md (le contexte)
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.
La graine 7 et 30 unités donnent cette première fiche (extraite de cards.json). Le brief que reçoit le sous-agent est le gabarit avec cette fiche, l’argumentaire et le chemin de réponse remplis.
La fiche de l’unité B001
{
"id": "B001",
"segment": "mid career",
"income": 114720,
"behaviour": "occasional",
"behaviour_note": "a treat, not a routine",
"age": 32,
"walk_minutes": 8.99,
"objection": "I do not know the brand",
"habit": "you order the same thing every day"
}
Dans la transcription ci-dessous, l’assistant lancerait 30 sous-agents en trois vagues. Pour montrer les fichiers, l’outil et le décompte sans aucun modèle, cinq des réponses ont été écrites à la main pour cette démonstration (quatre en fichiers, une via save), et une sixième réponse a été refusée. Le décompte compte donc 5 unités sur 30 et nomme les 25 autres. Les citations ci-dessous ne sont donc pas non plus ce qu’un modèle a écrit : c’est du texte de démonstration.
Les réponses de démonstration (un fichier chacune dans answers/ ; B005 et B006 sont des réponses classées avec save)
answers/B001.json { "id": "B001", "buys": true, "reason_code": "habit", "reason": "The bar keeps my usual order under my name, so I would stop at a tenth coffee for free.", "price_floor": 3.8 }
answers/B002.json { "id": "B002", "buys": false, "reason_code": "price", "reason": "A flat white at 4.20 is more than my usual place, and it is closer.", "price_floor": 3.2 }
answers/B003.json { "id": "B003", "buys": true, "reason_code": "need", "reason": "Opening at seven fits my route to the station, and I order the same drink every morning.", "price_floor": 4.5 }
answers/B004.json { "id": "B004", "buys": false, "reason_code": "trust", "reason": "I do not know the brand and nothing in the pitch tells me why I should switch.", "price_floor": 3.5 }
swarm-inbox/B005.txt Here is my answer as a buyer.
{ "id": "B005", "buys": true, "reason_code": "habit", "reason": "Same drink, same place, most mornings: the loyalty card would pay for itself.", "price_floor": 4.2 }
swarm-inbox/B006.txt { "id": "B006", "buys": "maybe", "reason_code": "other", "reason": "I could go either way." }
Des briefs au décompte, avec cinq réponses de démonstration écrites à la main
$ npx bmad-plus swarm briefs buyer-panel-20261019-2fttbuyer-panel-20261019-2ftt: 30 of 30 units need a sub-agent, in waves of 10.Wave 1: B001 /work/demo/app/.bmad-plus/swarm/runs/buyer-panel-20261019-2ftt/briefs/B001.md B002 /work/demo/app/.bmad-plus/swarm/runs/buyer-panel-20261019-2ftt/briefs/B002.md# ... unités B003 à B010, puis vagues 2 et 3 (B011 à B030) ...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.# Quatre fichiers de réponse (B001 à B004) sont maintenant écrits à la main dans answers/.$ npx bmad-plus swarm check buyer-panel-20261019-2fttbuyer-panel-20261019-2ftt: 4 of 30 valid answers. B005 missing: no answer file B006 missing: no answer file# ... et ainsi de suite jusqu’à B030. Code de sortie 1.$ npx bmad-plus swarm tally buyer-panel-20261019-2ftt/work/demo/app/.bmad-plus/swarm/runs/buyer-panel-20261019-2ftt/tally.md5 of 30 units counted; 25 skipped.Disclosure: Simulated units are not people. Read rates as an upper bound and confirm with real ones.$ npx bmad-plus swarm inspect buyer-panel-20261019-2fttbuyer-panel-20261019-2ftt: tallied (panel, approved by the user)units 30, briefed 30, valid 5, invalid 0, missing 25Disclosure: Simulated units are not people. Read rates as an upper bound and confirm with real ones.
Sortie capturée lors d’une exécution réelle. Source: bmad-plus 0.23.0, 2026-10-19
tally.md (début et section du taux)
> Simulated units are not people. Read rates as an upper bound and confirm with real ones.
# Simulated buyers see the pitch (run buyer-panel-20261019-2ftt)
5 of 30 units gave a valid answer; 25 skipped: B006 (no answer file), B007 (no answer file), ...
## Rate: buys
| group | yes | of | rate |
|---|---|---|---|
| all | 3 | 5 | 60.0% |
| segment = mid career | 2 | 4 | 50.0% |
| segment = high earner | 1 | 1 | 100.0% |
| segment = early career | 0 | 0 | n/a |
| behaviour = occasional | 1 | 3 | 33.3% |
| behaviour = daily regular | 2 | 2 | 100.0% |
| behaviour = explorer | 0 | 0 | n/a |
Comment le lire : le taux vient avec son dénominateur (« 3 sur 5 », et 5 unités sur 30 comptées), pas comme un pourcentage seul. Un groupe sans réponse valide affiche n/a, pas 0. La même exécution écrit aussi les comptes par code de raison, les deux premières citations par groupe mot pour mot, et la médiane, la moyenne, le minimum et le maximum de price_floor. Relancez init avec le même profil, --n 30 et --seed 7 pour un second argumentaire afin de comparer : le fichier cards.json des deux exécutions est identique à l’octet près (cmp ne signale aucune différence entre eux).
Les étapes de la recette, rejouées : 1 réponse valide sur 30#
La recette de la 0.23.0 comporte une étape essaim dans un projet vide, déroulée par une personne : aucun sous-agent ne tourne, le testeur tient le rôle d’une unité, classe une réponse valide pour B001 et en essaie une mauvaise. L’outil compte exactement cela, et le dit. Voici la même séquence rejouée avec la CLI de la 0.23.0 (les 30 chemins de briefs sont abrégés).
briefs, check, save, tally avec une seule réponse valide
$ npx bmad-plus swarm init --definition swarm/buyer-panel.json --profile swarm/profile.json --context pitch=swarm/pitch.md --n 30 --seed 7 --approvedbuyer-panel-20261019-9da2: panel of 30 units, budget 26, approval user.Seed 7 (same seed, same cards).Next: bmad-plus swarm briefs buyer-panel-20261019-9da2$ npx bmad-plus swarm briefs buyer-panel-20261019-9da2buyer-panel-20261019-9da2: 30 of 30 units need a sub-agent, in waves of 10.Wave 1:# ... une ligne par unité avec le chemin du brief, trois vagues ...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.# Le testeur écrit à la main une réponse valide : answers/B001.json.$ npx bmad-plus swarm check buyer-panel-20261019-9da2buyer-panel-20261019-9da2: 1 of 30 valid answers. B002 missing: no answer file# ... B003 à B030 de même.$ npx bmad-plus swarm save buyer-panel-20261019-9da2 B002 --input swarm/reply-bad.txtSwarm: Reply of unit B002 refused: field "buys" must be true or false.# swarm/reply-bad.txt contient un objet dont le champ buys vaut le texte « maybe ». Code de sortie 1.$ npx bmad-plus swarm tally buyer-panel-20261019-9da2/work/demo/app/.bmad-plus/swarm/runs/buyer-panel-20261019-9da2/tally.md1 of 30 units counted; 29 skipped.Disclosure: Simulated units are not people. Read rates as an upper bound and confirm with real ones.$ head -n 3 .bmad-plus/swarm/runs/buyer-panel-20261019-9da2/tally.md> Simulated units are not people. Read rates as an upper bound and confirm with real ones.# Simulated buyers see the pitch (run buyer-panel-20261019-9da2)
Un jury n’a besoin ni de profil, ni de --n, ni de graine : ses unités sont listées dans la définition, un angle chacune. Celui-ci (swarm/pitch-jury.json, affiché plus bas avec son gabarit) lit le même argumentaire sous l’angle du prix, de la confiance et de l’habitude. Le troisième juré porte son propre id (J-habit) ; les deux premiers reçoivent J001 et J002 du préfixe. Sa réponse n’a que trois champs : un verdict, l’affirmation la plus faible et jusqu’à deux corrections, et son décompte compte les verdicts et garde les trois premières citations.
swarm/pitch-jury.json (la définition)
{
"schemaVersion": 1,
"id": "pitch-jury",
"kind": "jury",
"title": "Three lenses on the pitch",
"units": {
"source": "list",
"idPrefix": "J",
"list": [
{ "lens": "price", "stance": "Every claim is a cost the buyer pays." },
{ "lens": "trust", "stance": "Who vouches for this, and why believe them?" },
{ "id": "J-habit", "lens": "habit", "stance": "What has to change in a normal morning?" }
]
},
"brief": { "template": "briefs/juror.md", "context": ["pitch"] },
"answer": {
"required": ["id", "verdict", "weakest_claim"],
"fields": {
"verdict": { "type": "enum", "values": ["convincing", "unclear", "weak"] },
"weakest_claim": { "type": "string", "min": 5, "max": 300 },
"fixes": { "type": "list", "maxItems": 2 }
}
},
"tally": { "counts": ["verdict"], "quotes": { "weakest_claim": 3 } },
"disclosure": "A simulated jury is a rehearsal, not a review by the people it imitates."
}
swarm/briefs/juror.md (le gabarit)
# Juror {{unit.id}}
Lens: {{card.lens}}. Stance: {{card.stance}}
Read the pitch below through that lens only and write a JSON object to `{{answer.path}}` with
`id` ("{{unit.id}}"), `verdict` (convincing, unclear or weak), `weakest_claim` and optional `fixes`.
{{context.pitch}}
{{disclosure}}
Les réponses de démonstration (answers/J001.json, answers/J002.json, answers/J-habit.json)
{ "id": "J001", "verdict": "unclear", "weakest_claim": "A flat white at 4.20 is stated, but nothing says what it is compared to.", "fixes": ["Compare the price with a nearby chain.", "Show what the loyalty card is worth in euros."] }
{ "id": "J002", "verdict": "weak", "weakest_claim": "No one vouches for the bar: there is no review, no name, no photo.", "fixes": ["Add two customer sentences with a first name."] }
{ "id": "J-habit", "verdict": "convincing", "weakest_claim": "Opening at seven is good; it does not say how long the queue is.", "fixes": ["Say how long a regular order takes."] }
Un jury de trois, avec trois réponses de démonstration écrites à la main
$ npx bmad-plus swarm init --definition swarm/pitch-jury.json --context pitch=swarm/pitch.mdpitch-jury-20261019-3qxm: jury of 3 units, budget 26, approval not-needed.Units listed, no seed.Next: bmad-plus swarm briefs pitch-jury-20261019-3qxm$ npx bmad-plus swarm briefs pitch-jury-20261019-3qxmpitch-jury-20261019-3qxm: 3 of 3 units need a sub-agent, in waves of 10.Each sub-agent: Read the brief and follow it exactly; it is your whole instruction.Disclosure: A simulated jury is a rehearsal, not a review by the people it imitates.# Trois fichiers de réponse sont écrits à la main dans answers/.$ npx bmad-plus swarm check pitch-jury-20261019-3qxmpitch-jury-20261019-3qxm: 3 of 3 valid answers.$ npx bmad-plus swarm tally pitch-jury-20261019-3qxm3 of 3 units counted; 0 skipped.Disclosure: A simulated jury is a rehearsal, not a review by the people it imitates.
Sortie capturée lors d’une exécution réelle. Source: bmad-plus 0.23.0, 2026-10-19
tally.md du jury
> A simulated jury is a rehearsal, not a review by the people it imitates.
# Three lenses on the pitch (run pitch-jury-20261019-3qxm)
3 of 3 units gave a valid answer; 0 skipped.
## Counts: verdict
| group | convincing | unclear | weak |
|---|---|---|---|
| all | 1 | 1 | 1 |
## Quotes: weakest_claim (up to 3 per group, verbatim)
**all**
- J001: "A flat white at 4.20 is stated, but nothing says what it is compared to."
- J002: "No one vouches for the bar: there is no review, no name, no photo."
- J-habit: "Opening at seven is good; it does not say how long the queue is."
Le nouvel argumentaire bat-il l’ancien auprès des mêmes personnes ?
panel
Même profil, mêmes --n et --seed ; ne changez que --context pitch=... ; comparez les deux décomptes.
Quel segment s’oppose au prix ?
panel
Mettez le segment dans groupBy, l’objection dans un champ enum et lisez counts par groupe.
Quelle est l’affirmation la plus faible de cette page ?
jury
Un angle par juré (prix, confiance, accessibilité, juridique) ; lisez les citations par angle.
Le plan survit-il à une lecture hostile ?
jury
Donnez à chaque juré une posture comme « suppose que cela échoue ; où ? » et décomptez les verdicts.
Peut-on rédiger douze pages sans se marcher dessus ?
fanout
Une unité par page avec un scope disjoint ; exécutez en sous-agents de session, ou exportez avec swarm plan pour Nexus.
Comparer deux argumentaires avec les mêmes acheteurs simulés#
À utiliser quand vous avez un profil et une définition de panel, et deux versions du texte à tester. Attendez-vous à une demande d’approbation si le panel dépasse 26.
Dans votre assistant IA
Lance l’essaim buyer-panel avec 30 unités et la graine 7 sur swarm/pitch-v1.md, puis recommence avec la même graine et le même profil sur swarm/pitch-v2.md. Si une exécution demande une approbation, demande-moi d’abord avec les chiffres. Lance les sous-agents une vague à la fois et ne réponds jamais à la place d’une unité. Rends compte à partir des deux décomptes seulement : la mention de transparence d’abord, puis le taux d’achat par segment pour chaque argumentaire avec son dénominateur, les codes de raison, et ce qui a changé entre les deux.
Ce que vous devriez obtenir Le premier init s’arrête avec le code de sortie 2 et la ligne « needs approval ... 30 sub-agents and the budget is 26 ». Après votre oui, trois vagues de 10 s’exécutent, check liste les unités manquantes éventuelles, les fichiers de décompte sont écrits, et le compte rendu cite la mention de transparence avant tout taux. Comme les deux exécutions partagent profil, --n et graine, leurs cards.json sont identiques et la différence vient du seul argumentaire. Lisez le résultat comme une borne haute à confirmer auprès de vraies personnes.
Faire attaquer une page par un jury avant le lancement#
À utiliser avec une définition de jury (un angle par juré) et le texte à lire comme fichier de contexte. Ni profil ni graine ne sont nécessaires.
Dans votre assistant IA
Lance l’essaim pitch-jury sur swarm/pitch.md. Un sous-agent par juré, chacun ne lisant que son propre brief. Lis chaque fichier de contexte avant de le passer. Quand le décompte est écrit, rapporte d’abord la mention de transparence, puis les comptes de verdicts et l’affirmation la plus faible citée par angle, et liste les corrections proposées par les jurés. N’écris ni ne modifie aucune réponse toi-même.
Ce que vous devriez obtenir Trois briefs sont écrits, trois sous-agents répondent, check indique 3 réponses valides sur 3 et tally.md s’ouvre par « A simulated jury is a rehearsal, not a review by the people it imitates. » Il montre ensuite combien de jurés ont trouvé l’argumentaire convaincant, peu clair ou faible, et cite chaque affirmation la plus faible avec l’id du juré.
Écrire plusieurs pages en parallèle et les vérifier#
À utiliser quand les unités modifient le dépôt, exigent un contrôle chacune ou doivent survivre à la session : exportez le fanout en plan Nexus.
Dans votre assistant IA
Initialise le fanout site-pages depuis swarm/page-fanout.json et swarm/units.json (about, pricing, contact), exporte-le avec swarm plan vers .bmad-plus/swarm/reports/RUN/plan.json et crée l’exécution Nexus à partir de là. Démarre une tâche à la fois sur cet hôte, laisse-moi relire la page, puis vérifie-la et accepte-la. N’accepte pas une tâche dont le contrôle échoue.
Ce que vous devriez obtenirswarm plan annonce « 3 tasks » et écrit un plan dont les tâches portent les scopes disjoints ; nexus create montre chaque tâche « not started; verification=pending; acceptance=pending ». Pour chaque tâche : start, la page est écrite, record, puis verify exécute le contrôle de l’unité et accept la marque acceptée. Une tâche dont le fichier de réponse est invalide reste non acceptée.
run.json kind, pack, hashes of every input, 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 rendered by briefs or 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)
16 Mio chacun au plus ; une réponse donnée à save, 1 Mio au plus
Profil
10 dimensions de 50 valeurs au plus, 20 intervalles, 20 listes de 200 entrées au plus
Champs de réponse
50 ; valeurs enum 50 ; éléments de liste jusqu’à 200 ; plafond de longueur de texte 20000
Champs groupBy ; citations par groupe
5 ; 1 à 10
Scopes de fanout par unité
20
Export vers Nexus
Une tâche par unité, maxParallel au plus 32 (Nexus accepte 200 tâches par exécution)
Frontières de sécurité : le runtime n’a ni processus enfant, ni réseau, ni eval ; chaque fichier est écrit de façon atomique et seulement sous .bmad-plus/swarm/ ; les chemins qui remontent, sont absolus, suivent un lien ou atteignent .git sont refusés ; les entrées sont figées à init, donc modifier un contexte ensuite ne change rien pour cette exécution.