# assurance

> Construire un dossier d’assurance sécurité : des affirmations étayées uniquement par des contrôles réellement exécutés, consignés dans un registre chaîné, puis vérifiés à un commit.

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

`assurance` est la commande qui porte le dossier d’assurance sécurité du pack Shield. Un dossier liste des affirmations sur votre système (par exemple « aucune dépendance de production n’a d’avis connu de gravité haute »), donne un argument pour chacune et l’étaye par des preuves. La seule preuve que BMAD+ accepte est un contrôle qu’il a lui-même exécuté : une commande, son code de sortie, l’empreinte de sa sortie et celles des fichiers qu’elle a écrits, ajoutés à un registre chaîné par empreintes. Une déclaration, un document ou un lien est refusé. On l’utilise quand quelqu’un, un auditeur, un client ou vous-même plus tard, demande de montrer que des mesures tiennent à un commit donné et que « on le croit » ne suffit pas.

> **Attention · Ce qu’un verdict n’est pas** Un dossier « supported » montre que les contrôles nommés ont réussi à un commit. Il ne certifie pas le système et n’est pas un avis d’audit ; c’est à un auditeur qualifié de juger ce que valent les preuves. De plus, `assurance run` exécute les commandes écrites dans le dossier avec la même confiance que vos propres scripts : lisez un dossier venu d’ailleurs avant de le lancer.

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

**Dans le terminal de votre projet**

```sh
npx bmad-plus assurance init <case-file>
npx bmad-plus assurance run <case-file> [--check <id>]... [--dir <path>] [--json]
npx bmad-plus assurance verify <case-file> [--ledger-head <sha256>] [--emit-check <file.json>] [--dir <path>] [--json]
```

**Actions**

| Action | Ce qu’elle fait |
| --- | --- |
| `init` | Copie le modèle Shield vers le fichier de dossier que vous nommez. Le fichier doit s’appeler `<id>.yaml` ; l’identifiant vient du nom. Il n’écrase jamais un fichier existant. |
| `run` | Exécute les contrôles du dossier (tous, ou ceux nommés par `--check`) et ajoute un enregistrement par contrôle au registre. |
| `verify` | Juge chaque contrôle sur sa dernière exécution et chaque affirmation sur ses preuves, puis affiche le verdict. N’exécute rien. |

**Options de assurance**

| Option | Argument | Défaut | Ce qu’elle fait |
| --- | --- | --- | --- |
| `-d, --directory` | `<path>` | dossier courant | Dossier du projet. |
| `--dir` | `<path>` | `_bmad-output/assurance` | Dossier des preuves dans le projet ; le registre se trouve dans `<dir>/<case id>/runs.jsonl`. Chemin relatif écrit avec `/`. |
| `--check` | `<id>` | tous les contrôles | `run` seulement : n’exécuter que ce contrôle. Répétez l’option pour en nommer plusieurs. |
| `--ledger-head` | `<sha256>` | aucune | `verify` seulement : échouer si le registre ne contient plus l’enregistrement portant cette empreinte (64 caractères hexadécimaux minuscules). |
| `--emit-check` | `<file>` | aucune | `verify` seulement : écrire aussi le verdict en résultat de contrôle JSON pour la CI. Le nom doit finir par `.json` et ne doit être ni le dossier ni son registre. |
| `--json` | - | désactivée | Afficher le résultat en JSON (`schemaVersion: 1`) plutôt qu’en texte. Les erreurs deviennent `{ "status": "error", "message": ... }`. |

<a id="case-file"></a>
## Le fichier de dossier

Par convention un dossier vit dans `_bmad/assurance/<id>.yaml`, mais tout chemin relatif au projet convient, sans `..` ni lien symbolique. `init` écrit le modèle Shield, qui est un exemple complet et fonctionnel (quatre contrôles, une affirmation de tête et quatre sous-affirmations). Remplacez chaque exemple par le vôtre, committez le fichier, puis lancez-le. Voici la forme ; les clés inconnues sont refusées.

**Forme d’un dossier (abrégée du modèle)**

