mjolnir-qa 1.0.9 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.es.md CHANGED
@@ -1,401 +1,431 @@
1
1
  <div align="center">
2
2
 
3
- <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
3
+ <img src="assets/readme/hero.svg" alt="Mjölnir. Los tests te dicen qué pasó. Mjölnir te dice en qué puedes confiar." width="100%" />
4
4
 
5
- ### Tus tests te mienten. Nosotros lo demostramos.
5
+ <br />
6
6
 
7
- **Verification Trust Engine para QA.** Mjölnir audita suites de tests y
8
- pipelines de CI, reporta una puntuación de idoneidad y muestra
9
- exactamente dónde se rompe la confianza.
7
+ Mjölnir encuentra tests que no pueden fallar y pipelines que no pueden ponerse en rojo,<br />
8
+ y luego puntúa hasta dónde se puede confiar en el resultado, con la evidencia de cada punto.
10
9
 
11
- [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=C19A34&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
12
- [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
13
- [![license](https://img.shields.io/badge/license-MIT-C19A34.svg?style=flat-square&labelColor=0A1119)](LICENSE)
14
- [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-37ABBD.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
15
-
16
- [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | Español | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
10
+ <br />
17
11
 
18
- > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-08.
12
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
13
+ [![downloads](https://img.shields.io/npm/dm/mjolnir-qa.svg?style=flat-square&color=1F6F7C&labelColor=0A1119)](https://www.npmjs.com/package/mjolnir-qa)
14
+ [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0A1119)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
15
+ [![coverage](https://img.shields.io/codecov/c/github/Sergey-Bar/Mjolnir?style=flat-square&color=1F6F7C&labelColor=0A1119&label=coverage)](https://codecov.io/gh/Sergey-Bar/Mjolnir)
16
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Sergey-Bar/Mjolnir/badge)](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
17
+ [![license](https://img.shields.io/badge/license-MIT-1F6F7C.svg?style=flat-square&labelColor=0A1119)](LICENSE)
18
+ [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-1F6F7C.svg?style=flat-square&labelColor=0A1119)](https://nodejs.org)
19
19
 
20
20
  ```bash
21
21
  npx mjolnir-qa@latest
22
22
  ```
23
23
 
24
- **¿Son tus tests dignos de confianza?**
24
+ [Míralo en acción](#míralo-en-acción) · [Inicio rápido](#inicio-rápido) · [Qué encuentra](#qué-encuentra-mjölnir) · [Puntuación](#la-puntuación-de-fiabilidad) · [Evidencia](#el-modelo-de-evidencia) · [Forense](#forense-de-ejecución) · [CI](#integridad-de-ci) · [Agentes](#agentes-de-ia) · [Seguridad](#confianza-y-seguridad) · [Límites](#lo-que-mjölnir-no-puede-decirte) · [Docs](#documentación)
25
+
26
+ <details>
27
+ <summary>Léelo en otro idioma: 22 traducciones</summary>
28
+
29
+ [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | Español | [Français](README.fr.md) | [Italiano](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
25
30
 
26
- [Míralo en acción](#-míralo-en-acción) ·
27
- [Inicio rápido](#-inicio-rápido) ·
28
- [Qué comprueba](#-qué-comprueba-mjölnir) ·
29
- [Puntuación](#cómo-funciona-la-puntuación) ·
30
- [CI](#-integración-ci) · [Configuración](#configuración) ·
31
- [Documentación](#-documentación)
31
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
32
+
33
+ <!-- Source hash: 3541b09e8d04 -->
34
+
35
+ </details>
32
36
 
33
37
  </div>
34
38
 
35
- ---
39
+ <br />
40
+
41
+ ## Un check verde es una afirmación, no una prueba
42
+
43
+ Un check verde significa que el pipeline no falló. No significa que los tests se ejecutaran, ni que hubieran podido fallar. Todos estos casos salen en verde:
44
+
45
+ - un `.only` en un commit que ejecutó 3 tests en lugar de 900
46
+ - `continue-on-error: true` en el job que debía bloquear
47
+ - `|| true` después del comando de tests
48
+ - un test que no verifica nada, o que tiene el cuerpo vacío
49
+ - un wrapper de reintentos que convierte un fallo real en un pase con suerte
50
+ - un informe que el workflow sube pero que nunca se generó
51
+ - un sleep fijo que sostiene una condición de carrera
52
+
53
+ Ninguno pone el pipeline en rojo, y todos parecen deliberados en la revisión. Por eso sobreviven. Aquí está Mjölnir leyendo uno real:
54
+
55
+ <p align="center">
56
+ <img src="assets/readme/scan.svg" alt="El workflow de CI del repositorio de demostración, leído línea a línea. Mjölnir marca cada hallazgo en la línea que reportó, con su regla, qué está mal, su nivel de evidencia y su tasa de falsos positivos medida." width="800" />
57
+ </p>
58
+
59
+ <sub>Cada hallazgo que el escaneo de demostración reportó para este workflow, en la línea reportada. Generado con `npm run docs:readme-brand` a partir de [`demo-report.json`](assets/readme/demo-report.json) y bloqueado contra desviaciones en CI.</sub>
60
+
61
+ **Modo estricto.** Las detecciones más agresivas — `.only`, `continue-on-error`, tests vacíos, abuso de reintentos — viven en la cuarentena. Solo se ejecutan con `--strict` y están limitadas a severidad `info`: señalan, pero nunca bloquean. El escaneo por defecto (`npx mjolnir-qa@latest` sin `--strict`) solo cubre reglas core y extended. Añade `--strict` cuando quieras la capa de asesoramiento también.
62
+
63
+ Mjölnir lee la suite, los workflows de CI y, si lo tienes, el informe de una ejecución real. No ejecuta tus tests, no instala tus dependencias ni ejecuta el código que escanea. Y cuando no tiene evidencia, lo dice en lugar de inventar confianza:
64
+
65
+ | Situación | Qué reporta Mjölnir |
66
+ | ---------------------------------------------------------------- | --------------------------------------------------------------------- |
67
+ | No se encontraron declaraciones de tests | Puntuación `null`, mostrada como **UNKNOWN**. Nunca un 100 inventado. |
68
+ | Sin baseline ni revisión comparable | **UNKNOWN**, con el motivo indicado. Nunca un 0 supuesto. |
69
+ | Escaneo interrumpido (presupuesto de tiempo, archivos ilegibles) | **PARTIAL**, salida `2`. Nunca presentado como limpio. |
70
+
71
+ <p align="center">
72
+ <img src="assets/readme/how-it-works.svg" alt="Cómo funciona Mjölnir. Lee la suite de tests y el pipeline de CI de forma estática, y el informe de una ejecución real cuando lo hay. Pondera cada hallazgo por su nivel de evidencia y su nivel de confianza, donde solo una ejecución real puede alcanzar L3 a L5, y produce hallazgos, una puntuación de fiabilidad y un gate de CI con códigos de salida congelados. En el bucle del agente, la IA escribe la corrección y Mjölnir vuelve a escanear para demostrarla." width="880" />
73
+ </p>
74
+
75
+ <sub>Compuesto para esta página y mostrado a 1:1. Generado con `npm run docs:readme-brand` y bloqueado contra desviaciones en CI; la puntuación, los recuentos y el ID de regla vienen de [`script.demo.json`](assets/video/script.demo.json), [`demo-report.json`](assets/readme/demo-report.json) y el registro de reglas, nunca escritos a mano. La misma imagen como póster: [`architecture.svg`](assets/readme/architecture.svg).</sub>
76
+
77
+ <br />
78
+
79
+ ## Míralo en acción
80
+
81
+ Un escaneo real de [`examples/demo-repo`](examples/demo-repo), una pequeña suite de Playwright con un workflow de CI. Aquí es adonde fueron sus puntos:
36
82
 
37
- ## 🎬 Míralo en acción
83
+ <p align="center">
84
+ <img src="assets/readme/terminal-hero.svg" alt="El desglose de deducciones de Mjölnir: WORTHINESS 80/100 WORTHY, la puntuación por categoría, el cuadro de deducciones por severidad y una lista FIX THIS FIRST" width="520" />
85
+ </p>
86
+
87
+ <sub>Generado con `npm run docs:hero` a partir de un escaneo real y bloqueado contra desviaciones en CI. El informe `--verbose` completo del mismo escaneo es [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
88
+
89
+ <details>
90
+ <summary><strong>Míralo</strong>: un escaneo, la corrección que imprime y el reescaneo que la demuestra</summary>
91
+
92
+ <br />
38
93
 
39
94
  <p align="center">
40
- <img src="assets/readme/demo.svg" alt="El informe --verbose completo de Mjölnir sobre un repo de demostración: WORTHINESS 75/100 NEEDS WORK, un desglose de diagnósticos por categoría, una lista FIX THIS FIRST y cada hallazgo con su ID de regla y número de línea a través de CI, Playwright, higiene de tests y reglas de Python" width="900" />
95
+ <a href="assets/video/mjolnir-demo.mp4">
96
+ <img src="assets/video/mjolnir-demo-poster.png" alt="Un fotograma de la grabación de demostración: npx mjolnir-qa@latest escaneando el repositorio de demostración en una ventana de terminal" width="900" />
97
+ </a>
41
98
  </p>
42
99
 
43
- <sub>La salida completa de `npx mjolnir-qa ./examples/demo-repo --verbose`,
44
- renderizada por el reporter real — nada recortado. Regenerada con
45
- `npm run docs:demo`;
46
- [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
- hace fallar la CI si se desvía de lo que la herramienta imprime.</sub>
100
+ <sub>Renderizado fotograma a fotograma a partir de un escaneo real con `npm run docs:video`; nunca grabado de pantalla. Selecciona el fotograma para abrir [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
48
101
 
49
- **Lo que acaba de pasar:**
102
+ </details>
50
103
 
51
- 1. Mjölnir descubrió los specs de Playwright, su configuración, el
52
- workflow de CI y un archivo de test de Python — cuatro
53
- lenguajes/formatos, una sola pasada.
54
- 2. Encontró evidencia que debilita la confianza en la suite — un
55
- `continue-on-error` que enmascara un job, un `|| true` que traga un
56
- código de salida, sleeps en duro, un selector frágil, URLs de
57
- staging hardcodeadas, una espera `networkidle`.
58
- 3. Convirtió cada uno en un hallazgo concreto con ID de regla,
59
- ubicación y arreglo — y una única puntuación sobre la que puedes
60
- hacer gate a una PR.
104
+ ### Un hallazgo, de cerca
61
105
 
62
- ### Un hallazgo de cerca
106
+ Cada hallazgo responde cuatro preguntas: dónde está, qué tan seguro está Mjölnir, con qué frecuencia se equivoca la regla y cómo corregirlo.
63
107
 
64
- Ejecuta `mjolnir explain QA-CI-001` sobre el primer hallazgo de arriba
65
- y obtienes:
108
+ <p align="center">
109
+ <img src="assets/readme/finding-anatomy.svg" alt="El primer hallazgo del escaneo de demostración, exactamente como lo imprime el terminal, con sus cuatro partes marcadas: dónde, qué tan seguro, con qué frecuencia se equivoca la regla, y la corrección." width="100%" />
110
+ </p>
111
+
112
+ `mjolnir explain QA-CI-001` imprime el historial de confianza completo de una regla, incluida su tasa de falsos positivos medida y el nivel que esa tasa le otorgó:
66
113
 
67
114
  ```text
68
- ▚ QA-CI-001 — continue-on-error masks a failing verification gate
115
+ ▍ QA-CI-001 — continue-on-error masks a failing verification gate
69
116
 
70
117
  Severity: error
71
118
  Confidence: high
119
+ Tier: quarantine
72
120
  Evidence: E2
73
- Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
121
+ QA impact: False-green risk (FALSE-GREEN)
122
+ Measured FP: 11% (19 hand-classified corpus verdicts)
123
+ FP risk: low (author estimate)
124
+ Languages: yaml
125
+ Frameworks: github-actions, azure-pipelines
74
126
 
75
127
  WHAT WAS FOUND (real detector output, not a mockup)
76
128
  Job `security-scan` runs a verification gate under `continue-on-error: true`.
77
129
 
78
130
  WHY IT MATTERS
79
- This job can fail every day and CI will still show green. The checkmark
80
- on this workflow cannot be trusted.
131
+ This job can fail every day and CI will still show green. The checkmark on
132
+ this workflow cannot be trusted.
81
133
 
82
134
  HOW TO FIX
83
135
  Remove continue-on-error, or scope it to individual non-blocking steps only.
84
- ```
85
136
 
86
- Esa es la unidad de valor: no un detalle de estilo, sino un lugar donde
87
- tu CI te dice que algo pasó cuando no pasó.
137
+ Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
88
138
 
89
- ---
139
+ WHAT WOULD CHANGE THE VERDICT
140
+ - a run report next to the scan target (mjolnir.report.json or test-results/)
141
+ corroborating this file lifts its findings to L3–L5
142
+ - a documented suppression (mjolnir.config.json) lowers the finding count
143
+ without claiming correctness
144
+ - quarantine findings run only under --strict and are advisory (E0) — they can
145
+ never gate CI
90
146
 
91
- ## ⚡ Inicio rápido
147
+ NEXT ACTION
148
+ Fix the first occurrence, then re-run: `mjolnir --scope changed`. Every
149
+ occurrence of this rule is listed in the scan output.
92
150
 
93
- Ejecútalo contra un repo para un informe completo y una puntuación de
94
- idoneidad:
151
+ HOW TO VERIFY THE FIX
152
+ Re-run `mjolnir` on the changed file(s) — this finding should no longer
153
+ appear. `mjolnir --scope changed` scopes the check to just what you touched.
95
154
 
96
- ```bash
97
- npx mjolnir-qa@latest
155
+ Docs: mjolnir rules --md (full catalog, this rule included)
98
156
  ```
99
157
 
100
- **En CI, el producto es un solo comando.** Escanea solo lo que la
101
- rama tocó y sale con código distinto de cero ante problemas nuevos:
158
+ Esa es la unidad de valor: un lugar donde CI reporta un pase que no se ganó.
159
+
160
+ <br />
161
+
162
+ ## Inicio rápido
102
163
 
103
164
  ```bash
104
- npx mjolnir-qa@latest --scope changed
165
+ npx mjolnir-qa@latest
105
166
  ```
106
167
 
107
- Suelta eso en un check de PR — `mjolnir ci install` escribe el
108
- workflow — y listo. Todo lo demás es opcional.
168
+ Escanea el directorio actual e imprime el Trust Report: qué encontró, hasta dónde puedes confiar en ello, por qué y qué hacer a continuación. Sale con `0` cuando no se encontró nada en el gate o por encima.
109
169
 
110
- | Comando | Qué hace |
111
- | ----------------------------------- | ------------------------------------------------------------------ |
112
- | `mjolnir` | Escaneo completo del repo + puntuación de idoneidad |
113
- | `mjolnir --scope changed` | Solo lo que introdujo tu rama — la forma de CI |
114
- | `mjolnir ci install` | Genera el workflow de PR asesor |
115
- | `mjolnir explain QA-CI-001` | Qué / por qué / arreglo + tasa de FP medida de una regla |
116
- | `mjolnir rules --unmeasured` | Las reglas que corren por suposición, no por medición |
117
- | `mjolnir --json` / `--format sarif` | Legible por máquina / GitHub Code Scanning |
118
- | `mjolnir --strict` | También ejecuta reglas del tier de cuarentena (mayor riesgo de FP) |
119
-
120
- <details>
121
- <summary><strong>Cuando algo es inestable</strong></summary>
170
+ En CI, escanea solo lo que introdujo la rama, para que una suite heredada no ahogue tu primer pull request:
122
171
 
123
- | Comando | Qué hace |
124
- | ----------------------------------- | ------------------------------------------------------------------------- |
125
- | `mjolnir forensics ./test-results/` | Datos reales de ejecución → veredictos `TRUE-FLAKE`, `FLAKY.md` |
126
- | `mjolnir triage ./test-results/` | Propuesta de cuarentena a partir del historial de ejecución |
127
- | `mjolnir pw-report ./test-results/` | Resumen de ejecución de Playwright — reintentos / inestables / más lentos |
128
- | `mjolnir doctor:playwright` | Escaneo profundo solo Playwright + Selector Health Score |
172
+ ```bash
173
+ npx mjolnir-qa@latest --scope changed
174
+ ```
129
175
 
130
- </details>
176
+ `mjolnir ci install` lo escribe como un workflow de GitHub Actions, usando la [action](https://github.com/Sergey-Bar/Mjolnir#readme) fijada a la etiqueta mayor `v1` (o `npx` a secas con `--no-action`). Se mantiene consultivo hasta que decidas que debe bloquear.
177
+
178
+ | Comando | Qué hace |
179
+ | ----------------------------------- | -------------------------------------------------------------------- |
180
+ | `mjolnir` | Trust Report: veredicto, confianza, siguiente acción |
181
+ | `mjolnir --scope changed` | Solo lo que introdujo tu rama (la forma para CI) |
182
+ | `mjolnir ci install` | Genera el workflow consultivo de PR (basado en la action) |
183
+ | `mjolnir explain QA-CI-001` | Qué, por qué y cómo corregir, más la tasa de FP medida |
184
+ | `mjolnir why src/a.spec.ts:42` | Por qué se marcó exactamente esta línea. Nunca bloquea. |
185
+ | `mjolnir forensics ./test-results/` | Evidencia de ejecución de una ejecución real |
186
+ | `mjolnir trust-report` | Trust Artifact autocontenido (md + json) |
187
+ | `mjolnir handoff` | Plan de remediación para un agente de código |
188
+ | `mjolnir --json` / `--format sarif` | Salida legible por máquina, GitHub Code Scanning |
189
+ | `mjolnir --format codequality` | Informe de GitLab Code Quality (artefacto del widget de MR) |
190
+ | `mjolnir --strict` | Ejecuta también las reglas del nivel quarantine (mayor riesgo de FP) |
131
191
 
132
192
  <details>
133
- <summary><strong>Ocasional / informes</strong></summary>
134
-
135
- | Comando | Qué hace |
136
- | ------------------------------- | ----------------------------------------------------------- |
137
- | `mjolnir fix --dry-run` / `fix` | Autocorrevisiones seguras con prueba |
138
- | `mjolnir baseline` / `diff` | Snapshot de hallazgos, luego reporta solo nuevos/empeorados |
139
- | `mjolnir impact --since <ref>` | Qué cambió desde un commit anterior |
140
- | `mjolnir debt` | Registro de deuda de tests con un modelo de coste |
141
- | `mjolnir handover` | Mapa de onboarding de la suite para QA recién incorporado |
142
- | `mjolnir stats` | Contadores locales de todos los tiempos de arreglos vistos |
143
- | `mjolnir badge` | JSON de endpoint de shields.io + snippet |
144
- | `mjolnir rules --md` | Catálogo completo de reglas (JSON o Markdown) |
145
- | `mjolnir doctor` | Autoauditoría de la propia base de reglas de Mjölnir |
146
- | `mjolnir create-rule <ID>` | Genera el esqueleto de una regla nueva + fixtures |
147
- | `mjolnir --format mermaid` | Diagrama de arquitectura de tests para un comentario de PR |
193
+ <summary><strong>Todos los demás comandos</strong>: triaje de flakes, informes, gobernanza</summary>
194
+
195
+ <br />
196
+
197
+ | Comando | Qué hace |
198
+ | ----------------------------------- | ----------------------------------------------------------------------------------- |
199
+ | `mjolnir --classic` | El banner de puntuación anterior al Trust Report |
200
+ | `mjolnir explain verdict` | Por qué el veredicto del escaneo guardado es el que es |
201
+ | `mjolnir triage ./test-results/` | Triaje guiado. Cada fila termina en una siguiente acción. |
202
+ | `mjolnir pw-report ./test-results/` | Resumen de ejecución de Playwright: reintentos, flakes, los más lentos |
203
+ | `mjolnir doctor:playwright` | Escaneo profundo solo de Playwright más Selector Health Score |
204
+ | `mjolnir fix --dry-run` / `fix` | Correcciones automáticas seguras, cada una reescaneada para demostrar que se aplicó |
205
+ | `mjolnir baseline` / `diff` | Guarda una instantánea de los hallazgos y reporta solo los nuevos o peores |
206
+ | `mjolnir impact --since <ref>` | Qué introdujo y resolvió un commit |
207
+ | `mjolnir summary` | Anotaciones de CI y un resumen del step a partir de un informe |
208
+ | `mjolnir pr-comment` | Un comentario de PR acotado, en Markdown |
209
+ | `mjolnir debt` | Registro de deuda de tests con un modelo de costes |
210
+ | `mjolnir handover` | Mapa de incorporación de la suite para un nuevo ingeniero de QA |
211
+ | `mjolnir init` | Detecta frameworks e imprime una lista de configuración |
212
+ | `mjolnir suppressions` | Lista los hallazgos suprimidos, para gobernanza |
213
+ | `mjolnir rules --unmeasured` | Las reglas que funcionan con suposiciones, no con mediciones |
214
+ | `mjolnir rules --md` | Catálogo completo de reglas (JSON o Markdown) |
215
+ | `mjolnir doctor` | Autoauditoría de la base de reglas de Mjölnir |
216
+ | `mjolnir create-rule <ID>` | Crea el esqueleto de una regla nueva y sus fixtures |
217
+ | `mjolnir stats` | Contadores locales históricos de las correcciones vistas |
218
+ | `mjolnir badge` | JSON de endpoint de shields.io y fragmento |
219
+ | `mjolnir --cache` | Reescaneos incrementales mediante una caché local de veredictos |
220
+ | `mjolnir --format mermaid` | Diagrama de arquitectura de tests para un comentario de PR |
221
+
222
+ `mjolnir help <command>` imprime el uso, ejemplos y el siguiente paso de cualquiera de ellos.
148
223
 
149
224
  </details>
150
225
 
151
- Instálalo globalmente en lugar de `npx` si lo prefieres:
152
- `npm i -g mjolnir-qa`. Requiere Node.js ≥ 22.18. Funciona en Windows,
153
- macOS y Linux.
154
-
155
- ---
226
+ Requiere **Node.js ≥ 22.18** en Windows, macOS o Linux. ¿Prefieres una instalación global? `npm i -g mjolnir-qa`. El mínimo viene de la cadena de compilación (tsdown apunta a él y el pipeline de publicación hace pruebas de humo contra él); las dependencias de ejecución no necesitan más.
156
227
 
157
- ## 👥 ¿Para quién es esto?
228
+ <br />
158
229
 
159
- - **QA / SDET** dueños de una suite e2e o de integración que necesitan
160
- evidencia de que la suite realmente merece el visto verde que
161
- produce.
162
- - **Equipos de Plataforma / DevEx** responsables de la integridad de CI
163
- y de los release gates — la gente a la que le importa que un
164
- `continue-on-error` nunca vuelva verde en silencio una pipeline roja.
165
- - **Mantenedores de OSS** que quieren un gate de verificación barato,
166
- siempre activo, que corra en local y en CI sin llamadas de red.
230
+ ## Qué encuentra Mjölnir
167
231
 
168
- ---
169
-
170
- ## 🔨 Qué comprueba Mjölnir
232
+ <p align="center">
233
+ <img src="assets/readme/stack.svg" alt="Funciona con tu stack: los lenguajes, frameworks de tests y sistemas de CI que cubren sus reglas, según el registro de reglas." width="100%" />
234
+ </p>
171
235
 
172
- | | |
173
- | --- | ------------------------------------------------------------------------------------------------------------------------------------- |
174
- | ⚖️ | **Puntuación de idoneidad** — un número, tabla de deducciones transparente, sin caja negra |
175
- | 🎭 | **Selector Health Score** — califica tus locators de Playwright, no solo tu tasa de aprobación |
176
- | 🔬 | **Forense de ejecución** — lee datos reales de ejecución de Playwright/JUnit para detectar `TRUE-FLAKE`, no solo conjeturas estáticas |
177
- | 🚨 | **Reglas de integridad de CI** — detecta `continue-on-error`, `\|\| true` y otros trucos de verde falso |
178
- | 🐍 | **Los cuatro bindings de Playwright** — TypeScript, Python, Java, C#/.NET — más pytest, JUnit/TestNG y workflows de CI |
179
- | 🔒 | **Local-first** — cero llamadas de red al escanear, cero telemetría, corre en segundos |
236
+ **79 reglas** en cuatro familias (higiene de tests, calidad de tests, Playwright e integridad de CI) para TypeScript y JavaScript, Python, Java, C# y YAML de GitHub Actions. Cubren Playwright en sus cuatro bindings, además de pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest y Mocha, con cobertura inicial para Cypress y Selenium. Nueve de ellas, para mostrar la forma:
180
237
 
181
- ### Las reglas
238
+ | ID | Regla | Severidad | Nivel |
239
+ | ------------ | ----------------------------------------------------------------------- | --------- | ---------- |
240
+ | QA-CI-001 | `continue-on-error` enmascara un gate de verificación que falla | error | quarantine |
241
+ | QA-CI-009 | Código de salida de tests no propagado (`\|` sin pipefail, cadenas `;`) | error | extended |
242
+ | QA-TEST-001 | Test enfocado en un commit (`.only`, `fit`) | error | quarantine |
243
+ | QA-TEST-003 | Test sin aserciones | error | quarantine |
244
+ | QA-TQUAL-009 | Aserción de promesa sin await | error | quarantine |
245
+ | QA-PW-002 | Aserción de locator sin await | error | core |
246
+ | QA-PW-004 | Selectores CSS/XPath frágiles | warning | quarantine |
247
+ | QA-PY-002 | Test omitido (`skip`, `xfail` no estricto) | warning | core |
248
+ | QA-CS-103 | Método de test sin aserciones | error | core |
182
249
 
183
- Cada regla llega con fixtures must-fire **y** must-not-fire. Una regla
184
- que se dispara sobre su propia fixture negativa no puede publicarse —
185
- ese es el cortafuegos de falsos positivos.
250
+ El catálogo completo se genera a partir del registro, nunca se mantiene a mano: `mjolnir rules --md`, [`docs/rules/`](docs/rules/) o la [guía de qué comprueba](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
186
251
 
187
252
  <details>
188
- <summary><strong>Higiene de tests</strong></summary>
189
-
190
- | ID | Regla | Severity |
191
- | ----------- | ------------------------------------------------------ | -------- |
192
- | QA-TEST-001 | Test enfocado committeado (`.only`, `fit`) | error |
193
- | QA-TEST-002 | Test saltado sin justificación | error |
194
- | QA-TEST-002 | Test saltado con justificación registrada | warning |
195
- | QA-TEST-003 | Test sin aserciones | error |
196
- | QA-TEST-004 | Sleep en duro (`waitForTimeout`, `sleep()`, `delay()`) | warning |
197
- | QA-TEST-006 | Abuso de reintentos que esconde inestabilidad | warning |
198
- | QA-TEST-010 | Cuerpo de test vacío | error |
253
+ <summary><strong>Todas las reglas mencionadas en este README</strong>, en una tabla</summary>
254
+
255
+ <br />
256
+
257
+ > Las reglas `quarantine` solo se ejecutan con `--strict` y nunca bloquean (se limitan a info). La severidad mostrada es la definida por el autor.
258
+
259
+ | ID | Familia | Regla | Severidad | Nivel |
260
+ | ------------ | ---------- | -------------------------------------------------------------- | --------- | ---------- |
261
+ | QA-TEST-001 | Higiene | Test enfocado en un commit (`.only`, `fit`) | error | quarantine |
262
+ | QA-TEST-002 | Higiene | Test omitido. Escala a `error` sin un motivo registrado. | warning | quarantine |
263
+ | QA-TEST-003 | Higiene | Test sin aserciones | error | quarantine |
264
+ | QA-TEST-004 | Higiene | Sleep fijo (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
265
+ | QA-TEST-006 | Higiene | Abuso de reintentos que oculta la inestabilidad | warning | quarantine |
266
+ | QA-TEST-010 | Higiene | Cuerpo de test vacío | error | quarantine |
267
+ | QA-TQUAL-002 | Calidad | Aserción tautológica | error | quarantine |
268
+ | QA-TQUAL-009 | Calidad | Aserción de promesa sin await | error | quarantine |
269
+ | QA-TQUAL-011 | Calidad | Tests comentados | warning | extended |
270
+ | QA-PW-002 | Playwright | Aserción de locator sin await | error | core |
271
+ | QA-PW-003 | Playwright | `page.pause()` / `test.only()` en un commit | error | core |
272
+ | QA-PW-004 | Playwright | Selectores CSS/XPath frágiles | warning | quarantine |
273
+ | QA-PW-123 | Playwright | URLs de entorno fijadas en el código | warning | quarantine |
274
+ | QA-PW-140 | Playwright | Captura de pantalla sin `maxDiffPixelRatio` | warning | core |
275
+ | QA-CI-001 | CI | `continue-on-error` enmascara un gate que falla | error | quarantine |
276
+ | QA-CI-002 | CI | `\|\| true` se traga los códigos de salida | error | extended |
277
+ | QA-CI-005 | CI | Informe consumido pero nunca generado | error | quarantine |
278
+ | QA-CI-007 | CI | Wrappers de reintentos alrededor de los tests | warning | extended |
279
+ | QA-CI-008 | CI | Un step que siempre tiene éxito enmascara fallos | error | quarantine |
280
+ | QA-CI-009 | CI | Código de salida no propagado (`\|` sin pipefail, cadenas `;`) | error | extended |
281
+ | QA-CI-010 | CI | Tests omitidos donde deben bloquear | error | quarantine |
282
+ | QA-PY-002 | Python | Test omitido (`skip`, `xfail` no estricto) | warning | core |
283
+ | QA-PY-003 | Python | Función de test sin aserciones | error | quarantine |
284
+ | QA-PY-005 | Python | `time.sleep()` en tests | warning | extended |
285
+ | QA-PY-012 | Python | Aserción tautológica | error | quarantine |
286
+ | QA-JV-101 | Java | Test desactivado (`@Disabled`) | warning | core |
287
+ | QA-JV-102 | Java | Sleep fijo (`Thread.sleep()`) | warning | extended |
288
+ | QA-JV-103 | Java | Método de test sin aserciones | error | extended |
289
+ | QA-JV-105 | Java | Sleep fijo con `waitForTimeout()` de Playwright | warning | core |
290
+ | QA-JV-106 | Java | Selector frágil en lugar de un locator por rol | warning | quarantine |
291
+ | QA-CS-101 | C# | Test omitido (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
292
+ | QA-CS-102 | C# | Sleep fijo (`Thread.Sleep` / `Task.Delay`) | warning | core |
293
+ | QA-CS-103 | C# | Método de test sin aserciones | error | core |
294
+ | QA-CS-105 | C# | Sleep fijo con `WaitForTimeoutAsync()` | warning | extended |
295
+ | QA-CS-106 | C# | Selector frágil en lugar de un locator por rol | warning | quarantine |
296
+
297
+ Python también incluye QA-PY-001…012 (higiene de pytest) y QA-PY-101…108 (Playwright para Python). Cypress y Selenium tienen conjuntos iniciales de tres reglas cada uno.
199
298
 
200
299
  </details>
201
300
 
202
- <details>
203
- <summary><strong>Calidad de tests</strong></summary>
301
+ Cada regla se publica con un fixture must-fire **y** otro must-not-fire, y una regla que se dispara con su propio fixture negativo no puede publicarse. Ese es el cortafuegos de falsos positivos; `mjolnir doctor` lo impone en la propia CI de este repositorio.
204
302
 
205
- | ID | Regla | Severity |
206
- | ------------ | ----------------------------- | -------- |
207
- | QA-TQUAL-002 | Aserción tautológica | error |
208
- | QA-TQUAL-009 | Aserción de promesa sin await | error |
209
- | QA-TQUAL-011 | Tests comentados | warning |
303
+ ### Selector Health Score
210
304
 
211
- </details>
305
+ `mjolnir doctor:playwright` califica cada locator según cómo encuentra un elemento: como lo haría un usuario (rol, etiqueta, texto), mediante un contrato explícito (`data-testid`) o por un accidente estructural (cadenas CSS, XPath). Cada archivo obtiene una puntuación de 0 a 100:
212
306
 
213
- <details>
214
- <summary><strong>Playwright 🎭</strong></summary>
307
+ ```text
308
+ ▍ SELECTOR HEALTH
215
309
 
216
- | ID | Regla | Severity |
217
- | --------- | ------------------------------------------- | -------- |
218
- | QA-PW-002 | Aserción de locator sin await | error |
219
- | QA-PW-003 | `page.pause()` / `test.only()` committeados | error |
220
- | QA-PW-004 | Selectores CSS/XPath frágiles | warning |
221
- | QA-PW-123 | URLs de entorno hardcodeadas | warning |
310
+ e2e/login.spec.ts
311
+ [█████████████░░░░░░░] 65 / 100
312
+ role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
222
313
 
223
- </details>
314
+ e2e/checkout.spec.ts
315
+ [██████████████████░░] 88 / 100
316
+ role/text: 4 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
317
+ ```
224
318
 
225
- <details>
226
- <summary><strong>Integridad de CI</strong></summary>
227
-
228
- | ID | Regla | Severity |
229
- | --------- | ----------------------------------------------------------------------- | -------- |
230
- | QA-CI-001 | `continue-on-error` enmascara fallos | error |
231
- | QA-CI-002 | `\|\| true` traga códigos de salida | error |
232
- | QA-CI-005 | Reporte consumido pero nunca generado | error |
233
- | QA-CI-007 | Wrappers de reintento alrededor de tests | warning |
234
- | QA-CI-008 | Paso siempre exitoso enmascara fallos | error |
235
- | QA-CI-009 | Código de salida del test no propagado (`\|` sin pipefail, cadenas `;`) | error |
236
- | QA-CI-010 | Tests saltados donde deben bloquear (guardas skip-on-PR) | error |
319
+ Esto mide **resiliencia, no corrección**. `.btn.btn-primary > div:nth-child(2)` pasa hoy y sigue pasando hasta que alguien toca el marcado. Una puntuación baja nunca afirma que el test esté roto, solo que depende de un marcado que nadie prometió mantener.
237
320
 
238
- </details>
321
+ <br />
239
322
 
240
- <details>
241
- <summary><strong>Python / pytest 🐍</strong></summary>
323
+ ## La puntuación de fiabilidad
242
324
 
243
- | ID | Regla | Severity |
244
- | --------- | ------------------------------------------ | -------- |
245
- | QA-PY-002 | Test saltado (`skip`, `xfail` no estricto) | warning |
246
- | QA-PY-003 | Función de test sin aserciones | error |
247
- | QA-PY-005 | `time.sleep()` en tests | warning |
248
- | QA-PY-012 | Aserción tautológica | error |
325
+ <p align="center">
326
+ <img src="assets/readme/score-gauge.svg" alt="La escala de fiabilidad de 0 a 100, con un marcador que recorre cada puntuación: UNWORTHY por debajo de 50, NEEDS WORK de 50 a 79, WORTHY de 80 a 99, FORGED en 100" width="720" />
327
+ </p>
249
328
 
250
- 20 reglas de Python en total (QA-PY-001…012 higiene pytest + QA-PY-101…108 Playwright-Python).
329
+ <sub>Cada puntuación de 0 a 100, situada por el `deriveScoreState` real. Generado con `npm run docs:gauge` y bloqueado contra desviaciones en CI.</sub>
251
330
 
252
- </details>
331
+ | Puntuación | Veredicto |
332
+ | ---------- | ----------------------------------------------------- |
333
+ | `0 – 49` | **UNWORTHY** |
334
+ | `50 – 79` | **NEEDS WORK** |
335
+ | `80 – 99` | **WORTHY** |
336
+ | `100` | **FORGED** |
337
+ | `null` | **UNKNOWN**: no se encontraron declaraciones de tests |
253
338
 
254
- <details>
255
- <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
339
+ **Cómo se calcula.** La severidad fija una deducción base (`error −8`, `warning −3`, `info −1`) y el nivel de evidencia la descuenta: E2 cuenta completo, E1 la mitad (redondeando hacia abajo), E0 nada. El total se normaliza por la exposición de la suite, es decir, deducciones por declaración de test en lugar de por archivo. El terminal imprime los mismos números descontados que usó la puntuación; no hay un segundo modelo oculto. Detalles: [docs/SCORING.md](docs/SCORING.md) y la [guía de puntuación](https://sergey-bar.github.io/Mjolnir/guide/scoring).
256
340
 
257
- | ID | Regla | Severity |
258
- | --------- | ---------------------------------------------- | -------- |
259
- | QA-JV-101 | Test deshabilitado (`@Disabled`) | warning |
260
- | QA-JV-102 | Sleep en duro (`Thread.sleep()`) | warning |
261
- | QA-JV-103 | Método de test sin aserciones | error |
262
- | QA-JV-105 | Sleep en duro de Playwright `waitForTimeout()` | warning |
263
- | QA-JV-106 | Selector frágil en vez de role locator | warning |
341
+ **Lo que 100 no significa.** No significa que el software sea correcto, que la suite sea adecuada o que el producto esté libre de defectos. Significa una sola cosa: **ninguna de las reglas evaluadas por Mjölnir produjo una deducción con este escaneo y este modelo de evidencia.**
264
342
 
265
- </details>
343
+ <br />
266
344
 
267
- <details>
268
- <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
345
+ ## El modelo de evidencia
269
346
 
270
- | ID | Regla | Severity |
271
- | --------- | --------------------------------------------- | -------- |
272
- | QA-CS-101 | Test saltado (`[Ignore]`, `[Fact(Skip=)]`) | warning |
273
- | QA-CS-102 | Sleep en duro (`Thread.Sleep` / `Task.Delay`) | warning |
274
- | QA-CS-103 | Método de test sin aserciones | error |
275
- | QA-CS-105 | Sleep en duro `WaitForTimeoutAsync()` | warning |
276
- | QA-CS-106 | Selector frágil en vez de role locator | warning |
347
+ Cada hallazgo lleva dos etiquetas: qué tan seguro está Mjölnir y hasta dónde se comprobó el hallazgo. Esa es la diferencia entre una herramienta que reporta patrones y una herramienta en la que puedes basar un release.
277
348
 
278
- </details>
349
+ **Qué tan seguro: el nivel de evidencia.**
350
+
351
+ | Nivel | Nombre | Significa | Deducción |
352
+ | ------ | ------------------- | ----------------------------------------------------------- | --------- |
353
+ | **E2** | Prueba determinista | El defecto está presente en el código tal como está escrito | Completa |
354
+ | **E1** | Evidencia de patrón | Coincidió un patrón fuertemente ligado al defecto | Mitad |
355
+ | **E0** | Observación | Vale la pena saberlo. No afirma que algo esté mal. | Cero |
279
356
 
280
- > El catálogo vivo completo — cada regla con tier, confidence, riesgo de
281
- > falso positivo y disponibilidad de autofix — se genera desde el
282
- > registro:
283
- >
284
- > ```bash
285
- > mjolnir rules --md
286
- > ```
287
- >
288
- > Las páginas por regla viven en [`docs/rules/`](docs/rules/).
289
-
290
- ### Cuánto está medido
291
-
292
- **78 de 99 reglas llevan una tasa de falsos positivos medida contra
293
- código OSS real** (≥ 10 hallazgos clasificados a mano cada una; ver
294
- [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Las otras 21 salen sobre la
295
- estimación del autor. Cada pie de escaneo te dice cuántas de las reglas
296
- _que se dispararon_ están medidas; `mjolnir rules --unmeasured` lista
297
- las que no; la página `mjolnir explain` de cada regla declara su
298
- está en cuarentena por ello. Hacer crecer esa cifra es el trabajo continuo
299
- del proyecto.
300
-
301
- ### Tiers de reglas y madurez por lenguaje
302
-
303
- Cada regla es `core`, `extended` o `quarantine`, asignado según su tasa
304
- de falsos positivos **medida**:
305
-
306
- | Tier | Significación | Escaneo por defecto | `--strict` |
307
- | ------------ | --------------------------------------------- | :-----------------: | :--------: |
308
- | `core` | ≤ 10 % de FP medido | ✅ | ✅ |
309
- | `extended` | ≤ 30 % de FP medido | ✅ | ✅ |
310
- | `quarantine` | por encima del 30 %, o aún sin medir (n < 10) | ❌ | ✅ |
311
-
312
- | Lenguaje | Adaptador | Cobertura hoy |
313
- | --------------- | ------------------ | ------------------------------------------------------------ |
314
- | TypeScript / JS | AST del compilador | la más amplia y medida — sobre todo `core`/`extended` |
315
- | Python / pytest | Capa de regex | amplia, auditada sobre corpus — sobre todo `core`/`extended` |
316
- | Java | Capa de regex | más nuevo — sobre todo `extended`/`quarantine` |
317
- | C# / .NET | Capa de regex | más nuevo — sobre todo `extended`/`quarantine` |
318
-
319
- TypeScript y Python tienen la cobertura medida más amplia. Java y C#
320
- están publicados, documentados, y permanecen fuera del número titular
321
- hasta que una suite consumidora real (no los propios tests de una
322
- librería de binding) haya sido auditada.
323
-
324
- ---
325
-
326
- ## Cómo funciona la puntuación
357
+ La confianza en una detección no es la fuerza de la prueba. Una regla puede estar segura de haber encontrado lo que buscaba y aun así estar mirando una heurística. Los hallazgos E1 están para leerlos y juzgarlos, nunca para aplicarlos a ciegas, y ese límite queda marcado en el hallazgo en el terminal, en el JSON y en el traspaso al agente.
358
+
359
+ **Hasta dónde se comprobó: el nivel de confianza.** La mayoría de los hallazgos provienen de leer tu código. Dale a Mjölnir el informe de una ejecución real de tests y podrá confirmar que el código realmente se ejecutó.
327
360
 
328
361
  <p align="center">
329
- <img src="assets/readme/terminal-hero.svg" alt="Salida de terminal de Mjölnir — WORTHINESS 75/100 NEEDS WORK, un desglose de diagnósticos por categoría y una lista FIX THIS FIRST" width="820" />
362
+ <img src="assets/readme/trust-ladder.svg" alt="La escalera de confianza de L0 a L5. De L0 a L2 provienen de leer el código; de L3 a L5 necesitan el informe de una ejecución real, marcado por una ruptura en la escalera." width="100%" />
330
363
  </p>
331
364
 
332
- <sub>Regenerada con `npm run docs:hero`;
333
- [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
334
- hace fallar la CI si se desvía de lo que el reporter realmente imprime.</sub>
365
+ | Nivel | En palabras sencillas | Qué se necesita |
366
+ | ------ | --------------------- | ---------------------------------------------------------------------- |
367
+ | **L0** | Anotado | Leer el código |
368
+ | **L1** | Parece el problema | Leer el código: coincidió un patrón |
369
+ | **L2** | Probado en el código | Leer el código: el defecto es estructural |
370
+ | **L3** | El archivo se ejecutó | Un informe de ejecución muestra que se ejecutó el archivo del hallazgo |
371
+ | **L4** | El test se ejecutó | Un informe de ejecución muestra que se ejecutó el test del hallazgo |
372
+ | **L5** | La ejecución coincide | El propio resultado de la ejecución confirma la clase de defecto |
335
373
 
336
- La puntuación es transparente: **error −8, warning −3, info −1**, luego
337
- normalizada por la exposición de la suite (deducciones por declaración
338
- de test). Las deducciones ponderadas por evidencia significan que las
339
- señales débiles cuestan menos. La terminal muestra los mismos números
340
- descontados que usa la puntuación — sin caja negra. Método completo:
341
- [docs/SCORING.md](docs/SCORING.md).
374
+ Un escaneo estático se detiene en L2. Solo el informe de una ejecución real (Playwright JSON, Jest o Vitest JSON, JUnit XML) puede elevar un hallazgo a L3 o más, de modo que un hallazgo que nunca se vio ejecutarse nunca puede afirmar que se ejecutó. Definiciones: [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
342
375
 
343
- **Veredictos**
376
+ ### Cuánto de esto está medido
344
377
 
345
- | Score | Veredicto |
346
- | ------- | ---------------- |
347
- | ≥ 80 | ✓ **WORTHY** |
348
- | 50 – 79 | ⚠ **NEEDS WORK** |
349
- | < 50 | ✖ **UNWORTHY** |
378
+ **74 de 79 reglas tienen una tasa de falsos positivos medida contra código OSS real** (al menos 10 hallazgos clasificados a mano cada una; consulta [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Las otras 5 se publican con la estimación del autor y lo dicen, regla por regla, en `mjolnir explain`. `mjolnir rules --unmeasured` las lista, y el pie de cada escaneo indica cuántas de las reglas que realmente se _dispararon_ están medidas.
350
379
 
351
- **Niveles de evidencia** — cada hallazgo lleva uno; fija el peso del
352
- hallazgo en la puntuación:
380
+ Las tasas siguen siendo públicas cuando son malas. QA-TEST-001 (un `.only` en un commit) sale mal en la auditoría sobre repositorios reales y por eso está en quarantine. La cifra actual de cada regla, incluida QA-PW-141, está en la auditoría.
353
381
 
354
- | Nivel | Significación | Impacto en la puntuación | Ejemplo |
355
- | ----- | -------------------- | ------------------------ | ------------------------------------------------------- |
356
- | E2 | Defecto determinista | Deducción completa | `.only` committeado — estructuralmente demostrable |
357
- | E1 | Patrón heurístico | Media deducción | `sleep()` detectado por regex — señal fuerte, no prueba |
358
- | E0 | Observación | Cero (solo info) | Reportado pero nunca hace gate a CI ni deduce |
382
+ ### Niveles de confianza
359
383
 
360
- La mayoría de las reglas son **E1**. El lema «we prove it» se refiere a
361
- este sistema: los hallazgos E2 son prueba estructural; los hallazgos E1
362
- son advertencias correctamente posicionadas, no pruebas formales.
384
+ Los niveles siguen la tasa de falsos positivos medida, no la opinión:
363
385
 
364
- Un repo vacío puntúa `null`, nunca un falso 100 — ver
365
- [Modelo de confianza](#modelo-de-confianza).
386
+ | Nivel | FP medido | Comportamiento |
387
+ | -------------- | -------------------------------- | --------------------------------------------------- |
388
+ | **core** | ≤ 10% | Informe predeterminado, bloquea |
389
+ | **extended** | ≤ 30% | Informe predeterminado, menor confianza |
390
+ | **quarantine** | > 30% o explícitamente declarado | Solo con `--strict`, limitado a info, nunca bloquea |
391
+ | _sin medir_ | n < 10 | No puede ascender a core hasta ser medida |
366
392
 
367
- ---
393
+ Las bandas de FP solo pueden degradar un nivel — nunca promueven una regla fuera de `quarantine` si fue declarada explícitamente allí. Una regla explícitamente puesta en quarantine permanece en quarantine sin importar su tasa de FP medida.
368
394
 
369
- ## 🎭 Selector Health Score
395
+ Ascensos, descensos y madurez por lenguaje: [ciclo de vida de las reglas](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
370
396
 
371
- La métrica titular para suites de Playwright — qué tan resilientes son
372
- tus locators:
397
+ ### Por qué esto no es un linter
373
398
 
374
- ```text
375
- ▚ SELECTOR HEALTH — e2e/checkout.spec.ts
399
+ Los linters te dicen si el código sigue unas reglas. Mjölnir te dice si se puede confiar en tu verificación.
376
400
 
377
- [█████████████████░░░] 83 / 100
378
- role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
379
- ```
401
+ | | Linters (ESLint, SonarQube) | Herramientas de cobertura | Revisión de código con IA | **Mjölnir** |
402
+ | ----------------------------------------------------------------------- | :-------------------------: | :-----------------------: | :-----------------------: | :--------------: |
403
+ | Puntúa el **sistema de verificación**, no el código del producto | No | No | No | Sí |
404
+ | Integridad de los workflows de CI (`continue-on-error`, `\|\| true`) | No | No | solo el diff | Sí |
405
+ | Califica la resiliencia de los locators de Playwright (Selector Health) | No | No | No | Sí |
406
+ | Lee datos de ejecución reales para veredictos `TRUE-FLAKE` | No | No | No | Sí |
407
+ | Publica una tasa de falsos positivos medida por regla | No | No | No | Sí |
408
+ | Marca tests sin aserciones | Sí\* | No | a veces | Sí |
409
+ | Detecta sleeps fijos (`waitForTimeout`, `time.sleep`) | Sí\* | No | a veces | Sí |
410
+ | Determinista (misma entrada, misma salida) | Sí | Sí | No | Sí |
411
+ | Coste por escaneo | gratis | gratis | tokens | **cero** (local) |
412
+
413
+ <sub>\*Cubierto por `eslint-plugin-jest` y `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`) y por las propias reglas de aserción de SonarQube. Las columnas describen el comportamiento predeterminado para la verificación de suites de tests; los plugins, los planes de pago y las reglas personalizadas cambian algunas respuestas. Es un resumen de posicionamiento, no un benchmark.</sub>
380
414
 
381
- Los locators basados en roles obtienen la puntuación completa. Las
382
- cadenas de clases CSS y XPath hunden la puntuación — se rompen con
383
- cualquier refactor del DOM sin decirte qué comportamiento regredijo.
415
+ Usa también la revisión con IA. Detecta matices, intención y fallos de diseño que ningún patrón puede encontrar. Mjölnir detecta lo que la revisión con IA pasa por alto porque parece intencional: un `.only` en un commit, un código de salida tragado, un `continue-on-error` en un job de tests. Eso requiere escanear, no razonar.
384
416
 
385
- ---
417
+ <br />
386
418
 
387
- ## 🔬 Evidencia de ejecución
419
+ ## Forense de ejecución
388
420
 
389
- La detección estática de inestabilidad es adivinar. Mjölnir lee
390
- **datos reales de ejecución** — reportes JSON de Playwright y XML de
391
- JUnit de cualquier runner:
421
+ El análisis estático razona sobre código que nunca se ejecutó. El análisis forense lee lo que realmente ocurrió: Playwright JSON, Jest JSON, Vitest JSON y JUnit XML de cualquier runner.
392
422
 
393
423
  ```bash
394
424
  mjolnir forensics ./test-results/
395
425
  ```
396
426
 
397
427
  ```text
398
- ▚ FLAKINESS LEADERBOARD
428
+ ▍ FLAKINESS LEADERBOARD
399
429
 
400
430
  3 tests · 1 failed · 1 flaky · 1 retried
401
431
 
@@ -405,303 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
405
435
  ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
406
436
  ```
407
437
 
408
- Un test que solo pasa en el intento ≥ 2 no es un test que pasa — es un
409
- test con suerte. Se marca `TRUE-FLAKE` sin importar el visto verde
410
- final.
438
+ `TRUE-FLAKE` no significa que el test se reintentó. Significa que el test **falló al menos un intento y luego terminó en verde**: un pase con suerte, marcado diga lo que diga el check final. `mjolnir triage` convierte ese historial en una propuesta de cuarentena, y `mjolnir pw-report` resume una ejecución. Esos mismos informes de ejecución son los que elevan los hallazgos a los niveles de confianza L3 y superiores.
411
439
 
412
- ---
440
+ <br />
413
441
 
414
- ## ⚡ Mjölnir no es otro linter
442
+ ## Integridad de CI
415
443
 
416
- Los linters te dicen si el código sigue reglas. Mjölnir te dice si tu
417
- verificación puede fiarse.
444
+ Un test puede pasar mientras el pipeline que lo rodea no puede fallar. Mjölnir también lee los workflows: `continue-on-error`, `|| true`, códigos de salida que nunca se propagan, steps que siempre tienen éxito, informes consumidos pero nunca generados y gates omitidos justo en los eventos que deberían bloquear. Cada hallazgo nombra el job, el step y la línea, y lleva su propio nivel de evidencia.
418
445
 
419
- | | ESLint / SonarQube | Herramientas de coverage | Revisión manual | **Mjölnir** |
420
- | ------------------------------------------------------------------- | :----------------: | :----------------------: | :-------------: | :---------: |
421
- | Integridad de workflows de CI (`continue-on-error`, `\|\| true`) | ❌ | ❌ | raramente | ✅ |
422
- | Multi-lenguaje (TS, Python, Java, C#) desde una sola herramienta | ❌ | ❌ | ❌ | ✅ |
423
- | Califica la resiliencia de locators de Playwright (Selector Health) | ❌ | ❌ | raramente | ✅ |
424
- | Marca tests sin aserciones reales | ✅ (plugin)\* | ❌ | a veces | ✅ |
425
- | Detecta sleeps en duro (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | a veces | ✅ |
426
- | Corre en segundos, cero llamadas de red al escanear | ✅ | ✅ | — | ✅ |
446
+ Genera el workflow de PR, consultivo por defecto:
427
447
 
428
- \*`eslint-plugin-jest` (`expect-expect`) y `eslint-plugin-playwright`
429
- (`expect-expect`, `no-wait-for-timeout`) cubren esto para sus
430
- frameworks respectivos.
448
+ ```bash
449
+ mjolnir ci install
450
+ ```
431
451
 
432
- **El análisis de ejecución** es una categoría aparte del linting
433
- estático:
452
+ O añade la action del Marketplace a un workflow que ya tengas:
434
453
 
435
- | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
436
- | ---------------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
437
- | Lee datos reales de ejecución para veredictos `TRUE-FLAKE` | parcial\* | parcial (tag) | ✅ |
438
- | Informe de triage de inestabilidad desde el historial | ❌ | ✅ | ✅ |
439
- | Se integra con la puntuación de idoneidad estática | ❌ | ❌ | ✅ |
454
+ ```yaml
455
+ - uses: Sergey-Bar/Mjolnir@v1
456
+ with:
457
+ scope: changed
458
+ fail-on: error
459
+ ```
440
460
 
441
- \*Playwright rastrea los reintentos internamente pero no produce un
442
- informe de inestabilidad independiente con etiquetas de veredicto.
461
+ Fija `@v1` para seguir la línea mayor, o una etiqueta exacta (`@v0.5.32`) para un gate reproducible. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) cubre el Marketplace, Smithery y los registros MCP.
443
462
 
444
- ---
463
+ Para llevar los hallazgos a GitHub Code Scanning, sube SARIF (requiere `security-events: write` a nivel de workflow o job):
445
464
 
446
- ## 🤖 ¿Por qué no usar simplemente revisión de código con IA?
465
+ ```yaml
466
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
467
+ continue-on-error: true
468
+ - uses: github/codeql-action/upload-sarif@v3
469
+ if: ${{ !cancelled() }}
470
+ with:
471
+ sarif_file: mjolnir.sarif
472
+ ```
447
473
 
448
- Problema distinto, capa distinta. Una revisión con IA puede detectar un
449
- cambio de test sospechoso en un diff; no demuestra que el sistema de
450
- verificación en su conjunto sea confiable — y solo ve el diff que le
451
- muestras.
474
+ En GitLab, `--format codequality` escribe el informe de Code Quality que leen el widget de MR y las anotaciones del diff ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Configuración del editor y del pipeline: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
452
475
 
453
- | | Revisión de código con IA (Copilot, etc.) | **Mjölnir** |
454
- | ------------------------------------------- | :---------------------------------------: | :------------------------------------: |
455
- | Coste por escaneo | Tokens (escala con el tamaño del diff) | **Cero** (local, instalado) |
456
- | Ve toda la suite + todas las configs de CI | Solo el diff de PR que le muestras | **Todo, cada vez** |
457
- | Determinista (misma entrada → misma salida) | ❌ (no determinista) | **✅** |
458
- | Detecta patrones dormidos durante meses | Solo si está en el contexto | **✅** (escanea todos los archivos) |
459
- | Recuerda hallazgos entre ejecuciones | ❌ (sin memoria entre sesiones) | **✅** (baseline + diff) |
460
- | Corre sin disparador humano | Necesita una PR o un prompt | **✅** (hook de CI, corre en segundos) |
476
+ ### Atribución por alcance modificado
461
477
 
462
- **Usa ambos.** La IA capta el matiz, la intención y los defectos de
463
- diseño que ninguna regex encuentra. Mjölnir capta los patrones
464
- estructurales que la IA pasa por alto porque parecen "intencionales" —
465
- un `.only` committeado, un código de salida tragado, un
466
- `continue-on-error` en un job de test. No son bugs que necesiten
467
- razonamiento; son hechos que necesitan escaneo.
478
+ ```bash
479
+ npx mjolnir-qa@latest --scope changed
480
+ ```
468
481
 
469
- ---
482
+ Los hallazgos se atribuyen a las líneas que añadió tu rama, medidas contra el **merge-base**. El alcance es el mismo conjunto de archivos que descubre un escaneo completo (specs TS/JS y configuraciones de adaptadores, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), más los cambios sin commit y sin seguimiento, así que funciona antes de hacer commit. La base se resuelve como `main → master → origin/main → origin/master → origin/HEAD`; puedes sobrescribirla con `--base <ref>`.
470
483
 
471
- ## 🤖 Integración CI
484
+ Cuando el merge-base no puede resolverse (un clon superficial, un HEAD separado, un destino fuera de git), los hallazgos pasan a atribuirse al archivo completo **y el informe lo dice.** Un fallback silencioso sería justo el tipo de defecto que esta herramienta existe para detectar.
472
485
 
473
- Un comando genera un workflow de PR — asesor por defecto, nunca
474
- bloqueante:
486
+ <br />
475
487
 
476
- ```bash
477
- mjolnir ci install
478
- ```
488
+ ## Agentes de IA
479
489
 
480
- O conéctalo de forma nativa a GitHub Code Scanning vía SARIF:
490
+ Los hallazgos solo valen algo si algo actúa sobre ellos.
481
491
 
482
- ```yaml
483
- - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
484
- - uses: github/codeql-action/upload-sarif@v3
485
- with:
486
- sarif_file: mjolnir.sarif
492
+ ```text
493
+ SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
487
494
  ```
488
495
 
489
- Configuración para editor y pipeline de SARIF:
490
- [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
491
-
492
- ### Cobertura de scope cambiado
496
+ **La IA escribe la corrección. Mjölnir la verifica.** La prueba viene del reescaneo, nunca del propio informe de éxito del agente.
493
497
 
494
- `--scope changed` atribuye hallazgos a líneas añadidas en tu rama
495
- frente al merge-base con `main`. Cubre archivos de tests
496
- (`*.spec.*`, `*.test.*`) más archivos de workflow de GitHub y
497
- configuraciones de Playwright en el diff. Cuando el merge-base no puede
498
- resolverse — clone superficial, HEAD detached, objetivo sin git, rama
499
- por defecto distinta — degrada con honestidad: los hallazgos vuelven a
500
- la atribución por archivo completo y el reporte lo dice. Sobrescribe la
501
- ref base con `--base <ref>`.
498
+ | Comando | Qué recibe el agente |
499
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
500
+ | `mjolnir mcp` | Un servidor [MCP](https://modelcontextprotocol.io) sobre stdio. `scan`, `explain` y `diff` se convierten en herramientas invocables. |
501
+ | `mjolnir handoff` | Un informe `--json` guardado se convierte en un plan determinista en Markdown: qué se detectó, el límite de evidencia de cada hallazgo, qué **no** debe cambiar y cómo verificarlo. |
502
+ | `mjolnir install` | Escribe en las superficies de agente que tu repo ya tiene (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`) para que el agente vuelva a escanear antes de afirmar que ha terminado. |
502
503
 
503
- ---
504
+ Añádelo a un cliente que tenga su propia CLI:
504
505
 
505
- ## Configuración
506
-
507
- Mjölnir es cero-config. Un `mjolnir.config.json` opcional (o
508
- `.mjolnir.json`) en la raíz del repo ajusta severidad, gating y scope —
509
- nunca cambia la semántica de detección.
506
+ ```bash
507
+ claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
508
+ ```
510
509
 
511
- | Key | Tipo | Efecto |
512
- | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
513
- | `exclude` | `string[]` | Globs de ignore adicionales (subconjunto de gitignore), encima de los predeterminados integrados |
514
- | `gate` | `"advisory" \| "error" \| "warning"` | Qué severidades salen con código distinto de cero (por defecto `error`; `advisory` nunca bloquea) |
515
- | `severityOverrides` | `{ "<RULE-ID>": severity }` | Reordena los hallazgos de una regla para tu repo |
516
- | `ignore` | `IgnoreEntry[]` | Suprime hallazgos — **`reason` es obligatorio**; las entradas expiran tras 90 días (una fecha `expires` explícita, o la fecha de última modificación del archivo de config para las entradas sin ella) |
517
- | `plugins` | `string[]` | Paquetes de reglas de terceros (ver [Modelo de confianza](#modelo-de-confianza)) |
510
+ O a cualquier cliente que acepte un bloque `mcpServers`:
518
511
 
519
512
  ```json
520
513
  {
521
- "gate": "error",
522
- "exclude": ["legacy/**"],
523
- "severityOverrides": { "QA-PW-141": "warning" },
524
- "ignore": [
525
- {
526
- "ruleId": "QA-TEST-004",
527
- "files": ["e2e/legacy-login.spec.ts"],
528
- "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
529
- "expires": "2026-12-31"
530
- }
531
- ]
514
+ "mcpServers": {
515
+ "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
516
+ }
532
517
  }
533
518
  ```
534
519
 
535
- - **`.mjolnirignore`** — un archivo sencillo estilo gitignore para
536
- exclusiones de rutas, mismo dialecto que `exclude`. Úsalo para ruido
537
- de máquina; usa `exclude` cuando la lista pertenece al control de
538
- versiones junto con el resto de la configuración.
539
- - **Overrides de CLI** — `--strict` (incluir reglas en cuarentena),
540
- `--width <cols>` y `--ascii` / `--no-ascii` (renderizado de terminal),
541
- `--tone blunt` (mensajes más secos), `--max-duration <sec>` (escaneo
542
- parcial acotado).
543
- - Supresión de reglas y ciclo de vida de deprecación:
544
- [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
520
+ **La salvaguarda importa más que la comodidad.** Cada hallazgo de un traspaso lleva su límite. **E2** dice _determinista: comprueba la ubicación y aplica la corrección_. **E1** dice _REQUIERE CONFIRMACIÓN: la observación por sí sola no demuestra el defecto_. Un agente que corrige E1 a ciegas, suprime una regla o edita una regla para subir la puntuación está haciendo exactamente lo que esta herramienta existe para detectar, así que el traspaso lo dice en el prompt, junto al hallazgo.
521
+
522
+ <br />
523
+
524
+ ## Confianza y seguridad
525
+
526
+ **Local primero, cero telemetría.** No existe ninguna API con capacidad de red (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket) en ningún lugar de `src/`, y [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) hace fallar la compilación si aparece una. También prohíbe `eval` y `new Function`. Escanear código no confiable nunca lo ejecuta: el análisis estático lee texto fuente, y el análisis forense procesa archivos de informe que ya existen en disco.
527
+
528
+ Dos salvedades: el propio `npx` descarga el paquete antes de que nada se ejecute, y la garantía cubre `src/`, no los plugins de terceros.
545
529
 
546
- Las entradas `ignore` también alimentan el comando independiente
547
- `mjolnir suppressions`, que lista lo que está suprimido actualmente y
548
- cuándo expira cada entrada.
530
+ **Los plugins no están aislados en un sandbox.** Los plugins JS (`mjolnir-rules/*.mjs`, o paquetes npm listados en `"plugins"`) se ejecutan con todos los privilegios de Node, el mismo modelo de confianza que los plugins de ESLint o Vitest. Cargarlos es opcional **por escaneo**: sin `--enable-plugins` (o `MJOLNIR_ENABLE_PLUGINS=1`) sus fuentes nunca se cargan, y un aviso en stderr lista lo que se omitió. Los manifiestos de reglas JSON no ejecutan código, y los prefijos de ID de las reglas core están reservados para que ningún plugin pueda suplantar una. Reporta vulnerabilidades a través de [SECURITY.md](SECURITY.md).
549
531
 
550
- ---
532
+ **Se ejecuta sobre sí mismo.** Un motor de confianza de verificación no tiene credibilidad si no es verificable él mismo. Cada ejecución de CI escanea este repositorio con la build que produjo esa misma ejecución. El gate falla con cualquier hallazgo de severidad error, y también con un escaneo **parcial** o una **regla que se cae**, porque un autoescaneo truncado que no reporta nada es el falso verde que este proyecto existe para detectar. `mjolnir doctor` vuelve a auditar la base de reglas en la misma ejecución (cortafuegos de fixtures, honestidad de los niveles, el límite del nivel core), y una comprobación INCONCLUSIVE falla exactamente igual que una que falla. Ambos informes se suben como artefactos de la build.
551
533
 
552
- ## 📐 Códigos de salida y contratos
534
+ ### Códigos de salida y el contrato de máquina
553
535
 
554
- Congelados — seguros para construir lógica de CI encima:
536
+ Congelados, para que puedas construir lógica de CI sobre ellos:
555
537
 
556
538
  | Código de salida | Significado |
557
539
  | ---------------- | ----------------------------------------------------------------------------------- |
558
- | `0` | Limpio — sin hallazgos en o por encima del gate |
559
- | `1` | Hallazgos en o por encima del gate |
560
- | `2` | Escaneo parcial (presupuesto de tiempo agotado, archivos ilegibles) — nunca bloquea |
561
- | `10` | Error de uso (flag inválido, objetivo faltante) |
540
+ | `0` | Limpio: ningún hallazgo en el gate o por encima |
541
+ | `1` | Hallazgos en el gate o por encima |
542
+ | `2` | Escaneo parcial (presupuesto de tiempo agotado, archivos ilegibles). Nunca bloquea. |
543
+ | `10` | Error de uso (flag incorrecto, destino ausente) |
562
544
  | `20` | Error interno |
563
545
 
564
- El reporte JSON/SARIF es `schemaVersion: 1`. Los IDs de reglas
565
- (`QA-<FAMILY>-NNN`) son inmutables una vez publicados y nunca se
566
- reutilizan.
567
-
568
- ---
569
-
570
- ## Modelo de confianza
571
-
572
- - **Local-first** — cero llamadas de red durante el escaneo. Nunca.
573
- Cero telemetría.
574
- - **Sin prueba falsa** — preferimos decir "desconocido" a "verificado".
575
- Un repo vacío recibe `score: null`, nunca un falso 100.
576
- - **Honestidad parcial** — si el análisis se acortó, la salida lo dice.
577
- Nunca "complete" cuando no lo es.
578
- - **Cortafuegos FP** — la detección corre sobre una vista del código
579
- sin comentarios ni cadenas (las reglas de TypeScript usan el AST del
580
- compilador): un patrón dentro de un comentario de prosa o una cadena
581
- de ejemplo de documentación es documentación, no un hallazgo.
582
- - **Medido, no afirmado** — solo reglas con tasa de falsos positivos de
583
- código OSS real salen en los tiers titulares (ver
584
- [Cuánto está medido](#cuánto-está-medido)); el pie del escaneo y
585
- `mjolnir rules --unmeasured` te dicen cuál es cuál.
586
- - **Confianza en plugins y puerta de ejecución** — los plugins son
587
- paquetes npm declarados
588
- bajo `"plugins"`; los módulos JS viven en `mjolnir-rules/*.mjs`.
589
- **No hay sandbox**: el código del plugin corre con
590
- todos los privilegios de Node, el mismo modelo de confianza que los
591
- plugins de ESLint o Vitest. Por eso, la ejecución de código es
592
- **opt-in en cada escaneo**: pasa `--enable-plugins` (o define
593
- `MJOLNIR_ENABLE_PLUGINS=1`), o las fuentes NO se cargan — un aviso
594
- ruidoso en stderr lista exactamente qué se omitió. Escanear código
595
- no confiable nunca lo ejecuta. Los manifiestos de reglas JSON
596
- (`mjolnir-rules/*.json`) no se ven afectados: declaran patrones regex
597
- y por diseño no ejecutan código. Los prefijos de IDs de reglas core
598
- están reservados y se rechazan de plugins y reglas externas para
599
- evitar suplantación.
600
- - **Reglas externas locales al workspace** (basadas en carpeta, cero
601
- red) — un directorio `mjolnir-rules/` junto al objetivo del escaneo
602
- carga reglas personalizadas: archivos JSON declaran patrones regex
603
- (sin código ejecutado), los módulos `.mjs`/`.js` exportan `rules`
604
- (confianza plena de Node, igual que los plugins). Las reglas externas
605
- llevan los mismos metadatos de confianza que core; nunca pueden
606
- salir en el tier core (core exige una tasa de FP medida del sidecar
607
- de corpus — un `tier: "core"` declarado se restringe a `extended`),
608
- obedecen los topes de tier y se verifican contra la deriva:
609
- `mjolnir rules --md --external` renderiza el catálogo desde los
610
- archivos cargados (procedencia `external`), y el generador de matriz
611
- acepta `--external <root>`.
612
-
613
- ---
614
-
615
- ## 🏗️ Arquitectura
546
+ `2` es deliberadamente distinto de `0`: un escaneo que no terminó no ha encontrado nada. Simplemente no ha terminado de buscar.
616
547
 
617
- <details>
618
- <summary>Desplegar árbol</summary>
548
+ Todo lo que consume una máquina (resultados de herramientas MCP, `--json`, SARIF 2.1) proviene de un único resultado canónico bajo un esquema versionado y **solo aditivo** (`schemaVersion: 1`, `contractVersion: 1`), para que ningún consumidor tenga que reconstruir el significado a partir de texto renderizado. Consulta [el contrato de máquina](docs/machine-contract.md). Los IDs de regla (`QA-<FAMILY>-NNN`) son inmutables una vez publicados y nunca se reutilizan.
619
549
 
620
- ```
621
- mjolnir/
622
- ├── src/
623
- │ ├── engine/ # LanguageAdapter interface + rule runner
624
- │ ├── adapters/ # typescript · python · java · csharp · github-actions
625
- │ ├── rules/ # rules across 8 families + the measured-FP table
626
- │ ├── playwright/ # Selector Health Score engine
627
- │ ├── discovery/ # workspace, frameworks, ignore resolution
628
- │ ├── scope/ # git merge-base changed-scope engine
629
- │ ├── scorer/ # transparent deduction table + prioritization
630
- │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
631
- │ ├── forensics/ # run-data ingestion · flake verdicts · triage
632
- │ ├── config/ # mjolnir.config.json + suppressions
633
- │ ├── plugins/ # third-party rule loading (no sandbox)
634
- │ └── commands/ # every subcommand
635
- └── tests/
636
- ├── fixtures/ # must-fire / must-not-fire per rule
637
- └── golden/ # frozen score regression locks
638
- ```
550
+ <br />
639
551
 
640
- </details>
552
+ ## Lo que Mjölnir no puede decirte
641
553
 
642
- - **Las reglas son funciones puras** —
643
- `(SourceFileContext) → Finding[]`, sin I/O, sin globales. Añadir un
644
- ecosistema = un adaptador + sus reglas.
645
- - **TypeScript/Playwright usa el AST del compilador** (ts-morph).
646
- Python, Java y C# corren sobre una capa de regex compartida con
647
- comentarios/cadenas enmascarados.
648
- - Existe una capa de AST tree-sitter WASM para Java y C# y es el
649
- siguiente paso de precisión — aún no está cableada al pipeline de
650
- escaneo síncrono.
554
+ - **No ejecuta tus tests.** Un escaneo limpio no es una suite que pasa.
555
+ - **No puede decirte que una aserción es _incorrecta_.** `expect(total).toBe(41)` parece sana. Mjölnir encuentra tests que _no pueden fallar_ y pipelines que _no pueden ponerse en rojo_, no tests que comprueban lo que no deben.
556
+ - **No demuestra la corrección de negocio.** Nada aquí dice que tu producto haga lo que pedía el requisito.
557
+ - **Un 100 no es prueba de una buena suite.** Si tu suite cubre tu riesgo real es otra pregunta, y esta herramienta no la responde.
558
+ - **5 de 79 reglas se publican con una estimación**, no con una tasa medida. Cada una lo dice en su propio hallazgo.
559
+ - **E1 no es E2.** Los hallazgos heurísticos merecen leerse, no aplicarse a ciegas.
560
+ - **Un repo vacío puntúa `null`, nunca 100.**
561
+ - **Un archivo llamado `*.spec.ts` sin declaraciones de tests no cuenta como cobertura.** Un repo cuyos únicos archivos spec contienen imports o tipos (cero llamadas `it`/`test`) puntúa `null`, no 100.
651
562
 
652
- ---
563
+ <br />
653
564
 
654
- ## 📚 Documentación
565
+ ## Documentación
655
566
 
656
- | Documento | Qué contiene |
657
- | ------------------------------------------------------ | ---------------------------------------------------------- |
658
- | [docs/SCORING.md](docs/SCORING.md) | Normalización de la puntuación + ponderación por evidencia |
659
- | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tasas de falsos positivos medidas + método |
660
- | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Estados de reglas, supresión, deprecación |
661
- | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Salida SARIF + configuración de editor/CI |
662
- | [docs/rules/](docs/rules/) | Catálogo generado por regla |
663
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Setup de desarrollo + flujo de contribución |
664
- | [CHANGELOG.md](CHANGELOG.md) | Historial de versiones |
665
- | [SECURITY.md](SECURITY.md) | Reporte de vulnerabilidades |
567
+ El sitio completo de documentación está en <https://sergey-bar.github.io/Mjolnir/>.
666
568
 
667
- ---
569
+ | Documento | Qué contiene |
570
+ | ------------------------------------------------------ | ------------------------------------------------------------------ |
571
+ | [docs/SCORING.md](docs/SCORING.md) | Normalización de la puntuación y ponderación de la evidencia |
572
+ | [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Vocabulario canónico: una palabra por concepto |
573
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tasas de falsos positivos medidas y el método |
574
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Estados de las reglas, niveles, supresión, obsolescencia |
575
+ | [docs/VERSIONING.md](docs/VERSIONING.md) | Política de semver, superficies congeladas, ciclo de obsolescencia |
576
+ | [docs/machine-contract.md](docs/machine-contract.md) | El resultado canónico legible por máquina |
577
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Salida SARIF y configuración del editor o de CI |
578
+ | [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab: informe de Code Quality, receta de MR, gate |
579
+ | [docs/rules/](docs/rules/) | Catálogo generado por regla |
580
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Entorno de desarrollo y flujo de contribución |
581
+ | [SUPPORT.md](SUPPORT.md) | Dónde preguntar, reportar y obtener ayuda |
582
+ | [SECURITY.md](SECURITY.md) | Reporte de vulnerabilidades |
583
+ | [CHANGELOG.md](CHANGELOG.md) | Historial de versiones |
668
584
 
669
- ## 📈 Estado
585
+ ### Estado
670
586
 
671
- **v0.5.x · beta abierta.** El esquema JSON y los códigos de salida son
672
- contratos congelados. TypeScript y Python tienen la cobertura medida
673
- más amplia; Java y C# son más nuevos — léelos a través de la
674
- [tabla de tiers](#tiers-de-reglas-y-madurez-por-lenguaje).
587
+ **Versión 1.** El esquema JSON y los códigos de salida son contratos congelados. TypeScript y Python tienen la cobertura medida más amplia. Java y C# son más recientes; léelos a través de la [tabla de madurez](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). Lo que viene después, sin fechas inventadas: [la hoja de ruta pública](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
675
588
 
676
- ---
589
+ ### Contribuir
677
590
 
678
- ## 🤝 Contribuir
679
-
680
- Las reglas nuevas son la contribución inicial más sencilla — un comando
681
- genera el esqueleto de la regla más sus fixtures must-fire **y**
682
- must-not-fire (la regla generada falla sus fixtures intencionalmente
683
- hasta que implementes detección real — un stub no puede publicarse):
591
+ Las reglas nuevas son la primera contribución más fácil. Un comando crea el esqueleto de la regla con sus fixtures must-fire **y** must-not-fire. La regla generada falla sus propios fixtures a propósito hasta que se escribe una detección real, porque un stub que se publica es una regla que nadie midió:
684
592
 
685
593
  ```bash
686
594
  mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
687
595
  ```
688
596
 
689
- El setup completo de desarrollo, los comandos de la barrera permanente
690
- y las leyes anti-creep / cortafuegos de fixtures están en
691
- [CONTRIBUTING.md](CONTRIBUTING.md).
597
+ El entorno de desarrollo, los comandos de los gates permanentes y las leyes anti-creep y del cortafuegos de fixtures están en [CONTRIBUTING.md](CONTRIBUTING.md).
692
598
 
693
- ---
599
+ <br />
694
600
 
695
601
  <div align="center">
696
602
 
697
- **Deja de publicar tests en los que no puedes confiar.**
603
+ <img src="assets/readme/closing.svg" alt="Pruébalo en tu repo." width="100%" />
698
604
 
699
605
  ```bash
700
606
  npx mjolnir-qa@latest
701
607
  ```
702
608
 
703
- **Star ⭐ · Watch 👀 · Contribute 🤝**
609
+ [Lee la guía](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Sitio de documentación](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
610
+
611
+ <br />
612
+
613
+ No preguntes si los tests pasaron.<br />
614
+ Pregunta si la evidencia demuestra que merecen confianza.
704
615
 
705
- Construido por [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
616
+ <sub>Creado por [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Licencia MIT</sub>
706
617
 
707
618
  </div>