mjolnir-qa 0.4.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.it.md ADDED
@@ -0,0 +1,709 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
4
+
5
+ ### I tuoi test ti mentono. Noi lo dimostriamo.
6
+
7
+ **Verification Trust Engine per la QA.** Mjölnir audita le suite di test
8
+ e le pipeline CI, riporta un punteggio di idoneità e mostra esattamente
9
+ dove la fiducia si rompe.
10
+
11
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=C9A227&labelColor=0B0F17)](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=0B0F17)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
13
+ [![license](https://img.shields.io/badge/license-MIT-C9A227.svg?style=flat-square&labelColor=0B0F17)](LICENSE)
14
+ [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-2E8C7F.svg?style=flat-square&labelColor=0B0F17)](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](README.fr.md) | Italiano | [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)
17
+
18
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-04.
19
+
20
+ ```bash
21
+ npx mjolnir-qa@latest
22
+ ```
23
+
24
+ **I tuoi test sono degni di fiducia?**
25
+
26
+ [Guardalo in azione](#-guardalo-in-azione) ·
27
+ [Avvio rapido](#-avvio-rapido) ·
28
+ [Cosa controlla](#-cosa-controlla-mjölnir) ·
29
+ [Punteggio](#come-funziona-il-punteggio) ·
30
+ [CI](#-integrazione-ci) · [Configurazione](#configurazione) ·
31
+ [Documentazione](#-documentazione)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🎬 Guardalo in azione
38
+
39
+ <p align="center">
40
+ <img src="assets/readme/demo.svg" alt="Il report --verbose completo di Mjölnir su un repo demo: WORTHINESS 75/100 NEEDS WORK, un dettaglio delle diagnosti per categoria, una lista FIX THIS FIRST e ogni riscontro con ID di regola e numero di riga attraverso CI, Playwright, igiene dei test e regole Python" width="900" />
41
+ </p>
42
+
43
+ <sub>L'output completo di `npx mjolnir-qa ./examples/demo-repo --verbose`,
44
+ renderizzato dal reporter vero — nulla di tagliato. Rigenerato con
45
+ `npm run docs:demo`;
46
+ [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
+ fa fallire la CI se deriva da ciò che lo strumento stampa.</sub>
48
+
49
+ **Cosa è appena successo:**
50
+
51
+ 1. Mjölnir ha individuato le spec Playwright, la sua configurazione, il
52
+ workflow CI e un file di test Python — quattro linguaggi/formati, un
53
+ solo passaggio.
54
+ 2. Ha trovato evidenze che indeboliscono la fiducia nella suite — un
55
+ `continue-on-error` che maschera un job, un `|| true` che ingoia un
56
+ exit code, sleep hardcoded, un selettore fragile, URL di staging
57
+ hardcoded, un'attesa `networkidle`.
58
+ 3. Di ciascuna ha fatto un riscontro concreto con ID di regola,
59
+ posizione e fix — e un unico punteggio su cui fare gate di una PR.
60
+
61
+ ### Un riscontro da vicino
62
+
63
+ Esegui `mjolnir explain QA-CI-001` sul primo riscontro qui sopra e
64
+ ottieni:
65
+
66
+ ```text
67
+ ▚▞ QA-CI-001 — continue-on-error masks a failing verification gate
68
+
69
+ Severity: error
70
+ Confidence: high
71
+ Evidence: E2
72
+ Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
73
+
74
+ WHAT WAS FOUND (real detector output, not a mockup)
75
+ Job `security-scan` runs a verification gate under `continue-on-error: true`.
76
+
77
+ 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.
80
+
81
+ HOW TO FIX
82
+ Remove continue-on-error, or scope it to individual non-blocking steps only.
83
+ ```
84
+
85
+ Questa è l'unità di valore: non una svista di stile, ma un punto in cui
86
+ la tua CI ti dice che qualcosa è passato quando non è passato.
87
+
88
+ ---
89
+
90
+ ## ⚡ Avvio rapido
91
+
92
+ Eseguilo su un repo per un report completo e un punteggio di idoneità:
93
+
94
+ ```bash
95
+ npx mjolnir-qa@latest
96
+ ```
97
+
98
+ **In CI il prodotto è un solo comando.** Scansiona solo ciò che il
99
+ branch ha toccato ed esce con un codice diverso da zero su problemi
100
+ nuovi:
101
+
102
+ ```bash
103
+ npx mjolnir-qa@latest --scope changed
104
+ ```
105
+
106
+ Metti quello in un check di PR — `mjolnir ci install` scrive il
107
+ workflow — e hai finito. Tutto il resto è opzionale.
108
+
109
+ | Comando | Cosa fa |
110
+ | ----------------------------------- | ---------------------------------------------------------------- |
111
+ | `mjolnir` | Scansione completa del repo + punteggio di idoneità |
112
+ | `mjolnir --scope changed` | Solo ciò che il tuo branch ha introdotto — la forma CI |
113
+ | `mjolnir ci install` | Genera il workflow di PR consultivo |
114
+ | `mjolnir explain QA-CI-001` | Cosa / perché / fix + tasso di FP misurato di una regola |
115
+ | `mjolnir rules --unmeasured` | Le regole che girano per assunzione, non per misurazione |
116
+ | `mjolnir --json` / `--format sarif` | Leggibile da macchina / GitHub Code Scanning |
117
+ | `mjolnir --strict` | Esegue anche le regole del tier quarantena (rischio FP più alto) |
118
+
119
+ <details>
120
+ <summary><strong>Quando qualcosa è instabile</strong></summary>
121
+
122
+ | Comando | Cosa fa |
123
+ | ----------------------------------- | ---------------------------------------------------------- |
124
+ | `mjolnir forensics ./test-results/` | Dati di run reali → verdetti `TRUE-FLAKE`, `FLAKY.md` |
125
+ | `mjolnir triage ./test-results/` | Proposta di quarantena dalla cronologia di esecuzione |
126
+ | `mjolnir pw-report ./test-results/` | Riepilogo di run Playwright — retry / flake / più lenti |
127
+ | `mjolnir doctor:playwright` | Scansione profonda solo Playwright + Selector Health Score |
128
+
129
+ </details>
130
+
131
+ <details>
132
+ <summary><strong>Occasionale / report</strong></summary>
133
+
134
+ | Comando | Cosa fa |
135
+ | ------------------------------- | ---------------------------------------------------------- |
136
+ | `mjolnir fix --dry-run` / `fix` | Auto-fix sicuri con prova |
137
+ | `mjolnir baseline` / `diff` | Snapshot dei riscontri, poi riporta solo nuovi/peggiorati |
138
+ | `mjolnir impact --since <ref>` | Cosa è cambiato da un commit precedente |
139
+ | `mjolnir debt` | Registro del debito di test con un modello di costo |
140
+ | `mjolnir handover` | Mappa di onboarding della suite per un nuovo QA |
141
+ | `mjolnir stats` | Contatori locali storici dei fix visti |
142
+ | `mjolnir badge` | JSON endpoint di shields.io + snippet |
143
+ | `mjolnir rules --md` | Catalogo completo delle regole (JSON o Markdown) |
144
+ | `mjolnir doctor` | Auto-audit della stessa base di regole di Mjölnir |
145
+ | `mjolnir create-rule <ID>` | Imposta lo scheletro di una nuova regola + fixture |
146
+ | `mjolnir --format mermaid` | Diagramma dell'architettura dei test per un commento di PR |
147
+
148
+ </details>
149
+
150
+ Installalo globalmente invece che con `npx` se preferisci:
151
+ `npm i -g mjolnir-qa`. Richiede Node.js ≥ 22.18. Funziona su Windows,
152
+ macOS e Linux.
153
+
154
+ ---
155
+
156
+ ## 👥 Per chi è?
157
+
158
+ - **QA / SDET** che possiedono una suite e2e o di integrazione e
159
+ hanno bisogno di evidenze che la suite meriti davvero la spunta
160
+ verde che produce.
161
+ - **Team Piattaforma / DevEx** responsabili dell'integrità CI e dei
162
+ release gate — le persone per cui un `continue-on-error` non deve mai
163
+ trasformare in silenzio una pipeline rossa in verde.
164
+ - **Maintainer OSS** che vogliono un gate di verifica economico,
165
+ sempre attivo, che gira in locale e in CI senza chiamate di rete.
166
+
167
+ ---
168
+
169
+ ## 🔨 Cosa controlla Mjölnir
170
+
171
+ | | |
172
+ | --- | -------------------------------------------------------------------------------------------------------------------------- |
173
+ | ⚖️ | **Punteggio di idoneità** — un numero, tabella di deduzioni trasparente, nessuna black box |
174
+ | 🎭 | **Selector Health Score** — valuta i tuoi locator Playwright, non solo il pass rate |
175
+ | 🔬 | **Forensica di runtime** — legge dati di run reali Playwright/JUnit per agganciare `TRUE-FLAKE`, non solo ipotesi statiche |
176
+ | 🚨 | **Regole di integrità CI** — becca `continue-on-error`, `\|\| true` e altri trucchi da falso verde |
177
+ | 🐍 | **Tutti e quattro i binding Playwright** — TypeScript, Python, Java, C#/.NET — più pytest, JUnit/TestNG e workflow CI |
178
+ | 🔒 | **Local-first** — zero chiamate di rete durante la scansione, zero telemetria, gira in secondi |
179
+
180
+ ### Le regole
181
+
182
+ Ogni regola arriva con fixture must-fire **e** must-not-fire. Una
183
+ regola che scatta sulla propria fixture negativa non può essere
184
+ pubblicata — quello è il firewall dei falsi positivi.
185
+
186
+ <details>
187
+ <summary><strong>Igiene dei test</strong></summary>
188
+
189
+ | ID | Regola | Severity |
190
+ | ----------- | -------------------------------------------------------- | -------- |
191
+ | QA-TEST-001 | Test focalizzato committato (`.only`, `fit`) | error |
192
+ | QA-TEST-002 | Test saltato senza giustificazione | error |
193
+ | QA-TEST-002 | Test saltato con giustificazione tracciata | warning |
194
+ | QA-TEST-003 | Test senza asserzioni | error |
195
+ | QA-TEST-004 | Sleep hardcoded (`waitForTimeout`, `sleep()`, `delay()`) | warning |
196
+ | QA-TEST-006 | Abuso di retry che nasconde l'instabilità | warning |
197
+ | QA-TEST-010 | Corpo del test vuoto | error |
198
+
199
+ </details>
200
+
201
+ <details>
202
+ <summary><strong>Qualità dei test</strong></summary>
203
+
204
+ | ID | Regla | Severity |
205
+ | ------------ | --------------------------------- | -------- |
206
+ | QA-TQUAL-001 | Verifica solo con mock | info |
207
+ | QA-TQUAL-002 | Asserzione tautologica | error |
208
+ | QA-TQUAL-009 | Asserzione di promise senza await | error |
209
+ | QA-TQUAL-011 | Test commentati | warning |
210
+
211
+ </details>
212
+
213
+ <details>
214
+ <summary><strong>Playwright 🎭</strong></summary>
215
+
216
+ | ID | Regla | Severity |
217
+ | --------- | --------------------------------------------- | -------- |
218
+ | QA-PW-002 | Asserzione di locator senza await | error |
219
+ | QA-PW-003 | `page.pause()` / `test.only()` committati | error |
220
+ | QA-PW-004 | Selettori CSS/XPath fragili | warning |
221
+ | QA-PW-005 | Logica di business dentro `page.evaluate()` | info |
222
+ | QA-PW-114 | Element handle legacy (`page.$`) | info |
223
+ | QA-PW-118 | Attese `networkidle` (instabili per progetto) | info |
224
+ | QA-PW-123 | URL di ambiente hardcoded | warning |
225
+
226
+ </details>
227
+
228
+ <details>
229
+ <summary><strong>Integrità CI</strong></summary>
230
+
231
+ | ID | Regla | Severity |
232
+ | --------- | ------------------------------------------------------------------ | -------- |
233
+ | QA-CI-001 | `continue-on-error` maschera i fallimenti | error |
234
+ | QA-CI-002 | `\|\| true` ingoia gli exit code | error |
235
+ | QA-CI-005 | Report consumato ma mai generato | error |
236
+ | QA-CI-007 | Wrapper di retry attorno ai test | warning |
237
+ | QA-CI-008 | Step sempre riuscito maschera i fallimenti | error |
238
+ | QA-CI-009 | Exit code del test non propagato (`\|` senza pipefail, catene `;`) | error |
239
+ | QA-CI-010 | Test saltati dove devono bloccare (guardie skip-on-PR) | error |
240
+
241
+ </details>
242
+
243
+ <details>
244
+ <summary><strong>Python / pytest 🐍</strong></summary>
245
+
246
+ | ID | Regla | Severity |
247
+ | --------- | ------------------------------------------ | -------- |
248
+ | QA-PY-002 | Test saltato (`skip`, `xfail` non strict) | warning |
249
+ | QA-PY-003 | Funzione di test senza asserzioni | error |
250
+ | QA-PY-005 | `time.sleep()` nei test | warning |
251
+ | QA-PY-006 | Corpo del test vuoto (`pass`) | info |
252
+ | QA-PY-010 | Dipendenza da casualità/tempo senza freeze | info |
253
+ | QA-PY-012 | Asserzione tautologica | error |
254
+
255
+ 20 regole Python in totale (QA-PY-001…012 igiene pytest + QA-PY-101…108 Playwright-Python).
256
+
257
+ </details>
258
+
259
+ <details>
260
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
261
+
262
+ | ID | Regla | Severity |
263
+ | --------- | --------------------------------------------- | -------- |
264
+ | QA-JV-101 | Test disabilitato (`@Disabled`) | warning |
265
+ | QA-JV-102 | Sleep hardcoded (`Thread.sleep()`) | warning |
266
+ | QA-JV-103 | Metodo di test senza asserzioni | error |
267
+ | QA-JV-105 | Sleep hardcoded Playwright `waitForTimeout()` | warning |
268
+ | QA-JV-106 | Selettore fragile invece di un role locator | warning |
269
+ | QA-JV-108 | URL di ambiente hardcoded nel test | info |
270
+ | QA-JV-111 | Mock blanket `page.route("**")` | info |
271
+
272
+ </details>
273
+
274
+ <details>
275
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
276
+
277
+ | ID | Regla | Severity |
278
+ | --------- | ----------------------------------------------- | -------- |
279
+ | QA-CS-101 | Test saltato (`[Ignore]`, `[Fact(Skip=)]`) | warning |
280
+ | QA-CS-102 | Sleep hardcoded (`Thread.Sleep` / `Task.Delay`) | warning |
281
+ | QA-CS-103 | Metodo di test senza asserzioni | error |
282
+ | QA-CS-105 | Sleep hardcoded `WaitForTimeoutAsync()` | warning |
283
+ | QA-CS-106 | Selettore fragile invece di un role locator | warning |
284
+ | QA-CS-108 | URL di ambiente hardcoded nel test | info |
285
+ | QA-CS-111 | Mock blanket `page.RouteAsync("**")` | info |
286
+
287
+ </details>
288
+
289
+ > Il catalogo live completo — ogni regola con tier, confidence, rischio
290
+ > di falso positivo e disponibilità di autofix — è generato dal
291
+ > registro:
292
+ >
293
+ > ```bash
294
+ > mjolnir rules --md
295
+ > ```
296
+ >
297
+ > Le pagine per regola vivono in [`docs/rules/`](docs/rules/).
298
+
299
+ ### Quanto è misurato
300
+
301
+ **74 regole su 99 portano un tasso di falsi positivi misurato su vero
302
+ codice OSS** (≥ 10 riscontri classificati a mano ciascuna; vedi
303
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Le altre 19 escono sulla stima
304
+ dell'autore. Ogni footer di scansione dice quante delle regole
305
+ _scattate_ sono misurate; `mjolnir rules --unmeasured` elenca quelle
306
+ che non lo sono; la pagina `mjolnir explain` di ogni regola dichiara il
307
+ suo stato. Pubblichiamo il tasso anche quando è brutto — QA-CS-103 si
308
+ audita al 95 % ed è in quarantena per questo. Far crescere quel 78 è il
309
+ lavoro continuo del progetto.
310
+
311
+ ### Tier delle regole e maturità per linguaggio
312
+
313
+ Ogni regola è `core`, `extended` o `quarantine`, assegnato in base al
314
+ suo tasso di falsi positivi **misurato**:
315
+
316
+ | Tier | Significato | Scansione predefinita | `--strict` |
317
+ | ------------ | --------------------------------------------- | :-------------------: | :--------: |
318
+ | `core` | ≤ 10 % di FP misurato | ✅ | ✅ |
319
+ | `extended` | ≤ 30 % di FP misurato | ✅ | ✅ |
320
+ | `quarantine` | sopra il 30 %, o non ancora misurato (n < 10) | ❌ | ✅ |
321
+
322
+ | Linguaggio | Adattatore | Copertura oggi |
323
+ | --------------- | ------------------- | --------------------------------------------------------- |
324
+ | TypeScript / JS | AST del compilatore | la più ampia e misurata — soprattutto `core`/`extended` |
325
+ | Python / pytest | Livello regex | ampia, auditata su corpus — soprattutto `core`/`extended` |
326
+ | Java | Livello regex | più recente — soprattutto `extended`/`quarantine` |
327
+ | C# / .NET | Livello regex | più recente — soprattutto `extended`/`quarantine` |
328
+
329
+ TypeScript e Python hanno la copertura misurata più ampia. Java e C#
330
+ sono pubblicati, documentati, e restano fuori dal numero di testa fino
331
+ a quando una vera suite consumatrice (non i test della stessa
332
+ libreria di binding) non sarà stata auditata.
333
+
334
+ ---
335
+
336
+ ## Come funziona il punteggio
337
+
338
+ <p align="center">
339
+ <img src="assets/readme/terminal-hero.svg" alt="Output terminale di Mjölnir — WORTHINESS 75/100 NEEDS WORK, un dettaglio delle diagnosti per categoria e una lista FIX THIS FIRST" width="820" />
340
+ </p>
341
+
342
+ <sub>Rigenerato con `npm run docs:hero`;
343
+ [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
344
+ fa fallire la CI se deriva da ciò che il reporter stampa davvero.</sub>
345
+
346
+ Il punteggio è trasparente: **error −8, warning −3, info −1**, poi
347
+ normalizzato per l'esposizione della suite (deduzioni per dichiarazione
348
+ di test). Le deduzioni ponderate per evidenza significano che i segnali
349
+ deboli costano meno. Il terminale mostra gli stessi numeri scontati che
350
+ usa il punteggio — niente black box. Metodo completo:
351
+ [docs/SCORING.md](docs/SCORING.md).
352
+
353
+ **Verdetti**
354
+
355
+ | Score | Verdetto |
356
+ | ------- | ---------------- |
357
+ | ≥ 80 | ✓ **WORTHY** |
358
+ | 50 – 79 | ⚠ **NEEDS WORK** |
359
+ | < 50 | ✖ **UNWORTHY** |
360
+
361
+ **Livelli di evidenza** — ogni riscontro ne porta uno; fissano il peso
362
+ del riscontro nel punteggio:
363
+
364
+ | Livello | Significato | Impatto sul punteggio | Esempio |
365
+ | ------- | ---------------------- | --------------------- | ------------------------------------------------------ |
366
+ | E2 | Difetto deterministico | Deduzione piena | `.only` committato — dimostrabile strutturalmente |
367
+ | E1 | Pattern euristico | Mezza deduzione | `sleep()` trovato via regex — segnale forte, non prova |
368
+ | E0 | Osservazione | Zero (solo info) | Riportato ma non fa mai gate alla CI né deduce |
369
+
370
+ La maggior parte delle regole è **E1**. Lo slogan «we prove it» si
371
+ riferisce a questo sistema: i riscontri E2 sono prova strutturale; i
372
+ riscontri E1 sono avvertimenti correttamente posizionati, non prove
373
+ formali.
374
+
375
+ Un repo vuoto ottiene `null`, mai un falso 100 — vedi
376
+ [Modello di fiducia](#modello-di-fiducia).
377
+
378
+ ---
379
+
380
+ ## 🎭 Selector Health Score
381
+
382
+ La metrica di testa per le suite Playwright — quanto sono resilienti
383
+ i tuoi locator:
384
+
385
+ ```text
386
+ ▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
387
+
388
+ [█████████████████░░░] 83 / 100
389
+ role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
390
+ ```
391
+
392
+ I locator basati sui ruoli prendono il punteggio pieno. Le catene di
393
+ classi CSS e XPath affossano il punteggio — si rompono a ogni refactor
394
+ del DOM senza dirti quale comportamento è regredito.
395
+
396
+ ---
397
+
398
+ ## 🔬 Evidenza di runtime
399
+
400
+ La rilevazione statica di instabilità è tirare a indovinare. Mjölnir
401
+ legge **veri dati di esecuzione** — report JSON Playwright e XML JUnit
402
+ da qualsiasi runner:
403
+
404
+ ```bash
405
+ mjolnir forensics ./test-results/
406
+ ```
407
+
408
+ ```text
409
+ ▚▞ FLAKINESS LEADERBOARD
410
+
411
+ 3 tests · 1 failed · 1 flaky · 1 retried
412
+
413
+ TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
414
+ ████████████████████ 6.0s · 2 attempts
415
+ FAILING declines an expired card (e2e/checkout.spec.ts)
416
+ ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
417
+ ```
418
+
419
+ Un test che passa solo dal tentativo ≥ 2 non è un test che passa — è un
420
+ test fortunato. Viene marcato `TRUE-FLAKE` a prescindere dalla spunta
421
+ verde finale.
422
+
423
+ ---
424
+
425
+ ## ⚡ Mjölnir non è un linter in più
426
+
427
+ I linter ti dicono se il codice segue le regole. Mjölnir ti dice se la
428
+ tua verifica può essere ritenuta affidabile.
429
+
430
+ | | ESLint / SonarQube | Strumenti di coverage | Review manuale | **Mjölnir** |
431
+ | ------------------------------------------------------------- | :----------------: | :-------------------: | :------------: | :---------: |
432
+ | Integrità dei workflow CI (`continue-on-error`, `\|\| true`) | ❌ | ❌ | raramente | ✅ |
433
+ | Cross-linguaggio (TS, Python, Java, C#) da un solo strumento | ❌ | ❌ | ❌ | ✅ |
434
+ | Valuta la resilienza dei locator Playwright (Selector Health) | ❌ | ❌ | raramente | ✅ |
435
+ | Segnala test senza vere asserzioni | ✅ (plugin)\* | ❌ | a volte | ✅ |
436
+ | Becca gli sleep hardcoded (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | a volte | ✅ |
437
+ | Gira in secondi, zero chiamate di rete durante la scansione | ✅ | ✅ | — | ✅ |
438
+
439
+ \*`eslint-plugin-jest` (`expect-expect`) e `eslint-plugin-playwright`
440
+ (`expect-expect`, `no-wait-for-timeout`) coprono questo per i rispettivi
441
+ framework.
442
+
443
+ **L'analisi di runtime** è una categoria a sé rispetto al linting
444
+ statico:
445
+
446
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
447
+ | -------------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
448
+ | Legge dati di run reali per verdetti `TRUE-FLAKE` | parziale\* | parziale (tag) | ✅ |
449
+ | Report di triage dell'instabilità dalla cronologia | ❌ | ✅ | ✅ |
450
+ | Si integra con il punteggio di idoneità statico | ❌ | ❌ | ✅ |
451
+
452
+ \*Playwright traccia i retry internamente ma non produce un report di
453
+ instabilità autonomo con etichette di verdetto.
454
+
455
+ ---
456
+
457
+ ## 🤖 Perché non usare semplicemente la code review con IA?
458
+
459
+ Problema diverso, livello diverso. Una review IA può beccare una
460
+ modifica sospetta a un test in un diff; non dimostra che il sistema di
461
+ verifica nel suo insieme sia affidabile — e vede solo il diff che gli
462
+ mostri.
463
+
464
+ | | Code review IA (Copilot, ecc.) | **Mjölnir** |
465
+ | --------------------------------------------- | :--------------------------------------: | :-----------------------------: |
466
+ | Costo per scansione | Token (scala con la dimensione del diff) | **Zero** (locale, installato) |
467
+ | Vede tutta la suite + tutte le config CI | Solo il diff di PR che mostri | **Tutto, ogni volta** |
468
+ | Deterministico (stesso input → stesso output) | ❌ (non deterministico) | **✅** |
469
+ | Becca pattern dormienti da mesi | Solo se è nel contesto | **✅** (scansiona tutti i file) |
470
+ | Ricorda i riscontri tra le esecuzioni | ❌ (nessuna memoria tra sessioni) | **✅** (baseline + diff) |
471
+ | Gira senza innesco umano | Serve una PR o un prompt | **✅** (hook CI, 3 secondi) |
472
+
473
+ **Usali entrambi.** L'IA becca la sfumatura, l'intento e i difetti di
474
+ design che nessuna regex trova. Mjölnir becca i pattern strutturali che
475
+ l'IA trascura perché sembrano "intenzionali" — un `.only` committato,
476
+ un exit code ingoiato, un `continue-on-error` su un job di test. Non
477
+ sono bug che richiedono ragionamento; sono fatti che richiedono
478
+ scansione.
479
+
480
+ ---
481
+
482
+ ## 🤖 Integrazione CI
483
+
484
+ Un comando genera un workflow di PR — consultivo per impostazione
485
+ predefinita, mai bloccante:
486
+
487
+ ```bash
488
+ mjolnir ci install
489
+ ```
490
+
491
+ Oppure collegalo nativamente a GitHub Code Scanning via SARIF:
492
+
493
+ ```yaml
494
+ - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
495
+ - uses: github/codeql-action/upload-sarif@v3
496
+ with:
497
+ sarif_file: mjolnir.sarif
498
+ ```
499
+
500
+ Setup per editor e pipeline per SARIF:
501
+ [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
502
+
503
+ ### Copertura del perimetro modificato
504
+
505
+ `--scope changed` attribuisce i riscontri alle righe aggiunte nel tuo
506
+ branch rispetto al merge-base con `main`. Copre i file di test
507
+ (`*.spec.*`, `*.test.*`) più i file di workflow GitHub e le
508
+ configurazioni Playwright nel diff. Quando il merge-base non si può
509
+ risolvere — clone shallow, HEAD detached, target non git, branch
510
+ predefinito diverso — degrada onestamente: i riscontri tornano a
511
+ un'attribuzione per intero file e il report lo dice. Sovrascrivi la ref
512
+ di base con `--base <ref>`.
513
+
514
+ ---
515
+
516
+ ## Configurazione
517
+
518
+ Mjölnir è zero-config. Un `mjolnir.config.json` opzionale (o
519
+ `.mjolnir.json`) alla radice del repo regola severità, gating e
520
+ perimetro — non cambia mai la semantica di rilevamento.
521
+
522
+ | Key | Tipo | Effetto |
523
+ | ------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
524
+ | `exclude` | `string[]` | Glob di ignore aggiuntivi (sottoinsieme gitignore), sopra i default integrati |
525
+ | `gate` | `"advisory" \| "error" \| "warning"` | Quali severità escono con codice diverso da zero (predefinito `error`; `advisory` non blocca mai) |
526
+ | `severityOverrides` | `{ "<RULE-ID>": severity }` | Riordina i riscontri di una regola per il tuo repo |
527
+ | `ignore` | `IgnoreEntry[]` | Sopprime riscontri — **`reason` è obbligatorio**; le voci scadono dopo 90 giorni (una data `expires` esplicita, o la data di modifica del file di config per le voci senza) |
528
+ | `plugins` | `string[]` | Pacchetti di regole di terze parti (vedi [Modello di fiducia](#modello-di-fiducia)) |
529
+
530
+ ```json
531
+ {
532
+ "gate": "error",
533
+ "exclude": ["legacy/**"],
534
+ "severityOverrides": { "QA-PW-118": "warning" },
535
+ "ignore": [
536
+ {
537
+ "ruleId": "QA-TEST-004",
538
+ "files": ["e2e/legacy-login.spec.ts"],
539
+ "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
540
+ "expires": "2026-12-31"
541
+ }
542
+ ]
543
+ }
544
+ ```
545
+
546
+ - **`.mjolnirignore`** — un file semplice in stile gitignore per le
547
+ esclusioni di percorsi, stesso dialetto di `exclude`. Usalo per il
548
+ rumore specifico della macchina; usa `exclude` quando la lista
549
+ appartiene al version control, accanto al resto della configurazione.
550
+ - **Override CLI** — `--strict` (includere le regole in quarantena),
551
+ `--width <cols>` e `--ascii` / `--no-ascii` (rendering terminale),
552
+ `--tone blunt` (messaggi più secchi), `--max-duration <sec>` (scansione
553
+ parziale limitata).
554
+ - Soppressione delle regole e ciclo di vita delle deprecazioni:
555
+ [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
556
+
557
+ Le voci `ignore` alimentano anche il comando autonomo
558
+ `mjolnir suppressions`, che elenca ciò che è attualmente soppresso e
559
+ quando scade ogni voce.
560
+
561
+ ---
562
+
563
+ ## 📐 Exit code & contratti
564
+
565
+ Congelati — sicuri su cui costruire logica CI:
566
+
567
+ | Exit code | Significato |
568
+ | --------- | -------------------------------------------------------------------------------- |
569
+ | `0` | Pulito — nessun riscontro a o sopra il gate |
570
+ | `1` | Riscontri a o sopra il gate |
571
+ | `2` | Scansione parziale (budget di tempo esaurito, file illeggibili) — non blocca mai |
572
+ | `10` | Errore d'uso (flag errato, target mancante) |
573
+ | `20` | Errore interno |
574
+
575
+ Il report JSON/SARIF è `schemaVersion: 1`. Gli ID di regola
576
+ (`QA-<FAMILY>-NNN`) sono immutabili una volta pubblicati e mai
577
+ riusati.
578
+
579
+ ---
580
+
581
+ ## Modello di fiducia
582
+
583
+ - **Local-first** — zero chiamate di rete durante la scansione. Mai.
584
+ Zero telemetria.
585
+ - **Nessuna prova falsa** — preferiamo dire "sconosciuto" che
586
+ "verificato". Un repo vuoto riceve `score: null`, mai un falso 100.
587
+ - **Onestà parziale** — se l'analisi è stata troncata, l'output lo dice.
588
+ Mai "complete" quando non lo è.
589
+ - **Firewall FP** — il rilevamento gira su una vista del codice senza
590
+ commenti/stringhe (le regole TypeScript usano l'AST del compilatore):
591
+ un pattern dentro un commento di prosa o una stringa di esempio di
592
+ documentazione è documentazione, non un riscontro.
593
+ - **Misurato, non affermato** — solo le regole con un tasso di falsi
594
+ positivi da vero codice OSS escono nei tier di testa (vedi
595
+ [Quanto è misurato](#quanto-è-misurato)); il footer della scansione e
596
+ `mjolnir rules --unmeasured` ti dicono quale è quale.
597
+ - **Fiducia nei plugin** — i plugin sono pacchetti npm dichiarati sotto
598
+ `"plugins"`. **Non c'è sandbox**: il codice del plugin gira con tutti
599
+ i privilegi Node, lo stesso modello di fiducia dei plugin ESLint o
600
+ Vitest. I prefissi di ID delle regole core sono riservati e rifiutati
601
+ dai plugin per evitare spoofing.
602
+ - **Regole esterne locali al workspace** (basate su cartella, zero
603
+ rete) — una directory `mjolnir-rules/` accanto al target della
604
+ scansione carica regole personalizzate: i file JSON dichiarano
605
+ pattern regex (nessun codice eseguito), i moduli `.mjs`/`.js`
606
+ esportano `rules` (fiducia Node piena, come i plugin). Le regole
607
+ esterne portano gli stessi metadati di fiducia del core; non possono
608
+ mai uscire nel tier core (core richiede un tasso di FP misurato dal
609
+ sidecar corpus — un `tier: "core"` dichiarato viene limitato a
610
+ `extended`), obbediscono ai tetti di tier e sono controllate contro
611
+ la deriva: `mjolnir rules --md --external` renderizza il catalogo dai
612
+ file caricati (provenienza `external`), e il generatore di matrice
613
+ accetta `--external <root>`.
614
+
615
+ ---
616
+
617
+ ## 🏗️ Architettura
618
+
619
+ <details>
620
+ <summary>Espandi l'albero</summary>
621
+
622
+ ```
623
+ mjolnir/
624
+ ├── src/
625
+ │ ├── engine/ # LanguageAdapter interface + rule runner
626
+ │ ├── adapters/ # typescript · python · java · csharp · github-actions
627
+ │ ├── rules/ # rules across 8 families + the measured-FP table
628
+ │ ├── playwright/ # Selector Health Score engine
629
+ │ ├── discovery/ # workspace, frameworks, ignore resolution
630
+ │ ├── scope/ # git merge-base changed-scope engine
631
+ │ ├── scorer/ # transparent deduction table + prioritization
632
+ │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
633
+ │ ├── forensics/ # run-data ingestion · flake verdicts · triage
634
+ │ ├── config/ # mjolnir.config.json + suppressions
635
+ │ ├── plugins/ # third-party rule loading (no sandbox)
636
+ │ └── commands/ # every subcommand
637
+ └── tests/
638
+ ├── fixtures/ # must-fire / must-not-fire per rule
639
+ └── golden/ # frozen score regression locks
640
+ ```
641
+
642
+ </details>
643
+
644
+ - **Le regole sono funzioni pure** —
645
+ `(SourceFileContext) → Finding[]`, niente I/O, niente globali.
646
+ Aggiungere un ecosistema = un adattatore + le sue regole.
647
+ - **TypeScript/Playwright usa l'AST del compilatore** (ts-morph).
648
+ Python, Java e C# girano su un livello regex condiviso con
649
+ commenti/stringhe mascherati.
650
+ - Un livello AST tree-sitter WASM per Java e C# esiste ed è il
651
+ prossimo passo di precisione — non è ancora cablato nella pipeline
652
+ di scansione sincrona.
653
+
654
+ ---
655
+
656
+ ## 📚 Documentazione
657
+
658
+ | Documento | Cosa contiene |
659
+ | ------------------------------------------------------ | ---------------------------------------------------------- |
660
+ | [docs/SCORING.md](docs/SCORING.md) | Normalizzazione del punteggio + ponderazione dell'evidenza |
661
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Tassi di falsi positivi misurati + metodo |
662
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Stati delle regole, soppressione, deprecazione |
663
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | Output SARIF + setup editor/CI |
664
+ | [docs/rules/](docs/rules/) | Catalogo generato per regola |
665
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Setup dev + workflow di contribuzione |
666
+ | [CHANGELOG.md](CHANGELOG.md) | Cronologia delle release |
667
+ | [SECURITY.md](SECURITY.md) | Segnalazione vulnerabilità |
668
+
669
+ ---
670
+
671
+ ## 📈 Stato
672
+
673
+ **v0.5.x · beta aperta.** Lo schema JSON e gli exit code sono contratti
674
+ congelati. TypeScript e Python hanno la copertura misurata più ampia;
675
+ Java e C# sono più recenti — leggili attraverso la
676
+ [tabella dei tier](#tier-delle-regole-e-maturità-per-linguaggio).
677
+
678
+ ---
679
+
680
+ ## 🤝 Contribuire
681
+
682
+ Le nuove regole sono il primo contributo più semplice — un comando
683
+ imposta lo scheletro della regola più le sue fixture must-fire **e**
684
+ must-not-fire (la regola generata fallisce apposta le fixture finché
685
+ non implementi il rilevamento vero — uno stub non può uscire):
686
+
687
+ ```bash
688
+ mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
689
+ ```
690
+
691
+ Setup dev completo, i comandi della barriera permanente e le leggi
692
+ anti-creep / firewall delle fixture sono in
693
+ [CONTRIBUTING.md](CONTRIBUTING.md).
694
+
695
+ ---
696
+
697
+ <div align="center">
698
+
699
+ **Smetti di pubblicare test di cui non ti puoi fidare.**
700
+
701
+ ```bash
702
+ npx mjolnir-qa@latest
703
+ ```
704
+
705
+ **Star ⭐ · Watch 👀 · Contribute 🤝**
706
+
707
+ Costruito da [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
708
+
709
+ </div>