```yaml
schema: bmad-plus/assurance-case/1
id: release-security
title: Release security case
scope: >-
  The service at the commit being released.
freshness:
  maxAgeDays: 30
  sameCommit: true
checks:
  - id: unit-tests
    run: [npm, test]
    timeoutSeconds: 900
  - id: secret-scan
    run: [gitleaks, detect, --no-banner, --redact, --report-path, reports/gitleaks.json]
    artifacts: [reports/gitleaks.json]
claims:
  - id: C1
    claim: The release ships no committed credential and no failing control test.
    argument: >-
      Holds when both sub-claims hold.
  - id: C1.1
    parent: C1
    claim: No credential is committed in the repository history.
    argument: >-
      The scanner reads every reachable commit and exits non-zero on a finding.
    controls: [ISO27001:A.5.17, SOC2:CC6.1]
    evidence:
      - check: secret-scan
        shows: gitleaks finds no secret and writes its report.
  - id: C1.2
    parent: C1
    claim: The behaviour the controls rely on is covered by passing tests.
    argument: >-
      The suite exercises access checks and input validation.
    controls: [ISO27001:A.8.29]
    evidence:
      - check: unit-tests
        shows: The test suite passes at this commit.
```

**Clés d’un dossier**

| Clé | Règle |
| --- | --- |
| `schema` | Doit valoir `bmad-plus/assurance-case/1`. |
| `id` | Minuscules, chiffres, points et tirets, 64 caractères au plus. |
| `title`, `scope` | Texte libre, de 1 à 4000 caractères chacun. |
| `freshness.maxAgeDays` | Nombre entier de jours de 1 à 366. Défaut 30. Une exécution plus ancienne est `stale`. |
| `freshness.sameCommit` | `true` (défaut) lie les preuves au commit extrait et à un arbre de travail propre. Mettez `false` seulement pour un projet hors git. |
| `checks[].id` | Identifiant unique du contrôle. |
| `checks[].run` | La commande sous forme de liste d’arguments séparés. Aucun shell n’intervient. Sous Windows, les programmes comme npm sont des fichiers `.cmd` et demandent `[cmd, /c, npm, ...]`. |
| `checks[].cwd` | Dossier relatif au projet où exécuter. Défaut `.`. |
| `checks[].env` | Noms de variables d’environnement supplémentaires que le contrôle peut voir. Par défaut il hérite seulement de `PATH`, `HOME`, des dossiers temporaires et de quelques variables système Windows. `BMAD_PLUS_ASSURANCE_KEY` ne peut jamais y figurer. |
| `checks[].timeoutSeconds` | Nombre entier de 1 à 21600. Défaut 600. |
| `checks[].expect.exitCode` | Code de sortie qui vaut succès, de 0 à 255. Défaut 0. |
| `checks[].artifacts` | Fichiers relatifs au projet que la commande doit écrire ; chacun est condensé avec l’exécution. Un fichier laissé inchangé par l’exécution n’est pas compté comme produit. |
| `claims[].id`, `claim`, `argument` | Obligatoires. L’argument dit pourquoi un contrôle réussi montre l’affirmation. |
| `claims[].parent` | Une affirmation déclarée plus haut. Une affirmation sans preuve doit avoir au moins une sous-affirmation, sinon elle est « seulement déclarée » et le dossier ne se charge pas. |
| `claims[].controls` | Liste facultative de références `FRAMEWORK:ID`. Référentiels acceptés : `ISO27001`, `SOC2`, `GDPR`, `EU-AI-Act`, `NIS2`, `NIST-800-53`. Une mesure mal saisie ou inventée est refusée. |
| `claims[].evidence` | Liste de `{ check, shows }`. `check` doit nommer un contrôle déclaré. |

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

- `init` écrit un seul fichier : le dossier, au chemin donné (les dossiers parents sont créés).
- `run` lit le dossier, exécute chaque contrôle choisi comme processus enfant sans shell, et ajoute une ligne par contrôle à `<dir>/<case id>/runs.jsonl`. Pendant son travail il détient `runs.jsonl.lock`. Il interroge git (`rev-parse`, `status`, `ls-files`) pour noter le commit et si l’arbre était propre.
- `verify` lit le dossier, le registre, git et les artefacts déclarés. Il n’écrit rien, sauf le fichier facultatif de `--emit-check`.
- Elle n’ouvre elle-même aucune connexion réseau. Les commandes que vous déclarez font ce qu’elles font ; c’est pourquoi l’inventaire de traitement des données liste `assurance-checks` comme un flux qui n’existe que lorsque vous invoquez `run`.
- Refusés partout : les chemins hors du projet et tout chemin qui traverse un lien symbolique.

<a id="ledger"></a>
## Le registre et sa clé

