# bmad-plus uat

> Référence de bmad-plus uat (alias recette) : lint, build, serve, import, read, gate et order pour les recettes d’acceptation humaine, avec options, codes de sortie et exemples.

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

Documentation BMAD+ › [Référence](https://bmad-plus.rochetta.fr/fr/docs/reference/) › bmad-plus uat

`bmad-plus uat` (alias `bmad-plus recette`) gère les recettes d’acceptation humaine. Une recette est une spécification JSON d’étapes qu’une personne joue à l’écran. La commande valide la spécification, construit une page HTML autonome pour le testeur, enregistre ses réponses dans une exécution JSON, liste les échecs et en tire un verdict de porte. Elle sert quand une livraison doit être vue en fonctionnement par une personne, et pas seulement par des tests. Sentinel la pilote à votre place ; vous en avez besoin pour scripter le processus, brancher une étape de CI ou vérifier ce que l’agent affirme.

> **Remarque · Ce que la porte prouve, et ce qu’elle ne prouve pas** La porte établit qu’une exécution est terminée, à jour, entièrement répondue et triée. Elle n’établit jamais que le testeur a regardé au bon endroit : tout verdict est donc étiqueté « observé par un humain ». Une porte « passed » peut encore reposer sur des échecs : ils doivent seulement être classés, chacun avec une décision.

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

**Dans le terminal de votre projet**

```sh
npx bmad-plus uat <action> [target] [options]
npx bmad-plus recette <action> [target] [options]
```

`action` est l’une des sept actions ci-dessous. `target` est un identifiant de recette (la commande cherche `<dir>/specs/<id>.json`, puis toute spécification de ce dossier dont l'`id` correspond) ou le chemin d’un fichier `.json`. `order` n’a pas de cible.

**Les sept actions**

| Action | Ce qu’elle fait | Écrit |
| --- | --- | --- |
| `lint` | Valide la spécification : schéma, clés inconnues, balisage, identifiants d’étapes et d’attendus, témoins, budget de 15 étapes / 30 minutes. Avec `--src`, vérifie que chaque libellé d’écran cité existe dans un fichier source. | rien |
| `build` | Lance d’abord le lint et refuse à la moindre erreur, puis écrit la page du testeur. | `<dir>/pages/uat-<id>.html` |
| `serve` | Sert la page construite sur 127.0.0.1 et enregistre chaque réponse envoyée par la page. Tourne jusqu’à Ctrl+C. | `<dir>/results/<id>/<runId>.json` |
| `import` | Enregistre un JSON de résultats exporté depuis la page (fichier ou entrée standard). | `<dir>/results/<id>/<runId>.json` |
| `read` | Liste chaque exécution et chaque attendu en échec ou bloqué avec son texte et la note du testeur. | rien |
| `gate` | Calcule le verdict : passed, failed, awaiting ou stale. `--emit-check` écrit en plus un vérificateur autonome. | `<dir>/checks/gate-<id>.cjs` (seulement avec `--emit-check`) |
| `order` | Calcule l’ordre de jeu de toutes les recettes de `<dir>/specs` à partir de leur `after` et des témoins partagés. | `<dir>/ORDER.md` |

**Options**

| Option | Argument | Défaut | Effet |
| --- | --- | --- | --- |
| `-d, --directory` | `<path>` | dossier courant | Dossier du projet. |
| `--dir` | `<path>` | `_bmad-output/uat` | Dossier des recettes dans le projet : `specs/`, `pages/`, `results/`, `triage/`, `checks/`. |
| `--src` | `<path>` | aucun | Dossier source scanné pour les libellés d’écran (lint, build). Répétable. |
| `--language` | `<code>` | voir plus bas | Langue de la page : un code (`fr`), un nom (`Français`) ou une balise de locale. |
| `--input` | `<file>` | aucun | import seulement : fichier de résultats à enregistrer ; `-` lit l’entrée standard. Obligatoire pour `import`. |
| `--port` | `<number>` | `4173` | serve seulement : port, lié à 127.0.0.1 uniquement. |
| `--emit-check` | aucun | désactivé | gate seulement : écrit aussi le vérificateur Nexus `checks/gate-<id>.cjs`. |
| `--json` | aucun | désactivé | Sortie lisible par machine, avec `schemaVersion: 1`. |

Langue de la page, par ordre de priorité : le champ `language` de la recette, puis `--language`, puis le `communication_language` lu dans `_bmad/config.yaml`, puis l’anglais. La page embarque toujours les dix langues de l’installateur (en, fr, es, de, pt-br, ru, zh, he, ja, it) et le testeur peut en changer sur la page.

<a id="reads-writes"></a>
## Ce qu’elle lit et écrit

- Lit : les spécifications de `<dir>/specs` (refusées au-delà de 4 Mio ou si le fichier est un lien symbolique), `_bmad/config.yaml` (build seulement, pour `communication_language`), les résultats de `<dir>/results/<id>/` et le fichier de tri `<dir>/triage/<id>.json`.
- Avec `--src` : ne lit que les fichiers source de ces extensions : ts, tsx, js, jsx, mjs, cjs, vue, svelte, html, htm, php, py, rb, java, kt, cs, go, json, yaml, yml, md. Elle ignore les liens symboliques, les dossiers commençant par un point, `node_modules`, `.git`, `dist`, `build`, `coverage`, `.next`, `.nuxt`, `vendor`, `__pycache__`, `.venv`, `venv`, `target` et `out`, saute les fichiers de plus de 512 Kio et s’arrête après 20000 fichiers.
- Écrit : uniquement les fichiers nommés dans le tableau ci-dessus. Les valeurs ressemblant à des identifiants collées dans une note de testeur sont remplacées par `[REDACTED]` avant que l’exécution n’atteigne le disque (`import` et `serve`) ; l’exécution garde alors le nombre de remplacements dans `redactions`.
- Réseau : aucun, sauf `serve`, qui n’écoute que sur 127.0.0.1. La page construite ne demande rien à une autre origine. Un testeur sur une autre machine utilise la page comme fichier ou comme artefact et renvoie le JSON pour `import`.
- `serve` ne répond qu’aux noms d’hôte `127.0.0.1:<port>` et `localhost:<port>` (tout autre en-tête Host reçoit HTTP 403), refuse un corps de requête de plus de 4 Mio et répond HTTP 409 quand une copie plus ancienne d’une exécution remplacerait une plus récente.

<a id="gate-rules"></a>
## Comment la porte décide

La porte prend l’exécution terminée le plus récent de la recette. S’il n’y en a pas, le statut est awaiting. Si cette exécution répond à une autre révision de la spécification (son `specSha256` diffère de la spécification actuelle), le statut est stale. Sinon le statut est failed tant qu’au moins une raison ci-dessous s’applique, et passed quand aucune ne s’applique.

- Un attendu n’a jamais reçu de réponse (les étapes optionnelles sont ignorées).
- Un attendu en échec ou bloqué n’a pas d’entrée dans `<dir>/triage/<id>.json`, ou sa classe est `undecided`, ou sa décision n’est pas l’une de `fix`, `amend-spec`, `remeasure`, `accept-risk`, `ask-tester`.
- Une décision est `accept-risk` sans `decidedBy`.
- Un attendu est bloqué et sa décision n’est pas `accept-risk` : rien n’a été observé, il faut le rejouer ou faire accepter le risque par une personne nommée.
- Une étape `writes: true` est passée, mais le tri n’a pas d’entrée `writeChecks` avec `verified: true` et un texte `evidence` : une coche sur une étape qui écrit est une observation, c’est la preuve en lecture seule qui tranche.

**Vocabulaire du tri**

| Classe | Sens | Décision usuelle |
| --- | --- | --- |
| `product` | L’écran diffère de ce que le code devait afficher. | `fix` |
| `recipe` | L’écran est juste, l’attendu le décrivait mal. | `amend-spec` |
| `data` | Le témoin n’est plus dans l’état supposé. | `remeasure` |
| `undecided` | Aucune note, ou non attribuable : interroger le testeur. Garde la porte au rouge. | `ask-tester` |

<a id="exit-codes"></a>
## Codes de sortie

**Code de sortie par action**

| Action | 0 | 1 | 2 | 3 |
| --- | --- | --- | --- | --- |
| `lint` | aucune erreur | au moins une erreur, ou tout échec | - | - |
| `build` | page écrite | erreur de lint, gabarit ayant perdu une garantie, ou tout échec | - | - |
| `import` | exécution enregistrée | tout échec | - | - |
| `read` | exécutions lues, rien en échec | un attendu en échec ou bloqué, ou tout échec | pas encore d’exécution (awaiting) | l’exécution le plus récent répond à une autre révision (stale) |
| `gate` | passed | failed (ou tout échec) | awaiting | stale |
| `order` | ordre écrit | un cycle, aucune spécification dans le dossier, ou tout échec | - | - |

Un échec est toute erreur levée : action inconnue, cible manquante, spécification introuvable, fichier illisible, `--input` absent. Elle affiche `uat: <message>` sur la sortie d’erreur, ou `{"schemaVersion":1,"status":"error","message":...}` avec `--json`, et sort avec 1. Un script de livraison qui doit distinguer une porte en échec (1) d’une porte qui n’a pas pu s’exécuter utilise la porte de projet `uat-release-gate.mjs`, qui ajoute le code 4 pour le second cas.

<a id="real-output"></a>
## Exemples de sortie

Le premier bloc a été rejoué avec la spécification d’exemple livrée dans la compétence `bmad-plus-uat` ; les blocs `read` et `gate` sont composés à partir des gabarits de messages de la commande, marqués comme simulés, et reprennent le jeu d’essai de `tests/unit/uat-cli.test.js`. Nombres de fichiers, tailles et empreintes dépendent de votre projet.

**Valider puis construire une recette**

```text
$ npx bmad-plus uat lint example-1.0.0
example-1.0.0: 3 steps, 5 expectations, 8 on-screen labels
  no error
$ npx bmad-plus uat build example-1.0.0
/work/app/_bmad-output/uat/pages/uat-example-1.0.0.html — 3 steps, 83 KiB, spec a4a4850b284d
opens in English; the tester can switch to any of the 10 languages on the page
carries 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 lint example-1.0.0 --src src
example-1.0.0: 3 steps, 5 expectations, 8 on-screen labels
  error   step 0-setup: do: the label "Folders" is in no source file — copy it from the component, accents included or not
  error   step 1-nominal: where: the label "Technical" is in no source file — copy it from the component, accents included or not
# Quatre autres erreurs du même genre suivent et la commande sort avec 1 : ici `src` est un dossier vide, donc aucun libellé de la spécification n’y est trouvé. `--src` doit pointer vers les dossiers qui contiennent réellement les libellés de vos écrans.
```

Sortie capturée lors d’une exécution réelle. Source: Rejoué avec le paquet 0.23.0 et la spécification d’exemple de la compétence bmad-plus-uat, dans un projet jetable ; le chemin du projet est remplacé par /work/app

**Lire une exécution, puis demander la porte** (Simulé)

```text
$ npx bmad-plus uat read demo-1.0.0

■ Demo 1.0.0 — Sarah — 2026-09-16 08:00, finished
  1 passed · 1 failed · 0 blocked · 0 skipped · 0 to do   (_bmad-output/uat/results/demo-1.0.0/20260916-sarah-aaaa.json)
  ✗ 1-confirm/a (failed) — Confirm
      expectation: The line reads OPCO confirmed.
      seen instead: the line still reads OPCO to confirm
# Code de sortie 1 : un échec existe.
$ npx bmad-plus uat gate demo-1.0.0
demo-1.0.0: failed (run 20260916-sarah-aaaa by Sarah)
  - 1-confirm/a is failed and not triaged
# Code de sortie 1. Une fois l’échec classé dans triage/demo-1.0.0.json (classe product, décision fix), la même commande affiche « demo-1.0.0: passed ... » et sort avec 0.
```

Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/uat.js (modèles de read et gate) ; tests/unit/uat-cli.test.js (jeu d’essai demo-1.0.0, exécution 20260916-sarah-aaaa)

<a id="errors"></a>
## Erreurs et conduite à tenir

**Messages affichés par uat**

| Message | Cause | Que faire |
| --- | --- | --- |
| `uat: an id or a spec file is required` | Aucune cible pour une action qui en exige une. | Passez l’identifiant de la recette ou le chemin du fichier `.json`. |
| `uat: no spec with the id "<id>" in <dir>/specs` | Aucun fichier du dossier specs ne porte cet `id`. | Vérifiez `--dir` et `--directory` ; la correspondance porte sur le champ `id`, pas seulement sur le nom du fichier. |
| `step <id>: ...` ou `unknown key at spec level: "<key>" ...` (listés comme `error`) | Une faute de frappe ou un champ manquant dans la spécification : les clés inconnues sont refusées, pas ignorées. | Corrigez la spécification et relancez `lint`. |
| `the label "<text>" is in no source file` | Un libellé d’écran cité est introuvable dans les dossiers donnés avec `--src`. | Copiez le libellé depuis le composant, accents compris ou non. Sans `--src`, ce contrôle ne s’exécute pas. |
| `<id>: not built, N error(s)` | `build` lance le lint et refuse d’écrire la page à la moindre erreur. | Corrigez les erreurs listées. |
| `the page template ... lost a guarantee — not built` | Le gabarit de page ne porte plus l’une des 17 garanties. | Réinstallez ou mettez BMAD+ à jour ; ne modifiez pas le gabarit à la main. |
| `no page at <file> — run "bmad-plus uat build <id>" first` | `serve` exige une page construite. | Lancez `build`, puis `serve`. |
| `--input <file> or --input - is required` | `import` sans `--input`. | Passez le JSON exporté, ou `-` pour l’entrée standard. |
| `these results answer <a>, not <b>` | L’exécution importée appartient à une autre recette. | Importez-le avec l’identifiant auquel il répond. |
| `unsupported results schema: ...` ou `results without runId` | Le fichier n’est pas une exécution exportée par la page. | Exportez de nouveau depuis la page, ou utilisez l’export de l’artefact. |
| `<id>: no run yet` (code 2) | Aucun JSON dans `<dir>/results/<id>/`. | Servez ou envoyez la page, puis `import` des réponses. |
| `unknown action "<x>" (lint, build, serve, import, read, gate, order)` | Action mal écrite. | Utilisez l’une des sept actions. |

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

<a id="example-cli-uat-write-and-build"></a>
### Faire écrire et construire la recette par Sentinel

Une version est prête pour l’environnement de test. Vous voulez une page qu’un collègue non technique peut jouer.

**Dans votre assistant IA**

```text
Sentinel, écris la recette d’acceptation de cette livraison sur notre environnement de test. Valide-la avec lint sur src, construis la page et dis-moi quelles étapes écrivent pour de bon.
```

**Ce que vous devriez obtenir** Sentinel écrit la spécification dans `_bmad-output/uat/specs/<id>.json`, lance `bmad-plus uat lint <id> --src src` et corrige ce qui est signalé, puis lance `bmad-plus uat build <id>`, qui écrit `_bmad-output/uat/pages/uat-<id>.html` et liste les garanties de la page. Il vous remet le lien ou le fichier, la durée et les étapes marquées `writes: true`, chacune avec son contrôle `verify` en lecture seule. Si la CLI n’est pas disponible, il le dit au lieu de prétendre que la page est construite.

<a id="example-cli-uat-read-and-gate"></a>
### Lire l’exécution, trier, demander la porte

Le testeur a terminé la page et renvoyé le JSON.

**Dans votre assistant IA**

```text
Sentinel, importe le fichier de résultats results.json pour la recette demo-1.0.0, lis l’exécution, classe chaque échec et dis-moi si la porte passe.
```

**Ce que vous devriez obtenir** Sentinel lance `uat import demo-1.0.0 --input results.json`, puis `uat read demo-1.0.0`, qui affiche chaque attendu en échec ou bloqué avec son texte et la note du testeur. Il écrit une classe et une décision pour chacun dans `_bmad-output/uat/triage/demo-1.0.0.json`, vous interroge quand une note n’explique pas l’échec (la classe `undecided` garde la porte au rouge), puis rapporte `uat gate demo-1.0.0` avec le statut et le code de sortie (0 passed, 1 failed, 2 awaiting, 3 stale). Il étiquette la preuve « observé par un humain ».

**Brancher la porte dans un script de livraison (porte de projet copiée depuis templates/)**

```sh
node scripts/uat-release-gate.mjs || exit 1
# deploy only below this line
```

La compétence livre deux portes de projet dans `.agents/skills/bmad-plus-uat/templates/` : `uat-spec-present.mjs` échoue quand aucune recette ne nomme la version de `package.json`, et `uat-release-gate.mjs` lance `uat gate` avec la CLI installée dans le projet (0 passed, 1 failed, 2 aucune exécution terminée, 3 autre révision, 4 la porte n’a pas pu s’exécuter, ce qui n’est pas un succès).

**Voir aussi**

- [Vue d’ensemble de la CLI](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-overview/)
- [nexus : exécutions durables qui peuvent utiliser la porte comme contrôle](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-nexus/)
- [review : la même logique de preuve pour la revue de code](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-review/)
- [Le bloc uat de config.yaml](https://bmad-plus.rochetta.fr/fr/docs/reference/config-yaml/)
- [Dépannage](https://bmad-plus.rochetta.fr/fr/docs/faq/troubleshooting/)

[Documentation BMAD+](https://bmad-plus.rochetta.fr/fr/docs/) · [Référence](https://bmad-plus.rochetta.fr/fr/docs/reference/)

[Code sur GitHub](https://github.com/lrochetta/BMAD-PLUS)
