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/README.fr.md CHANGED
@@ -1,404 +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. Les tests vous disent ce qui a réussi. Mjölnir vous dit à quoi vous pouvez vous fier." width="100%" />
4
4
 
5
- ### Vos tests vous mentent. Nous le prouvons.
5
+ <br />
6
6
 
7
- **Verification Trust Engine pour la QA.** Mjölnir audite les suites de
8
- tests et les pipelines CI, rapporte un score de fiabilité et montre
9
- exactement où la confiance se brise.
7
+ Mjölnir trouve les tests qui ne peuvent pas échouer et les pipelines qui ne peuvent pas passer au rouge,<br />
8
+ puis évalue jusqu'où le résultat mérite confiance, avec la preuve de chaque point.
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](README.es.md) | Français | [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
- **Vos tests sont-ils dignes de confiance ?**
24
+ [Le voir à l'œuvre](#le-voir-à-lœuvre) · [Démarrage rapide](#démarrage-rapide) · [Ce qu'il trouve](#ce-que-mjölnir-trouve) · [Score](#le-score-de-fiabilité) · [Preuves](#le-modèle-de-preuves) · [Forensique](#forensique-dexécution) · [CI](#intégrité-de-la-ci) · [Agents](#agents-ia) · [Sécurité](#confiance-et-sécurité) · [Limites](#ce-que-mjölnir-ne-peut-pas-vous-dire) · [Docs](#documentation)
25
+
26
+ <details>
27
+ <summary>Lire dans une autre langue — 22 traductions</summary>
28
+
29
+ [English](README.md) | [简体中文](README.zh.md) | [繁體中文](README.zht.md) | [한국어](README.ko.md) | [Deutsch](README.de.md) | [Español](README.es.md) | Français | [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)
30
+
31
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
32
+
33
+ <!-- Source hash: 3541b09e8d04 -->
25
34
 
26
- [Le voir en action](#-le-voir-en-action) ·
27
- [Démarrage rapide](#-démarrage-rapide) ·
28
- [Ce qu'il vérifie](#-ce-que-mjölnir-vérifie) ·
29
- [Scoring](#comment-le-score-fonctionne) ·
30
- [CI](#-intégration-ci) · [Configuration](#configuration) ·
31
- [Documentation](#-documentation)
35
+ </details>
32
36
 
33
37
  </div>
34
38
 
35
- ---
39
+ <br />
40
+
41
+ ## Une coche verte est une affirmation, pas une preuve
42
+
43
+ Une coche verte signifie que le pipeline n'a pas échoué. Elle ne signifie pas que les tests ont tourné, ni qu'ils auraient pu échouer. Chacun de ces cas passe au vert :
44
+
45
+ - un `.only` commité qui a exécuté 3 tests au lieu de 900
46
+ - `continue-on-error: true` sur le job censé servir de barrière
47
+ - `|| true` après la commande de test
48
+ - un test qui n'affirme rien, ou dont le corps est vide
49
+ - un wrapper de relance qui transforme un vrai échec en réussite chanceuse
50
+ - un rapport que le workflow téléverse mais n'a jamais généré
51
+ - un sleep fixe qui maintient une condition de concurrence
52
+
53
+ Aucun ne fait passer le pipeline au rouge, et chacun semble délibéré en revue. C'est pour cela qu'ils survivent. Voici Mjölnir lisant un cas réel :
54
+
55
+ <p align="center">
56
+ <img src="assets/readme/scan.svg" alt="Le workflow CI du dépôt de démonstration, lu ligne par ligne. Mjölnir signale chaque constat à la ligne rapportée, avec sa règle, ce qui ne va pas, son niveau de preuve et son taux de faux positifs mesuré." width="800" />
57
+ </p>
58
+
59
+ <sub>Chaque constat que le scan de démonstration a rapporté pour ce workflow, à la ligne rapportée. Généré par `npm run docs:readme-brand` à partir de [`demo-report.json`](assets/readme/demo-report.json) et verrouillé contre toute dérive en CI.</sub>
60
+
61
+ **Mode strict.** Les détections les plus agressives — `.only`, `continue-on-error`, tests vides, abus de retry — vivent dans le niveau quarantaine. Elles ne tournent qu'avec `--strict` et sont limitées à la sévérité `info` : elles signalent, ne bloquent jamais. Le scan par défaut (`npx mjolnir-qa@latest` sans `--strict`) ne couvre que les règles core et extended. Ajoutez `--strict` quand vous voulez aussi la couche consultative.
62
+
63
+ Mjölnir lit la suite, les workflows CI et, si vous en avez un, le rapport d'une exécution réelle. Il n'exécute pas vos tests, n'installe pas vos dépendances et n'exécute pas le code qu'il analyse. Et quand il n'a pas de preuve, il le dit au lieu d'inventer de la confiance :
64
+
65
+ | Situation | Ce que Mjölnir rapporte |
66
+ | ------------------------------------------------------ | --------------------------------------------------------------- |
67
+ | Aucune déclaration de test trouvée | Score `null`, affiché comme **UNKNOWN**. Jamais un 100 inventé. |
68
+ | Aucune baseline ni révision comparable | **UNKNOWN**, avec la raison indiquée. Jamais un 0 supposé. |
69
+ | Scan interrompu (budget de temps, fichiers illisibles) | **PARTIAL**, sortie `2`. Jamais présenté comme propre. |
70
+
71
+ <p align="center">
72
+ <img src="assets/readme/how-it-works.svg" alt="Comment fonctionne Mjölnir. Il lit la suite de tests et le pipeline CI de manière statique, ainsi que le rapport d'une exécution réelle quand il y en a un. Il pondère chaque constat par son niveau de preuve et son niveau de confiance, où seule une exécution réelle peut atteindre L3 à L5, et produit des constats, un score de fiabilité et une barrière CI aux codes de sortie figés. Dans la boucle d'agent, l'IA écrit le correctif et Mjölnir relance le scan pour le prouver." width="880" />
73
+ </p>
74
+
75
+ <sub>Composé pour cette page et affiché à 1:1. Généré par `npm run docs:readme-brand` et verrouillé contre toute dérive en CI ; le score, les décomptes et l'ID de règle proviennent de [`script.demo.json`](assets/video/script.demo.json), de [`demo-report.json`](assets/readme/demo-report.json) et du registre des règles, jamais saisis à la main. La même image en affiche : [`architecture.svg`](assets/readme/architecture.svg).</sub>
76
+
77
+ <br />
78
+
79
+ ## Le voir à l'œuvre
80
+
81
+ Un vrai scan de [`examples/demo-repo`](examples/demo-repo), une petite suite Playwright avec un workflow CI. Voici où sont partis ses points :
82
+
83
+ <p align="center">
84
+ <img src="assets/readme/terminal-hero.svg" alt="Le détail des déductions de Mjölnir : WORTHINESS 75/100 NEEDS WORK, le score par catégorie, l'encadré des déductions par sévérité et une liste FIX THIS FIRST" width="520" />
85
+ </p>
86
+
87
+ <sub>Généré par `npm run docs:hero` à partir d'un vrai scan et verrouillé contre toute dérive en CI. Le rapport `--verbose` complet du même scan est [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
88
+
89
+ <details>
90
+ <summary><strong>Le regarder</strong> — un scan, le correctif qu'il affiche et le nouveau scan qui le prouve</summary>
36
91
 
37
- ## 🎬 Le voir en action
92
+ <br />
38
93
 
39
94
  <p align="center">
40
- <img src="assets/readme/demo.svg" alt="Le rapport --verbose complet de Mjölnir sur un dépôt de démo : WORTHINESS 75/100 NEEDS WORK, une répartition des diagnostics par catégorie, une liste FIX THIS FIRST, et chaque constat avec son ID de règle et son numéro de ligne à travers CI, Playwright, l'hygiène des tests et les règles Python" width="900" />
95
+ <a href="assets/video/mjolnir-demo.mp4">
96
+ <img src="assets/video/mjolnir-demo-poster.png" alt="Une image de l'enregistrement de démonstration : npx mjolnir-qa@latest analysant le dépôt de démonstration dans une fenêtre de terminal" width="900" />
97
+ </a>
41
98
  </p>
42
99
 
43
- <sub>La sortie complète de `npx mjolnir-qa ./examples/demo-repo --verbose`,
44
- rendue par le vrai reporter — rien de rogné. Régénérée par
45
- `npm run docs:demo` ;
46
- [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
- fait échouer la CI si elle dérive de ce que l'outil imprime.</sub>
100
+ <sub>Rendu image par image à partir d'un vrai scan par `npm run docs:video` ; jamais enregistré à l'écran. Sélectionnez l'image pour ouvrir [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
48
101
 
49
- **Ce qui vient de se passer :**
102
+ </details>
103
+
104
+ ### Un constat, de près
50
105
 
51
- 1. Mjölnir a découvert les specs Playwright, sa configuration, le
52
- workflow CI et un fichier de test Python — quatre langages/formats,
53
- une seule passe.
54
- 2. Il a trouvé des preuves qui affaiblissent la confiance dans la suite
55
- — un `continue-on-error` qui masque un job, un `|| true` qui avale un
56
- code de sortie, des sleeps en dur, un sélecteur fragile, des URLs de
57
- staging codées en dur, une attente `networkidle`.
58
- 3. Il en a fait un constat concret avec un ID de règle, un emplacement
59
- et un correctif — et un score unique sur lequel gate une PR.
106
+ Chaque constat répond à quatre questions : où il se trouve, à quel point Mjölnir est sûr, à quelle fréquence la règle se trompe, et comment le corriger.
60
107
 
61
- ### Un constat de près
108
+ <p align="center">
109
+ <img src="assets/readme/finding-anatomy.svg" alt="Le premier constat du scan de démonstration, exactement tel que le terminal l'affiche, avec ses quatre parties repérées : où, à quel point c'est sûr, à quelle fréquence la règle se trompe, et le correctif." width="100%" />
110
+ </p>
62
111
 
63
- Exécutez `mjolnir explain QA-CI-001` sur le premier constat ci-dessus
64
- et vous obtenez :
112
+ `mjolnir explain QA-CI-001` affiche tout le dossier de confiance d'une règle, y compris son taux de faux positifs mesuré et le niveau que ce taux lui a valu :
65
113
 
66
114
  ```text
67
- ▚ QA-CI-001 — continue-on-error masks a failing verification gate
115
+ ▍ QA-CI-001 — continue-on-error masks a failing verification gate
68
116
 
69
117
  Severity: error
70
118
  Confidence: high
119
+ Tier: quarantine
71
120
  Evidence: E2
72
- 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
73
126
 
74
127
  WHAT WAS FOUND (real detector output, not a mockup)
75
128
  Job `security-scan` runs a verification gate under `continue-on-error: true`.
76
129
 
77
130
  WHY IT MATTERS
78
- This job can fail every day and CI will still show green. The checkmark
79
- 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.
80
133
 
81
134
  HOW TO FIX
82
135
  Remove continue-on-error, or scope it to individual non-blocking steps only.
83
- ```
84
136
 
85
- C'est l'unité de valeur : pas une broutille de style, mais un endroit
86
- où votre CI affiche quelque chose comme réussi alors que ça ne l'est
87
- 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
- ## ⚡ Démarrage rapide
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
- Exécutez-le sur un dépôt pour un rapport complet et un score de
94
- fiabilité :
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, le produit tient en une commande.** Il ne scanne que ce que la
101
- branche a touché et sort avec un code non nul sur de nouveaux
102
- problèmes :
158
+ Voilà l'unité de valeur : un endroit où la CI rapporte une réussite qu'elle n'a pas méritée.
159
+
160
+ <br />
161
+
162
+ ## Démarrage rapide
103
163
 
104
164
  ```bash
105
- npx mjolnir-qa@latest --scope changed
165
+ npx mjolnir-qa@latest
106
166
  ```
107
167
 
108
- Déposez ça dans un check de PR — `mjolnir ci install` écrit le workflow —
109
- et c'est fini. Tout le reste est optionnel.
168
+ Il analyse le répertoire courant et affiche le Trust Report : ce qu'il a trouvé, jusqu'où vous pouvez vous y fier, pourquoi, et quoi faire ensuite. Il sort avec `0` quand rien n'a été trouvé au niveau de la barrière ou au-dessus.
110
169
 
111
- | Commande | Ce qu'elle fait |
112
- | ----------------------------------- | ---------------------------------------------------------------------- |
113
- | `mjolnir` | Scan complet du dépôt + score de fiabilité |
114
- | `mjolnir --scope changed` | Uniquement ce que votre branche a introduit — la forme CI |
115
- | `mjolnir ci install` | Génère le workflow de PR consultatif |
116
- | `mjolnir explain QA-CI-001` | Quoi / pourquoi / correctif + taux de FP mesuré d'une règle |
117
- | `mjolnir rules --unmeasured` | Les règles qui tournent sur hypothèse, pas sur mesure |
118
- | `mjolnir --json` / `--format sarif` | Lisible par machine / GitHub Code Scanning |
119
- | `mjolnir --strict` | Exécute aussi les règles du tier quarantaine (risque de FP plus élevé) |
170
+ En CI, n'analysez que ce que la branche a introduit, pour qu'une suite historique ne noie pas votre première pull request :
120
171
 
121
- <details>
122
- <summary><strong>Quand quelque chose est instable</strong></summary>
123
-
124
- | Commande | Ce qu'elle fait |
125
- | ----------------------------------- | -------------------------------------------------------------- |
126
- | `mjolnir forensics ./test-results/` | Vraies données d'exécution → verdicts `TRUE-FLAKE`, `FLAKY.md` |
127
- | `mjolnir triage ./test-results/` | Proposition de quarantaine issue de l'historique d'exécution |
128
- | `mjolnir pw-report ./test-results/` | Synthèse de run Playwright — retries / flakes / plus lents |
129
- | `mjolnir doctor:playwright` | Scan profond Playwright uniquement + Selector Health Score |
172
+ ```bash
173
+ npx mjolnir-qa@latest --scope changed
174
+ ```
130
175
 
131
- </details>
176
+ `mjolnir ci install` l'écrit sous forme de workflow GitHub Actions, en utilisant l'[action](https://github.com/Sergey-Bar/Mjolnir#readme) épinglée sur le tag majeur `v1` (ou un simple `npx` avec `--no-action`). Il reste consultatif jusqu'à ce que vous décidiez qu'il doit bloquer.
177
+
178
+ | Commande | Ce qu'elle fait |
179
+ | ----------------------------------- | ----------------------------------------------------------------------- |
180
+ | `mjolnir` | Trust Report : verdict, confiance, prochaine action |
181
+ | `mjolnir --scope changed` | Uniquement ce que votre branche a introduit (la forme CI) |
182
+ | `mjolnir ci install` | Génère le workflow de PR consultatif (basé sur l'action) |
183
+ | `mjolnir explain QA-CI-001` | Quoi, pourquoi et correctif, plus le taux de FP mesuré |
184
+ | `mjolnir why src/a.spec.ts:42` | Pourquoi cette ligne précise a été signalée. Ne bloque jamais. |
185
+ | `mjolnir forensics ./test-results/` | Preuves d'exécution tirées d'une exécution réelle |
186
+ | `mjolnir trust-report` | Trust Artifact autonome (md + json) |
187
+ | `mjolnir handoff` | Plan de remédiation pour un agent de code |
188
+ | `mjolnir --json` / `--format sarif` | Sortie lisible par machine, GitHub Code Scanning |
189
+ | `mjolnir --format codequality` | Rapport GitLab Code Quality (artefact du widget de MR) |
190
+ | `mjolnir --strict` | Exécute aussi les règles du niveau quarantine (risque de FP plus élevé) |
132
191
 
133
192
  <details>
134
- <summary><strong>Occasionnel / rapports</strong></summary>
135
-
136
- | Commande | Ce qu'elle fait |
137
- | ------------------------------- | ----------------------------------------------------------------- |
138
- | `mjolnir fix --dry-run` / `fix` | Auto-corrections sûres avec preuve |
139
- | `mjolnir baseline` / `diff` | Instantané des constats, puis rapport des seuls nouveaux/aggravés |
140
- | `mjolnir impact --since <ref>` | Ce qui a changé depuis un commit antérieur |
141
- | `mjolnir debt` | Registre de dette de test avec un modèle de coût |
142
- | `mjolnir handover` | Carte d'onboarding de la suite pour un nouveau QA |
143
- | `mjolnir stats` | Compteurs locaux de tous les correctifs vus |
144
- | `mjolnir badge` | JSON d'endpoint shields.io + snippet |
145
- | `mjolnir rules --md` | Catalogue complet des règles (JSON ou Markdown) |
146
- | `mjolnir doctor` | Auto-audit de la propre base de règles de Mjölnir |
147
- | `mjolnir create-rule <ID>` | Scafholde une nouvelle règle + ses fixtures |
148
- | `mjolnir --format mermaid` | Diagramme d'architecture de test pour un commentaire de PR |
193
+ <summary><strong>Toutes les autres commandes</strong> — tri des tests instables, rapports, gouvernance</summary>
194
+
195
+ <br />
196
+
197
+ | Commande | Ce qu'elle fait |
198
+ | ----------------------------------- | -------------------------------------------------------------------------------- |
199
+ | `mjolnir --classic` | La bannière de score d'avant le Trust Report |
200
+ | `mjolnir explain verdict` | Pourquoi le verdict du scan enregistré est ce qu'il est |
201
+ | `mjolnir triage ./test-results/` | Tri guidé. Chaque ligne se termine par une prochaine action. |
202
+ | `mjolnir pw-report ./test-results/` | Résumé d'exécution Playwright : relances, tests instables, les plus lents |
203
+ | `mjolnir doctor:playwright` | Scan approfondi dédié à Playwright plus Selector Health Score |
204
+ | `mjolnir fix --dry-run` / `fix` | Correctifs automatiques sûrs, chacun re-scanné pour prouver qu'il s'est appliqué |
205
+ | `mjolnir baseline` / `diff` | Photographie les constats, puis ne rapporte que les nouveaux ou les aggravés |
206
+ | `mjolnir impact --since <ref>` | Ce qu'un commit a introduit et résolu |
207
+ | `mjolnir summary` | Annotations CI et résumé d'étape à partir d'un rapport |
208
+ | `mjolnir pr-comment` | Un commentaire de PR ciblé, en Markdown |
209
+ | `mjolnir debt` | Registre de la dette de tests avec un modèle de coût |
210
+ | `mjolnir handover` | Carte d'intégration de la suite pour un nouvel ingénieur QA |
211
+ | `mjolnir init` | Détecte les frameworks, affiche une checklist d'installation |
212
+ | `mjolnir suppressions` | Liste les constats supprimés, pour la gouvernance |
213
+ | `mjolnir rules --unmeasured` | Les règles qui reposent sur une hypothèse, pas sur une mesure |
214
+ | `mjolnir rules --md` | Catalogue complet des règles (JSON ou Markdown) |
215
+ | `mjolnir doctor` | Auto-audit de la base de règles de Mjölnir |
216
+ | `mjolnir create-rule <ID>` | Crée le squelette d'une nouvelle règle et de ses fixtures |
217
+ | `mjolnir stats` | Compteurs locaux cumulés des correctifs observés |
218
+ | `mjolnir badge` | JSON d'endpoint shields.io et extrait |
219
+ | `mjolnir --cache` | Re-scans incrémentaux via un cache local des verdicts |
220
+ | `mjolnir --format mermaid` | Diagramme d'architecture de tests pour un commentaire de PR |
221
+
222
+ `mjolnir help <command>` affiche l'usage, des exemples et la prochaine étape pour chacune d'elles.
149
223
 
150
224
  </details>
151
225
 
152
- Installez-le globalement plutôt qu'en `npx` si vous préférez :
153
- `npm i -g mjolnir-qa`. Requiert Node.js ≥ 22.18. Fonctionne sous
154
- Windows, macOS et Linux.
155
-
156
- ---
157
-
158
- ## 👥 À qui ça s'adresse ?
159
-
160
- - **QA / SDET** propriétaires d'une suite e2e ou d'intégration qui ont
161
- besoin de la preuve que la suite mérite vraiment la coche verte
162
- qu'elle produit.
163
- - **Équipes Plateforme / DevEx** responsables de l'intégrité CI et des
164
- release gates — celles et ceux pour qui un `continue-on-error` ne
165
- doit jamais repeindre en vert une pipeline rouge en silence.
166
- - **Mainteneurs OSS** qui veulent un gate de vérification bon marché,
167
- toujours actif, qui tourne en local et en CI sans aucun appel réseau.
226
+ Nécessite **Node.js ≥ 22.18** sous Windows, macOS ou Linux. Vous préférez une installation globale ? `npm i -g mjolnir-qa`. Ce minimum vient de la chaîne de build (tsdown le cible et le pipeline de publication fait des tests de fumée dessus) ; les dépendances d'exécution n'en demandent pas davantage.
168
227
 
169
- ---
228
+ <br />
170
229
 
171
- ## 🔨 Ce que Mjölnir vérifie
230
+ ## Ce que Mjölnir trouve
172
231
 
173
- | | |
174
- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
175
- | ⚖️ | **Score de fiabilité** — un chiffre, une table de déductions transparente, aucune boîte noire |
176
- | 🎭 | **Selector Health Score** — note vos locators Playwright, pas seulement votre taux de réussite |
177
- | 🔬 | **Forensique d'exécution** — lit de vraies données de run Playwright/JUnit pour détecter `TRUE-FLAKE`, pas seulement des suppositions statiques |
178
- | 🚨 | **Règles d'intégrité CI** — attrape `continue-on-error`, `\|\| true` et autres astuces à faux vert |
179
- | 🐍 | **Les quatre bindings Playwright** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG et workflows CI |
180
- | 🔒 | **Local-first** — zéro appel réseau pendant le scan, zéro télémétrie, s'exécute en secondes |
181
-
182
- ### Les règles
183
-
184
- Chaque règle est livrée avec des fixtures must-fire **et** must-not-fire.
185
- Une règle qui se déclenche sur sa propre fixture négative ne peut pas
186
- sortir — c'est le pare-feu anti faux positifs.
187
-
188
- <details>
189
- <summary><strong>Hygiène des tests</strong></summary>
190
-
191
- | ID | Règle | Severity |
192
- | ----------- | ----------------------------------------------------- | -------- |
193
- | QA-TEST-001 | Test focalisé commité (`.only`, `fit`) | error |
194
- | QA-TEST-002 | Test sauté sans justification | error |
195
- | QA-TEST-002 | Test sauté avec justification tracée | warning |
196
- | QA-TEST-003 | Test sans assertion | error |
197
- | QA-TEST-004 | Sleep en dur (`waitForTimeout`, `sleep()`, `delay()`) | warning |
198
- | QA-TEST-006 | Abus de retry masquant l'instabilité | warning |
199
- | QA-TEST-010 | Corps de test vide | error |
200
-
201
- </details>
232
+ <p align="center">
233
+ <img src="assets/readme/stack.svg" alt="Fonctionne avec votre stack : les langages, frameworks de test et systèmes CI couverts par ses règles, d'après le registre des règles." width="100%" />
234
+ </p>
202
235
 
203
- <details>
204
- <summary><strong>Qualité des tests</strong></summary>
236
+ **79 règles** en quatre familles — hygiène des tests, qualité des tests, Playwright et intégrité de la CI — pour TypeScript et JavaScript, Python, Java, C# et le YAML de GitHub Actions. Elles couvrent Playwright dans ses quatre bindings, ainsi que pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest et Mocha, avec une couverture de départ pour Cypress et Selenium. Neuf d'entre elles, pour donner une idée :
205
237
 
206
- | ID | Règle | Severity |
207
- | ------------ | -------------------------------- | -------- |
208
- | QA-TQUAL-002 | Assertion tautologique | error |
209
- | QA-TQUAL-009 | Assertion de promise non awaitée | error |
210
- | QA-TQUAL-011 | Tests commentés | warning |
238
+ | ID | Règle | Sévérité | Niveau |
239
+ | ------------ | ---------------------------------------------------------------------- | -------- | ---------- |
240
+ | QA-CI-001 | `continue-on-error` masque une barrière de vérification en échec | error | quarantine |
241
+ | QA-CI-009 | Code de sortie des tests non propagé (`\|` sans pipefail, chaînes `;`) | error | extended |
242
+ | QA-TEST-001 | Test ciblé commité (`.only`, `fit`) | error | quarantine |
243
+ | QA-TEST-003 | Test sans assertion | error | quarantine |
244
+ | QA-TQUAL-009 | Assertion de promesse sans await | error | quarantine |
245
+ | QA-PW-002 | Assertion de locator sans await | error | core |
246
+ | QA-PW-004 | Sélecteurs CSS/XPath fragiles | warning | quarantine |
247
+ | QA-PY-002 | Test ignoré (`skip`, `xfail` non strict) | warning | core |
248
+ | QA-CS-103 | Méthode de test sans assertion | error | core |
211
249
 
212
- </details>
250
+ Le catalogue complet est généré depuis le registre, jamais maintenu à la main : `mjolnir rules --md`, [`docs/rules/`](docs/rules/), ou le [guide de ce qu'il vérifie](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
213
251
 
214
252
  <details>
215
- <summary><strong>Playwright 🎭</strong></summary>
216
-
217
- | ID | Règle | Severity |
218
- | --------- | --------------------------------------- | -------- |
219
- | QA-PW-002 | Assertion de locator non awaitée | error |
220
- | QA-PW-003 | `page.pause()` / `test.only()` commités | error |
221
- | QA-PW-004 | Sélecteurs CSS/XPath fragiles | warning |
222
- | QA-PW-123 | URLs d'environnement codées en dur | warning |
253
+ <summary><strong>Toutes les règles citées dans ce README</strong>, dans un seul tableau</summary>
254
+
255
+ <br />
256
+
257
+ > Les règles `quarantine` ne tournent que sous `--strict` et ne bloquent jamais (elles sont plafonnées à info). La sévérité indiquée est celle définie par l'auteur.
258
+
259
+ | ID | Famille | Règle | Sévérité | Niveau |
260
+ | ------------ | ---------- | ------------------------------------------------------------ | -------- | ---------- |
261
+ | QA-TEST-001 | Hygiène | Test ciblé commité (`.only`, `fit`) | error | quarantine |
262
+ | QA-TEST-002 | Hygiène | Test ignoré. Passe à `error` sans raison suivie. | warning | quarantine |
263
+ | QA-TEST-003 | Hygiène | Test sans assertion | error | quarantine |
264
+ | QA-TEST-004 | Hygiène | Sleep fixe (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
265
+ | QA-TEST-006 | Hygiène | Abus de relances masquant l'instabilité | warning | quarantine |
266
+ | QA-TEST-010 | Hygiène | Corps de test vide | error | quarantine |
267
+ | QA-TQUAL-002 | Qualité | Assertion tautologique | error | quarantine |
268
+ | QA-TQUAL-009 | Qualité | Assertion de promesse sans await | error | quarantine |
269
+ | QA-TQUAL-011 | Qualité | Tests commentés | warning | extended |
270
+ | QA-PW-002 | Playwright | Assertion de locator sans await | error | core |
271
+ | QA-PW-003 | Playwright | `page.pause()` / `test.only()` commité | error | core |
272
+ | QA-PW-004 | Playwright | Sélecteurs CSS/XPath fragiles | warning | quarantine |
273
+ | QA-PW-123 | Playwright | URL d'environnement codées en dur | warning | quarantine |
274
+ | QA-PW-140 | Playwright | Capture d'écran sans `maxDiffPixelRatio` | warning | core |
275
+ | QA-CI-001 | CI | `continue-on-error` masque une barrière en échec | error | quarantine |
276
+ | QA-CI-002 | CI | `\|\| true` avale les codes de sortie | error | extended |
277
+ | QA-CI-005 | CI | Rapport consommé mais jamais généré | error | quarantine |
278
+ | QA-CI-007 | CI | Wrappers de relance autour des tests | warning | extended |
279
+ | QA-CI-008 | CI | Étape toujours réussie qui masque les échecs | error | quarantine |
280
+ | QA-CI-009 | CI | Code de sortie non propagé (`\|` sans pipefail, chaînes `;`) | error | extended |
281
+ | QA-CI-010 | CI | Tests ignorés là où ils doivent bloquer | error | quarantine |
282
+ | QA-PY-002 | Python | Test ignoré (`skip`, `xfail` non strict) | warning | core |
283
+ | QA-PY-003 | Python | Fonction de test sans assertion | error | quarantine |
284
+ | QA-PY-005 | Python | `time.sleep()` dans les tests | warning | extended |
285
+ | QA-PY-012 | Python | Assertion tautologique | error | quarantine |
286
+ | QA-JV-101 | Java | Test désactivé (`@Disabled`) | warning | core |
287
+ | QA-JV-102 | Java | Sleep fixe (`Thread.sleep()`) | warning | extended |
288
+ | QA-JV-103 | Java | Méthode de test sans assertion | error | extended |
289
+ | QA-JV-105 | Java | Sleep fixe via `waitForTimeout()` de Playwright | warning | core |
290
+ | QA-JV-106 | Java | Sélecteur fragile au lieu d'un locator par rôle | warning | quarantine |
291
+ | QA-CS-101 | C# | Test ignoré (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
292
+ | QA-CS-102 | C# | Sleep fixe (`Thread.Sleep` / `Task.Delay`) | warning | core |
293
+ | QA-CS-103 | C# | Méthode de test sans assertion | error | core |
294
+ | QA-CS-105 | C# | Sleep fixe via `WaitForTimeoutAsync()` | warning | extended |
295
+ | QA-CS-106 | C# | Sélecteur fragile au lieu d'un locator par rôle | warning | quarantine |
296
+
297
+ Python fournit aussi QA-PY-001…012 (hygiène pytest) et QA-PY-101…108 (Playwright pour Python). Cypress et Selenium disposent de jeux de départ de trois règles chacun.
223
298
 
224
299
  </details>
225
300
 
226
- <details>
227
- <summary><strong>Intégrité CI</strong></summary>
228
-
229
- | ID | Règle | Severity |
230
- | --------- | -------------------------------------------------------------------- | -------- |
231
- | QA-CI-001 | `continue-on-error` masque les échecs | error |
232
- | QA-CI-002 | `\|\| true` avale les codes de sortie | error |
233
- | QA-CI-005 | Rapport consommé mais jamais généré | error |
234
- | QA-CI-007 | Wrappers de retry autour des tests | warning |
235
- | QA-CI-008 | Step toujours réussi masquant les échecs | error |
236
- | QA-CI-009 | Code de sortie du test non propagé (`\|` sans pipefail, chaînes `;`) | error |
237
- | QA-CI-010 | Tests sautés là où ils doivent bloquer (gardes skip-on-PR) | error |
301
+ Chaque règle est livrée avec une fixture must-fire **et** une fixture must-not-fire, et une règle qui se déclenche sur sa propre fixture négative ne peut pas être livrée. C'est le pare-feu anti-faux-positifs ; `mjolnir doctor` l'applique dans la CI de ce dépôt.
238
302
 
239
- </details>
303
+ ### Selector Health Score
240
304
 
241
- <details>
242
- <summary><strong>Python / pytest 🐍</strong></summary>
305
+ `mjolnir doctor:playwright` note chaque locator selon la façon dont il trouve un élément : comme le ferait un utilisateur (rôle, libellé, texte), par un contrat explicite (`data-testid`), ou par un accident structurel (chaînes CSS, XPath). Chaque fichier reçoit un score de 0 à 100 :
243
306
 
244
- | ID | Règle | Severity |
245
- | --------- | --------------------------------------- | -------- |
246
- | QA-PY-002 | Test sauté (`skip`, `xfail` non strict) | warning |
247
- | QA-PY-003 | Fonction de test sans assertion | error |
248
- | QA-PY-005 | `time.sleep()` dans les tests | warning |
249
- | QA-PY-012 | Assertion tautologique | error |
307
+ ```text
308
+ ▍ SELECTOR HEALTH
250
309
 
251
- 20 règles Python au total (QA-PY-001…012 hygiène pytest + QA-PY-101…108 Playwright-Python).
310
+ e2e/login.spec.ts
311
+ [█████████████░░░░░░░] 65 / 100
312
+ role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
252
313
 
253
- </details>
314
+ e2e/checkout.spec.ts
315
+ [█████████████████░░░] 86 / 100
316
+ role/text: 3 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
317
+ ```
254
318
 
255
- <details>
256
- <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
319
+ Cela mesure la **robustesse, pas l'exactitude**. `.btn.btn-primary > div:nth-child(2)` passe aujourd'hui et continue de passer jusqu'à ce que quelqu'un touche au balisage. Un score bas n'affirme jamais que le test est cassé, seulement qu'il dépend d'un balisage que personne n'a promis de conserver.
257
320
 
258
- | ID | Règle | Severity |
259
- | --------- | ------------------------------------------- | -------- |
260
- | QA-JV-101 | Test désactivé (`@Disabled`) | warning |
261
- | QA-JV-102 | Sleep en dur (`Thread.sleep()`) | warning |
262
- | QA-JV-103 | Méthode de test sans assertion | error |
263
- | QA-JV-105 | Sleep en dur Playwright `waitForTimeout()` | warning |
264
- | QA-JV-106 | Sélecteur fragile au lieu d'un role locator | warning |
321
+ <br />
265
322
 
266
- </details>
323
+ ## Le score de fiabilité
267
324
 
268
- <details>
269
- <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
325
+ <p align="center">
326
+ <img src="assets/readme/score-gauge.svg" alt="L'échelle de fiabilité de 0 à 100, avec un marqueur qui parcourt chaque score : UNWORTHY sous 50, NEEDS WORK de 50 à 79, WORTHY de 80 à 99, FORGED à 100" width="720" />
327
+ </p>
270
328
 
271
- | ID | Règle | Severity |
272
- | --------- | -------------------------------------------- | -------- |
273
- | QA-CS-101 | Test sauté (`[Ignore]`, `[Fact(Skip=)]`) | warning |
274
- | QA-CS-102 | Sleep en dur (`Thread.Sleep` / `Task.Delay`) | warning |
275
- | QA-CS-103 | Méthode de test sans assertion | error |
276
- | QA-CS-105 | Sleep en dur `WaitForTimeoutAsync()` | warning |
277
- | QA-CS-106 | Sélecteur fragile au lieu d'un role locator | warning |
329
+ <sub>Chaque score de 0 à 100, placé par le vrai `deriveScoreState`. Généré par `npm run docs:gauge` et verrouillé contre toute dérive en CI.</sub>
278
330
 
279
- </details>
331
+ | Score | Verdict |
332
+ | --------- | ------------------------------------------------ |
333
+ | `0 – 49` | **UNWORTHY** |
334
+ | `50 – 79` | **NEEDS WORK** |
335
+ | `80 – 99` | **WORTHY** |
336
+ | `100` | **FORGED** |
337
+ | `null` | **UNKNOWN** : aucune déclaration de test trouvée |
280
338
 
281
- > Le catalogue live complet — chaque règle avec son tier, sa
282
- > confidence, son risque de faux positif et sa disponibilité d'autofix —
283
- > est généré depuis le registre :
284
- >
285
- > ```bash
286
- > mjolnir rules --md
287
- > ```
288
- >
289
- > Les pages par règle vivent sous [`docs/rules/`](docs/rules/).
339
+ **Comment il est calculé.** La sévérité fixe une déduction de base (`error −8`, `warning −3`, `info −1`) et le niveau de preuve la réduit : E2 compte en entier, E1 à moitié (arrondi à l'inférieur), E0 pas du tout. Le total est normalisé par l'exposition de la suite, c'est-à-dire en déductions par déclaration de test plutôt que par fichier. Le terminal affiche les mêmes nombres réduits que ceux utilisés par le score ; il n'y a pas de second modèle caché. Détails : [docs/SCORING.md](docs/SCORING.md) et le [guide du score](https://sergey-bar.github.io/Mjolnir/guide/scoring).
290
340
 
291
- ### Quelle part est mesurée
292
-
293
- **78 des 99 règles portent un taux de faux positifs mesuré sur du vrai
294
- code OSS** (≥ 10 constats classés à la main chacun ; voir
295
- [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Les 21 autres sortent sur
296
- l'estimation de l'auteur. Chaque pied de scan vous dit combien des
297
- règles _déclenchées_ sont mesurées ; `mjolnir rules --unmeasured` liste
298
- celles qui ne le sont pas ; la page `mjolnir explain` de chaque règle
299
- énonce son statut. Nous publions le taux même quand il est laid —
300
- grandir ce chiffre est le travail continu du projet.
341
+ **Ce que 100 ne veut pas dire.** Cela ne veut pas dire que le logiciel est correct, que la suite est suffisante ou que le produit est exempt de défauts. Cela veut dire une seule chose : **aucune des règles évaluées par Mjölnir n'a produit de déduction avec ce scan et ce modèle de preuves.**
301
342
 
302
- ### Tiers de règles et maturité par langage
343
+ <br />
303
344
 
304
- Chaque règle est `core`, `extended` ou `quarantine`, attribué d'après
305
- son taux de faux positifs **mesuré** :
345
+ ## Le modèle de preuves
306
346
 
307
- | Tier | Signification | Scan par défaut | `--strict` |
308
- | ------------ | ------------------------------------------------ | :-------------: | :--------: |
309
- | `core` | ≤ 10 % de FP mesuré | ✅ | ✅ |
310
- | `extended` | ≤ 30 % de FP mesuré | ✅ | ✅ |
311
- | `quarantine` | au-dessus de 30 %, ou pas encore mesuré (n < 10) | ❌ | ✅ |
347
+ Chaque constat porte deux étiquettes : à quel point Mjölnir est sûr, et jusqu'où le constat a été vérifié. C'est la différence entre un outil qui signale des motifs et un outil sur lequel on peut conditionner une release.
312
348
 
313
- | Langage | Adaptateur | Couverture aujourd'hui |
314
- | --------------- | --------------- | ---------------------------------------------------------- |
315
- | TypeScript / JS | AST compilateur | la plus large, la plus mesurée — surtout `core`/`extended` |
316
- | Python / pytest | Couche regex | large, auditée sur corpus — surtout `core`/`extended` |
317
- | Java | Couche regex | plus récent — surtout `extended`/`quarantine` |
318
- | C# / .NET | Couche regex | plus récent — surtout `extended`/`quarantine` |
349
+ **À quel point c'est sûr — le niveau de preuve.**
319
350
 
320
- TypeScript et Python ont la couverture mesurée la plus large. Java et
321
- C# sont livrés, documentés, et restent hors du chiffre vedette tant
322
- qu'une vraie suite consommatrice (pas les propres tests d'une
323
- bibliothèque de binding) n'a pas été auditée.
351
+ | Niveau | Nom | Signifie | Déduction |
352
+ | ------ | ------------------- | -------------------------------------------------------------- | --------- |
353
+ | **E2** | Preuve déterministe | Le défaut est présent dans le code tel qu'il est écrit | Totale |
354
+ | **E1** | Preuve par motif | Un motif fortement lié au défaut a correspondu | Moitié |
355
+ | **E0** | Observation | Bon à savoir. Pas une affirmation que quelque chose ne va pas. | Nulle |
324
356
 
325
- ---
357
+ La confiance dans une détection n'est pas la force de la preuve. Une règle peut être certaine d'avoir trouvé ce qu'elle cherchait tout en regardant une heuristique. Les constats E1 sont là pour être lus et jugés, jamais appliqués aveuglément, et cette limite est inscrite sur le constat dans le terminal, le JSON et le passage de relais à l'agent.
326
358
 
327
- ## Comment le score fonctionne
359
+ **Jusqu'où c'est vérifié — le niveau de confiance.** La plupart des constats viennent de la lecture de votre code. Donnez à Mjölnir le rapport d'une vraie exécution de tests et il pourra confirmer que le code a bien tourné.
328
360
 
329
361
  <p align="center">
330
- <img src="assets/readme/terminal-hero.svg" alt="Sortie terminal de Mjölnir — WORTHINESS 75/100 NEEDS WORK, une répartition des diagnostics par catégorie et une liste FIX THIS FIRST" width="820" />
362
+ <img src="assets/readme/trust-ladder.svg" alt="L'échelle de confiance de L0 à L5. L0 à L2 viennent de la lecture du code ; L3 à L5 exigent le rapport d'une exécution réelle, marqué par une rupture dans l'échelle." width="100%" />
331
363
  </p>
332
364
 
333
- <sub>Régénérée par `npm run docs:hero` ;
334
- [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
335
- fait échouer la CI si elle dérive de ce que le reporter imprime
336
- réellement.</sub>
365
+ | Niveau | En clair | Ce qu'il faut |
366
+ | ------ | --------------------- | --------------------------------------------------------------------- |
367
+ | **L0** | Noté | Lire le code |
368
+ | **L1** | Ressemble au problème | Lire le code : un motif a correspondu |
369
+ | **L2** | Prouvé dans le code | Lire le code : le défaut est structurel |
370
+ | **L3** | Le fichier a tourné | Un rapport d'exécution montre que le fichier du constat a été exécuté |
371
+ | **L4** | Le test a tourné | Un rapport d'exécution montre que le test du constat a été exécuté |
372
+ | **L5** | L'exécution concorde | Le résultat même de l'exécution confirme la classe de défaut |
337
373
 
338
- Le score est transparent : **error −8, warning −3, info −1**, puis
339
- normalisé par l'exposition de la suite (déductions par déclaration de
340
- test). Les déductions pondérées par la preuve signifient que les
341
- signaux faibles coûtent moins cher. Le terminal affiche les mêmes
342
- chiffres actualisés que ceux du score — pas de boîte noire. Méthode
343
- complète : [docs/SCORING.md](docs/SCORING.md).
374
+ Un scan statique s'arrête à L2. Seul le rapport d'une exécution réelle (Playwright JSON, Jest ou Vitest JSON, JUnit XML) peut élever un constat à L3 ou plus, si bien qu'un constat qu'on n'a jamais vu tourner ne peut jamais prétendre l'avoir fait. Définitions : [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
344
375
 
345
- **Verdicts**
376
+ ### Quelle part est mesurée
346
377
 
347
- | Score | Verdict |
348
- | ------- | ---------------- |
349
- | ≥ 80 | ✓ **WORTHY** |
350
- | 50 – 79 | ⚠ **NEEDS WORK** |
351
- | < 50 | ✖ **UNWORTHY** |
378
+ **74 règles sur 79 ont un taux de faux positifs mesuré sur du vrai code OSS** (au moins 10 constats classés à la main chacune ; voir [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Les 5 autres reposent sur l'estimation de l'auteur et le disent, règle par règle, dans `mjolnir explain`. `mjolnir rules --unmeasured` les liste, et le pied de chaque scan indique combien des règles qui se sont réellement _déclenchées_ sont mesurées.
352
379
 
353
- **Niveaux de preuve** — chaque constat en porte un ; il fixe le poids
354
- du constat dans le score :
380
+ Les taux restent publics quand ils sont mauvais. QA-TEST-001 (un `.only` commité) obtient un mauvais audit sur des dépôts réels et se trouve en quarantine pour cette raison. Le chiffre actuel de chaque règle, QA-PW-141 comprise, figure dans l'audit.
355
381
 
356
- | Niveau | Signification | Impact sur le score | Exemple |
357
- | ------ | ------------------- | --------------------- | --------------------------------------------------------- |
358
- | E2 | Défaut déterministe | Déduction pleine | `.only` commité — structurellement prouvable |
359
- | E1 | Motif heuristique | Déduction moitié | `sleep()` détecté par regex — signal fort, pas une preuve |
360
- | E0 | Observation | Zéro (info seulement) | Rapporté mais ne gate jamais la CI ni ne déduit |
382
+ ### Niveaux de confiance des règles
361
383
 
362
- La plupart des règles sont **E1**. Le slogan « we prove it » renvoie à
363
- ce système : les constats E2 sont une preuve structurelle ; les
364
- constats E1 sont des avertissements correctement positionnés, pas des
365
- preuves formelles.
384
+ Les niveaux suivent le taux de faux positifs mesuré, pas une opinion :
366
385
 
367
- Un dépôt vide obtient `null`, jamais un faux 100 — voir
368
- [Modèle de confiance](#modèle-de-confiance).
386
+ | Niveau | FP mesuré | Comportement |
387
+ | -------------- | ------------------------------ | -------------------------------------------------------- |
388
+ | **core** | ≤ 10% | Rapport par défaut, bloque |
389
+ | **extended** | ≤ 30% | Rapport par défaut, confiance moindre |
390
+ | **quarantine** | > 30% ou explicitement déclaré | Uniquement `--strict`, plafonné à info, ne bloque jamais |
391
+ | _non mesurée_ | n < 10 | Ne peut être promue en core avant d'être mesurée |
369
392
 
370
- ---
393
+ Les bandes de FP ne peuvent que rétrograder un niveau — elles ne promeuvent jamais une règle hors de `quarantine` si elle y a été explicitement déclarée. Une règle explicitement mise en quarantine y reste quelle que soit son taux de FP mesuré.
371
394
 
372
- ## 🎭 Selector Health Score
395
+ Promotion, rétrogradation et maturité par langage : [cycle de vie des règles](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
373
396
 
374
- La métrique vedette pour les suites Playwright — la résilience de vos
375
- locators :
397
+ ### Pourquoi ce n'est pas un linter
376
398
 
377
- ```text
378
- ▚ SELECTOR HEALTH — e2e/checkout.spec.ts
399
+ Les linters vous disent si le code suit des règles. Mjölnir vous dit si votre vérification mérite confiance.
379
400
 
380
- [█████████████████░░░] 83 / 100
381
- role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
382
- ```
401
+ | | Linters (ESLint, SonarQube) | Outils de couverture | Revue de code par IA | **Mjölnir** |
402
+ | ---------------------------------------------------------------- | :-------------------------: | :------------------: | :------------------: | :--------------: |
403
+ | Note le **système de vérification**, pas le code produit | Non | Non | Non | Oui |
404
+ | Intégrité des workflows CI (`continue-on-error`, `\|\| true`) | Non | Non | seulement le diff | Oui |
405
+ | Note la robustesse des locators Playwright (Selector Health) | Non | Non | Non | Oui |
406
+ | Lit de vraies données d'exécution pour les verdicts `TRUE-FLAKE` | Non | Non | Non | Oui |
407
+ | Publie un taux de faux positifs mesuré par règle | Non | Non | Non | Oui |
408
+ | Signale les tests sans assertion | Oui\* | Non | parfois | Oui |
409
+ | Détecte les sleeps fixes (`waitForTimeout`, `time.sleep`) | Oui\* | Non | parfois | Oui |
410
+ | Déterministe (même entrée, même sortie) | Oui | Oui | Non | Oui |
411
+ | Coût par scan | gratuit | gratuit | tokens | **zéro** (local) |
412
+
413
+ <sub>\*Couvert par `eslint-plugin-jest` et `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`) ainsi que par les propres règles d'assertion de SonarQube. Les colonnes décrivent le comportement par défaut pour la vérification de suites de tests ; les plugins, les offres payantes et les règles personnalisées changent certaines réponses. C'est un résumé de positionnement, pas un benchmark.</sub>
383
414
 
384
- Les locators basés sur les rôles obtiennent la note maximale. Les
385
- chaînes de classes CSS et le XPath effondrent le score — ils cassent à
386
- chaque refonte du DOM sans vous dire quel comportement a régressé.
415
+ Utilisez aussi la revue par IA. Elle saisit les nuances, l'intention et les défauts de conception qu'aucun motif ne peut trouver. Mjölnir détecte ce que la revue par IA laisse passer parce que cela semble intentionnel : un `.only` commité, un code de sortie avalé, un `continue-on-error` sur un job de tests. Cela demande un scan, pas un raisonnement.
387
416
 
388
- ---
417
+ <br />
389
418
 
390
- ## 🔬 Preuves d'exécution
419
+ ## Forensique d'exécution
391
420
 
392
- La détection statique d'instabilité, c'est deviner. Mjölnir lit de
393
- **vraies données d'exécution** — rapports JSON Playwright et XML JUnit
394
- de n'importe quel runner :
421
+ L'analyse statique raisonne sur du code qui n'a jamais tourné. La forensique lit ce qui s'est réellement passé : Playwright JSON, Jest JSON, Vitest JSON et JUnit XML de n'importe quel runner.
395
422
 
396
423
  ```bash
397
424
  mjolnir forensics ./test-results/
398
425
  ```
399
426
 
400
427
  ```text
401
- ▚ FLAKINESS LEADERBOARD
428
+ ▍ FLAKINESS LEADERBOARD
402
429
 
403
430
  3 tests · 1 failed · 1 flaky · 1 retried
404
431
 
@@ -408,305 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
408
435
  ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
409
436
  ```
410
437
 
411
- Un test qui ne passe qu'à partir de la tentative ≥ 2 n'est pas un test
412
- qui passe — c'est un test chanceux. Il est marqué `TRUE-FLAKE` quel que
413
- soit le vert final de la coche.
438
+ `TRUE-FLAKE` ne signifie pas que le test a été relancé. Cela signifie que le test **a échoué au moins une tentative puis a terminé au vert** : une réussite chanceuse, signalée quoi que dise la coche finale. `mjolnir triage` transforme cet historique en proposition de quarantaine, et `mjolnir pw-report` résume une exécution. Ce sont ces mêmes rapports d'exécution qui élèvent les constats aux niveaux de confiance L3 et au-delà.
414
439
 
415
- ---
440
+ <br />
416
441
 
417
- ## ⚡ Mjölnir n'est pas un linter de plus
442
+ ## Intégrité de la CI
418
443
 
419
- Les linters vous disent si le code suit des règles. Mjölnir vous dit si
420
- votre vérification peut être crue.
444
+ Un test peut réussir alors que le pipeline qui l'entoure ne peut pas échouer. Mjölnir lit aussi les workflows : `continue-on-error`, `|| true`, les codes de sortie jamais propagés, les étapes toujours réussies, les rapports consommés mais jamais générés, et les barrières ignorées sur les événements qui devraient bloquer. Chaque constat nomme le job, l'étape et la ligne, et porte son propre niveau de preuve.
421
445
 
422
- | | ESLint / SonarQube | Outils de coverage | Revue manuelle | **Mjölnir** |
423
- | ------------------------------------------------------------- | :----------------: | :----------------: | :------------: | :---------: |
424
- | Intégrité des workflows CI (`continue-on-error`, `\|\| true`) | ❌ | ❌ | rarement | ✅ |
425
- | Multi-langage (TS, Python, Java, C#) depuis un seul outil | ❌ | ❌ | ❌ | ✅ |
426
- | Note la résilience des locators Playwright (Selector Health) | ❌ | ❌ | rarement | ✅ |
427
- | Signale les tests sans vraies assertions | ✅ (plugin)\* | ❌ | parfois | ✅ |
428
- | Détecte les sleeps en dur (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | parfois | ✅ |
429
- | Tourne en secondes, zéro appel réseau pendant le scan | ✅ | ✅ | — | ✅ |
446
+ Générez le workflow de PR, consultatif par défaut :
430
447
 
431
- \*`eslint-plugin-jest` (`expect-expect`) et `eslint-plugin-playwright`
432
- (`expect-expect`, `no-wait-for-timeout`) couvrent cela pour leurs
433
- frameworks respectifs.
448
+ ```bash
449
+ mjolnir ci install
450
+ ```
434
451
 
435
- **L'analyse d'exécution** est une catégorie à part du linting
436
- statique :
452
+ Ou ajoutez l'action du Marketplace à un workflow existant :
437
453
 
438
- | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
439
- | ----------------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
440
- | Lit de vraies données de run pour des verdicts `TRUE-FLAKE` | partiel\* | partiel (tag) | ✅ |
441
- | Rapport de triage d'instabilité depuis l'historique | ❌ | ✅ | ✅ |
442
- | S'intègre au score de fiabilité statique | ❌ | ❌ | ✅ |
454
+ ```yaml
455
+ - uses: Sergey-Bar/Mjolnir@v1
456
+ with:
457
+ scope: changed
458
+ fail-on: error
459
+ ```
443
460
 
444
- \*Playwright suit les retries en interne mais ne produit pas de rapport
445
- d'instabilité autonome avec des étiquettes de verdict.
461
+ Épinglez `@v1` pour suivre la ligne majeure, ou un tag exact (`@v0.5.32`) pour une barrière reproductible. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) couvre le Marketplace, Smithery et les registres MCP.
446
462
 
447
- ---
463
+ Pour envoyer les constats dans GitHub Code Scanning, téléversez le SARIF (nécessite `security-events: write` au niveau du workflow ou du job) :
448
464
 
449
- ## 🤖 Pourquoi pas simplement une revue de code par 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
+ ```
450
473
 
451
- Un problème différent, une autre couche. Une revue IA peut repérer un
452
- changement de test suspect dans un diff ; elle ne prouve pas que le
453
- système de vérification dans son ensemble est digne de confiance — et
454
- elle ne voit que le diff que vous lui montrez.
474
+ Sur GitLab, `--format codequality` écrit le rapport Code Quality que lisent le widget de MR et les annotations du diff ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Configuration de l'éditeur et du pipeline : [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
455
475
 
456
- | | Revue de code IA (Copilot, etc.) | **Mjölnir** |
457
- | ------------------------------------------- | :------------------------------------: | :---------------------------------: |
458
- | Coût par scan | Tokens (évolue avec la taille du diff) | **Zéro** (local, installé) |
459
- | Voit toute la suite + toutes les configs CI | Seulement le diff de PR montré | **Tout, à chaque fois** |
460
- | Déterministe (même entrée → même sortie) | ❌ (non déterministe) | **✅** |
461
- | Détecte des motifs dormants depuis des mois | Seulement s'il est dans le contexte | **✅** (scanne tous les fichiers) |
462
- | Se souvient des constats entre les runs | ❌ (aucune mémoire entre sessions) | **✅** (baseline + diff) |
463
- | Tourne sans déclencheur humain | Nécessite une PR ou un prompt | **✅** (hook CI, quelques secondes) |
476
+ ### Attribution sur le périmètre modifié
464
477
 
465
- **Utilisez les deux.** L'IA attrape la nuance, l'intention et les
466
- défauts de conception qu'aucune regex ne trouve. Mjölnir attrape les
467
- motifs structurels que l'IA néglige parce qu'ils semblent
468
- « intentionnels » — un `.only` commité, un code de sortie avalé, un
469
- `continue-on-error` sur un job de test. Ce ne sont pas des bugs qui
470
- demandent du raisonnement ; ce sont des faits qui demandent un scan.
478
+ ```bash
479
+ npx mjolnir-qa@latest --scope changed
480
+ ```
471
481
 
472
- ---
482
+ Les constats sont attribués aux lignes ajoutées par votre branche, mesurées par rapport à la **merge-base**. Le périmètre est le même ensemble de fichiers qu'un scan complet découvre (specs TS/JS et configurations d'adaptateurs, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), plus les modifications non commitées et non suivies, si bien que cela fonctionne avant le commit. La base est résolue selon `main → master → origin/main → origin/master → origin/HEAD` ; remplacez-la avec `--base <ref>`.
473
483
 
474
- ## 🤖 Intégration CI
484
+ Quand la merge-base ne peut pas être résolue (un clone superficiel, un HEAD détaché, une cible hors de git), les constats se replient sur une attribution au fichier entier **et le rapport le dit.** Un repli silencieux serait exactement le type de défaut que cet outil existe pour détecter.
475
485
 
476
- Une commande génère un workflow de PR — consultatif par défaut, jamais
477
- bloquant :
486
+ <br />
478
487
 
479
- ```bash
480
- mjolnir ci install
481
- ```
488
+ ## Agents IA
482
489
 
483
- Ou branchez-le nativement dans GitHub Code Scanning via SARIF :
490
+ Les constats ne valent quelque chose que si quelque chose agit dessus.
484
491
 
485
- ```yaml
486
- - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
487
- - uses: github/codeql-action/upload-sarif@v3
488
- with:
489
- sarif_file: mjolnir.sarif
492
+ ```text
493
+ SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
490
494
  ```
491
495
 
492
- Configuration éditeur et pipeline pour SARIF :
493
- [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
496
+ **L'IA écrit le correctif. Mjölnir le vérifie.** La preuve vient du nouveau scan, jamais du propre compte rendu de réussite de l'agent.
494
497
 
495
- ### Couverture du périmètre modifié
498
+ | Commande | Ce que reçoit l'agent |
499
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
500
+ | `mjolnir mcp` | Un serveur [MCP](https://modelcontextprotocol.io) sur stdio. `scan`, `explain` et `diff` deviennent des outils appelables. |
501
+ | `mjolnir handoff` | Un rapport `--json` enregistré devient un plan Markdown déterministe : ce qui a été détecté, la limite de preuve de chaque constat, ce qui ne doit **pas** changer, comment vérifier. |
502
+ | `mjolnir install` | Écrit dans les surfaces d'agent que votre dépôt possède déjà (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`) pour que l'agent relance le scan avant d'affirmer qu'il a terminé. |
496
503
 
497
- `--scope changed` attribue les constats aux lignes ajoutées dans votre
498
- branche par rapport à la base de fusion avec `main`. Il couvre les
499
- fichiers de tests (`*.spec.*`, `*.test.*`) plus les fichiers de
500
- workflow GitHub et les configurations Playwright du diff. Quand la base
501
- de fusion ne peut pas être résolue — clone superficiel, HEAD détaché,
502
- cible non-git, branche par défaut différente — il dégrade honnêtement :
503
- les constats retombent sur une attribution au fichier entier, et le
504
- rapport le dit. Remplacez la ref de base avec `--base <ref>`.
504
+ Ajoutez-le à un client qui fournit sa propre CLI :
505
505
 
506
- ---
507
-
508
- ## Configuration
509
-
510
- Mjölnir est zéro-config. Un `mjolnir.config.json` optionnel (ou
511
- `.mjolnir.json`) à la racine du dépôt ajuste sévérité, gating et
512
- périmètre — il ne change jamais la sémantique de détection.
506
+ ```bash
507
+ claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
508
+ ```
513
509
 
514
- | Clé | Type | Effet |
515
- | ------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
516
- | `exclude` | `string[]` | Globs d'ignore supplémentaires (sous-ensemble gitignore), au-dessus des valeurs par défaut intégrées |
517
- | `gate` | `"advisory" \| "error" \| "warning"` | Quelles sévérités sortent avec un code non nul (défaut `error` ; `advisory` ne bloque jamais) |
518
- | `severityOverrides` | `{ "<RULE-ID>": severity }` | Re-classe les constats d'une règle pour votre dépôt |
519
- | `ignore` | `IgnoreEntry[]` | Supprime des constats — **`reason` est obligatoire** ; les entrées expirent après 90 jours (une date `expires` explicite, ou la date de dernière modification du fichier de config pour les entrées sans) |
520
- | `plugins` | `string[]` | Paquets de règles tiers (voir [Modèle de confiance](#modèle-de-confiance)) |
510
+ Ou à tout client qui accepte un bloc `mcpServers` :
521
511
 
522
512
  ```json
523
513
  {
524
- "gate": "error",
525
- "exclude": ["legacy/**"],
526
- "severityOverrides": { "QA-PW-141": "warning" },
527
- "ignore": [
528
- {
529
- "ruleId": "QA-TEST-004",
530
- "files": ["e2e/legacy-login.spec.ts"],
531
- "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
532
- "expires": "2026-12-31"
533
- }
534
- ]
514
+ "mcpServers": {
515
+ "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
516
+ }
535
517
  }
536
518
  ```
537
519
 
538
- - **`.mjolnirignore`** — un fichier simple façon gitignore pour les
539
- exclusions de chemins, même dialecte que `exclude`. Utilisez-le pour
540
- le bruit propre à la machine ; utilisez `exclude` quand la liste
541
- appartient au contrôle de version, à côté du reste de la config.
542
- - **Surclassements CLI** — `--strict` (inclure les règles en
543
- quarantaine), `--width <cols>` et `--ascii` / `--no-ascii` (rendu
544
- terminal), `--tone blunt` (messages plus directs),
545
- `--max-duration <sec>` (scan partiel borné).
546
- - Suppression de règles et cycle de vie de dépréciation :
547
- [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
520
+ **Le garde-fou compte plus que la commodité.** Chaque constat d'un passage de relais porte sa limite. **E2** dit _déterministe : vérifiez l'emplacement et appliquez le correctif_. **E1** dit _CONFIRMATION REQUISE : l'observation seule ne prouve pas le défaut_. Un agent qui corrige un E1 aveuglément, supprime une règle ou modifie une règle pour faire monter le score fait exactement ce que cet outil existe pour détecter ; le passage de relais le dit donc dans le prompt, à côté du constat.
521
+
522
+ <br />
523
+
524
+ ## Confiance et sécurité
525
+
526
+ **Local d'abord, zéro télémétrie.** Aucune API capable d'accéder au réseau (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket) n'existe nulle part dans `src/`, et [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) fait échouer le build si l'une d'elles apparaît. Il interdit aussi `eval` et `new Function`. Analyser du code non fiable ne l'exécute jamais : l'analyse statique lit du texte source, et la forensique analyse des fichiers de rapport déjà présents sur le disque.
527
+
528
+ Deux réserves : `npx` lui-même télécharge le paquet avant que quoi que ce soit ne tourne, et la garantie couvre `src/`, pas les plugins tiers.
548
529
 
549
- Les entrées `ignore` alimentent aussi la commande autonome
550
- `mjolnir suppressions`, qui liste ce qui est actuellement supprimé et
551
- quand chaque entrée expire.
530
+ **Les plugins ne sont pas isolés dans un bac à sable.** Les plugins JS (`mjolnir-rules/*.mjs`, ou les paquets npm listés sous `"plugins"`) tournent avec tous les privilèges de Node, le même modèle de confiance que les plugins ESLint ou Vitest. Les charger est un choix explicite **par scan** : sans `--enable-plugins` (ou `MJOLNIR_ENABLE_PLUGINS=1`), leurs sources ne sont jamais chargées, et un avis sur stderr liste ce qui a été ignoré. Les manifestes de règles JSON n'exécutent aucun code, et les préfixes d'ID des règles core sont réservés pour qu'un plugin ne puisse pas se faire passer pour l'une d'elles. Signalez les vulnérabilités via [SECURITY.md](SECURITY.md).
552
531
 
553
- ---
532
+ **Il s'analyse lui-même.** Un moteur de confiance de vérification n'a aucune légitimité s'il n'est pas lui-même vérifiable. Chaque exécution de la CI analyse ce dépôt avec le build produit par cette même exécution. La barrière échoue sur tout constat de sévérité error, ainsi que sur un scan **partiel** ou une **règle qui plante**, parce qu'un auto-scan tronqué qui ne rapporte rien est exactement le faux vert que ce projet existe pour détecter. `mjolnir doctor` ré-audite la base de règles dans la même exécution (pare-feu des fixtures, honnêteté des niveaux, plafond du niveau core), et une vérification INCONCLUSIVE échoue exactement comme une vérification en échec. Les deux rapports sont téléversés comme artefacts de build.
554
533
 
555
- ## 📐 Codes de sortie & contrats
534
+ ### Codes de sortie et contrat machine
556
535
 
557
- Figés — sûrs pour construire de la logique CI dessus :
536
+ Figés, pour que vous puissiez bâtir de la logique CI dessus :
558
537
 
559
538
  | Code de sortie | Signification |
560
539
  | -------------- | ------------------------------------------------------------------------------ |
561
- | `0` | Propre — aucun constat au-dessus ou à l'égal du gate |
562
- | `1` | Constats au-dessus ou à l'égal du gate |
563
- | `2` | Scan partiel (budget de temps atteint, fichiers illisibles) — ne bloque jamais |
564
- | `10` | Erreur d'usage (mauvais flag, cible manquante) |
540
+ | `0` | Propre : aucun constat au niveau de la barrière ou au-dessus |
541
+ | `1` | Constats au niveau de la barrière ou au-dessus |
542
+ | `2` | Scan partiel (budget de temps atteint, fichiers illisibles). Ne bloque jamais. |
543
+ | `10` | Erreur d'utilisation (option invalide, cible manquante) |
565
544
  | `20` | Erreur interne |
566
545
 
567
- Le rapport JSON/SARIF est `schemaVersion: 1`. Les ID de règles
568
- (`QA-<FAMILY>-NNN`) sont immuables une fois livrés et jamais
569
- réutilisés.
570
-
571
- ---
572
-
573
- ## Modèle de confiance
574
-
575
- - **Local-first** — zéro appel réseau pendant le scan. Jamais. Zéro
576
- télémétrie.
577
- - **Pas de fausse preuve** — nous préférons dire « inconnu » que
578
- « vérifié ». Un dépôt vide obtient `score: null`, jamais un faux 100.
579
- - **Honnêteté partielle** — si l'analyse a été interrompue, la sortie le
580
- dit. Jamais « complete » quand ça ne l'est pas.
581
- - **Pare-feu FP** — la détection tourne sur une vue du code sans
582
- commentaires ni chaînes (les règles TypeScript utilisent l'AST du
583
- compilateur) : un motif dans un commentaire de prose ou une chaîne
584
- d'exemple de doc est de la documentation, pas un constat.
585
- - **Mesuré, pas affirmé** — seules les règles avec un taux de faux
586
- positifs issu de vrai code OSS sortent dans les tiers vedettes (voir
587
- [Quelle part est mesurée](#quelle-part-est-mesurée)) ; le pied de scan
588
- et `mjolnir rules --unmeasured` vous disent lesquels.
589
- - **Confiance plugins & porte d'exécution** — les plugins sont des paquets npm déclarés
590
- sous `"plugins"` ; les modules JS vivent dans `mjolnir-rules/*.mjs`.
591
- Il n'y a **pas de sandbox** : le code d'un plugin
592
- tourne avec tous les privilèges Node, le même modèle de confiance que
593
- les plugins ESLint ou Vitest. Pour cette raison, l'exécution de code est
594
- **opt-in à chaque scan** : passez `--enable-plugins` (ou définissez
595
- `MJOLNIR_ENABLE_PLUGINS=1`), sinon les sources ne sont PAS chargées — une
596
- notification stderr bruyante liste exactement ce qui a été ignoré.
597
- Scanner du code non fiable ne l'exécute jamais. Les manifestes de règles
598
- JSON (`mjolnir-rules/*.json`) ne sont pas concernés : ils déclarent des
599
- motifs regex et n'exécutent aucun code par conception. Les préfixes
600
- d'ID de règles core sont
601
- réservés et refusés aux plugins et règles externes pour empêcher
602
- l'usurpation.
603
- - **Règles externes locales au workspace** (par dossier, zéro réseau) —
604
- un répertoire `mjolnir-rules/` à côté de la cible du scan charge des
605
- règles personnalisées : des fichiers JSON déclarent des motifs regex
606
- (aucun code exécuté), les modules `.mjs`/`.js` exportent `rules`
607
- (confiance Node complète, comme les plugins). Les règles externes
608
- portent les mêmes métadonnées de confiance que core ; elles ne
609
- peuvent jamais sortir dans le tier core (core exige un taux de FP
610
- mesuré depuis le sidecar corpus — un `tier: "core"` déclaré est
611
- borné à `extended`), obéissent aux caps de tier et sont vérifiées
612
- contre la dérive : `mjolnir rules --md --external` rend le catalogue
613
- depuis les fichiers chargés (provenance `external`), et le générateur
614
- de matrice accepte `--external <root>`.
615
-
616
- ---
617
-
618
- ## 🏗️ Architecture
546
+ `2` est volontairement distinct de `0` : un scan qui n'a pas terminé n'a pas « rien trouvé ». Il n'a pas fini de chercher.
619
547
 
620
- <details>
621
- <summary>Déplier l'arbre</summary>
548
+ Tout ce qu'une machine consomme (résultats des outils MCP, `--json`, SARIF 2.1) provient d'un unique résultat canonique sous un schéma versionné et **uniquement additif** (`schemaVersion: 1`, `contractVersion: 1`), si bien qu'aucun consommateur n'a à reconstruire le sens à partir du texte affiché. Voir [le contrat machine](docs/machine-contract.md). Les ID de règle (`QA-<FAMILY>-NNN`) sont immuables une fois publiés et ne sont jamais réutilisés.
622
549
 
623
- ```
624
- mjolnir/
625
- ├── src/
626
- │ ├── engine/ # LanguageAdapter interface + rule runner
627
- │ ├── adapters/ # typescript · python · java · csharp · github-actions
628
- │ ├── rules/ # rules across 8 families + the measured-FP table
629
- │ ├── playwright/ # Selector Health Score engine
630
- │ ├── discovery/ # workspace, frameworks, ignore resolution
631
- │ ├── scope/ # git merge-base changed-scope engine
632
- │ ├── scorer/ # transparent deduction table + prioritization
633
- │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
634
- │ ├── forensics/ # run-data ingestion · flake verdicts · triage
635
- │ ├── config/ # mjolnir.config.json + suppressions
636
- │ ├── plugins/ # third-party rule loading (no sandbox)
637
- │ └── commands/ # every subcommand
638
- └── tests/
639
- ├── fixtures/ # must-fire / must-not-fire per rule
640
- └── golden/ # frozen score regression locks
641
- ```
550
+ <br />
642
551
 
643
- </details>
552
+ ## Ce que Mjölnir ne peut pas vous dire
644
553
 
645
- - **Les règles sont des fonctions pures** —
646
- `(SourceFileContext) → Finding[]`, pas d'I/O, pas de globales. Ajouter
647
- un écosystème = un adaptateur + ses règles.
648
- - **TypeScript/Playwright utilise l'AST du compilateur** (ts-morph).
649
- Python, Java et C# tournent sur une couche regex partagée avec
650
- commentaires/chaînes masqués.
651
- - Une couche AST tree-sitter WASM pour Java et C# existe et constitue
652
- la prochaine étape de précision — elle n'est pas encore câblée dans
653
- le pipeline de scan synchrone.
554
+ - **Il n'exécute pas vos tests.** Un scan propre n'est pas une suite qui passe.
555
+ - **Il ne peut pas vous dire qu'une assertion est _fausse_.** `expect(total).toBe(41)` a l'air saine. Mjölnir trouve les tests qui _ne peuvent pas échouer_ et les pipelines qui _ne peuvent pas passer au rouge_, pas les tests qui vérifient la mauvaise chose.
556
+ - **Il ne prouve pas l'exactitude métier.** Rien ici ne dit que votre produit fait ce que l'exigence demandait.
557
+ - **Un 100 n'est pas la preuve d'une bonne suite.** Savoir si votre suite couvre votre risque réel est une autre question, et cet outil n'y répond pas.
558
+ - **5 règles sur 79 reposent sur une estimation**, pas sur un taux mesuré. Chacune le dit sur son propre constat.
559
+ - **E1 n'est pas E2.** Les constats heuristiques méritent d'être lus, pas d'être appliqués aveuglément.
560
+ - **Un dépôt vide obtient `null`, jamais 100.**
561
+ - **Un fichier nommé `*.spec.ts` sans déclaration de test ne compte pas comme couverture.** Un dépôt dont les seuls fichiers spec contiennent des imports ou des types (zéro appel `it`/`test`) obtient `null`, pas 100.
654
562
 
655
- ---
563
+ <br />
656
564
 
657
- ## 📚 Documentation
565
+ ## Documentation
658
566
 
659
- | Document | Contenu |
660
- | ------------------------------------------------------ | -------------------------------------------------- |
661
- | [docs/SCORING.md](docs/SCORING.md) | Normalisation du score + pondération par la preuve |
662
- | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Taux de faux positifs mesurés + méthode |
663
- | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | États des règles, suppression, dépréciation |
664
- | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Sortie SARIF + configuration éditeur/CI |
665
- | [docs/rules/](docs/rules/) | Catalogue généré par règle |
666
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Setup dev + workflow de contribution |
667
- | [CHANGELOG.md](CHANGELOG.md) | Historique des versions |
668
- | [SECURITY.md](SECURITY.md) | Signalement de vulnérabilités |
567
+ Le site de documentation complet se trouve sur <https://sergey-bar.github.io/Mjolnir/>.
669
568
 
670
- ---
569
+ | Document | Contenu |
570
+ | ------------------------------------------------------ | ----------------------------------------------------------- |
571
+ | [docs/SCORING.md](docs/SCORING.md) | Normalisation du score et pondération des preuves |
572
+ | [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Vocabulaire canonique : un mot par concept |
573
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Taux de faux positifs mesurés et méthode |
574
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | États des règles, niveaux, suppression, dépréciation |
575
+ | [docs/VERSIONING.md](docs/VERSIONING.md) | Politique semver, surfaces figées, cycle de dépréciation |
576
+ | [docs/machine-contract.md](docs/machine-contract.md) | Le résultat canonique lisible par machine |
577
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Sortie SARIF et configuration de l'éditeur ou de la CI |
578
+ | [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab : rapport Code Quality, recette de MR, barrière |
579
+ | [docs/rules/](docs/rules/) | Catalogue généré par règle |
580
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Environnement de développement et processus de contribution |
581
+ | [SUPPORT.md](SUPPORT.md) | Où poser des questions, signaler et obtenir de l'aide |
582
+ | [SECURITY.md](SECURITY.md) | Signalement des vulnérabilités |
583
+ | [CHANGELOG.md](CHANGELOG.md) | Historique des versions |
671
584
 
672
- ## 📈 Statut
585
+ ### Statut
673
586
 
674
- **v0.5.x · bêta ouverte.** Le schéma JSON et les codes de sortie sont
675
- des contrats figés. TypeScript et Python ont la couverture mesurée la
676
- plus large ; Java et C# sont plus récents — lisez-les via la
677
- [table des tiers](#tiers-de-règles-et-maturité-par-langage).
587
+ **Version 1.** Le schéma JSON et les codes de sortie sont des contrats figés. TypeScript et Python ont la couverture mesurée la plus large. Java et C# sont plus récents ; lisez-les à travers le [tableau de maturité](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). La suite, sans dates inventées : [la feuille de route publique](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
678
588
 
679
- ---
589
+ ### Contribuer
680
590
 
681
- ## 🤝 Contribuer
682
-
683
- Les nouvelles règles sont la première contribution la plus simple —
684
- une commande scaffolde la règle plus ses fixtures must-fire **et**
685
- must-not-fire (la règle générée échoue volontairement à ses fixtures
686
- tant que vous n'implémentez pas une vraie détection — un stub ne peut
687
- pas sortir) :
591
+ Les nouvelles règles sont la première contribution la plus facile. Une commande crée le squelette de la règle avec ses fixtures must-fire **et** must-not-fire. La règle générée échoue volontairement sur ses propres fixtures tant qu'une vraie détection n'est pas écrite, parce qu'un stub livré est une règle que personne n'a mesurée :
688
592
 
689
593
  ```bash
690
594
  mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
691
595
  ```
692
596
 
693
- Le setup dev complet, les commandes de la barrière permanente et les
694
- lois anti-dérive / pare-feu à fixtures sont dans
695
- [CONTRIBUTING.md](CONTRIBUTING.md).
597
+ L'environnement de développement, les commandes des barrières permanentes et les lois anti-creep et du pare-feu des fixtures se trouvent dans [CONTRIBUTING.md](CONTRIBUTING.md).
696
598
 
697
- ---
599
+ <br />
698
600
 
699
601
  <div align="center">
700
602
 
701
- **Ne livrez plus des tests auxquels vous ne pouvez pas faire
702
- confiance.**
603
+ <img src="assets/readme/closing.svg" alt="Lancez-le sur votre dépôt." width="100%" />
703
604
 
704
605
  ```bash
705
606
  npx mjolnir-qa@latest
706
607
  ```
707
608
 
708
- **Star ⭐ · Watch 👀 · Contribute 🤝**
609
+ [Lire le guide](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Site de documentation](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
610
+
611
+ <br />
612
+
613
+ Ne demandez pas si les tests ont réussi.<br />
614
+ Demandez si les preuves montrent qu'ils méritent la confiance.
709
615
 
710
- Construit par [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
616
+ <sub>Créé par [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · Licence MIT</sub>
711
617
 
712
618
  </div>