# Las cinco verificaciones

> Qué detecta cada verificación de audit, con qué severidad, qué lee de AI-FIRST.md, cuándo se omite y cómo se silencia.

`audit` corre cinco verificaciones, siempre en el mismo orden. Todas son código puro —git, sistema de archivos y expresiones regulares—, sin modelo, sin conexión y sin API key: la misma entrada da el mismo resultado. Cada una lee una parte de `AI-FIRST.md`, y si esa parte falta se reporta omitida, nunca aprobada.

## Resumen

| # | Verificación                                                                          | Sev.   | Qué detecta                                                  | Por qué importa                                     | Lee de `AI-FIRST.md`                                              |
| - | ------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------ | --------------------------------------------------- | ----------------------------------------------------------------- |
| 1 | [Zona Prohibida tocada](#zona-prohibida-tocada)                                       | **P0** | Se tocó una ruta declarada como Zona Prohibida               | Es lo único que corta el flujo aun sin `--estricto` | `zonas_prohibidas`                                                |
| 2 | [Decisión sin fila en ADR](#decisi%C3%B3n-sin-fila-en-adr)                            | **P1** | Señales de decisión arquitectónica sin fila nueva en el ADR  | La decisión existe en el código y no en el registro | `superficies_de_decision`, `artefactos.adr`                       |
| 3 | [Alcance excedido](#alcance-excedido)                                                 | **P1** | Más archivos tocados de los que declara la spec activa       | El alcance creció y nadie lo midió                  | `alcance.spec`, `alcance.tolerancia`                              |
| 4 | [Artefacto huérfano](#artefacto-hu%C3%A9rfano)                                        | **P2** | Un documento declarado, o una ruta que menciona, no existe   | Un documento apunta a algo que ya nadie mantiene    | `artefactos`                                                      |
| 5 | [Inventario de componentes desactualizado](#inventario-de-componentes-desactualizado) | **P2** | Componentes en disco que el inventario no nombra, o al revés | El inventario empezó a mentir                       | `artefactos.inventario_componentes`, `artefactos.componentes_dir` |

Cuatro de las cinco son deterministas y no admiten discusión: una ruta cambió o no cambió, un archivo referenciado existe o no existe, un componente aparece en el inventario o no aparece. **La segunda no lo es**, porque «se tomó una decisión arquitectónica» no cabe en una expresión regular: se resuelve declarando dónde un cambio se presume decisión, produce falsos positivos y por eso se puede silenciar. Una verificación que no se puede silenciar termina desactivada entera, que es peor que tenerla ruidosa.

Los patrones de ruta que usan las verificaciones 1, 2 y 3 siguen las reglas de [Patrones de ruta](https://ai-first.falcux.com/docs/referencia/ai-first-md/#patrones-de-ruta). Qué es «tocado» depende del modo, árbol o rango: ver [Modos](https://ai-first.falcux.com/docs/referencia/audit/#modos).

***

## Zona Prohibida tocada

**Severidad:** P0 · **Identificador:** `zona-prohibida`

Compara cada archivo tocado contra los patrones de `zonas_prohibidas`. Un hallazgo por zona, no por archivo: tocar tres migraciones es una Zona Prohibida tocada, con los tres archivos en el detalle. Si la zona declara `razon`, el mensaje la incluye.

* **Lee:** `zonas_prohibidas[].ruta` y `zonas_prohibidas[].razon`.
* **Se omite si** no hay ninguna zona declarada: `no hay «zonas_prohibidas» declaradas`.
* **No se silencia.** Una Zona Prohibida no impide el cambio, lo hace visible: el hallazgo existe para que un humano decida. Si el cambio estaba aprobado, se publica igual; lo que no se hace es quitar la zona para que el detector calle.

```text
✗ Zona Prohibida tocada
    P0  Zona Prohibida «.env*» tocada — credenciales
          .env.local
```

Por qué y cómo se eligen las zonas está en [Gobierno del contexto](https://ai-first.falcux.com/docs/parte-2/gobierno-del-contexto/).

***

## Decisión sin fila en ADR

**Severidad:** P1 · **Identificador:** `decision-sin-adr`

Busca dos señales de que se tomó una decisión arquitectónica:

* **Cambió un archivo que coincide con `superficies_de_decision`.**
* **Un `package.json`, en cualquier carpeta, ganó o perdió una dependencia de producción.** Se comparan las claves de `dependencies`; `devDependencies` no cuenta. Esta señal no hay que declararla.

Si hay alguna señal y el archivo de `artefactos.adr` no ganó una fila en el mismo tramo, emite un P1. **Una fila** es una línea agregada que empieza con un encabezado de nivel 2 o 3 seguido de `ADR-` y un número, como `## ADR-007 — La cola de trabajos pasa a SQS`.

* **Lee:** `superficies_de_decision` y `artefactos.adr`.

* **Se omite si** no hay `artefactos.adr`: `no hay «artefactos.adr» declarado`. Sin superficies declaradas, igual corre: le queda la señal de las dependencias.

* **Se silencia de dos formas:**

  1. **Agregando la fila al ADR**, si de verdad era una decisión.

  2. **Con la anotación en el cuerpo de un commit del rango**, si no lo era, por ejemplo subir un timeout en un `*.config.*`:

     ```text
     <!-- ai-first: sin-decision -->
     ```

     La anotación **sólo se lee en modo rango**, con `--base`: en modo árbol no hay commits cuyo cuerpo leer. Ahí el P1 queda como advertencia y no corta salvo con `--estricto`. El hook `pre-push` corre en modo rango cuando la rama ya existe en el remoto, y el flujo de CI siempre.

```text
✗ Decisión sin fila en ADR
    P1  Hay señales de decisión arquitectónica y ADR.md no ganó ninguna fila
          vite.config.ts (superficie «**/*.config.*»)
          Silenciar: agregar la fila al ADR, o «<!-- ai-first: sin-decision -->» en el cuerpo del commit.
```

Una dependencia aparece en el detalle como `package.json: +nombre` o `package.json: -nombre`. Qué merece una fila y qué no está en [Gobierno del contexto](https://ai-first.falcux.com/docs/parte-2/gobierno-del-contexto/).

***

## Alcance excedido

**Severidad:** P1 · **Identificador:** `alcance-excedido`

Compara los archivos tocados contra los que declara la spec activa. Si los que quedan fuera superan `tolerancia`, emite un solo P1 con la lista.

**La spec activa** es `alcance.spec`: un archivo, o una carpeta, y entonces cuenta cada `.md` o `.mdx` que haya dentro y no empiece por punto. `init` la apunta a `docs/changes/pending/`, donde vive el CHG del cambio en curso.

**Cómo declara archivos una spec:** en una sección cuyo encabezado empiece por «Archivos» o «Alcance», de cualquier nivel y sin importar mayúsculas, con cada ruta entre acentos graves. Se admiten patrones. La sección llega hasta el siguiente encabezado de igual o mayor nivel, y lo que esté dentro de un bloque de código no cuenta.

```md
## Archivos


- `src/lib/cola.ts`
- `src/lib/cola.test.ts`
- `docs/specs/cola.md`
```

En la 0.5.1, una ruta declarada tiene que llevar una barra o una extensión conocida, como `.ts`, `.md`, `.json` o `.yml`. Un archivo sin carpeta y sin extensión reconocida —`.npmrc`, `LICENSE`, `Makefile`— no se puede declarar: si lo tocas, cuenta fuera del alcance y sólo lo cubre la tolerancia.

**Nunca cuentan como fuera de alcance:** `AI-FIRST.md`, la propia spec o su carpeta, y cualquier archivo declarado en `artefactos`. Actualizar el registro de sesión al cerrar es parte del protocolo, no un desvío.

* **Lee:** `alcance.spec` y `alcance.tolerancia`. Sin tolerancia declarada vale 0.

* **Se omite si:**

  * no hay `alcance.spec`: `no hay «alcance.spec» declarado`;
  * no hay ninguna spec en esa ruta: `no hay ninguna spec activa en docs/changes/pending/`. Es el caso normal cuando no hay un cambio abierto: no hay alcance que exceder;
  * las specs no listan archivos: `las specs en … no listan archivos (sección «Archivos» o «Alcance» con rutas entre acentos graves)`.

* **Se silencia** declarando en la spec lo que de verdad entró en el cambio, o subiendo `tolerancia` si el proyecto acepta cierto margen.

```text
✗ Alcance excedido
    P1  3 archivos fuera del alcance declarado (tolerancia: 0)
          src/b.ts
          src/c.ts
          vite.config.ts
```

***

## Artefacto huérfano

**Severidad:** P2 · **Identificador:** `artefacto-huerfano`

Verifica que existan los documentos declarados en `artefactos` y que las rutas que mencionan resuelvan. Dos cosas cuentan como huérfano:

* **Un artefacto declarado que no existe.** Una clave que apunta a una carpeta sólo tiene que existir; un archivo, además, se recorre.
* **Una ruta mencionada dentro de un artefacto que no existe**, sea en un enlace `[texto](ruta)` o entre acentos graves.

**Qué cuenta como mención de ruta:** un token con barra o con una extensión conocida, sin espacios. No cuentan las URL, lo que empieza con `/`, `@`, `-` o `~`, las refs de git como `origin/main`, los patrones con `*` y los marcadores como `CHG-XXX` o `docs/…`. **Lo que está dentro de un bloque de código tampoco**: ahí las rutas son ejemplos.

**Cómo resuelve, en orden:**

1. Desde la raíz del repo y desde la carpeta del documento. Lo que `.gitignore` cubre, como `dist/` o `.env`, cuenta como existente: está ausente a propósito.
2. Un nombre sin carpeta, como `cola.ts`, se busca por nombre en todo el repo: la prosa nombra archivos, no siempre los ubica.
3. Una ruta con carpeta cuyo **primer segmento no existe** en la raíz se asume de otro árbol —otro proyecto, un ejemplo, un repo externo— y no se reporta. Un check que no puede verificar algo no lo cuenta como fallo.

* **Lee:** todas las claves de `artefactos`, las conocidas y las que agregues.
* **Se omite si** no hay ningún artefacto declarado: `no hay «artefactos» declarados`. Tras `init` nunca pasa, porque declara `adr` y `agents`.
* **Se silencia** corrigiendo la ruta o quitando la mención. Si la ruta es de otro repositorio, escríbela con su carpeta o sin acentos graves; si es un ejemplo, dentro de un bloque de código.

```text
✗ Artefacto huérfano
    P2  docs/ARQUITECTURA.md menciona src/lib/queue.ts, que no existe
```

Un artefacto que no existe se reporta como `«artefactos.clave» apunta a ruta, que no existe`. En JSON, la `ruta` del hallazgo lleva el número de línea: `docs/ARQUITECTURA.md:3`.

***

## Inventario de componentes desactualizado

**Severidad:** P2 · **Identificador:** `inventario-componentes`

Compara los componentes que hay en `componentes_dir` con lo que nombra el documento de `inventario_componentes`. Es por nombre, así que un renombre sin actualizar el inventario también lo caza. Mira en las dos direcciones:

* **Un componente en disco que el inventario no menciona.** Basta con que el nombre aparezca como palabra entera en cualquier parte del documento.
* **Un encabezado del inventario que nombra un componente que ya no existe.** Sólo cuentan los encabezados de nivel 2 o más con forma de componente: entre acentos graves o ángulos, como `` `Boton` `` o `<Boton>`, o con mayúscula interna, como `BotonPrimario`. Una palabra capitalizada suelta, como «Formularios», es un título de sección y se deja pasar.

**Qué cuenta como componente:** cada archivo `.astro`, `.tsx`, `.jsx`, `.vue`, `.svelte`, `.ts` o `.js` dentro de la carpeta, a cualquier profundidad. `Boton/index.tsx` es el componente `Boton`; un `index` en la raíz de la carpeta es el barril de exportación y no cuenta. Se ignoran los archivos de prueba, de stories y de declaración de tipos, lo que empieza por `.` o `_`, y `node_modules`.

* **Lee:** `artefactos.inventario_componentes` y `artefactos.componentes_dir`. Hacen falta las dos.

* **Se omite si:**

  * falta alguna de las dos claves: `faltan «artefactos.inventario_componentes» o «artefactos.componentes_dir»`. Es lo que pasa en un repo recién iniciado: `init` las deja comentadas;
  * la carpeta no existe o no es una carpeta;
  * el inventario no existe. Eso ya lo reporta la verificación de artefactos huérfanos.

* **Se silencia** agregando el componente al inventario, o quitando el encabezado del que ya se borró.

```text
✗ Inventario de componentes desactualizado
    P2  Tarjeta existe en src/components/ y no aparece en COMPONENTES.md
    P2  COMPONENTES.md documenta Modal, que ya no existe en src/components/
```

Nota

Los ejemplos de hallazgo de las verificaciones 2 a 5 salen de una corrida del detector sobre un repo de prueba con esas cinco situaciones montadas. El de la verificación 1 es la corrida de [`ai-first audit`](https://ai-first.falcux.com/docs/referencia/audit/#reporte-legible).

## Relacionado

[ai-first audit](https://ai-first.falcux.com/docs/referencia/audit/)Modos, puntaje y códigos de salida.

[El formato de AI-FIRST.md](https://ai-first.falcux.com/docs/referencia/ai-first-md/)Cada campo que leen las verificaciones.

[Auditar en local y en CI](https://ai-first.falcux.com/docs/guias/auditar-en-local-y-en-ci/)Qué hacer con cada hallazgo en el hook y en el pull request.

[Gobierno del contexto](https://ai-first.falcux.com/docs/parte-2/gobierno-del-contexto/)Los cuatro instrumentos que estas verificaciones miden.
