mjolnir-qa 1.0.8 → 2.0.0
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/CHANGELOG.md +208 -0
- package/README.ar.md +434 -478
- package/README.bn.md +434 -491
- package/README.br.md +434 -520
- package/README.bs.md +431 -500
- package/README.da.md +433 -509
- package/README.de.md +432 -522
- package/README.es.md +428 -517
- package/README.fr.md +426 -520
- package/README.gr.md +433 -517
- package/README.he.md +433 -475
- package/README.it.md +436 -525
- package/README.ja.md +436 -506
- package/README.ko.md +434 -494
- package/README.md +416 -426
- package/README.no.md +435 -509
- package/README.pl.md +433 -511
- package/README.ru.md +434 -515
- package/README.th.md +434 -484
- package/README.tr.md +427 -504
- package/README.uk.md +433 -505
- package/README.vi.md +436 -494
- package/README.zh.md +433 -462
- package/README.zht.md +433 -462
- package/dist/cli.d.mts +590 -111
- package/dist/cli.mjs +9937 -8951
- package/dist/mcp/stdio.mjs +1904 -816
- package/dist/rolldown-runtime-8H4AJuhK.mjs +14 -0
- package/package.json +7 -4
package/README.es.md
CHANGED
|
@@ -1,401 +1,431 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="assets/readme/
|
|
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
|
-
|
|
5
|
+
<br />
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
13
|
-
[](LICENSE)
|
|
14
|
-
[](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
|
-
|
|
12
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
13
|
+
[](https://www.npmjs.com/package/mjolnir-qa)
|
|
14
|
+
[](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/Sergey-Bar/Mjolnir)
|
|
16
|
+
[](https://scorecard.dev/viewer/?uri=github.com/Sergey-Bar/Mjolnir)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://nodejs.org)
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx mjolnir-qa@latest
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
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
|
-
[
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
83
|
+
<p align="center">
|
|
84
|
+
<img src="assets/readme/terminal-hero.svg" alt="El desglose de deducciones de Mjölnir: WORTHINESS 75/100 NEEDS WORK, 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
|
-
<
|
|
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>
|
|
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
|
-
|
|
102
|
+
</details>
|
|
50
103
|
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
y
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
npx mjolnir-qa@latest
|
|
155
|
+
Docs: mjolnir rules --md (full catalog, this rule included)
|
|
98
156
|
```
|
|
99
157
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
165
|
+
npx mjolnir-qa@latest
|
|
105
166
|
```
|
|
106
167
|
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
| `mjolnir
|
|
140
|
-
| `mjolnir
|
|
141
|
-
| `mjolnir
|
|
142
|
-
| `mjolnir
|
|
143
|
-
| `mjolnir
|
|
144
|
-
| `mjolnir
|
|
145
|
-
| `mjolnir
|
|
146
|
-
| `mjolnir
|
|
147
|
-
| `mjolnir
|
|
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
|
-
|
|
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
|
-
|
|
228
|
+
<br />
|
|
158
229
|
|
|
159
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
| QA-TEST-
|
|
197
|
-
| QA-TEST-
|
|
198
|
-
| QA-TEST-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
|
|
307
|
+
```text
|
|
308
|
+
▍ SELECTOR HEALTH
|
|
215
309
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
314
|
+
e2e/checkout.spec.ts
|
|
315
|
+
[█████████████████░░░] 86 / 100
|
|
316
|
+
role/text: 3 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
|
|
317
|
+
```
|
|
224
318
|
|
|
225
|
-
|
|
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
|
-
|
|
321
|
+
<br />
|
|
239
322
|
|
|
240
|
-
|
|
241
|
-
<summary><strong>Python / pytest 🐍</strong></summary>
|
|
323
|
+
## La puntuación de fiabilidad
|
|
242
324
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
343
|
+
<br />
|
|
266
344
|
|
|
267
|
-
|
|
268
|
-
<summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
|
|
345
|
+
## El modelo de evidencia
|
|
269
346
|
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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/
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376
|
+
### Cuánto de esto está medido
|
|
344
377
|
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
365
|
-
|
|
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
|
-
|
|
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
|
-
|
|
372
|
-
tus locators:
|
|
397
|
+
### Por qué esto no es un linter
|
|
373
398
|
|
|
374
|
-
|
|
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
|
-
|
|
378
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
419
|
+
## Forense de ejecución
|
|
388
420
|
|
|
389
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
442
|
+
## Integridad de CI
|
|
415
443
|
|
|
416
|
-
|
|
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
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
448
|
+
```bash
|
|
449
|
+
mjolnir ci install
|
|
450
|
+
```
|
|
431
451
|
|
|
432
|
-
|
|
433
|
-
estático:
|
|
452
|
+
O añade la action del Marketplace a un workflow que ya tengas:
|
|
434
453
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
454
|
+
```yaml
|
|
455
|
+
- uses: Sergey-Bar/Mjolnir@v1
|
|
456
|
+
with:
|
|
457
|
+
scope: changed
|
|
458
|
+
fail-on: error
|
|
459
|
+
```
|
|
440
460
|
|
|
441
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
|
|
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
|
-
|
|
474
|
-
bloqueante:
|
|
486
|
+
<br />
|
|
475
487
|
|
|
476
|
-
|
|
477
|
-
mjolnir ci install
|
|
478
|
-
```
|
|
488
|
+
## Agentes de IA
|
|
479
489
|
|
|
480
|
-
|
|
490
|
+
Los hallazgos solo valen algo si algo actúa sobre ellos.
|
|
481
491
|
|
|
482
|
-
```
|
|
483
|
-
|
|
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
|
-
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
(
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
-
|
|
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
|
-
|
|
534
|
+
### Códigos de salida y el contrato de máquina
|
|
553
535
|
|
|
554
|
-
Congelados
|
|
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
|
|
559
|
-
| `1` | Hallazgos en o por encima
|
|
560
|
-
| `2` | Escaneo parcial (presupuesto de tiempo agotado, archivos ilegibles)
|
|
561
|
-
| `10` | Error de uso (flag
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
552
|
+
## Lo que Mjölnir no puede decirte
|
|
641
553
|
|
|
642
|
-
- **
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
- **
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
-
|
|
649
|
-
|
|
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
|
-
##
|
|
565
|
+
## Documentación
|
|
655
566
|
|
|
656
|
-
|
|
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
|
-
|
|
585
|
+
### Estado
|
|
670
586
|
|
|
671
|
-
**
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
616
|
+
<sub>Creado por [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Licencia MIT</sub>
|
|
706
617
|
|
|
707
618
|
</div>
|