Chaque enregistrement de `runs.jsonl` est une ligne JSON : l’identifiant du contrôle, un numéro de séquence, l’empreinte de la commande, le commit (`revision`) et `dirty` (s’il existait des modifications suivies ou des fichiers non suivis non ignorés), les heures de début et de fin, le code de sortie, un éventuel dépassement de délai, la taille et l’empreinte de la sortie avec un extrait expurgé de ses 2048 derniers octets, l’empreinte de chaque artefact déclaré (ou `null` si l’exécution ne l’a pas écrit), l’empreinte de l’enregistrement précédent et son propre `sha256`. Modifier, réordonner ou retirer un enregistrement avant le dernier casse la chaîne, et `verify` juge alors tout le dossier non étayé.

La chaîne seule ne révèle que les modifications négligentes : quiconque peut écrire le fichier peut ajouter un enregistrement qui se vérifie, ou couper les derniers. Deux éléments gardés hors du registre comblent cet écart.

- La clé. Définissez `BMAD_PLUS_ASSURANCE_KEY` (au moins 32 caractères) depuis un secret de CI. `run` ajoute alors un HMAC-SHA256 à chaque enregistrement et `verify` refuse tout enregistrement que la clé n’authentifie pas. La clé n’est jamais transmise à un contrôle. Utilisez la même clé pour `run` et `verify` : un registre écrit sans clé est refusé dès que la clé est définie.
- La tête. `run` affiche l’empreinte de son dernier enregistrement. Gardez-la là où ceux qui écrivent le registre ne peuvent pas la changer (le journal du job de CI, le dossier d’audit) et passez-la à `verify --ledger-head`. Un registre qui ne contient plus cet enregistrement est refusé : les exécutions retirées se voient.

<a id="verdict"></a>
## Comment le verdict est construit

**Statut d’un contrôle (d’après sa dernière exécution)**

| Statut | Signification |
| --- | --- |
| `passed` | S’est exécuté, a fait ce que le contrôle attend, et est récent. |
| `failed` | N’a pas démarré, a dépassé le délai, est sorti avec un autre code que celui attendu, ou un artefact n’a pas été produit par l’exécution. |
| `stale` | A réussi, mais à un autre commit, sur des modifications non commitées ou des fichiers non suivis, avec une commande qui a changé depuis, avec un artefact qui a changé depuis, plus ancien que `maxAgeDays`, ou avec une heure de fin dans le futur. |
| `missing` | Ne s’est jamais exécuté. |

Une affirmation est `supported` seulement quand tous ses contrôles de preuve sont `passed` et que toutes ses sous-affirmations sont `supported` ; sinon elle est `unsupported`, avec les raisons. Le dossier est `supported` seulement quand chaque affirmation l’est, que le registre est intact, que toute `--ledger-head` s’y trouve encore et, avec `sameCommit: true`, qu’il y a un commit et que l’arbre de travail n’a ni modification non commitée ni fichier non suivi (le dossier du registre et les artefacts du dossier ne comptent pas). Le verdict liste aussi `controls.supported` et `controls.unsupported` : les mesures étiquetées sur des affirmations qui ont, ou n’ont pas, de preuve exécutée.

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

**Codes de sortie par action**

| Action | Code | Signification |
| --- | --- | --- |
| `init` | 0 | Fichier de dossier écrit. |
| `run` | 0 | Chaque contrôle choisi s’est terminé comme il l’attend. |
| `run` | 1 | Au moins un contrôle choisi a échoué (démarrage impossible, délai dépassé, mauvais code de sortie, ou artefact déclaré non produit). Les enregistrements sont écrits quand même. |
| `verify` | 0 | Le dossier est `supported`. |
| `verify` | 1 | Le dossier est `unsupported`. |
| toutes | 3 | La commande n’a pas pu faire son travail : action inconnue, dossier absent ou invalide, clé ou tête incorrecte, registre non intact avec `run`, verrou tenu, contrôle non déclaré, `--emit-check` refusé. Affiché `assurance: <message>` sur le flux d’erreur. |

<a id="real-output"></a>
## À quoi ressemble la sortie

Les trois transcriptions ci-dessous sont composées à partir des modèles de messages du code source, avec des valeurs illustratives (chemins, durées et empreintes sont inventés). Elles sont marquées comme simulées pour cette raison.

**Démarrer un dossier, puis l’exécuter** (Simulé)

