Comment review scope, anchor et gate rendent une revue vérifiable, les règles intégrées, et comment une personne déroule une recette lue, triée et soumise à la porte. Pourquoi un tag en exige une.
Deux sortes de preuve s’ajoutent aux tests automatisés. Une revue de code vérifiable montre ce qui a été relu, où se trouve chaque constat et pourquoi la revue s’est arrêtée. Une recette humaine montre qu’une personne a vu la chaîne fonctionner à l’écran. Ce sont deux preuves que l’on peut relire après coup, et toutes deux refusent de dire « tout va bien » quand la preuve est incomplète. Cette page s’adresse aux relecteurs, aux responsables d’équipe et à quiconque décide si une version part.
Une revue écrite en prose ne se distingue pas d’une revue qui a lu douze fichiers sur quarante et n’a rien trouvé. BMAD+ laisse la revue elle-même à l’agent hôte (il n’appelle aucun modèle) et ajoute les trois choses que seul du code peut garantir.
Ce qui doit être relu est calculé, scellé par un hash et listé, avec une raison nommée pour chaque fichier écarté.
L’endroit d’un constat vient d’une citation textuelle du code, localisée par la CLI. Un numéro de ligne donné par un modèle n’est jamais cru.
Le verdict découle de la couverture (chaque fichier sélectionné pris en compte), jamais du nombre de constats.
L’identifiant que vous passez (demo-1 ci-dessous) nomme un dossier, _bmad-output/review/<id>/, où vit toute la preuve. Le terminal montre une exécution réelle sur un projet Git de deux commits : le second a réécrit une fonction avec une boucle décalée d’un cran et a aussi commité un fichier .env.
review scope, rules, puis un gate qui dit incomplete
$ npx bmad-plus review scope demo-1 --base HEAD~1 --effort high/work/demo/app4/_bmad-output/review/demo-1/scope.json1 file(s) to review, 4 changed lines, 1 unit(s) — scope d29789f5ea88 excluded 1 × secretchecklist: /work/demo/app4/_bmad-output/review/demo-1/checklist.mdplan: high effort, 3 pass(es), refutation passEvery selected file must end in coverage.json as completed, failed or waived (with a reason).$ npx bmad-plus review rules src/cart.jsrule set 77b96cb6389d (built-in)src/cart.js gets 2 rule(s): general code builtin ** javascript-typescript code builtin **/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx} **/*.{vue,svelte}$ npx bmad-plus review gate demo-1demo-1: incomplete — coverage 0/1 completed (0 waived), 0 open finding(s), 0 refuted - no coverage.json: which files were reviewed is not established - no findings.json: write it even when there is nothing to report - 1 selected file(s) never accounted for: src/cart.js# Code de sortie 2. Le fichier `.env` a été écarté comme secret avant qu’un motif puisse le ramener.
Sortie capturée lors d’une exécution réelle. Source: bmad-plus 0.23.0, 2026-10-18
L’agent hôte relit maintenant. Il lit scope.json et checklist.md (les règles de revue qui s’appliquent aux fichiers sélectionnés, chacune une fois, avec les fichiers qu’elle couvre) et écrit deux fichiers : findings.json et coverage.json. Un constat cite le code ; le fichier est lié au périmètre par son hash.
findings.json (un constat confirmé ; le hash du périmètre est abrégé)
{
"schema": "bmad-plus/review-findings/1",
"scopeSha256": "d29789f5ea88...",
"findings": [
{
"id": "F1",
"path": "src/cart.js",
"existing_code": "for (let i = 0; i <= items.length; i++) sum += items[i].price;",
"content": "The loop runs one step past the end: items[items.length] is undefined, so reading .price throws.",
"category": "correctness",
"severity": "high",
"confidence": "high",
"disposition": "confirmed",
"evidence": "With items = [{price: 1}], i reaches 1 and items[1] is undefined, so items[1].price throws.",
"trigger": "Any non-empty cart.",
"consequence": "total() throws a TypeError."
}
]
}
coverage.json (chaque fichier sélectionné doit y figurer)
anchor, puis gate avec un fichier de contrôle pour la CI
$ npx bmad-plus review anchor demo-1/work/demo/app4/_bmad-output/review/demo-1/findings.anchored.json — 1 located, 0 ambiguous, 0 unlocated$ npx bmad-plus review gate demo-1 --emit-check _bmad-output/review/demo-1/check.jsondemo-1: findings — coverage 1/1 completed (0 waived), 1 open finding(s), 0 refuted check: /work/demo/app4/_bmad-output/review/demo-1/check.json# Code de sortie 1. Au premier essai, le constat n’avait pas de champ evidence : le gate disait « incomplete » avec la raison « a confirmed finding needs evidence » jusqu’à son ajout.
Résout les références, calcule le diff depuis la base de fusion, sélectionne les fichiers, écrit scope.json et checklist.md. Options : --base REF (défaut HEAD~1, ou HEAD avec --workspace), --head REF (défaut HEAD), --workspace (arbre de travail y compris fichiers non suivis), --include GLOB, --exclude GLOB (répétables), --effort low|medium|high.
0 ; 3 en cas d’erreur
review anchor ID
Valide findings.json, localise chaque citation existing_code dans la version de tête et écrit findings.anchored.json. Les identifiants dans le texte cité deviennent [REDACTED].
0 tout localisé ; 1 invalide ou non localisé
review gate ID
Valide constats et couverture par rapport au hash du périmètre et en déduit le verdict. --emit-check FILE.json écrit aussi un document de contrôle pour la CI.
0 clean ; 1 findings ; 2 incomplete ; 3 erreur
review continue ID
Reprend une revue interrompue sur le même périmètre scellé et écrit continue.json avec ce qui reste dû.
0 ; 3 si le code, les règles ou un fichier sélectionné ont bougé
review compare ID --since EARLIER
Classe les constats de deux revues du même travail en nouveaux, persistants, résolus, réfutés et non relus.
0 ; 3 en cas d’erreur
review rules [PATH]
Liste les règles effectives, ou celles qui s’appliquent à un chemin.
Chaque fichier écarté porte une raison : secret (.env*, clés, certificats, credentials/, secrets/, .npmrc, .netrc ; vérifié en premier, aucun motif d’inclusion ne les ramène), binary, deleted, excluded-by-pattern, generated-or-vendored (fichiers de verrouillage, node_modules, dist, build, fichiers minifiés ; un motif d’inclusion peut les ramener) et not-included. Les fichiers sélectionnés sont découpés en unités ordonnées par dossier, de 8 fichiers et 400 lignes modifiées au plus, pour que des relecteurs en parallèle se partagent le travail de façon déterministe. Le dossier de revue lui-même ne fait jamais partie du changement.
--effort fixe la profondeur avant la lecture : low est une passe ; medium (par défaut) est deux passes et une passe de réfutation ; high est trois passes et une passe de réfutation. Un changement de 150 lignes ou plus est survolé avant d’être lu, et un changement de 400 lignes ou plus en plusieurs unités est relu en unités parallèles. L’effort est scellé dans l’identité du périmètre.
La couverture ou les constats manquent ou sont invalides ; ils répondent à un autre périmètre ; un fichier sélectionné n’est pas pris en compte ou a échoué ; le journal d’exécution dit que la revue s’est arrêtée avant la fin ou a abandonné une unité ; un constat ouvert n’est pas ancré.
2
findings
Le périmètre est entièrement couvert et au moins un constat est ouvert (confirmé ou non résolu).
1
clean
Le périmètre est entièrement couvert et aucun constat n’est ouvert. Cela veut dire « aucun constat ouvert dans un périmètre entièrement couvert » ; ce n’est pas une preuve que le code est correct, et la commande le dit.
0
Les constats utilisent des vocabulaires fermés : gravité critical, high, medium ou low ; confiance high, medium ou low ; état confirmed, refuted ou unresolved ; dix catégories (correctness, security, data-loss, concurrency, performance et d’autres). Une valeur inconnue est une erreur, jamais corrigée. Un constat réfuté garde son motif cité ; un constat confirmé garde sa preuve.
L’ancrage compare des lignes consécutives entières après normalisation des espaces. Une correspondance est localisée, plusieurs sont ambiguës, aucune est non localisée. Un constat ouvert non localisé garde le gate à incomplete : citez un extrait exact plus long.
Une revue peut consigner pourquoi elle s’est arrêtée (champ run.stop de coverage.json : completed, budget, time-limit, failure-streak, interrupted). Tout sauf completed garde le gate à incomplete, même si tous les fichiers sont listés : une revue arrêtée n’est jamais clean. Trois échecs de suite sur la même unité l’abandonnent pour cette revue.
review continue refuse (sortie 3, statut moved) quand le commit, les règles ou un fichier sélectionné ont changé ; la réponse est alors un nouveau périmètre et review compare. Dans compare, un constat est identifié par son chemin, sa catégorie et sa citation normalisée, jamais par la ligne ou l’id, et un constat disparu n’est compté résolu que si la revue ultérieure a terminé son fichier ; sinon il est « non relu ».
Plancher de masquage : avant l’écriture de findings.anchored.json, les valeurs ressemblant à des identifiants dans les champs cités (blocs de clé privée, identifiants dans les URL, en-têtes d’autorisation, tokens bearer, valeurs de clés nommées comme des identifiants, formes de tokens connues) deviennent [REDACTED]. C’est un plancher, pas un scanner : un secret sans forme reconnaissable peut passer.
Une règle est un document Markdown lié à des motifs de chemin ; checklist.md liste chaque règle qui s’applique à au moins un chemin sélectionné, une fois, avec les fichiers qu’elle couvre. Une règle est une consigne de relecture, pas un constat. Une règle peut nommer un group (une famille) pour que des relecteurs parallèles prennent une unité par groupe.
Les huit règles livrées avec BMAD+
Règle
Groupe
S’applique à
general
code
Tous les fichiers : appelants, comportement supprimé, limites, erreurs, confiance, tests.
javascript-typescript
code
Fichiers JS, TS, JSX, TSX, Vue et Svelte.
python
code
Fichiers Python.
sql-and-migrations
data
Fichiers SQL et de migration.
shell
code
Scripts shell.
ci-workflows
delivery
Fichiers de workflow CI.
containers
delivery
Fichiers de construction de conteneurs.
configuration
delivery
Fichiers de configuration.
Un projet ajoute, remplace ou désactive des règles dans _bmad/review-rules.yaml (schéma bmad-plus/review-rules/1) : une règle est {id, title, globs, doc} avec un document Markdown situé dans _bmad/ de 64 Ko au plus, et disable: [id] en retire une. Une clé inconnue, un id en double, un document hors de _bmad/ et la désactivation d’une règle inconnue sont refusés, jamais ignorés. Le jeu de règles est haché dans le périmètre : un fichier de constats répond donc à un seul jeu de règles. Avec le pack Shield installé, six règles de conformité s’ajoutent : shield-access-control, shield-personal-data, shield-cryptography, shield-logging, shield-ai-integrations et shield-change-and-supply-chain. Chacune nomme les contrôles auxquels elle se rattache, comme ISO27001:A.8.5, SOC2:CC6.1, GDPR:Art.32 ou NIS2:Art.21(2)(d).
Deux workflows GitHub Actions prêts à copier se trouvent dans docs/recipes/ci-review/. review-scope.yml scelle le périmètre et publie la liste de contrôle dans le résumé du job et comme artefact (ni agent, ni modèle). review-gate.yml scelle le périmètre, fait écrire findings.json et coverage.json à l’agent hôte installé et connecté sur un runner auto-hébergé, vérifie que rien d’autre que ces deux fichiers n’a changé dans le dossier de revue ni rien en dehors dans l’extraction, puis ancre et passe la porte ; le job échoue sur une revue incomplète ou des constats ouverts. Les deux ne tournent que pour les pull requests du même dépôt, avec des permissions en lecture seule et tout épinglé. gate --emit-check écrit un document dont le bloc github a la forme d’un check run, avec 50 annotations au plus sur des lignes ancrées (critical et high en échec, medium en avertissement, low en notice).
Relire du travail non commité dans un seul dossier#
À utiliser quand le changement n’est pas encore commité ou qu’un seul dossier compte. --base HEAD --workspace compare l’arbre de travail, fichiers non suivis compris, au dernier commit, et --include restreint le périmètre.
Dans votre assistant IA
Sentinel, relis mon travail non commité dans src/billing uniquement. Scelle le périmètre avec bmad-plus review scope billing-1 --base HEAD --workspace --include "src/billing/**" --effort medium et applique les règles de la liste de contrôle. Cite le code de chaque constat mot pour mot, prends en compte chaque fichier sélectionné dans coverage.json, lance review anchor et review gate, et dis-moi exactement quels fichiers ont été écartés et pourquoi. Si le gate répond incomplete, dis ce qui manque au lieu de parler de clean.
Ce que vous devriez obtenir_bmad-output/review/billing-1/ contient scope.json et checklist.md, puis findings.json, findings.anchored.json et coverage.json une fois la revue écrite. Le compte rendu donne le verdict (incomplete, findings ou clean) avec le chiffre de couverture, liste chaque constat ouvert avec son code cité, et nomme les fichiers écartés : un .env à côté du code est écarté comme secret, un fichier hors de src/billing comme not-included.
Les tests automatisés prouvent que les pièces se comportent. Une recette prouve qu’une personne a vu la chaîne fonctionner à l’écran : celui qui écrit, celui qui lit, l’écran, le geste. La compétence Core bmad-plus-uat (pilotée par Sentinel ; commandes uat, alias recette) transforme une livraison en une page autonome qu’un testeur non technique parcourt pas à pas. Ses coches deviennent un résultat JSON que l’agent lit, trie et sur lequel il applique la porte.
01
Écrire la spec
Un fichier JSON _bmad-output/uat/specs/<product>-<version>.json (schéma bmad-plus/uat-spec/2). Une vraie séquence d’étapes, chacune avec where, do et expect ; un clic par ligne do, un fait vérifiable par attente ; les libellés d’écran mot pour mot, copiés du code ; un témoin (ce sur quoi repose le test) avec la requête en lecture seule qui prouve son état ; writes: true plus un contrôle verify sur chaque étape qui persiste quelque chose.
02
Vérifier et construire
uat lint vérifie les libellés par rapport à vos fichiers source, le balisage, le budget (15 étapes et 30 minutes par défaut) et les témoins. uat build écrit la page, qui porte le SHA-256 de la spec pour qu’un résultat dise toujours à quelle révision il a répondu.
Dans le terminal du projet
npx bmad-plus uat lint ID --src src
npx bmad-plus uat build ID
03
Remettre la page
En local, uat serve n’écoute que sur 127.0.0.1 et écrit les réponses directement dans le projet à chaque coche. Ailleurs, envoyez le fichier HTML ; le testeur enregistre le JSON et vous le chargez avec uat import. Dans Claude Code ou claude.ai, la page peut être publiée comme artefact dont la base de données garde les réponses. Dites clairement où vivent les réponses, et quelles étapes écrivent pour de vrai.
Dans le terminal du projet
npx bmad-plus uat serve ID --port 4173
04
Lire le résultat
uat read affiche, pour chaque résultat, le testeur, les dates, les nombres de réussis, échoués, bloqués, ignorés et restants, les avertissements de périmé ou de non signé, et chaque échec avec le texte de l’attente et la note du testeur.
Dans le terminal du projet
npx bmad-plus uat read ID
05
Trier, confirmer les écritures, passer la porte
Classez chaque attente échouée ou bloquée dans triage/<id>.json, exécutez en lecture seule le verify de chaque étape d’écriture réussie et consignez-le, puis lancez uat gate.
Dans le terminal du projet
npx bmad-plus uat gate ID
lint, build, puis un gate sans résultat (la spec d’exemple livrée)
$ npx bmad-plus uat lint example-1.0.0example-1.0.0: 3 steps, 5 expectations, 8 on-screen labels no error$ npx bmad-plus uat build example-1.0.0/work/demo/app4/_bmad-output/uat/pages/uat-example-1.0.0.html — 3 steps, 83 KiB, spec a4a4850b284dopens in English; the tester can switch to any of the 10 languages on the pagecarries the 17 page guarantees: hidden-before-start, no-answer-before-run, verified-local-write, honest-save-status, save-on-every-change, restore-before-capabilities, newer-copy-wins, revision-guard, other-tab-notice, unreadable-draft-kept, progress-accessible, storage-explained, finished-is-not-accepted, unload-guard, utf8-and-escaped-diacritics, export-is-the-run, no-third-party-requests$ npx bmad-plus uat gate example-1.0.0example-1.0.0: awaiting - no run yet# Code de sortie 2 : awaiting. Le fichier de spec est l’exemple livré dans la compétence uat de Core (templates/example-uat-spec.json).
Sortie capturée lors d’une exécution réelle. Source: bmad-plus 0.23.0, 2026-10-18
Verdicts de la porte, classes de tri et types de vérification#
Codes de sortie de uat read et uat gate
Code
Verdict de uat gate
uat read
0
passed : le résultat est terminé, à jour, complet et trié.
Aucune étape échouée.
1
failed.
Au moins un échec.
2
awaiting : pas encore de résultat terminé.
Aucun résultat.
3
stale : le résultat répondait à une autre révision de la spec.
Périmé.
Tri : classer chaque échec avant de corriger quoi que ce soit
Classe
Signe
Suite
product
L’écran diffère de ce que le code devait afficher.
Une tâche de correction sur la story et une étape de rejeu dans la recette de la version suivante.
recipe
L’écran montre le résultat voulu ; l’attente le décrivait mal.
Amender la spec, reconstruire (nouvelle empreinte), rejouer les étapes concernées, et dire qu’aucun changement de produit n’était nécessaire.
data
Le témoin n’est plus dans l’état supposé.
Relancer la requête du témoin, le réinitialiser ou le renommer, rejouer.
undecided
Pas de note, ou non attribuable.
Demander au testeur ce qu’il a vu ; ne jamais deviner. La porte reste rouge.
Une décision est consignée pour chaque échec trié : fix, amend-spec, remeasure, accept-risk (avec un responsable nommé) ou ask-tester. Les contrôles verify ont un type : sql, http, command ou manual. Une coche sur une étape d’écriture n’est pas une écriture : son verify est exécuté en lecture seule et consigné dans writeChecks, car une étape cochée neuf fois sur neuf s’est un jour révélée n’avoir rien écrit. La porte dit ce qu’elle établit : « human-observed: complete, current and triaged ». Elle n’établit pas que le testeur a regardé au bon endroit.
advisory construit et propose la page, ne bloque jamais. gate fait attendre la livraison jusqu’à ce que la porte soit réussie. off ne produit aucune recette et le rapport de livraison le dit. Un bloc absent vaut advisory.
granularity
delivery
Une recette par livraison.
environment
nom et url vides
L’environnement de test visé par la recette (demandé à l’installation).
page_budget
15 étapes, 30 minutes
Au-delà, découper en pages ordonnées.
dir
_bmad-output/uat
Où vivent specs, pages, résultats et tri.
Pourquoi un tag de version exige une recette acceptée#
BMAD+ applique la règle à lui-même. Le 2026-09-16, une commande (studio list) s’est révélée cassée dans une vraie installation, depuis des semaines. La suite de tests était verte, le contrat d’installation passait et la CI était verte. Ce qu’aucun d’eux ne faisait, c’était installer le produit et l’utiliser comme le fait une personne. Depuis la 0.17.0, une version n’atteint pas npm tant qu’une personne n’a pas installé le candidat dans un dossier vide, parcouru la page de recette, et que chaque échec n’a pas été classé. La recette de la 0.23.0 a été déroulée dans un projet vide le 2026-10-18 : 28 attentes sur 28 réussies, chaque étape d’écriture confirmée en lecture seule. Un premier passage de cette recette avait révélé un défaut de désinstallation qui laissait derrière lui les compétences de design copiées ; il a été corrigé avant le tag. Les résultats eux-mêmes restent sur la machine du mainteneur, car ils portent des chemins locaux ; le CHANGELOG consigne le défaut qu’il a révélé.
Dans le dépôt BMAD+, npm run uat:spec échoue quand la version de package.json n’a pas de recette, et npm run uat:gate échoue sauf si le dernier résultat est terminé, répond à la révision courante, ne laisse rien sans réponse, a chaque échec classé avec une décision et chaque étape d’écriture réussie confirmée en lecture seule. Un hook de pré-push impose les deux lors d’un push de tag v*.
Une attente bloquée exige un rejeu ou une décision explicite accept-risk par un responsable nommé. Une recette cochée uniquement pour faire passer la porte est pire que pas de recette : si une étape ne peut pas être exécutée, la réponse est « blocked » avec la raison.
Votre projet peut faire pareil avec deux scripts livrés dans la compétence uat (templates/) : uat-spec-present.mjs (une version sans recette est refusée, avant le push) et uat-release-gate.mjs (sortie 0 réussi, 1 échoué, 2 pas de résultat terminé, 3 autre révision, 4 la porte n’a pas pu s’exécuter, ce qui n’est pas un succès). BMAD+ n’installe aucun hook de cycle de vie : ces scripts sont la couche qu’une consigne ou un agent ne peut pas contourner ; branchez-les là où ils mordent.
Préparer une page de recette pour un formulaire de réservation en préproduction#
À utiliser quand une version arrive sur un site de préproduction et qu’un collègue non technique doit la confirmer à l’écran. Préférez des données de test jetables : l’étape qui crée une réservation écrit pour de vrai.
Dans votre assistant IA
Sentinel, écris la recette du formulaire de réservation que nous venons de livrer en préproduction. Le testeur travaille à l’accueil, ce n’est pas un développeur : une étape par geste, un fait vérifiable par attente, les libellés d’écran copiés du code. Pour l’étape qui crée une réservation, ajoute la requête en lecture seule que tu lanceras ensuite pour prouver que la ligne existe. Lance uat lint avec --src src, puis uat build, et montre-moi les garanties que le build liste. Dis-moi le fichier ou le lien, la durée, quelles étapes écrivent et où les réponses seront stockées.
Ce que vous devriez obtenir Une spec dans _bmad-output/uat/specs/, une page construite dans _bmad-output/uat/pages/uat-<id>.html, la ligne de lint (« N steps, M expectations, K on-screen labels checked against F source files »), une requête verify sur l’étape de réservation, la liste des garanties de la page et une déclaration claire de l’endroit où vivent les réponses. La page n’est pas remise si lint ou build échoue.
À utiliser quand le testeur a terminé ou s’est arrêté. Le fichier de résultat est dans _bmad-output/uat/results/<id>/, ou importé avec uat import.
Dans votre assistant IA
Sentinel, lis le dernier résultat de recette avec uat read. Classe chaque attente échouée ou bloquée comme product, recipe, data ou undecided dans le fichier de tri et interroge-moi sur celles que tu ne sais pas attribuer. Exécute en lecture seule le contrôle verify de chaque étape d’écriture réussie et consigne le résultat. Lance ensuite uat gate et rapporte son verdict, le testeur et ce qui reste ouvert. Ne marque aucune étape non jouée comme réussie.
Ce que vous devriez obtenir Un fichier de tri avec une classe et une décision par échec, des entrées writeChecks pour les étapes d’écriture, et une ligne de porte comme <id>: passed (run <runId> by <tester>) avec le code de sortie 0, ou failed, awaiting ou stale avec les raisons. Un échec non tranché garde la porte rouge jusqu’à la réponse du testeur.