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.no.md ADDED
@@ -0,0 +1,696 @@
1
+ <div align="center">
2
+
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
4
+
5
+ ### Testene dine lyver til deg. Vi beviser det.
6
+
7
+ **Verification Trust Engine for QA.** Mjölnir auditor testsuiter og
8
+ CI-pipelines, rapporterer en verdighetsscore og viser nøyaktig hvor
9
+ tilliten bryter sammen.
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](README.it.md) | [Dansk](README.da.md) | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | Norsk | [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
+ **Er testene dine verd tillit?**
25
+
26
+ [Se det virke](#-se-det-virke) ·
27
+ [Rask start](#-rask-start) ·
28
+ [Hva den sjekker](#-hva-mjölnir-sjekker) ·
29
+ [Scoring](#slik-fungerer-scoren) ·
30
+ [CI](#-ci-integrasjon) · [Konfigurasjon](#konfigurasjon) ·
31
+ [Dokumentasjon](#-dokumentasjon)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🎬 Se det virke
38
+
39
+ <p align="center">
40
+ <img src="assets/readme/demo.svg" alt="Mjölnirs komplette --verbose-rapport over et demo-repo: WORTHINESS 75/100 NEEDS WORK, en oppdeling av diagnostikk etter kategori, en FIX THIS FIRST-liste og hvert funn med regel-ID og linjenummer på tvers av CI-, Playwright-, testhygiene- og Python-regler" width="900" />
41
+ </p>
42
+
43
+ <sub>Det komplette `npx mjolnir-qa ./examples/demo-repo --verbose`-resultatet,
44
+ renderet av den ekte reporteren — ingenting klippet vekk. Regenereres
45
+ med `npm run docs:demo`;
46
+ [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
+ får CI til å feile hvis artefakten avviker fra det verktøyet skriver
48
+ ut.</sub>
49
+
50
+ **Hva som nettopp skjedde:**
51
+
52
+ 1. Mjölnir oppdaget Playwright-specs, konfigurasjonen, CI-workflowen og
53
+ en Python-testfil — fire språk/formater, ett gjennomløp.
54
+ 2. Den fant beviser som svekker tilliten til suiten — en
55
+ `continue-on-error` som maskerer en jobb, en `|| true` som svelger en
56
+ exit-kode, harde sleeps, en skjør selector, hardkodede staging-URLer,
57
+ en `networkidle`-ventetid.
58
+ 3. Den gjorde hvert av dem til et konkret funn med regel-ID, plassering
59
+ og fiks — og til én score du kan gate en PR på.
60
+
61
+ ### Ett funn på nært hold
62
+
63
+ Kjør `mjolnir explain QA-CI-001` på det første funnet over, og du får:
64
+
65
+ ```text
66
+ ▚▞ QA-CI-001 — continue-on-error masks a failing verification gate
67
+
68
+ Severity: error
69
+ Confidence: high
70
+ Evidence: E2
71
+ Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
72
+
73
+ WHAT WAS FOUND (real detector output, not a mockup)
74
+ Job `security-scan` runs a verification gate under `continue-on-error: true`.
75
+
76
+ WHY IT MATTERS
77
+ This job can fail every day and CI will still show green. The checkmark
78
+ on this workflow cannot be trusted.
79
+
80
+ HOW TO FIX
81
+ Remove continue-on-error, or scope it to individual non-blocking steps only.
82
+ ```
83
+
84
+ Det er verdiens enhet: ikke en stilprikke, men et sted der CI-en din
85
+ forteller deg at noe besto, selv om det ikke gjorde det.
86
+
87
+ ---
88
+
89
+ ## ⚡ Rask start
90
+
91
+ Kjør den mot et repo for en full rapport og en verdighetsscore:
92
+
93
+ ```bash
94
+ npx mjolnir-qa@latest
95
+ ```
96
+
97
+ **I CI er produktet én kommando.** Den skanner bare det branchen rørte
98
+ og avslutter med ikke-null ved nye problemer:
99
+
100
+ ```bash
101
+ npx mjolnir-qa@latest --scope changed
102
+ ```
103
+
104
+ Legg det inn som en PR-sjekk — `mjolnir ci install` skriver workflowen —
105
+ og du er ferdig. Alt annet er valgfritt.
106
+
107
+ | Kommando | Hva den gjør |
108
+ | ----------------------------------- | --------------------------------------------------- |
109
+ | `mjolnir` | Skann av hele repoet + verdighetsscore |
110
+ | `mjolnir --scope changed` | Bare det branchen din innførte — CI-formen |
111
+ | `mjolnir ci install` | Genererer den rådgivende PR-workflowen |
112
+ | `mjolnir explain QA-CI-001` | Hva / hvorfor / fiks + målt FP-rate for én regel |
113
+ | `mjolnir rules --unmeasured` | Reglene som kjører på antagelse, ikke måling |
114
+ | `mjolnir --json` / `--format sarif` | Maskinlesbart / GitHub Code Scanning |
115
+ | `mjolnir --strict` | Kjør også quarantine-tier-regler (høyere FP-risiko) |
116
+
117
+ <details>
118
+ <summary><strong>Når noe er flaky</strong></summary>
119
+
120
+ | Kommando | Hva den gjør |
121
+ | ----------------------------------- | ------------------------------------------------------- |
122
+ | `mjolnir forensics ./test-results/` | Ekte kjøringsdata → `TRUE-FLAKE`-dommer, `FLAKY.md` |
123
+ | `mjolnir triage ./test-results/` | Karanteneforslag fra utførelseshistorikken |
124
+ | `mjolnir pw-report ./test-results/` | Playwright-runoversikt — retries / flakes / de tregeste |
125
+ | `mjolnir doctor:playwright` | Dypskann kun Playwright + Selector Health Score |
126
+
127
+ </details>
128
+
129
+ <details>
130
+ <summary><strong>Av og til / rapporter</strong></summary>
131
+
132
+ | Kommando | Hva den gjør |
133
+ | ------------------------------- | -------------------------------------------------------- |
134
+ | `mjolnir fix --dry-run` / `fix` | Trygge autofikser med bevis |
135
+ | `mjolnir baseline` / `diff` | Øyeblikksbilde av funn, rapporter så bare nye/forverrede |
136
+ | `mjolnir impact --since <ref>` | Hva som endret seg siden et tidligere commit |
137
+ | `mjolnir debt` | Testgjeldsregister med en kostnadsmodell |
138
+ | `mjolnir handover` | Onboarding-kart over suiten for ny QA |
139
+ | `mjolnir stats` | Lokale all-time-tellere av sette fikser |
140
+ | `mjolnir badge` | shields.io-endepunkt-JSON + snippet |
141
+ | `mjolnir rules --md` | Fullstendig regelkatalog (JSON eller Markdown) |
142
+ | `mjolnir doctor` | Selvaudit av Mjölnirs egen regelbase |
143
+ | `mjolnir create-rule <ID>` | Scaffold en ny regel + fixtures |
144
+ | `mjolnir --format mermaid` | Testarkitekturdiagram til en PR-kommentar |
145
+
146
+ </details>
147
+
148
+ Installer globalt i stedet for `npx` hvis du foretrekker det:
149
+ `npm i -g mjolnir-qa`. Krever Node.js ≥ 22.18. Fungerer på Windows,
150
+ macOS og Linux.
151
+
152
+ ---
153
+
154
+ ## 👥 Hvem er det for?
155
+
156
+ - **QA / SDET** som eier en e2e- eller integrasjonssuite og trenger
157
+ bevis for at suiten faktisk fortjener den grønne haken den
158
+ produserer.
159
+ - **Plattform-/DevEx-team** som har ansvar for CI-integritet og
160
+ release gates — folket som bryr seg om at en `continue-on-error`
161
+ aldri stille maler en rød pipeline grønn.
162
+ - **OSS-maintainere** som vil ha en billig, alltid på verifikasjonsgate
163
+ som kjører lokalt og i CI uten nettverkskall.
164
+
165
+ ---
166
+
167
+ ## 🔨 Hva Mjölnir sjekker
168
+
169
+ | | |
170
+ | --- | ---------------------------------------------------------------------------------------------------------------------------- |
171
+ | ⚖️ | **Verdighetsscore** — ett tall, transparent fradragstabell, ingen black box |
172
+ | 🎭 | **Selector Health Score** — vurderer Playwright-locatorene dine, ikke bare pass rate |
173
+ | 🔬 | **Runtime-forundersøkelse** — leser ekte Playwright/JUnit-kjøringsdata og fanger `TRUE-FLAKE`, ikke bare statiske gjetninger |
174
+ | 🚨 | **CI-integritetsregler** — fanger `continue-on-error`, `\|\| true` og andre falskgrønne triks |
175
+ | 🐍 | **Alle fire Playwright-bindings** — TypeScript, Python, Java, C#/.NET — pluss pytest, JUnit/TestNG og CI-workflows |
176
+ | 🔒 | **Local-first** — null nettverkskall under skanning, null telemetri, kjører på sekunder |
177
+
178
+ ### Reglene
179
+
180
+ Hver regel leveres med både must-fire- **og** must-not-fire-fixtures.
181
+ En regel som utløses på sin egen negative fixture, kan ikke skipes —
182
+ det er false-positive-brannmuren.
183
+
184
+ <details>
185
+ <summary><strong>Testhygiene</strong></summary>
186
+
187
+ | ID | Regel | Severity |
188
+ | ----------- | ---------------------------------------------------- | -------- |
189
+ | QA-TEST-001 | Commitet fokusert test (`.only`, `fit`) | error |
190
+ | QA-TEST-002 | Hoppet over test uten begrunnelse | error |
191
+ | QA-TEST-002 | Hoppet over test med registrert begrunnelse | warning |
192
+ | QA-TEST-003 | Test uten assertions | error |
193
+ | QA-TEST-004 | Hardt sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
194
+ | QA-TEST-006 | Retry-misbruk som skjuler flakiness | warning |
195
+ | QA-TEST-010 | Tomt testkropp | error |
196
+
197
+ </details>
198
+
199
+ <details>
200
+ <summary><strong>Testkvalitet</strong></summary>
201
+
202
+ | ID | Regel | Severity |
203
+ | ------------ | ------------------------------- | -------- |
204
+ | QA-TQUAL-001 | Kun-mock-verifisering | info |
205
+ | QA-TQUAL-002 | Tautologisk assertion | error |
206
+ | QA-TQUAL-009 | Assertion på promise uten await | error |
207
+ | QA-TQUAL-011 | Utkommenterte tester | warning |
208
+
209
+ </details>
210
+
211
+ <details>
212
+ <summary><strong>Playwright 🎭</strong></summary>
213
+
214
+ | ID | Regel | Severity |
215
+ | --------- | ---------------------------------------- | -------- |
216
+ | QA-PW-002 | Locator-assertion uten await | error |
217
+ | QA-PW-003 | `page.pause()` / `test.only()` commitet | error |
218
+ | QA-PW-004 | Skjøre CSS/XPath-selektorer | warning |
219
+ | QA-PW-005 | Forretningslogikk i `page.evaluate()` | info |
220
+ | QA-PW-114 | Legacy element handles (`page.$`) | info |
221
+ | QA-PW-118 | `networkidle`-ventetid (flaky by design) | info |
222
+ | QA-PW-123 | Hardkodede miljø-URLer | warning |
223
+
224
+ </details>
225
+
226
+ <details>
227
+ <summary><strong>CI-integritet</strong></summary>
228
+
229
+ | ID | Regel | Severity |
230
+ | --------- | ------------------------------------------------------------------ | -------- |
231
+ | QA-CI-001 | `continue-on-error` maskerer feil | error |
232
+ | QA-CI-002 | `\|\| true` svelger exit-koder | error |
233
+ | QA-CI-005 | Rapport forbrukes, men genereres aldri | error |
234
+ | QA-CI-007 | Retry-wrappers rundt tester | warning |
235
+ | QA-CI-008 | Alltid-vellykket step maskerer feil | error |
236
+ | QA-CI-009 | Testens exit-kode propageres ikke (`\|` uten pipefail, `;`-kjeder) | error |
237
+ | QA-CI-010 | Tester hoppes over der de må blokkere (skip-on-PR-guards) | error |
238
+
239
+ </details>
240
+
241
+ <details>
242
+ <summary><strong>Python / pytest 🐍</strong></summary>
243
+
244
+ | ID | Regel | Severity |
245
+ | --------- | ---------------------------------------------- | -------- |
246
+ | QA-PY-002 | Hoppet over test (`skip`, ikke-strikt `xfail`) | warning |
247
+ | QA-PY-003 | Testfunksjon uten assertions | error |
248
+ | QA-PY-005 | `time.sleep()` i tester | warning |
249
+ | QA-PY-006 | Tomt testkropp (`pass`) | info |
250
+ | QA-PY-010 | Tilfeldighets-/tidsavhengighet uten freeze | info |
251
+ | QA-PY-012 | Tautologisk assertion | error |
252
+
253
+ 20 Python-regler totalt (QA-PY-001…012 pytest-hygiene + QA-PY-101…108 Playwright-Python).
254
+
255
+ </details>
256
+
257
+ <details>
258
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
259
+
260
+ | ID | Regel | Severity |
261
+ | --------- | ----------------------------------------- | -------- |
262
+ | QA-JV-101 | Deaktivert test (`@Disabled`) | warning |
263
+ | QA-JV-102 | Hardt sleep (`Thread.sleep()`) | warning |
264
+ | QA-JV-103 | Testmetode uten assertions | error |
265
+ | QA-JV-105 | Playwright hardt sleep `waitForTimeout()` | warning |
266
+ | QA-JV-106 | Skjør selector i stedet for role-locator | warning |
267
+ | QA-JV-108 | Hardkodet miljø-URL i test | info |
268
+ | QA-JV-111 | Blanket-mock `page.route("**")` | info |
269
+
270
+ </details>
271
+
272
+ <details>
273
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
274
+
275
+ | ID | Regel | Severity |
276
+ | --------- | ---------------------------------------------- | -------- |
277
+ | QA-CS-101 | Hoppet over test (`[Ignore]`, `[Fact(Skip=)]`) | warning |
278
+ | QA-CS-102 | Hardt sleep (`Thread.Sleep` / `Task.Delay`) | warning |
279
+ | QA-CS-103 | Testmetode uten assertions | error |
280
+ | QA-CS-105 | Hardt sleep `WaitForTimeoutAsync()` | warning |
281
+ | QA-CS-106 | Skjør selector i stedet for role-locator | warning |
282
+ | QA-CS-108 | Hardkodet miljø-URL i test | info |
283
+ | QA-CS-111 | Blanket-mock `page.RouteAsync("**")` | info |
284
+
285
+ </details>
286
+
287
+ > Den fulle, levende katalogen — hver regel med tier, confidence,
288
+ > false-positive-risiko og autofix-tilgjengelighet — genereres fra
289
+ > registret:
290
+ >
291
+ > ```bash
292
+ > mjolnir rules --md
293
+ > ```
294
+ >
295
+ > Sider per regel ligger under [`docs/rules/`](docs/rules/).
296
+
297
+ ### Hvor mye er målt
298
+
299
+ **74 av 99 regler bærer en false-positive-rate målt mot ekte OSS-kode**
300
+ (≥ 10 håndklassifiserte funn hver; se
301
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). De andre 19 skiper på
302
+ forfatterens estimat. Hver skann-fotnote forteller hvor mange av de
303
+ _utløste_ reglene som er målt; `mjolnir rules --unmeasured` lister de
304
+ umålte; hver regels `mjolnir explain`-side angir statusen. Vi publiserer
305
+ raten selv når den er stygg — QA-CS-103 auditeres til 95 % og er satt i
306
+ karantene for det. Å få de 78 til å vokse er prosjektets fortsatte
307
+ arbeid.
308
+
309
+ ### Regel-tiers og språkmodenhet
310
+
311
+ Hver regel er `core`, `extended` eller `quarantine`, tildelt ut fra sin
312
+ **målte** false-positive-rate:
313
+
314
+ | Tier | Betydning | Standardskann | `--strict` |
315
+ | ------------ | ---------------------------------------- | :-----------: | :--------: |
316
+ | `core` | ≤ 10 % målt FP | ✅ | ✅ |
317
+ | `extended` | ≤ 30 % målt FP | ✅ | ✅ |
318
+ | `quarantine` | over 30 %, eller ennå ikke målt (n < 10) | ❌ | ✅ |
319
+
320
+ | Språk | Adapter | Dekning i dag |
321
+ | --------------- | -------------- | ------------------------------------------------ |
322
+ | TypeScript / JS | Kompilator-AST | bredeste, mest målte — mest `core`/`extended` |
323
+ | Python / pytest | Regex-lag | bredt, corpus-auditeret — mest `core`/`extended` |
324
+ | Java | Regex-lag | nyere — mest `extended`/`quarantine` |
325
+ | C# / .NET | Regex-lag | nyere — mest `extended`/`quarantine` |
326
+
327
+ TypeScript og Python har den bredeste målte dekningen. Java og C# er
328
+ skipet, dokumentert og holdes utenfor overskriftstallet til en ekte
329
+ forbrukersuite (ikke et binding-biblioteks egne tester) er auditeret.
330
+
331
+ ---
332
+
333
+ ## Slik fungerer scoren
334
+
335
+ <p align="center">
336
+ <img src="assets/readme/terminal-hero.svg" alt="Mjölnir-terminalutskrift — WORTHINESS 75/100 NEEDS WORK, en oppdeling av diagnostikk etter kategori og en FIX THIS FIRST-liste" width="820" />
337
+ </p>
338
+
339
+ <sub>Regenereres med `npm run docs:hero`;
340
+ [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
341
+ får CI til å feile hvis artefakten avviker fra det reporteren faktisk
342
+ skriver ut.</sub>
343
+
344
+ Scoren er transparent: **error −8, warning −3, info −1**, deretter
345
+ normalisert etter suitens eksponering (fradrag per testdeklarasjon).
346
+ Bevisvektede fradrag betyr at svake signaler koster mindre. Terminalen
347
+ viser de samme diskonterte tallene scoren bruker — ingen black box.
348
+ Full metode: [docs/SCORING.md](docs/SCORING.md).
349
+
350
+ **Dommer**
351
+
352
+ | Score | Domme |
353
+ | ------- | ---------------- |
354
+ | ≥ 80 | ✓ **WORTHY** |
355
+ | 50 – 79 | ⚠ **NEEDS WORK** |
356
+ | < 50 | ✖ **UNWORTHY** |
357
+
358
+ **Bevisnivåer** — hvert funn bærer ett; det setter funnets vekt i
359
+ scoren:
360
+
361
+ | Nivå | Betydning | Score-effekt | Eksempel |
362
+ | ---- | --------------------- | --------------- | --------------------------------------------------- |
363
+ | E2 | Deterministisk defekt | Fullt fradrag | Commitet `.only` — strukturelt bevisbart |
364
+ | E1 | Heuristisk mønster | Halvt fradrag | Regex-truffet `sleep()` — sterkt signal, ikke bevis |
365
+ | E0 | Observasjon | Null (kun info) | Rapportert, men gater aldri CI eller trekker fra |
366
+
367
+ De fleste regler er **E1**. Slagordet «we prove it» viser til dette
368
+ systemet: E2-funn er strukturelt bevis; E1-funn er korrekt plasserte
369
+ advarsler, ikke formelle beviser.
370
+
371
+ Et tomt repo scorer `null`, aldri en falsk 100 — se
372
+ [Tillitsmodellen](#tillitsmodellen).
373
+
374
+ ---
375
+
376
+ ## 🎭 Selector Health Score
377
+
378
+ Hovedmetrikken for Playwright-suiter — hvor robuste locatorene dine er:
379
+
380
+ ```text
381
+ ▚▞ SELECTOR HEALTH — e2e/checkout.spec.ts
382
+
383
+ [█████████████████░░░] 83 / 100
384
+ role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
385
+ ```
386
+
387
+ Rollebaserte locators får full score. CSS-klassekjeder og XPath senker
388
+ scoren — de brekker ved enhver DOM-refaktor uten å fortelle deg hvilken
389
+ atferd som har regressert.
390
+
391
+ ---
392
+
393
+ ## 🔬 Runtime-bevis
394
+
395
+ Statisk flakiness-deteksjon er gjetting. Mjölnir leser **ekte
396
+ kjøringsdata** — Playwright JSON-rapporter og JUnit-XML fra enhver
397
+ runner:
398
+
399
+ ```bash
400
+ mjolnir forensics ./test-results/
401
+ ```
402
+
403
+ ```text
404
+ ▚▞ FLAKINESS LEADERBOARD
405
+
406
+ 3 tests · 1 failed · 1 flaky · 1 retried
407
+
408
+ TRUE-FLAKE completes checkout with saved card (e2e/checkout.spec.ts)
409
+ ████████████████████ 6.0s · 2 attempts
410
+ FAILING declines an expired card (e2e/checkout.spec.ts)
411
+ ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
412
+ ```
413
+
414
+ En test som bare består fra forsøk ≥ 2, er ikke en bestått test — det
415
+ er en heldig test. Den merkes `TRUE-FLAKE` uansett den endelige grønne
416
+ haken.
417
+
418
+ ---
419
+
420
+ ## ⚡ Mjölnir er ikke enda en linter
421
+
422
+ Lintere forteller deg om koden følger regler. Mjölnir forteller deg om
423
+ verifiseringen din kan stoles på.
424
+
425
+ | | ESLint / SonarQube | Coverage-verktøy | Manuell review | **Mjölnir** |
426
+ | -------------------------------------------------------------- | :----------------: | :--------------: | :------------: | :---------: |
427
+ | CI-workflow-integritet (`continue-on-error`, `\|\| true`) | ❌ | ❌ | sjelden | ✅ |
428
+ | Tverrspråklig (TS, Python, Java, C#) fra ett verktøy | ❌ | ❌ | ❌ | ✅ |
429
+ | Vurderer robustheten til Playwright-locators (Selector Health) | ❌ | ❌ | sjelden | ✅ |
430
+ | Markerer tester uten ekte assertions | ✅ (plugin)\* | ❌ | av og til | ✅ |
431
+ | Fanger harde sleeps (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | av og til | ✅ |
432
+ | Kjører på sekunder, null nettverkskall under skanning | ✅ | ✅ | — | ✅ |
433
+
434
+ \*`eslint-plugin-jest` (`expect-expect`) og `eslint-plugin-playwright`
435
+ (`expect-expect`, `no-wait-for-timeout`) dekker dette for sine
436
+ respektive rammeverk.
437
+
438
+ **Runtime-analyse** er en egen kategori ved siden av statisk linting:
439
+
440
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
441
+ | ----------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
442
+ | Leser ekte kjøringsdata til `TRUE-FLAKE`-dommer | delvis\* | delvis (tag) | ✅ |
443
+ | Flaky-triage-rapport fra utførelseshistorikken | ❌ | ✅ | ✅ |
444
+ | Integrerer med den statiske verdighetsscoren | ❌ | ❌ | ✅ |
445
+
446
+ \*Playwright sporer retries internt, men produserer ikke en selvstendig
447
+ flakiness-rapport med dommeetiketter.
448
+
449
+ ---
450
+
451
+ ## 🤖 Hvorfor ikke bare bruke AI-kodereview?
452
+
453
+ Annet problem, annet lag. AI-review kan spotte en mistenkelig
454
+ testendring i en diff; det beviser ikke at verifiseringssystemet som
455
+ helhet er troverdig — og det ser bare diffen du viser det.
456
+
457
+ | | AI-kodereview (Copilot m.fl.) | **Mjölnir** |
458
+ | ------------------------------------------- | :-------------------------------------: | :--------------------------: |
459
+ | Kostnad per skann | Tokens (skalerer med diffstørrelsen) | **Null** (lokal, installert) |
460
+ | Ser hele suiten + alle CI-konfigs | Bare PR-diffen du viser | **Alt, hver gang** |
461
+ | Deterministisk (samme input → samme output) | ❌ (ikke-deterministisk) | **✅** |
462
+ | Fanger mønstre som har sovet i måneder | Bare hvis det er i konteksten | **✅** (skanner alle filer) |
463
+ | Husker funn mellom kjøringer | ❌ (ingen hukommelse på tvers av økter) | **✅** (baseline + diff) |
464
+ | Kjører uten menneskelig utløser | Krever en PR eller prompt | **✅** (CI-hook, 3 sekunder) |
465
+
466
+ **Bruk begge.** AI fanger nyanse, intensjon og designfeil ingen regex
467
+ kan finne. Mjölnir fanger de strukturelle mønstrene AI overser fordi de
468
+ ser «intensjonelle» ut — et commitet `.only`, en oppslukt exit-kode, en
469
+ `continue-on-error` på et testjobb. Det er ikke bugs som krever
470
+ resonnering; det er fakta som krever skanning.
471
+
472
+ ---
473
+
474
+ ## 🤖 CI-integrasjon
475
+
476
+ Én kommando genererer en PR-workflow — rådgivende som standard, aldri
477
+ blokkerende:
478
+
479
+ ```bash
480
+ mjolnir ci install
481
+ ```
482
+
483
+ Eller koble den nativt inn i GitHub Code Scanning via SARIF:
484
+
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
490
+ ```
491
+
492
+ Editor- og pipeline-oppsett for SARIF:
493
+ [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
494
+
495
+ ### Changed-scope-dekning
496
+
497
+ `--scope changed` tilskriver funn de linjene branchen din la til
498
+ i forhold til merge-base med `main`. Den dekker testfiler (`*.spec.*`,
499
+ `*.test.*`) pluss GitHub-workflowfiler og Playwright-konfigurasjoner i
500
+ diffen. Når merge-base ikke kan resolve — shallow clone, detached HEAD,
501
+ ikke-git-mål, annen default-branch — degraderer den ærlig: funn faller
502
+ tilbake til hel-fil-attribuering, og rapporten sier det. Overskriv
503
+ base-ref med `--base <ref>`.
504
+
505
+ ---
506
+
507
+ ## Konfigurasjon
508
+
509
+ Mjölnir er zero-config. En valgfri `mjolnir.config.json` (eller
510
+ `.mjolnir.json`) i roten av repoet fininnstiller severity, gating og
511
+ scope — den endrer aldri deteksjonssemantikken.
512
+
513
+ | Key | Type | Effekt |
514
+ | ------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
515
+ | `exclude` | `string[]` | Ekstra ignore-globs (gitignore-delmengde), oppå de innebygde defaultene |
516
+ | `gate` | `"advisory" \| "error" \| "warning"` | Hvilke severities som avslutter med ikke-null (default `error`; `advisory` blokkerer aldri) |
517
+ | `severityOverrides` | `{ "<RULE-ID>": severity }` | Omrangerer en regels funn for repoet ditt |
518
+ | `ignore` | `IgnoreEntry[]` | Undertrykker funn — **`reason` er påkrevd**; oppføringer utløper etter 90 dager (en eksplisitt `expires`-dato, eller config-filens last-modified-tid for oppføringer uten) |
519
+ | `plugins` | `string[]` | Regelpakker fra tredjepart (se [Tillitsmodellen](#tillitsmodellen)) |
520
+
521
+ ```json
522
+ {
523
+ "gate": "error",
524
+ "exclude": ["legacy/**"],
525
+ "severityOverrides": { "QA-PW-118": "warning" },
526
+ "ignore": [
527
+ {
528
+ "ruleId": "QA-TEST-004",
529
+ "files": ["e2e/legacy-login.spec.ts"],
530
+ "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
531
+ "expires": "2026-12-31"
532
+ }
533
+ ]
534
+ }
535
+ ```
536
+
537
+ - **`.mjolnirignore`** — en enkel gitignore-lignende fil for
538
+ sti-ekskluderinger, samme dialekt som `exclude`. Bruk den for
539
+ maskinspesifikk støy; bruk `exclude` når listen hører hjemme i
540
+ versjonskontroll sammen med resten av konfigurasjonen.
541
+ - **CLI-overrides** — `--strict` (inkluder karantèneregler),
542
+ `--width <cols>` og `--ascii` / `--no-ascii` (terminalrendering),
543
+ `--tone blunt` (hardere meldinger), `--max-duration <sec>`
544
+ (begrenset delvis skanning).
545
+ - Regelundertrykkelse og deprecation-levetid:
546
+ [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
547
+
548
+ `ignore`-oppføringer driver også den selvstendige kommandoen
549
+ `mjolnir suppressions`, som lister hva som er undertrykket nå, og når
550
+ hver oppføring utløper.
551
+
552
+ ---
553
+
554
+ ## 📐 Exit-koder & kontrakter
555
+
556
+ Frosne — trygge å bygge CI-logikk på:
557
+
558
+ | Exit-kode | Betydning |
559
+ | --------- | --------------------------------------------------------------------------- |
560
+ | `0` | Rent — ingen funn på eller over gaten |
561
+ | `1` | Funn på eller over gaten |
562
+ | `2` | Delvis skanning (tidsbudsjett brukt opp, ulesebare filer) — blokkerer aldri |
563
+ | `10` | Bruksfeil (ugyldig flagg, manglende mål) |
564
+ | `20` | Intern feil |
565
+
566
+ JSON/SARIF-rapporten er `schemaVersion: 1`. Regel-IDer
567
+ (`QA-<FAMILY>-NNN`) er uforanderlige én gang skipet og gjenbrukes
568
+ aldri.
569
+
570
+ ---
571
+
572
+ ## Tillitsmodellen
573
+
574
+ - **Local-first** — null nettverkskall under skanning. Ever. Null
575
+ telemetri.
576
+ - **Ingen falsk bevis** — vi sier heller «ukjent» enn «verifisert». Et
577
+ tomt repo får `score: null`, aldri en falsk 100.
578
+ - **Delvis ærlighet** — hvis analysen ble avkortet, sier utdataene det.
579
+ Aldri «complete» når det ikke er tilfelle.
580
+ - **FP-brannmur** — deteksjon kjører på et kommentar-/streng-fritt view
581
+ av koden (TypeScript-regler bruker kompilator-AST): et mønster inne i
582
+ en prosakommentar eller en doc-eksempelstreng er dokumentasjon, ikke
583
+ et funn.
584
+ - **Målt, ikke påstått** — bare regler med en false-positive-rate fra
585
+ ekte OSS-kode skiper i overskriftstierne (se
586
+ [Hvor mye er målt](#hvor-mye-er-målt)); skann-fotnoten og
587
+ `mjolnir rules --unmeasured` forteller deg hvilke som er hva.
588
+ - **Plugin-tillit** — plugins er npm-pakker deklarert under
589
+ `"plugins"`. Det er **ingen sandbox**: plugin-kode kjører med fulle
590
+ Node-privilegier, samme tillitsmodell som ESLint- eller
591
+ Vitest-plugins. Core regel-ID-prefiks er reservert og avvises fra
592
+ plugins mot spoofing.
593
+ - **Workspace-lokale eksterne regler** (mappebaserte, null nettverk) —
594
+ en `mjolnir-rules/`-mappe ved siden av skannemålet loader
595
+ tilpassede regler: JSON-filer deklarerer regex-mønstre (ingen kode
596
+ eksekveres), `.mjs`/`.js`-moduler eksporterer `rules` (full
597
+ Node-tillit, som plugins). Eksterne regler bærer samme
598
+ trust-metadata som core; de kan aldri skipe i core-tieren (core
599
+ krever en målt FP-rate fra corpus-sidecaren — en deklarert
600
+ `tier: "core"` klemmes til `extended`), adlyder tier-grenser og
601
+ sjekkes for drift: `mjolnir rules --md --external` renderer
602
+ katalogen fra de lastede filene (proveniens `external`), og
603
+ matrisegeneratoren aksepterer `--external <root>`.
604
+
605
+ ---
606
+
607
+ ## 🏗️ Arkitektur
608
+
609
+ <details>
610
+ <summary>Utvid treet</summary>
611
+
612
+ ```
613
+ mjolnir/
614
+ ├── src/
615
+ │ ├── engine/ # LanguageAdapter interface + rule runner
616
+ │ ├── adapters/ # typescript · python · java · csharp · github-actions
617
+ │ ├── rules/ # rules across 8 families + the measured-FP table
618
+ │ ├── playwright/ # Selector Health Score engine
619
+ │ ├── discovery/ # workspace, frameworks, ignore resolution
620
+ │ ├── scope/ # git merge-base changed-scope engine
621
+ │ ├── scorer/ # transparent deduction table + prioritization
622
+ │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
623
+ │ ├── forensics/ # run-data ingestion · flake verdicts · triage
624
+ │ ├── config/ # mjolnir.config.json + suppressions
625
+ │ ├── plugins/ # third-party rule loading (no sandbox)
626
+ │ └── commands/ # every subcommand
627
+ └── tests/
628
+ ├── fixtures/ # must-fire / must-not-fire per rule
629
+ └── golden/ # frozen score regression locks
630
+ ```
631
+
632
+ </details>
633
+
634
+ - **Regler er rene funksjoner** — `(SourceFileContext) → Finding[]`,
635
+ ingen I/O, ingen globals. Nye økosystem = én adapter + dens regler.
636
+ - **TypeScript/Playwright bruker kompilator-AST** (ts-morph). Python,
637
+ Java og C# kjører på et delt regex-lag med maskerte kommentar/strenger.
638
+ - Et tree-sitter WASM AST-lag for Java og C# finnes og er neste
639
+ presisjonssteg — det er ennå ikke koblet på den synkrone
640
+ skanne-pipelinen.
641
+
642
+ ---
643
+
644
+ ## 📚 Dokumentasjon
645
+
646
+ | Dokument | Hva som er i det |
647
+ | ------------------------------------------------------ | ------------------------------------------- |
648
+ | [docs/SCORING.md](docs/SCORING.md) | Score-normalisering + bevisvektning |
649
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Målte false-positive-rater + metode |
650
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Regeltilstander, undertrykking, deprecation |
651
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF-utdata + editor/CI-oppsett |
652
+ | [docs/rules/](docs/rules/) | Generert katalog per regel |
653
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Dev-oppsett + bidragsflyt |
654
+ | [CHANGELOG.md](CHANGELOG.md) | Utgivelseshistorikk |
655
+ | [SECURITY.md](SECURITY.md) | Sårbarhetsrapportering |
656
+
657
+ ---
658
+
659
+ ## 📈 Status
660
+
661
+ **v0.5.x · åpen beta.** JSON-skjemaet og exit-kodene er frosne
662
+ kontrakter. TypeScript og Python har den bredeste målte dekningen; Java
663
+ og C# er nyere — les dem gjennom
664
+ [tiers-tabellen](#regel-tiers-og-språkmodenhet).
665
+
666
+ ---
667
+
668
+ ## 🤝 Bidra
669
+
670
+ Nye regler er den enkleste første bidraget — én kommando scaffolder
671
+ regelen pluss dens must-fire- **og** must-not-fire-fixtures (den
672
+ genererte regelen feiler bevisst fixturene sine til du implementerer
673
+ ekte deteksjon — en stub kan ikke skipes):
674
+
675
+ ```bash
676
+ mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
677
+ ```
678
+
679
+ Full dev-oppsett, standing-gate-kommandoene og anti-creep- /
680
+ fixture-brannmur-lovene er i [CONTRIBUTING.md](CONTRIBUTING.md).
681
+
682
+ ---
683
+
684
+ <div align="center">
685
+
686
+ **Slutt å skipe tester du ikke kan stole på.**
687
+
688
+ ```bash
689
+ npx mjolnir-qa@latest
690
+ ```
691
+
692
+ **Star ⭐ · Watch 👀 · Contribute 🤝**
693
+
694
+ Bygget av [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
695
+
696
+ </div>