```text
$ npx bmad-plus assurance init _bmad/assurance/release-security.yaml
_bmad/assurance/release-security.yaml: case release-security started from the Shield template.
Replace every example claim and check with those of the project, commit it, then run it.
$ npx bmad-plus assurance run _bmad/assurance/release-security.yaml
/home/me/app/_bmad-output/assurance/release-security/runs.jsonl
  passed unit-tests               exit 0 in 41.3 s
  passed dependency-audit         exit 0 in 6.8 s
  passed secret-scan              exit 0 in 3.1 s
  passed review-gate              exit 0 in 1.2 s
Recorded 4 run(s); ledger head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c.
Keep the head outside the ledger and verify with bmad-plus assurance verify _bmad/assurance/release-security.yaml --ledger-head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c.
```

Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (modèles de messages de initAction et runAction)

**Vérifier au même commit** (Simulé)

```text
$ npx bmad-plus assurance verify _bmad/assurance/release-security.yaml --ledger-head 3f9c1b7e5a2d4c6e8b0a1d3f5c7e9b2a4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a3c --emit-check ci/assurance.json
release-security: supported — 5/5 claim(s) supported, 4 recorded run(s)
  controls with executed evidence: ISO27001:A.8.8, NIST-800-53:RA-5, ISO27001:A.5.17, SOC2:CC6.1, ISO27001:A.8.32, SOC2:CC8.1, ISO27001:A.8.29
  check: /home/me/app/ci/assurance.json
```

Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (modèle de message de verifyAction)

**Vérifier après un changement de code** (Simulé)

```text
$ npx bmad-plus assurance verify _bmad/assurance/release-security.yaml
release-security: unsupported — 3/5 claim(s) supported, 4 recorded run(s)
  stale   unit-tests: it ran at 1a2b3c4d5e6f, not at 9f8e7d6c5b4a
  unsupported claim C1: sub-claim C1.4 is unsupported
  unsupported claim C1.4: evidence unit-tests is stale
  controls with executed evidence: ISO27001:A.8.8, NIST-800-53:RA-5, ISO27001:A.5.17, SOC2:CC6.1, ISO27001:A.8.32, SOC2:CC8.1
# Code de sortie 1. Relancez le contrôle périmé au nouveau commit, puis vérifiez.
```

Transcription illustrative, non capturée lors d’une exécution réelle. Source: tools/cli/commands/assurance.js (verifyAction), tools/cli/lib/assurance.js (motifs de judgeCheck)

Avec `--emit-check`, le fichier écrit est un objet JSON de schéma `bmad-plus/assurance-check/1` : le statut, la révision, les affirmations avec leurs raisons, les mesures, et une entrée `github` (`name`, `conclusion` valant `success` ou `failure`, un titre comme `3/5 claims supported` et un tableau récapitulatif en Markdown) qu’une étape de CI peut publier comme exécution de contrôle. Il ne contient aucune date, donc le même verdict produit le même fichier.

<a id="errors"></a>
## Erreurs et que faire

**Messages affichés après assurance: (code de sortie 3)**

| Message | Cause | Que faire |
| --- | --- | --- |
| `unknown action "x" (init, run, verify)` | Le premier argument n’est pas l’une des trois actions. | Utilisez `init`, `run` ou `verify`. |
| `no assurance case at <path>` | Le fichier de dossier n’existe pas. | Vérifiez le chemin ou créez-le avec `init`. |
| `<path> already exists` | `init` n’écrase jamais. | Choisissez un autre nom ou modifiez le dossier existant. |
| `case file: name it <id>.yaml ...` | Le nom du fichier n’est pas `<id>.yaml` avec un identifiant valide. | Utilisez des minuscules, chiffres, points et tirets dans l’identifiant. |
| `<case>: claim X is only asserted; support it with evidence from a check or with sub-claims` | Une affirmation n’a ni preuve ni sous-affirmation. | Ajoutez `evidence` nommant un contrôle, ou des sous-affirmations avec `parent`. |
| `evidence must name a check that runs (check: <id>); a statement, document or link is only asserted` | Un élément de preuve n’est pas de la forme `{ check, shows }`. | Écrivez un contrôle dont la commande montre l’affirmation, et désignez-le. |
| `BMAD_PLUS_ASSURANCE_KEY must hold at least 32 characters` | La clé est définie mais trop courte. | Générez un secret plus long, ou supprimez la variable. |
| `the ledger <file> is not intact (record N ...)` | `run` a trouvé un enregistrement modifié, réordonné ou non authentifié (par exemple écrit avant que la clé soit définie). | Gardez le fichier pour examen et enregistrez dans un autre `--dir`. |
| `another run holds <file>.lock; delete it only if no run is in progress` | Une exécution est active, ou l’une a été interrompue. | Attendez ; si aucune ne tourne, supprimez le fichier de verrou. |
| `check X is not declared in <case>` | `--check` nomme un contrôle inconnu. | Utilisez un identifiant de la liste `checks`. |
| `the ledger head must be a record digest of 64 lowercase hexadecimal characters` | `--ledger-head` est mal formé. | Copiez la tête exactement comme `run` l’a affichée. |
| `--emit-check writes a .json file` / `--emit-check must not overwrite the case or its ledger` | Mauvais nom de sortie. | Choisissez un chemin `.json` qui n’est ni le dossier ni le registre. |
| `<path> passes through a symbolic link` / `must be a relative path inside the project, written with /` | Le chemin sort du projet ou traverse un lien. | Utilisez un chemin relatif normal avec des séparateurs `/`. |

