# assurance

> Construir un caso de aseguramiento de seguridad: afirmaciones respaldadas solo por comprobaciones realmente ejecutadas, registradas en un diario encadenado y verificadas en un commit.

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

Documentación de BMAD+ › [Referencia](https://bmad-plus.rochetta.fr/es/docs/reference/) › assurance

`assurance` es el comando detrás del caso de aseguramiento de seguridad del pack Shield. Un caso enumera afirmaciones sobre tu sistema (por ejemplo «ninguna dependencia de producción tiene un aviso conocido de gravedad alta»), da un argumento para cada una y la respalda con evidencias. La única evidencia que BMAD+ acepta es una comprobación que ejecutó él mismo: un comando, su código de salida, la huella de su salida y las de los archivos que escribió, añadidos a un diario encadenado por huellas. Una declaración, un documento o un enlace se rechaza. Se usa cuando alguien, un auditor, un cliente o tú mismo más adelante, pide demostrar que unos controles se cumplen en un commit concreto y «creemos que sí» no basta.

> **Advertencia · Lo que un veredicto no es** Un caso «supported» demuestra que las comprobaciones nombradas pasaron en un commit. No certifica el sistema ni es una opinión de auditoría; un auditor cualificado decide cuánto valen las evidencias. Además, `assurance run` ejecuta los comandos escritos en el caso con la misma confianza que tus propios scripts: lee un caso ajeno antes de ejecutarlo.

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

**En la terminal de tu proyecto**

```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]
```

**Acciones**

| Acción | Qué hace |
| --- | --- |
| `init` | Copia la plantilla de Shield al archivo de caso que indiques. El archivo debe llamarse `<id>.yaml`; el id sale del nombre. Nunca sobrescribe un archivo existente. |
| `run` | Ejecuta las comprobaciones del caso (todas, o las indicadas con `--check`) y añade un registro por comprobación al diario. |
| `verify` | Juzga cada comprobación por su última ejecución y cada afirmación por sus evidencias, y muestra el veredicto. No ejecuta nada. |

**Opciones de assurance**

| Opción | Argumento | Por defecto | Qué hace |
| --- | --- | --- | --- |
| `-d, --directory` | `<path>` | carpeta actual | Carpeta del proyecto. |
| `--dir` | `<path>` | `_bmad-output/assurance` | Carpeta de evidencias dentro del proyecto; el diario está en `<dir>/<case id>/runs.jsonl`. Ruta relativa escrita con `/`. |
| `--check` | `<id>` | todas las comprobaciones | Solo `run`: ejecutar solo esta comprobación. Repite la opción para varias. |
| `--ledger-head` | `<sha256>` | ninguno | Solo `verify`: fallar si el diario ya no contiene el registro con esta huella (64 caracteres hexadecimales en minúscula). |
| `--emit-check` | `<file>` | ninguno | Solo `verify`: escribir además el veredicto como resultado de comprobación JSON para CI. El nombre debe acabar en `.json` y no puede ser el caso ni su diario. |
| `--json` | - | desactivada | Imprimir el resultado en JSON (`schemaVersion: 1`) en lugar de texto. Los errores pasan a ser `{ "status": "error", "message": ... }`. |

<a id="case-file"></a>
## El archivo de caso

Por convención un caso vive en `_bmad/assurance/<id>.yaml`, pero sirve cualquier ruta relativa al proyecto sin `..` ni enlaces simbólicos. `init` escribe la plantilla de Shield, que es un ejemplo completo y funcional (cuatro comprobaciones, una afirmación principal y cuatro subafirmaciones). Sustituye cada ejemplo por el tuyo, haz commit del archivo y luego ejecútalo. Esta es la forma; las claves desconocidas se rechazan.

**Forma de un caso (abreviada de la plantilla)**

```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.
```

**Claves de un caso**

| Clave | Regla |
| --- | --- |
| `schema` | Debe ser `bmad-plus/assurance-case/1`. |
| `id` | Minúsculas, dígitos, puntos y guiones, hasta 64 caracteres. |
| `title`, `scope` | Texto libre, de 1 a 4000 caracteres cada uno. |
| `freshness.maxAgeDays` | Número entero de días de 1 a 366. Por defecto 30. Una ejecución más antigua es `stale`. |
| `freshness.sameCommit` | `true` (por defecto) liga las evidencias al commit activo y a un árbol de trabajo limpio. Pon `false` solo en un proyecto fuera de git. |
| `checks[].id` | Id único de la comprobación. |
| `checks[].run` | El comando como lista de argumentos separados. No interviene ningún shell. En Windows, programas como npm son archivos `.cmd` y necesitan `[cmd, /c, npm, ...]`. |
| `checks[].cwd` | Carpeta relativa al proyecto donde ejecutar. Por defecto `.`. |
| `checks[].env` | Nombres de variables de entorno adicionales que la comprobación puede ver. Por defecto solo hereda `PATH`, `HOME`, las carpetas temporales y algunas variables de sistema de Windows. `BMAD_PLUS_ASSURANCE_KEY` nunca puede figurar. |
| `checks[].timeoutSeconds` | Número entero de 1 a 21600. Por defecto 600. |
| `checks[].expect.exitCode` | Código de salida que cuenta como éxito, de 0 a 255. Por defecto 0. |
| `checks[].artifacts` | Archivos relativos al proyecto que el comando debe escribir; cada uno se resume con la ejecución. Un archivo que la ejecución dejó igual no cuenta como producido. |
| `claims[].id`, `claim`, `argument` | Obligatorios. El argumento dice por qué una comprobación superada demuestra la afirmación. |
| `claims[].parent` | Una afirmación declarada antes. Una afirmación sin evidencia debe tener al menos una subafirmación; si no, está «solo afirmada» y el caso no carga. |
| `claims[].controls` | Lista opcional de referencias `FRAMEWORK:ID`. Marcos aceptados: `ISO27001`, `SOC2`, `GDPR`, `EU-AI-Act`, `NIS2`, `NIST-800-53`. Un control mal escrito o inventado se rechaza. |
| `claims[].evidence` | Lista de `{ check, shows }`. `check` debe nombrar una comprobación declarada. |

<a id="reads-writes"></a>
## Qué lee y qué escribe

- `init` escribe un solo archivo: el caso, en la ruta que indiques (se crean las carpetas padre).
- `run` lee el caso, ejecuta cada comprobación elegida como proceso hijo sin shell y añade una línea por comprobación a `<dir>/<case id>/runs.jsonl`. Mientras trabaja mantiene `runs.jsonl.lock`. Consulta git (`rev-parse`, `status`, `ls-files`) para anotar el commit y si el árbol estaba limpio.
- `verify` lee el caso, el diario, git y los artefactos declarados. No escribe nada salvo el archivo opcional de `--emit-check`.
- No abre por sí mismo ninguna conexión de red. Los comandos que declares hacen lo que hagan; por eso el inventario de tratamiento de datos lista `assurance-checks` como un flujo que solo existe cuando invocas `run`.
- Rechazado en todas partes: rutas fuera del proyecto y cualquier ruta que pase por un enlace simbólico.

<a id="ledger"></a>
## El diario y su clave

Cada registro de `runs.jsonl` es una línea JSON: el id de la comprobación, un número de secuencia, la huella del comando, el commit (`revision`) y `dirty` (si había cambios rastreados o archivos sin rastrear no ignorados), las horas de inicio y fin, el código de salida, si superó el tiempo, el tamaño y la huella de la salida con un extracto censurado de sus últimos 2048 bytes, la huella de cada artefacto declarado (o `null` si la ejecución no lo escribió), la huella del registro anterior y su propio `sha256`. Editar, reordenar o quitar un registro antes del último rompe la cadena, y `verify` juzga entonces todo el caso como no respaldado.

La cadena sola solo delata ediciones descuidadas: quien pueda escribir el archivo puede añadir un registro que se verifica o cortar los últimos. Dos elementos guardados fuera del diario cierran esa brecha.

- La clave. Define `BMAD_PLUS_ASSURANCE_KEY` (al menos 32 caracteres) desde un secreto de CI. `run` añade entonces un HMAC-SHA256 a cada registro y `verify` rechaza cualquier registro que la clave no autentique. La clave nunca se entrega a una comprobación. Usa la misma clave en `run` y `verify`: un diario escrito sin clave se rechaza en cuanto la clave está definida.
- La cabeza. `run` imprime la huella de su último registro. Guárdala donde quienes escriben el diario no puedan cambiarla (el log del job de CI, el archivo de auditoría) y pásala a `verify --ledger-head`. Un diario que ya no contiene ese registro se rechaza, así que las ejecuciones retiradas se notan.

<a id="verdict"></a>
## Cómo se construye el veredicto

**Estado de una comprobación (según su última ejecución)**

| Estado | Significado |
| --- | --- |
| `passed` | Se ejecutó, hizo lo que espera la comprobación y es reciente. |
| `failed` | No arrancó, superó el tiempo, salió con otro código distinto del esperado, o un artefacto no lo produjo la ejecución. |
| `stale` | Pasó, pero en otro commit, con cambios sin confirmar o archivos sin rastrear, con un comando que cambió después, con un artefacto que cambió después, más antiguo que `maxAgeDays` o con una hora de fin en el futuro. |
| `missing` | Nunca se ejecutó. |

Una afirmación es `supported` solo cuando todas sus comprobaciones de evidencia son `passed` y todas sus subafirmaciones son `supported`; si no, es `unsupported`, con los motivos. El caso es `supported` solo cuando lo es cada afirmación, el diario está intacto, cualquier `--ledger-head` sigue en él y, con `sameCommit: true`, hay un commit y el árbol de trabajo no tiene cambios sin confirmar ni archivos sin rastrear (la carpeta del diario y los artefactos del caso no cuentan). El veredicto también lista `controls.supported` y `controls.unsupported`: los controles etiquetados en afirmaciones que tienen, o no, evidencia ejecutada.

<a id="exit-codes"></a>
## Códigos de salida

**Códigos de salida por acción**

| Acción | Código | Significado |
| --- | --- | --- |
| `init` | 0 | Archivo de caso escrito. |
| `run` | 0 | Cada comprobación elegida terminó como espera. |
| `run` | 1 | Al menos una comprobación elegida falló (no arrancó, superó el tiempo, código de salida incorrecto o artefacto declarado no producido). Los registros se escriben igualmente. |
| `verify` | 0 | El caso es `supported`. |
| `verify` | 1 | El caso es `unsupported`. |
| cualquiera | 3 | El comando no pudo hacer su trabajo: acción desconocida, caso ausente o inválido, clave o cabeza incorrecta, diario no intacto en `run`, bloqueo retenido, comprobación no declarada, `--emit-check` rechazado. Se imprime `assurance: <message>` en el flujo de error. |

<a id="real-output"></a>
## Cómo se ve la salida

Las tres transcripciones siguientes se componen a partir de las plantillas de mensajes del código fuente, con valores ilustrativos (rutas, tiempos y huellas son inventados). Por eso están marcadas como simuladas.

**Iniciar un caso y ejecutarlo** (Simulado)

```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.
```

Transcripción ilustrativa, no capturada de una ejecución real. Fuente: tools/cli/commands/assurance.js (plantillas de mensajes de initAction y runAction)

**Verificar en el mismo commit** (Simulado)

```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
```

Transcripción ilustrativa, no capturada de una ejecución real. Fuente: tools/cli/commands/assurance.js (plantilla de mensaje de verifyAction)

**Verificar tras un cambio de código** (Simulado)

```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
# Código de salida 1. Ejecuta de nuevo la comprobación obsoleta en el nuevo commit y verifica.
```

Transcripción ilustrativa, no capturada de una ejecución real. Fuente: tools/cli/commands/assurance.js (verifyAction), tools/cli/lib/assurance.js (motivos de judgeCheck)

Con `--emit-check`, el archivo escrito es un objeto JSON con el esquema `bmad-plus/assurance-check/1`: el estado, la revisión, las afirmaciones con sus motivos, los controles y una entrada `github` (`name`, `conclusion` con valor `success` o `failure`, un título como `3/5 claims supported` y una tabla resumen en Markdown) que un paso de CI puede publicar como ejecución de comprobación. No lleva marca de tiempo, así que el mismo veredicto produce el mismo archivo.

<a id="errors"></a>
## Errores y qué hacer

**Mensajes impresos tras assurance: (código de salida 3)**

| Mensaje | Causa | Qué hacer |
| --- | --- | --- |
| `unknown action "x" (init, run, verify)` | El primer argumento no es una de las tres acciones. | Usa `init`, `run` o `verify`. |
| `no assurance case at <path>` | El archivo de caso no existe. | Revisa la ruta o créalo con `init`. |
| `<path> already exists` | `init` nunca sobrescribe. | Elige otro nombre o edita el caso existente. |
| `case file: name it <id>.yaml ...` | El nombre del archivo no es `<id>.yaml` con un id válido. | Usa minúsculas, dígitos, puntos y guiones en el id. |
| `<case>: claim X is only asserted; support it with evidence from a check or with sub-claims` | Una afirmación no tiene evidencia ni subafirmaciones. | Añade `evidence` que nombre una comprobación, o subafirmaciones con `parent`. |
| `evidence must name a check that runs (check: <id>); a statement, document or link is only asserted` | Un elemento de evidencia no tiene la forma `{ check, shows }`. | Escribe una comprobación cuyo comando demuestre la afirmación y señálala. |
| `BMAD_PLUS_ASSURANCE_KEY must hold at least 32 characters` | La clave está definida pero es demasiado corta. | Genera un secreto más largo o quita la variable. |
| `the ledger <file> is not intact (record N ...)` | `run` encontró un registro editado, reordenado o no autenticado (por ejemplo escrito antes de definir la clave). | Conserva el archivo para inspección y registra en otro `--dir`. |
| `another run holds <file>.lock; delete it only if no run is in progress` | Hay una ejecución activa o se interrumpió una. | Espera; si no hay ninguna, borra el archivo de bloqueo. |
| `check X is not declared in <case>` | `--check` nombra una comprobación desconocida. | Usa un id de la lista `checks`. |
| `the ledger head must be a record digest of 64 lowercase hexadecimal characters` | `--ledger-head` está mal formado. | Copia la cabeza exactamente como la imprimió `run`. |
| `--emit-check writes a .json file` / `--emit-check must not overwrite the case or its ledger` | Nombre de salida incorrecto. | Elige una ruta `.json` que no sea el caso ni el diario. |
| `<path> passes through a symbolic link` / `must be a relative path inside the project, written with /` | La ruta sale del proyecto o cruza un enlace. | Usa una ruta relativa normal con separadores `/`. |

<a id="verdict-reasons"></a>
### Motivos por los que verify sale con 1

- `the ledger is not intact: ...` o `... is not authenticated by BMAD_PLUS_ASSURANCE_KEY`: se alteró un registro o la clave no coincide. Reconstruye el diario ejecutando de nuevo las comprobaciones en un `--dir` nuevo.
- `the ledger no longer holds the head ... it was anchored to`: se quitaron registros o se sustituyó el diario.
- `the working tree has uncommitted changes or untracked files`: haz commit y vuelve a ejecutar y verificar. Las evidencias describen el commit, no tu árbol de trabajo.
- `there is no commit to bind the evidence to`: la carpeta no es un repositorio git con un commit. Pon `freshness.sameCommit: false` solo en un proyecto fuera de git.
- Una comprobación está `missing`, `failed` o `stale`: la línea `<status> <check>: <reasons>` dice por qué. Ejecútala de nuevo en el commit actual.

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

<a id="example-cli-assurance-start-case"></a>
### Que Shield construya y ejecute un caso para una versión

Vas a publicar una versión y un cliente pidió la prueba de que los controles de seguridad se cumplen en ese commit exacto. El árbol de trabajo está confirmado.

**En tu asistente de IA**

```text
Shield, inicia un caso de aseguramiento para esta versión. Mantén cada afirmación lo bastante pequeña para que un comando la demuestre, etiquétalas con los controles que respaldan, luego ejecuta las comprobaciones en este commit y verifica el caso. Dime qué afirmaciones no están respaldadas y por qué.
```

**Lo que deberías obtener** Shield ejecuta `bmad-plus assurance init _bmad/assurance/<id>.yaml`, sustituye las afirmaciones y comprobaciones de ejemplo de la plantilla por las de tu proyecto (una afirmación principal, subafirmaciones cada una respaldada por un comando que sale con error cuando la afirmación falla) y te pide que hagas commit del archivo. Después ejecuta `assurance run`, guarda la cabeza del diario impresa y ejecuta `assurance verify --ledger-head <head> --emit-check ...`. Informa de cada afirmación no respaldada con el motivo que imprime `verify` y lista los controles con evidencia ejecutada. Indica si se usaron una clave y una cabeza; sin ellas, aclara que las evidencias están registradas pero no a prueba de manipulación. Nunca da por cumplida una afirmación no respaldada.

<a id="example-cli-assurance-explain-verdict"></a>
### Explicar un veredicto no respaldado

Un job de CI falló en `assurance verify`, y el caso estaba en verde la semana pasada.

**En tu asistente de IA**

```text
Shield, verifica el caso release-security y explica cada afirmación no respaldada. No vuelvas a ejecutar nada hasta decirme qué está obsoleto y por qué.
```

**Lo que deberías obtener** Shield ejecuta `bmad-plus assurance verify _bmad/assurance/release-security.yaml` (solo lectura) y lee la salida: la línea de estado, cada comprobación no superada con sus motivos (otro commit, cambios sin confirmar, comando o artefacto modificado, más antiguo que `maxAgeDays`) y cada afirmación no respaldada. Los explica con palabras sencillas, te dice qué comprobaciones repetir con `assurance run --check <id>` y espera tu visto bueno antes de ejecutar nada, porque `run` ejecuta los comandos escritos en el caso.

**Véase también**

- [Resumen de la CLI](https://bmad-plus.rochetta.fr/es/docs/reference/cli-overview/)
- [ai-register: el otro archivo de evidencias de Shield](https://bmad-plus.rochetta.fr/es/docs/reference/cli-ai-register/)
- [review: su puerta puede ser una comprobación de un caso](https://bmad-plus.rochetta.fr/es/docs/reference/cli-review/)
- [Solución de problemas](https://bmad-plus.rochetta.fr/es/docs/faq/troubleshooting/)

[Documentación de BMAD+](https://bmad-plus.rochetta.fr/es/docs/) · [Referencia](https://bmad-plus.rochetta.fr/es/docs/reference/)

[Código en GitHub](https://github.com/lrochetta/BMAD-PLUS)