<a id="verdict-reasons"></a>
### Raisons qui font sortir verify avec 1

- `the ledger is not intact: ...` ou `... is not authenticated by BMAD_PLUS_ASSURANCE_KEY` : un enregistrement a été modifié, ou la clé ne correspond pas. Reconstruisez le registre en relançant les contrôles dans un nouveau `--dir`.
- `the ledger no longer holds the head ... it was anchored to` : des enregistrements ont été retirés ou le registre remplacé.
- `the working tree has uncommitted changes or untracked files` : committez, puis relancez et vérifiez. Les preuves décrivent le commit, pas votre arbre de travail.
- `there is no commit to bind the evidence to` : le dossier n’est pas un dépôt git avec un commit. Mettez `freshness.sameCommit: false` seulement pour un projet hors git.
- Un contrôle est `missing`, `failed` ou `stale` : la ligne `<status> <check>: <reasons>` dit pourquoi. Relancez ce contrôle au commit courant.

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

<a id="example-cli-assurance-start-case"></a>
### Faire construire et exécuter un dossier par Shield pour une version

Vous allez publier une version et un client a demandé la preuve que les mesures de sécurité tiennent à ce commit précis. L’arbre de travail est commité.

**Dans votre assistant IA**

```text
Shield, démarre un dossier d’assurance pour cette version. Garde chaque affirmation assez petite pour qu’une commande la montre, étiquette-les avec les mesures qu’elles soutiennent, puis exécute les contrôles à ce commit et vérifie le dossier. Dis-moi quelles affirmations ne sont pas étayées et pourquoi.
```

**Ce que vous devriez obtenir** Shield lance `bmad-plus assurance init _bmad/assurance/<id>.yaml`, remplace les affirmations et contrôles d’exemple du modèle par ceux de votre projet (une affirmation de tête, des sous-affirmations chacune appuyée par une commande qui sort en erreur quand l’affirmation tombe) et vous demande de commiter le fichier. Il lance ensuite `assurance run`, garde la tête de registre affichée et lance `assurance verify --ledger-head <head> --emit-check ...`. Il rapporte chaque affirmation non étayée avec la raison donnée par `verify` et liste les mesures qui ont une preuve exécutée. Il dit si une clé et une tête ont servi ; sans elles, il précise que les preuves sont consignées mais pas infalsifiables. Il ne dit jamais qu’une affirmation non étayée est satisfaite.

<a id="example-cli-assurance-explain-verdict"></a>
### Expliquer un verdict non étayé

Un job de CI a échoué sur `assurance verify`, alors que le dossier était vert la semaine dernière.

**Dans votre assistant IA**

```text
Shield, vérifie le dossier release-security et explique chaque affirmation non étayée. Ne relance rien avant de m’avoir dit ce qui est périmé et pourquoi.
```

**Ce que vous devriez obtenir** Shield lance `bmad-plus assurance verify _bmad/assurance/release-security.yaml` (lecture seule) et lit la sortie : la ligne de statut, chaque contrôle non réussi avec ses raisons (autre commit, modifications non commitées, commande ou artefact modifié, plus ancien que `maxAgeDays`) et chaque affirmation non étayée. Il les explique en mots simples, vous dit quels contrôles relancer avec `assurance run --check <id>`, et attend votre feu vert avant d’exécuter quoi que ce soit, car `run` exécute les commandes écrites dans le dossier.

**Voir aussi**

- [Vue d’ensemble de la CLI](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-overview/)
- [ai-register : l’autre fichier de preuves de Shield](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-ai-register/)
- [review : sa porte peut être un contrôle d’un dossier](https://bmad-plus.rochetta.fr/fr/docs/reference/cli-review/)
- [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)
