mjolnir-qa 1.0.9 → 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.da.md CHANGED
@@ -1,398 +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. Tests fortæller dig, hvad der bestod. Mjölnir fortæller dig, hvad du kan stole på." width="100%" />
4
4
 
5
- ### Dine tests lyver for dig. Vi beviser det.
5
+ <br />
6
6
 
7
- **Verification Trust Engine til QA.** Mjölnir auditor testsuiter og
8
- CI-pipelines, rapporterer en værdighedsscore og viser præcis, hvor
9
- tilliden brister.
7
+ Mjölnir finder tests, der ikke kan fejle, og pipelines, der ikke kan blive røde,<br />
8
+ og vurderer derefter, hvor langt resultatet er til at stole på, med beviset for hvert 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](README.fr.md) | [Italiano](README.it.md) | Dansk | [日本語](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
- **Er dine tests værd at stole på?**
24
+ [Se det i aktion](#se-det-i-aktion) · [Kom hurtigt i gang](#kom-hurtigt-i-gang) · [Hvad det finder](#hvad-mjölnir-finder) · [Score](#worthiness-scoren) · [Evidens](#evidensmodellen) · [Kørselsanalyse](#kørselsanalyse) · [CI](#ci-integritet) · [Agenter](#ai-agenter) · [Sikkerhed](#tillid-og-sikkerhed) · [Grænser](#hvad-mjölnir-ikke-kan-fortælle-dig) · [Dokumentation](#dokumentation)
25
+
26
+ <details>
27
+ <summary>Læs på et andet sprog — 22 oversættelser</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](README.fr.md) | [Italiano](README.it.md) | Dansk | [日本語](README.ja.md) | [Polski](README.pl.md) | [Русский](README.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
25
30
 
26
- [Se det virke](#-se-det-virke) ·
27
- [Hurtig start](#-hurtig-start) ·
28
- [Hvad den tjekker](#-hvad-mjölnir-tjekker) ·
29
- [Scoring](#sådan-fungerer-scoren) ·
30
- [CI](#-ci-integration) · [Konfiguration](#konfiguration) ·
31
- [Dokumentation](#-dokumentation)
31
+ > 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-15.
32
+
33
+ <!-- Source hash: 3541b09e8d04 -->
34
+
35
+ </details>
32
36
 
33
37
  </div>
34
38
 
35
- ---
39
+ <br />
40
+
41
+ ## Et grønt flueben er en påstand, ikke et bevis
42
+
43
+ Et grønt flueben betyder, at pipelinen ikke fejlede. Det betyder ikke, at testene kørte, eller at de kunne have fejlet. Hver eneste af disse går grønt igennem:
44
+
45
+ - en committet `.only`, der kørte 3 tests i stedet for 900
46
+ - `continue-on-error: true` på det job, der skulle have blokeret
47
+ - `|| true` efter testkommandoen
48
+ - en test, der ikke tjekker noget, eller som har en tom krop
49
+ - en retry-wrapper, der gør en reel fejl til et heldigt bestået
50
+ - en rapport, som workflowet uploader, men aldrig har genereret
51
+ - et fast sleep, der holder sammen på en race condition
52
+
53
+ Ingen af dem gør pipelinen rød, og hver af dem ser tilsigtet ud i review. Det er derfor, de overlever. Her læser Mjölnir et rigtigt eksempel:
54
+
55
+ <p align="center">
56
+ <img src="assets/readme/scan.svg" alt="Demo-repositoriets CI-workflow, læst linje for linje. Mjölnir markerer hvert fund på den rapporterede linje, med dets regel, hvad der er galt, dets evidensniveau og dets målte falsk-positiv-rate." width="800" />
57
+ </p>
58
+
59
+ <sub>Hvert fund, som demo-scannet rapporterede for dette workflow, på den rapporterede linje. Genereret af `npm run docs:readme-brand` ud fra [`demo-report.json`](assets/readme/demo-report.json) og låst mod afvigelser i CI.</sub>
60
+
61
+ **Streng tilstand.** De mest aggressive registreringer — `.only`, `continue-on-error`, tomme tests, misbrug af genkørsler — lever i karantænelaget. De kører kun med `--strict` og er begrænset til `info`-alvorlighed: de flagger, de blokerer aldrig. Standardscanningen (`npx mjolnir-qa@latest` uden `--strict`) dækker kun kerne- og udvidede regler. Tilføj `--strict` når du også vil have rådgivningslaget.
62
+
63
+ Mjölnir læser testsuiten, CI-workflowene og, hvis du har en, rapporten fra en rigtig kørsel. Det kører ikke dine tests, installerer ikke dine afhængigheder og udfører ikke den kode, det scanner. Og når det ikke har evidens, siger det det i stedet for at opfinde tillid:
64
+
65
+ | Situation | Hvad Mjölnir rapporterer |
66
+ | -------------------------------------------- | ----------------------------------------------------------- |
67
+ | Ingen testdeklarationer fundet | Score `null`, vist som **UNKNOWN**. Aldrig et opdigtet 100. |
68
+ | Ingen baseline eller sammenlignelig revision | **UNKNOWN**, med årsagen angivet. Aldrig et antaget 0. |
69
+ | Scan afbrudt (tidsbudget, ulæselige filer) | **PARTIAL**, exit `2`. Aldrig præsenteret som rent. |
70
+
71
+ <p align="center">
72
+ <img src="assets/readme/how-it-works.svg" alt="Sådan virker Mjölnir. Det læser testsuiten og CI-pipelinen statisk, og rapporten fra en rigtig kørsel, når der er en. Det vægter hvert fund efter dets evidensniveau og tillidsniveau, hvor kun en rigtig kørsel kan nå L3 til L5, og leverer fund, en worthiness-score og en CI-gate med fastfrosne exitkoder. I agent-loopet skriver AI rettelsen, og Mjölnir scanner igen for at bevise den." width="880" />
73
+ </p>
74
+
75
+ <sub>Komponeret til denne side og vist i 1:1. Genereret af `npm run docs:readme-brand` og låst mod afvigelser i CI; score, antal og regel-ID kommer fra [`script.demo.json`](assets/video/script.demo.json), [`demo-report.json`](assets/readme/demo-report.json) og regelregistret, aldrig tastet ind i hånden. Samme billede som plakat: [`architecture.svg`](assets/readme/architecture.svg).</sub>
76
+
77
+ <br />
78
+
79
+ ## Se det i aktion
80
+
81
+ Et rigtigt scan af [`examples/demo-repo`](examples/demo-repo), en lille Playwright-suite med et CI-workflow. Her er, hvor dens point forsvandt hen:
82
+
83
+ <p align="center">
84
+ <img src="assets/readme/terminal-hero.svg" alt="Mjölnirs opgørelse af fradrag: WORTHINESS 75/100 NEEDS WORK, scoren pr. kategori, fradragsboksen pr. alvorlighed og en FIX THIS FIRST-liste" width="520" />
85
+ </p>
86
+
87
+ <sub>Genereret af `npm run docs:hero` ud fra et rigtigt scan og låst mod afvigelser i CI. Den fulde `--verbose`-rapport fra samme scan er [`demo.svg`](assets/readme/demo.svg) (`npm run docs:demo`).</sub>
88
+
89
+ <details>
90
+ <summary><strong>Se det</strong> — et scan, rettelsen det udskriver, og det nye scan, der beviser den</summary>
36
91
 
37
- ## 🎬 Se det virke
92
+ <br />
38
93
 
39
94
  <p align="center">
40
- <img src="assets/readme/demo.svg" alt="Mjölnirs fulde --verbose-rapport over et demo-repo: WORTHINESS 75/100 NEEDS WORK, en opdeling af diagnostik efter kategori, en FIX THIS FIRST-liste og hvert fund med regel-ID og linjenummer på tværs af CI-, Playwright-, testhygiejne- og Python-regler" width="900" />
95
+ <a href="assets/video/mjolnir-demo.mp4">
96
+ <img src="assets/video/mjolnir-demo-poster.png" alt="Et billede fra demo-optagelsen: npx mjolnir-qa@latest scanner demo-repositoriet i et terminalvindue" width="900" />
97
+ </a>
41
98
  </p>
42
99
 
43
- <sub>Det komplette `npx mjolnir-qa ./examples/demo-repo --verbose`-output,
44
- renderet af den rigtige reporter — intet klippet væk. Regenereres med
45
- `npm run docs:demo`;
46
- [`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
47
- får CI til at fejle, hvis artefaktet afviger fra, hvad værktøjet
48
- printer.</sub>
100
+ <sub>Renderet billede for billede ud fra et rigtigt scan af `npm run docs:video`; aldrig optaget fra skærmen. Vælg billedet for at åbne [`mjolnir-demo.mp4`](assets/video/mjolnir-demo.mp4).</sub>
49
101
 
50
- **Hvad der lige er sket:**
102
+ </details>
51
103
 
52
- 1. Mjölnir opdagede Playwright-specs, dens konfiguration,
53
- CI-workflowet og en Python-testfil — fire sprog/formater, ét
54
- gennemløb.
55
- 2. Den fandt beviser, der svækker tilliden til suiten — en
56
- `continue-on-error`, der maskerer et job, en `|| true`, der synker
57
- en exit-kode, hårde sleeps, en skrøbelig selector, hårdkodede
58
- staging-URL'er, en `networkidle`-venten.
59
- 3. Den gjorde hver af dem til et konkret fund med regel-ID, placering
60
- og fix — og til én score, du kan gate en PR på.
104
+ ### Ét fund helt tæt på
61
105
 
62
- ### Ét fund på nært hold
106
+ Hvert fund besvarer fire spørgsmål: hvor det er, hvor sikker Mjölnir er, hvor ofte reglen tager fejl, og hvordan det rettes.
63
107
 
64
- Kør `mjolnir explain QA-CI-001` på det første fund ovenfor, og du får:
108
+ <p align="center">
109
+ <img src="assets/readme/finding-anatomy.svg" alt="Det første fund fra demo-scannet, præcis som terminalen udskriver det, med dets fire dele markeret: hvor, hvor sikkert, hvor ofte reglen tager fejl, og rettelsen." width="100%" />
110
+ </p>
111
+
112
+ `mjolnir explain QA-CI-001` udskriver en regels samlede tillidsprofil, inklusive dens målte falsk-positiv-rate og det niveau, raten har givet den:
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
- Det er værdiens enhed: ikke en stilprik, men et sted, hvor dit CI
86
- fortæller dig, at noget bestod, selvom det ikke gjorde.
137
+ Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
138
+
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
146
+
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.
150
+
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.
154
+
155
+ Docs: mjolnir rules --md (full catalog, this rule included)
156
+ ```
87
157
 
88
- ---
158
+ Det er værdienheden: ét sted, hvor CI rapporterer et bestået, den ikke har gjort sig fortjent til.
89
159
 
90
- ## ⚡ Hurtig start
160
+ <br />
91
161
 
92
- Kør den mod et repo for en fuld rapport og en værdighedsscore:
162
+ ## Kom hurtigt i gang
93
163
 
94
164
  ```bash
95
165
  npx mjolnir-qa@latest
96
166
  ```
97
167
 
98
- **I CI er produktet én kommando.** Den scanner kun det, branchen rørte,
99
- og afslutter med ikke-nul ved nye problemer:
168
+ Det scanner den aktuelle mappe og udskriver Trust Report: hvad det fandt, hvor langt du kan stole på det, hvorfor, og hvad du skal gøre nu. Det afslutter med `0`, når intet på eller over gaten blev fundet.
169
+
170
+ I CI skal du kun scanne det, grenen har tilføjet, så en ældre testsuite ikke drukner din første pull request:
100
171
 
101
172
  ```bash
102
173
  npx mjolnir-qa@latest --scope changed
103
174
  ```
104
175
 
105
- Smid det ind som et PR-check — `mjolnir ci install` skriver workflowet —
106
- og du er færdig. Alt andet er valgfrit.
107
-
108
- | Kommando | Hvad den gør |
109
- | ----------------------------------- | -------------------------------------------------- |
110
- | `mjolnir` | Scanning af hele repoet + værdighedsscore |
111
- | `mjolnir --scope changed` | Kun det, din branch introducerede — CI-formen |
112
- | `mjolnir ci install` | Genererer den vejledende PR-workflow |
113
- | `mjolnir explain QA-CI-001` | Hvad / hvorfor / fix + målt FP-rate for én regel |
114
- | `mjolnir rules --unmeasured` | Reglerne, der kører på antagelse, ikke måling |
115
- | `mjolnir --json` / `--format sarif` | Maskinlæsbart / GitHub Code Scanning |
116
- | `mjolnir --strict` | Kør også quarantine-tier-regler (højere FP-risiko) |
117
-
118
- <details>
119
- <summary><strong>Når noget er flaky</strong></summary>
176
+ `mjolnir ci install` skriver det som et GitHub Actions-workflow med [action'en](https://github.com/Sergey-Bar/Mjolnir#readme) fastlåst til major-tagget `v1` (eller rent `npx` med `--no-action`). Det forbliver rådgivende, indtil du beslutter, at det skal blokere.
120
177
 
121
178
  | Kommando | Hvad den gør |
122
179
  | ----------------------------------- | --------------------------------------------------------- |
123
- | `mjolnir forensics ./test-results/` | Ægte kørselsdata → `TRUE-FLAKE`-domme, `FLAKY.md` |
124
- | `mjolnir triage ./test-results/` | Karantæneforslag fra eksekveringshistorikken |
125
- | `mjolnir pw-report ./test-results/` | Playwright-runoversigt — retries / flakes / de langsomste |
126
- | `mjolnir doctor:playwright` | Deep scan kun Playwright + Selector Health Score |
127
-
128
- </details>
180
+ | `mjolnir` | Trust Report: dom, sikkerhed, næste handling |
181
+ | `mjolnir --scope changed` | Kun det, din gren har tilføjet (CI-formen) |
182
+ | `mjolnir ci install` | Generér det rådgivende PR-workflow (action-baseret) |
183
+ | `mjolnir explain QA-CI-001` | Hvad, hvorfor og rettelse, plus den målte FP-rate |
184
+ | `mjolnir why src/a.spec.ts:42` | Hvorfor netop denne linje blev markeret. Blokerer aldrig. |
185
+ | `mjolnir forensics ./test-results/` | Kørselsevidens fra en rigtig kørsel |
186
+ | `mjolnir trust-report` | Selvstændigt Trust-artefakt (md + json) |
187
+ | `mjolnir handoff` | Udbedringsplan til en kodeagent |
188
+ | `mjolnir --json` / `--format sarif` | Maskinlæsbart output, GitHub Code Scanning |
189
+ | `mjolnir --format codequality` | GitLab Code Quality-rapport (MR-widget-artefakt) |
190
+ | `mjolnir --strict` | Kør også regler på quarantine-niveau (højere FP-risiko) |
129
191
 
130
192
  <details>
131
- <summary><strong>Lejlighedsvis / rapporter</strong></summary>
132
-
133
- | Kommando | Hvad den gør |
134
- | ------------------------------- | ------------------------------------------------------- |
135
- | `mjolnir fix --dry-run` / `fix` | Sikre autofixes med bevis |
136
- | `mjolnir baseline` / `diff` | Snapshot af fund, rapportér derefter kun nye/forværrede |
137
- | `mjolnir impact --since <ref>` | Hvad der ændrede sig siden et tidligere commit |
138
- | `mjolnir debt` | Testgældsregister med en kostmodel |
139
- | `mjolnir handover` | Onboarding-kort over suiten til ny QA |
140
- | `mjolnir stats` | Lokale all-time-tællere af sete fixes |
141
- | `mjolnir badge` | shields.io-endpoint-JSON + snippet |
142
- | `mjolnir rules --md` | Fuldt regelkatalog (JSON eller Markdown) |
143
- | `mjolnir doctor` | Selvundersøgelse af Mjölnirs egen regelbase |
144
- | `mjolnir create-rule <ID>` | Scaffold en ny regel + fixtures |
145
- | `mjolnir --format mermaid` | Testarkitekturdiagram til en PR-kommentar |
193
+ <summary><strong>Alle andre kommandoer</strong> — triage af ustabile tests, rapportering, governance</summary>
194
+
195
+ <br />
196
+
197
+ | Kommando | Hvad den gør |
198
+ | ----------------------------------- | ------------------------------------------------------------------------- |
199
+ | `mjolnir --classic` | Scorebanneret fra før Trust Report |
200
+ | `mjolnir explain verdict` | Hvorfor dommen for det gemte scan er, som den er |
201
+ | `mjolnir triage ./test-results/` | Guidet triage. Hver række ender med en næste handling. |
202
+ | `mjolnir pw-report ./test-results/` | Opsummering af Playwright-kørsel: retries, ustabile tests, de langsomste |
203
+ | `mjolnir doctor:playwright` | Dybdescan kun for Playwright plus Selector Health Score |
204
+ | `mjolnir fix --dry-run` / `fix` | Sikre autorettelser, hver scannet igen for at bevise, at den virkede |
205
+ | `mjolnir baseline` / `diff` | Gem et øjebliksbillede af fund, og rapportér derefter kun nye eller værre |
206
+ | `mjolnir impact --since <ref>` | Hvad et commit tilføjede og løste |
207
+ | `mjolnir summary` | CI-annoteringer og en step-opsummering ud fra en rapport |
208
+ | `mjolnir pr-comment` | En afgrænset PR-kommentar, som Markdown |
209
+ | `mjolnir debt` | Register over testgæld med en omkostningsmodel |
210
+ | `mjolnir handover` | Introduktionskort over suiten til en ny QA-ingeniør |
211
+ | `mjolnir init` | Find frameworks, udskriv en tjekliste til opsætning |
212
+ | `mjolnir suppressions` | List undertrykte fund, til governance |
213
+ | `mjolnir rules --unmeasured` | De regler, der kører på antagelser, ikke målinger |
214
+ | `mjolnir rules --md` | Fuldt regelkatalog (JSON eller Markdown) |
215
+ | `mjolnir doctor` | Selvrevision af Mjölnirs egen regelbase |
216
+ | `mjolnir create-rule <ID>` | Opret skelettet til en ny regel og dens fixtures |
217
+ | `mjolnir stats` | Lokale tællere over alle rettelser, der nogensinde er set |
218
+ | `mjolnir badge` | shields.io-endpoint-JSON og snippet |
219
+ | `mjolnir --cache` | Inkrementelle genscanninger via en lokal cache over domme |
220
+ | `mjolnir --format mermaid` | Diagram over testarkitekturen til en PR-kommentar |
221
+
222
+ `mjolnir help <command>` udskriver brug, eksempler og næste skridt for hver af dem.
146
223
 
147
224
  </details>
148
225
 
149
- Installér globalt i stedet for `npx`, hvis du foretrækker det:
150
- `npm i -g mjolnir-qa`. Kræver Node.js ≥ 22.18. Virker på Windows, macOS
151
- og Linux.
152
-
153
- ---
154
-
155
- ## 👥 Hvem er det til?
226
+ Kræver **Node.js ≥ 22.18** på Windows, macOS eller Linux. Foretrækker du en global installation? `npm i -g mjolnir-qa`. Minimumskravet kommer fra build-værktøjskæden (tsdown sigter mod den, og release-pipelinen røgtester mod den); kørselsafhængighederne kræver ikke mere end det.
156
227
 
157
- - **QA / SDET**, der ejer en e2e- eller integrationsuite og har brug
158
- for beviser for, at suiten faktisk fortjener den grønne check, den
159
- producerer.
160
- - **Platform-/DevEx-teams**, der har ansvaret for CI-integritet og
161
- release gates — folkene, for hvem en `continue-on-error` aldrig må
162
- male en rød pipeline grøn i stilhed.
163
- - **OSS-maintainere**, der vil have en billig, altid aktiveret
164
- verifikationsgate, der kører lokalt og i CI uden netværkskald.
228
+ <br />
165
229
 
166
- ---
230
+ ## Hvad Mjölnir finder
167
231
 
168
- ## 🔨 Hvad Mjölnir tjekker
232
+ <p align="center">
233
+ <img src="assets/readme/stack.svg" alt="Virker med din stack: de sprog, testframeworks og CI-systemer, som reglerne dækker, fra regelregistret." width="100%" />
234
+ </p>
169
235
 
170
- | | |
171
- | --- | ------------------------------------------------------------------------------------------------------------------- |
172
- | ⚖️ | **Værdighedsscore** — ét tal, transparent fradragstabel, ingen black box |
173
- | 🎭 | **Selector Health Score** — bedømmer dine Playwright-locators, ikke kun din pass rate |
174
- | 🔬 | **Runtime-forundersøgelse** — læser ægte Playwright/JUnit-kørselsdata og fanger `TRUE-FLAKE`, ikke kun statiske gæt |
175
- | 🚨 | **CI-integritetsregler** — fanger `continue-on-error`, `\|\| true` og andre falsk-grønne tricks |
176
- | 🐍 | **Alle fire Playwright-bindings** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG og CI-workflows |
177
- | 🔒 | **Local-first** — nul netværkskald under scanning, nul telemetri, kører på sekunder |
236
+ **79 regler** i fire familier — testhygiejne, testkvalitet, Playwright og CI-integritet — på tværs af TypeScript og JavaScript, Python, Java, C# og GitHub Actions-YAML. De dækker Playwright i alle fire bindings, plus pytest, JUnit, TestNG, NUnit, xUnit, MSTest, Jest, Vitest og Mocha, med startdækning for Cypress og Selenium. Ni af dem, så du kan se formen:
178
237
 
179
- ### Reglerne
238
+ | ID | Regel | Alvorlighed | Niveau |
239
+ | ------------ | ----------------------------------------------------------------- | ----------- | ---------- |
240
+ | QA-CI-001 | `continue-on-error` skjuler en fejlende verifikations-gate | error | quarantine |
241
+ | QA-CI-009 | Testens exitkode videregives ikke (`\|` uden pipefail, `;`-kæder) | error | extended |
242
+ | QA-TEST-001 | Fokuseret test committet (`.only`, `fit`) | error | quarantine |
243
+ | QA-TEST-003 | Test uden assertions | error | quarantine |
244
+ | QA-TQUAL-009 | Promise-assertion uden await | error | quarantine |
245
+ | QA-PW-002 | Locator-assertion uden await | error | core |
246
+ | QA-PW-004 | Skrøbelige CSS/XPath-selektorer | warning | quarantine |
247
+ | QA-PY-002 | Sprunget test over (`skip`, ikke-streng `xfail`) | warning | core |
248
+ | QA-CS-103 | Testmetode uden assertions | error | core |
180
249
 
181
- Hver regel leveres med både must-fire- **og** must-not-fire-fixtures.
182
- En regel, der udløses på sin egen negative fixture, kan ikke skibes —
183
- det er false-positive-firewallen.
250
+ Det fulde katalog genereres ud fra registret, aldrig vedligeholdt i hånden: `mjolnir rules --md`, [`docs/rules/`](docs/rules/) eller [guiden til, hvad det tjekker](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks).
184
251
 
185
252
  <details>
186
- <summary><strong>Testhygiejne</strong></summary>
187
-
188
- | ID | Regel | Severity |
189
- | ----------- | ---------------------------------------------------- | -------- |
190
- | QA-TEST-001 | Committet fokuseret test (`.only`, `fit`) | error |
191
- | QA-TEST-002 | Sprunget test uden begrundelse | error |
192
- | QA-TEST-002 | Sprunget test med registreret begrundelse | warning |
193
- | QA-TEST-003 | Test uden assertions | error |
194
- | QA-TEST-004 | Hårdt sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning |
195
- | QA-TEST-006 | Retry-misbrug, der skjuler flakiness | warning |
196
- | QA-TEST-010 | Tomt testlegeme | error |
253
+ <summary><strong>Alle regler nævnt i denne README</strong>, i én tabel</summary>
254
+
255
+ <br />
256
+
257
+ > `quarantine`-regler kører kun under `--strict` og blokerer aldrig (de er begrænset til info). Den viste alvorlighed er forfatterens.
258
+
259
+ | ID | Familie | Regel | Alvorlighed | Niveau |
260
+ | ------------ | ---------- | --------------------------------------------------------------------- | ----------- | ---------- |
261
+ | QA-TEST-001 | Hygiejne | Fokuseret test committet (`.only`, `fit`) | error | quarantine |
262
+ | QA-TEST-002 | Hygiejne | Sprunget test over. Eskalerer til `error` uden en sporet begrundelse. | warning | quarantine |
263
+ | QA-TEST-003 | Hygiejne | Test uden assertions | error | quarantine |
264
+ | QA-TEST-004 | Hygiejne | Fast sleep (`waitForTimeout`, `sleep()`, `delay()`) | warning | extended |
265
+ | QA-TEST-006 | Hygiejne | Misbrug af retries, der skjuler ustabilitet | warning | quarantine |
266
+ | QA-TEST-010 | Hygiejne | Tom testkrop | error | quarantine |
267
+ | QA-TQUAL-002 | Kvalitet | Tautologisk assertion | error | quarantine |
268
+ | QA-TQUAL-009 | Kvalitet | Promise-assertion uden await | error | quarantine |
269
+ | QA-TQUAL-011 | Kvalitet | Udkommenterede tests | warning | extended |
270
+ | QA-PW-002 | Playwright | Locator-assertion uden await | error | core |
271
+ | QA-PW-003 | Playwright | `page.pause()` / `test.only()` committet | error | core |
272
+ | QA-PW-004 | Playwright | Skrøbelige CSS/XPath-selektorer | warning | quarantine |
273
+ | QA-PW-123 | Playwright | Hardkodede miljø-URL'er | warning | quarantine |
274
+ | QA-PW-140 | Playwright | Skærmbillede uden `maxDiffPixelRatio` | warning | core |
275
+ | QA-CI-001 | CI | `continue-on-error` skjuler en fejlende gate | error | quarantine |
276
+ | QA-CI-002 | CI | `\|\| true` sluger exitkoder | error | extended |
277
+ | QA-CI-005 | CI | Rapport brugt, men aldrig genereret | error | quarantine |
278
+ | QA-CI-007 | CI | Retry-wrappers omkring tests | warning | extended |
279
+ | QA-CI-008 | CI | Step, der altid lykkes, skjuler fejl | error | quarantine |
280
+ | QA-CI-009 | CI | Exitkode videregives ikke (`\|` uden pipefail, `;`-kæder) | error | extended |
281
+ | QA-CI-010 | CI | Tests sprunget over, hvor de skal blokere | error | quarantine |
282
+ | QA-PY-002 | Python | Sprunget test over (`skip`, ikke-streng `xfail`) | warning | core |
283
+ | QA-PY-003 | Python | Testfunktion uden assertions | error | quarantine |
284
+ | QA-PY-005 | Python | `time.sleep()` i tests | warning | extended |
285
+ | QA-PY-012 | Python | Tautologisk assertion | error | quarantine |
286
+ | QA-JV-101 | Java | Deaktiveret test (`@Disabled`) | warning | core |
287
+ | QA-JV-102 | Java | Fast sleep (`Thread.sleep()`) | warning | extended |
288
+ | QA-JV-103 | Java | Testmetode uden assertions | error | extended |
289
+ | QA-JV-105 | Java | Fast sleep med Playwright `waitForTimeout()` | warning | core |
290
+ | QA-JV-106 | Java | Skrøbelig selektor i stedet for rollebaseret locator | warning | quarantine |
291
+ | QA-CS-101 | C# | Sprunget test over (`[Ignore]`, `[Fact(Skip=)]`) | warning | core |
292
+ | QA-CS-102 | C# | Fast sleep (`Thread.Sleep` / `Task.Delay`) | warning | core |
293
+ | QA-CS-103 | C# | Testmetode uden assertions | error | core |
294
+ | QA-CS-105 | C# | Fast sleep med `WaitForTimeoutAsync()` | warning | extended |
295
+ | QA-CS-106 | C# | Skrøbelig selektor i stedet for rollebaseret locator | warning | quarantine |
296
+
297
+ Python har desuden QA-PY-001…012 (pytest-hygiejne) og QA-PY-101…108 (Playwright til Python). Cypress og Selenium har startsæt på tre regler hver.
197
298
 
198
299
  </details>
199
300
 
200
- <details>
201
- <summary><strong>Testkvalitet</strong></summary>
301
+ Hver regel leveres med en must-fire- **og** en must-not-fire-fixture, og en regel, der udløses på sin egen negative fixture, kan ikke leveres. Det er firewallen mod falske positiver; `mjolnir doctor` håndhæver den i dette repositories egen CI.
202
302
 
203
- | ID | Regel | Severity |
204
- | ------------ | ------------------------------- | -------- |
205
- | QA-TQUAL-002 | Tautologisk assertion | error |
206
- | QA-TQUAL-009 | Assertion på promise uden await | error |
207
- | QA-TQUAL-011 | Udkommenterede tests | warning |
303
+ ### Selector Health Score
208
304
 
209
- </details>
305
+ `mjolnir doctor:playwright` bedømmer hver locator efter, hvordan den finder et element: som en bruger ville (rolle, label, tekst), via en eksplicit kontrakt (`data-testid`) eller ved et strukturelt tilfælde (CSS-kæder, XPath). Hver fil får en score fra 0 til 100:
210
306
 
211
- <details>
212
- <summary><strong>Playwright 🎭</strong></summary>
307
+ ```text
308
+ ▍ SELECTOR HEALTH
213
309
 
214
- | ID | Regel | Severity |
215
- | --------- | ---------------------------------------- | -------- |
216
- | QA-PW-002 | Locator-assertion uden await | error |
217
- | QA-PW-003 | `page.pause()` / `test.only()` committet | error |
218
- | QA-PW-004 | Skrøbelige CSS/XPath-selectors | warning |
219
- | QA-PW-123 | Hårdkodede miljø-URL'er | warning |
310
+ e2e/login.spec.ts
311
+ [█████████████░░░░░░░] 65 / 100
312
+ role/text: 1 · testid: 0 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
220
313
 
221
- </details>
314
+ e2e/checkout.spec.ts
315
+ [█████████████████░░░] 86 / 100
316
+ role/text: 3 · testid: 1 · plain-css: 0 · css-chains: 1 ⚠ · xpath: 0
317
+ ```
222
318
 
223
- <details>
224
- <summary><strong>CI-integritet</strong></summary>
225
-
226
- | ID | Regel | Severity |
227
- | --------- | ----------------------------------------------------------------- | -------- |
228
- | QA-CI-001 | `continue-on-error` maskerer fejl | error |
229
- | QA-CI-002 | `\|\| true` synker exit-koder | error |
230
- | QA-CI-005 | Rapport forbruges, men genereres aldrig | error |
231
- | QA-CI-007 | Retry-wrappers omkring tests | warning |
232
- | QA-CI-008 | Altid-succesfuldt step maskerer fejl | error |
233
- | QA-CI-009 | Testens exit-kode propageres ikke (`\|` uden pipefail, `;`-kæder) | error |
234
- | QA-CI-010 | Tests sprunget over, hvor de skal blokere (skip-on-PR-guards) | error |
319
+ Det måler **robusthed, ikke korrekthed**. `.btn.btn-primary > div:nth-child(2)` består i dag og bliver ved med at bestå, indtil nogen rører ved markuppen. En lav score påstår aldrig, at testen er i stykker, kun at den afhænger af markup, som ingen har lovet at bevare.
235
320
 
236
- </details>
321
+ <br />
237
322
 
238
- <details>
239
- <summary><strong>Python / pytest 🐍</strong></summary>
323
+ ## Worthiness-scoren
240
324
 
241
- | ID | Regel | Severity |
242
- | --------- | ------------------------------------------- | -------- |
243
- | QA-PY-002 | Sprunget test (`skip`, ikke-strikt `xfail`) | warning |
244
- | QA-PY-003 | Testfunktion uden assertions | error |
245
- | QA-PY-005 | `time.sleep()` i tests | warning |
246
- | QA-PY-012 | Tautologisk assertion | error |
325
+ <p align="center">
326
+ <img src="assets/readme/score-gauge.svg" alt="Worthiness-skalaen fra 0 til 100, med en markør, der gennemløber hver score: UNWORTHY under 50, NEEDS WORK fra 50 til 79, WORTHY fra 80 til 99, FORGED ved 100" width="720" />
327
+ </p>
247
328
 
248
- 20 Python-regler i alt (QA-PY-001…012 pytest-hygiejne + QA-PY-101…108 Playwright-Python).
329
+ <sub>Hver score fra 0 til 100, placeret af den rigtige `deriveScoreState`. Genereret af `npm run docs:gauge` og låst mod afvigelser i CI.</sub>
249
330
 
250
- </details>
331
+ | Score | Dom |
332
+ | --------- | ------------------------------------------- |
333
+ | `0 – 49` | **UNWORTHY** |
334
+ | `50 – 79` | **NEEDS WORK** |
335
+ | `80 – 99` | **WORTHY** |
336
+ | `100` | **FORGED** |
337
+ | `null` | **UNKNOWN**: ingen testdeklarationer fundet |
251
338
 
252
- <details>
253
- <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
339
+ **Sådan beregnes den.** Alvorligheden fastsætter et grundfradrag (`error −8`, `warning −3`, `info −1`), og evidensniveauet nedskriver det: E2 tæller fuldt, E1 halvt (rundet ned), E0 slet ikke. Summen normaliseres efter suitens eksponering, altså fradrag pr. testdeklaration i stedet for pr. fil. Terminalen udskriver de samme nedskrevne tal, som scoren brugte; der er ingen skjult anden model. Detaljer: [docs/SCORING.md](docs/SCORING.md) og [scoringsguiden](https://sergey-bar.github.io/Mjolnir/guide/scoring).
254
340
 
255
- | ID | Regel | Severity |
256
- | --------- | -------------------------------------------- | -------- |
257
- | QA-JV-101 | Deaktiveret test (`@Disabled`) | warning |
258
- | QA-JV-102 | Hårdt sleep (`Thread.sleep()`) | warning |
259
- | QA-JV-103 | Testmetode uden assertions | error |
260
- | QA-JV-105 | Playwright hårdt sleep `waitForTimeout()` | warning |
261
- | QA-JV-106 | Skrøbelig selector i stedet for role-locator | warning |
341
+ **Hvad 100 ikke betyder.** Det betyder ikke, at softwaren er korrekt, at suiten er tilstrækkelig, eller at produktet er fejlfrit. Det betyder én ting: **ingen af Mjölnirs evaluerede regler gav et fradrag under dette scan og denne evidensmodel.**
262
342
 
263
- </details>
343
+ <br />
264
344
 
265
- <details>
266
- <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
345
+ ## Evidensmodellen
267
346
 
268
- | ID | Regel | Severity |
269
- | --------- | -------------------------------------------- | -------- |
270
- | QA-CS-101 | Sprunget test (`[Ignore]`, `[Fact(Skip=)]`) | warning |
271
- | QA-CS-102 | Hårdt sleep (`Thread.Sleep` / `Task.Delay`) | warning |
272
- | QA-CS-103 | Testmetode uden assertions | error |
273
- | QA-CS-105 | Hårdt sleep `WaitForTimeoutAsync()` | warning |
274
- | QA-CS-106 | Skrøbelig selector i stedet for role-locator | warning |
347
+ Hvert fund har to etiketter: hvor sikker Mjölnir er, og hvor langt fundet er blevet tjekket. Det er forskellen på et værktøj, der rapporterer mønstre, og et værktøj, du kan lade en release afhænge af.
275
348
 
276
- </details>
349
+ **Hvor sikkert — evidensniveauet.**
350
+
351
+ | Niveau | Navn | Betyder | Fradrag |
352
+ | ------ | -------------------- | -------------------------------------------------------- | ------- |
353
+ | **E2** | Deterministisk bevis | Defekten findes i koden, som den er skrevet | Fuldt |
354
+ | **E1** | Mønsterevidens | Et mønster, der er stærkt knyttet til defekten, matchede | Halvt |
355
+ | **E0** | Observation | Værd at vide. Ikke en påstand om, at noget er galt. | Nul |
277
356
 
278
- > Det fulde, levende katalog — hver regel med tier, confidence,
279
- > false-positive-risiko og autofix-tilgængelighed — genereres fra
280
- > registreren:
281
- >
282
- > ```bash
283
- > mjolnir rules --md
284
- > ```
285
- >
286
- > Sider pr. regel ligger under [`docs/rules/`](docs/rules/).
287
-
288
- ### Hvor meget er målt
289
-
290
- **78 af 99 regler bærer en false-positive-rate målt mod rigtig OSS-kode**
291
- (≥ 10 håndklassificerede fund hver; se
292
- [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). De andre 21 skiber på forfatterens
293
- estimat. Hver scan-fodnote fortæller, hvor mange af de _udløste_ regler,
294
- der er målt; `mjolnir rules --unmeasured` lister de uregistrerede; hver
295
- regels `mjolnir explain`-side angiver dens status. Vi offentliggør
296
- karantæne for det. At få det tal til at vokse er projektets fortsatte
297
- arbejde.
298
-
299
- ### Regel-tiers og sproglig modenhed
300
-
301
- Hver regel er `core`, `extended` eller `quarantine`, tildelt ud fra sin
302
- **målte** false-positive-rate:
303
-
304
- | Tier | Betydning | Standardscan | `--strict` |
305
- | ------------ | ----------------------------------------- | :----------: | :--------: |
306
- | `core` | ≤ 10 % målt FP | ✅ | ✅ |
307
- | `extended` | ≤ 30 % målt FP | ✅ | ✅ |
308
- | `quarantine` | over 30 %, eller endnu ikke målt (n < 10) | ❌ | ✅ |
309
-
310
- | Sprog | Adapter | Dækning i dag |
311
- | --------------- | ------------ | ------------------------------------------------ |
312
- | TypeScript / JS | Compiler-AST | bredeste, mest målte — mest `core`/`extended` |
313
- | Python / pytest | Regex-lag | bredt, corpus-auditeret — mest `core`/`extended` |
314
- | Java | Regex-lag | nyere — mest `extended`/`quarantine` |
315
- | C# / .NET | Regex-lag | nyere — mest `extended`/`quarantine` |
316
-
317
- TypeScript og Python har den bredeste målte dækning. Java og C# er
318
- skibet, dokumenteret og holdes uden for overskriftstallet, indtil en
319
- rigtig forbrugersuite (ikke et binding-biblioteks egne tests) er blevet
320
- auditeret.
321
-
322
- ---
323
-
324
- ## Sådan fungerer scoren
357
+ Sikkerhed i en detektion er ikke styrken af beviset. En regel kan være sikker på, at den fandt det, den ledte efter, og stadig kigge på en heuristik. E1-fund er der for at blive læst og vurderet, aldrig anvendt i blinde, og den grænse står på fundet i terminalen, i JSON'en og i overdragelsen til agenten.
358
+
359
+ **Hvor langt det er tjekket — tillidsniveauet.** De fleste fund kommer fra at læse din kode. Giv Mjölnir rapporten fra en rigtig testkørsel, så kan det bekræfte, at koden faktisk kørte.
325
360
 
326
361
  <p align="center">
327
- <img src="assets/readme/terminal-hero.svg" alt="Mjölnir-terminaloutput — WORTHINESS 75/100 NEEDS WORK, en opdeling af diagnostik efter kategori og en FIX THIS FIRST-liste" width="820" />
362
+ <img src="assets/readme/trust-ladder.svg" alt="Tillidsstigen fra L0 til L5. L0 til L2 kommer fra at læse koden; L3 til L5 kræver en rigtig kørselsrapport, markeret med et brud i stigen." width="100%" />
328
363
  </p>
329
364
 
330
- <sub>Regenereres med `npm run docs:hero`;
331
- [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
332
- får CI til at fejle, hvis artefaktet afviger fra, hvad reporteren
333
- faktisk printer.</sub>
365
+ | Niveau | Med almindelige ord | Hvad det kræver |
366
+ | ------ | ------------------- | ---------------------------------------------------- |
367
+ | **L0** | Noteret | At læse koden |
368
+ | **L1** | Ligner problemet | At læse koden: et mønster matchede |
369
+ | **L2** | Bevist i koden | At læse koden: defekten er strukturel |
370
+ | **L3** | Filen kørte | En kørselsrapport viser, at fundets fil blev udført |
371
+ | **L4** | Testen kørte | En kørselsrapport viser, at fundets test blev udført |
372
+ | **L5** | Kørslen er enig | Kørslens eget resultat bekræfter defektklassen |
334
373
 
335
- Scoren er transparent: **error −8, warning −3, info −1**, derefter
336
- normaliseret efter suitens eksponering (fradrag pr. testdeklaration).
337
- Bevisvægtede fradrag betyder, at svage signaler koster mindre.
338
- Terminalen viser de samme diskonterede tal, som scoren bruger — ingen
339
- black box. Fuld metode: [docs/SCORING.md](docs/SCORING.md).
374
+ Et statisk scan stopper ved L2. Kun en rigtig kørselsrapport (Playwright JSON, Jest eller Vitest JSON, JUnit XML) kan løfte et fund til L3 eller højere, så et fund, der aldrig er set køre, aldrig kan påstå, at det gjorde. Definitioner: [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md).
340
375
 
341
- **Domme**
376
+ ### Hvor meget af dette er målt
342
377
 
343
- | Score | Domme |
344
- | ------- | ---------------- |
345
- | ≥ 80 | ✓ **WORTHY** |
346
- | 50 – 79 | ⚠ **NEEDS WORK** |
347
- | < 50 | ✖ **UNWORTHY** |
378
+ **74 af 79 regler har en falsk-positiv-rate målt mod rigtig OSS-kode** (mindst 10 håndklassificerede fund hver; se [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). De øvrige 5 bygger på forfatterens skøn og siger det, regel for regel, i `mjolnir explain`. `mjolnir rules --unmeasured` lister dem, og hver scanfod angiver, hvor mange af de regler, der faktisk _blev udløst_, der er målt.
348
379
 
349
- **Bevisniveauer** — hvert fund bærer ét; det sætter fundets vægt i
350
- scoren:
380
+ Raterne forbliver offentlige, også når de er dårlige. QA-TEST-001 (en committet `.only`) klarer sig dårligt i revisionen på rigtige repositorier og sidder derfor i quarantine. Det aktuelle tal for hver regel, inklusive QA-PW-141, står i revisionen.
351
381
 
352
- | Niveau | Betydning | Score-effekt | Eksempel |
353
- | ------ | --------------------- | -------------- | -------------------------------------------------- |
354
- | E2 | Deterministisk defekt | Fuldt fradrag | Committet `.only` — strukturelt beviseligt |
355
- | E1 | Heuristisk mønster | Halvt fradrag | Regex-fundet `sleep()` — stærkt signal, ikke bevis |
356
- | E0 | Iagttagelse | Nul (kun info) | Rapporteret, men gater aldrig CI eller trækker fra |
382
+ ### Tillidsniveauer
357
383
 
358
- De fleste regler er **E1**. Slagordet „we prove it" henviser til dette
359
- system: E2-fund er strukturelt bevis; E1-fund er korrekt positionerede
360
- advarsler, ikke formelle beviser.
384
+ Niveauerne følger den målte falsk-positiv-rate, ikke holdninger:
361
385
 
362
- Et tomt repo scorer `null`, aldrig en falsk 100 — se
363
- [Tillidsmodellen](#tillidsmodellen).
386
+ | Niveau | Målt FP | Adfærd |
387
+ | -------------- | ------------------------------ | --------------------------------------------------- |
388
+ | **core** | ≤ 10% | Standardrapport, blokerer |
389
+ | **extended** | ≤ 30% | Standardrapport, lavere sikkerhed |
390
+ | **quarantine** | > 30% eller eksplicit erklæret | Kun `--strict`, begrænset til info, blokerer aldrig |
391
+ | _ikke målt_ | n < 10 | Kan ikke forfremmes til core, før den er målt |
364
392
 
365
- ---
393
+ FP-bånd kan kun degradere et niveau — de forfremmer aldrig en regel ud af `quarantine`, hvis den var eksplicit erklæret der. En eksplicit karantænesat regel forbliver i quarantine uanset dens målte FP-rate.
366
394
 
367
- ## 🎭 Selector Health Score
395
+ Forfremmelse, degradering og modenhed pr. sprog: [reglernes livscyklus](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
368
396
 
369
- Hovedmetrikken for Playwright-suiter — hvor robuste dine locators er:
397
+ ### Hvorfor det ikke er en linter
370
398
 
371
- ```text
372
- ▚ SELECTOR HEALTH — e2e/checkout.spec.ts
399
+ Linters fortæller dig, om koden følger regler. Mjölnir fortæller dig, om din verifikation er til at stole på.
373
400
 
374
- [█████████████████░░░] 83 / 100
375
- role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
376
- ```
401
+ | | Linters (ESLint, SonarQube) | Coverage-værktøjer | AI-kodereview | **Mjölnir** |
402
+ | ------------------------------------------------------------- | :-------------------------: | :----------------: | :-----------: | :--------------: |
403
+ | Scorer **verifikationssystemet**, ikke produktkoden | Nej | Nej | Nej | Ja |
404
+ | CI-workflowintegritet (`continue-on-error`, `\|\| true`) | Nej | Nej | kun diffen | Ja |
405
+ | Bedømmer robustheden af Playwright-locators (Selector Health) | Nej | Nej | Nej | Ja |
406
+ | Læser rigtige kørselsdata til `TRUE-FLAKE`-domme | Nej | Nej | Nej | Ja |
407
+ | Offentliggør en målt falsk-positiv-rate pr. regel | Nej | Nej | Nej | Ja |
408
+ | Markerer tests uden assertions | Ja\* | Nej | sommetider | Ja |
409
+ | Fanger faste sleeps (`waitForTimeout`, `time.sleep`) | Ja\* | Nej | sommetider | Ja |
410
+ | Deterministisk (samme input, samme output) | Ja | Ja | Nej | Ja |
411
+ | Pris pr. scan | gratis | gratis | tokens | **nul** (lokalt) |
377
412
 
378
- Rollebaserede locators får fuld score. CSS-klassekæder og XPath synker
379
- scoren — de brækker ved enhver DOM-refaktor uden at fortælle dig,
380
- hvilken adfærd der er regresseret.
413
+ <sub>\*Dækket af `eslint-plugin-jest` og `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`) og af SonarQubes egne assertion-regler. Kolonnerne beskriver standardadfærden for verifikation af testsuiter; plugins, betalte planer og egne regler ændrer nogle svar. Dette er en positioneringsoversigt, ikke en benchmark.</sub>
381
414
 
382
- ---
415
+ Brug også AI-review. Det fanger nuancer, hensigt og designfejl, som intet mønster kan finde. Mjölnir fanger det, AI-review overser, fordi det ser tilsigtet ud: en committet `.only`, en slugt exitkode, en `continue-on-error` på et testjob. Det kræver scanning, ikke ræsonnement.
383
416
 
384
- ## 🔬 Runtime-beviser
417
+ <br />
385
418
 
386
- Statisk flakiness-detektion er gætteri. Mjölnir læser **ægte
387
- eksekveringsdata** — Playwright JSON-rapporter og JUnit-XML fra enhver
388
- runner:
419
+ ## Kørselsanalyse
420
+
421
+ Statisk analyse ræsonnerer om kode, der aldrig har kørt. Kørselsanalysen læser, hvad der faktisk skete: Playwright JSON, Jest JSON, Vitest JSON og JUnit XML fra enhver runner.
389
422
 
390
423
  ```bash
391
424
  mjolnir forensics ./test-results/
392
425
  ```
393
426
 
394
427
  ```text
395
- ▚ FLAKINESS LEADERBOARD
428
+ ▍ FLAKINESS LEADERBOARD
396
429
 
397
430
  3 tests · 1 failed · 1 flaky · 1 retried
398
431
 
@@ -402,293 +435,184 @@ FAILING declines an expired card (e2e/checkout.spec.ts)
402
435
  ████░░░░░░░░░░░░░░░░ 1.1s · 1 attempt
403
436
  ```
404
437
 
405
- En test, der kun består fra forsøg ≥ 2, er ikke en bestået test — det
406
- er en heldig test. Den markeres `TRUE-FLAKE` uanset den endelige grønne
407
- check.
438
+ `TRUE-FLAKE` betyder ikke, at testen blev prøvet igen. Det betyder, at testen **fejlede mindst ét forsøg og derefter endte grønt**: et heldigt bestået, markeret uanset hvad det endelige flueben siger. `mjolnir triage` gør den historik til et karantæneforslag, og `mjolnir pw-report` opsummerer en kørsel. Det er de samme kørselsrapporter, der løfter fund til tillidsniveau L3 og derover.
408
439
 
409
- ---
440
+ <br />
410
441
 
411
- ## ⚡ Mjölnir er ikke endnu en linter
442
+ ## CI-integritet
412
443
 
413
- Lintere fortæller dig, om koden følger regler. Mjölnir fortæller dig,
414
- om din verifikation kan stoles på.
444
+ En test kan bestå, mens pipelinen omkring den ikke kan fejle. Mjölnir læser også workflowene: `continue-on-error`, `|| true`, exitkoder, der aldrig videregives, steps, der altid lykkes, rapporter, der bruges, men aldrig genereres, og gates, der springes over ved netop de hændelser, der burde blokere. Hvert fund angiver job, step og linje og har sit eget evidensniveau.
415
445
 
416
- | | ESLint / SonarQube | Coverage-værktøjer | Manuelt review | **Mjölnir** |
417
- | --------------------------------------------------------- | :----------------: | :----------------: | :------------: | :---------: |
418
- | CI-workflow-integritet (`continue-on-error`, `\|\| true`) | ❌ | ❌ | sjældent | ✅ |
419
- | På tværs af sprog (TS, Python, Java, C#) fra ét værktøj | ❌ | ❌ | ❌ | ✅ |
420
- | Bedømmer Playwright-locators robusthed (Selector Health) | ❌ | ❌ | sjældent | ✅ |
421
- | Markerer tests uden rigtige assertions | ✅ (plugin)\* | ❌ | nogle gange | ✅ |
422
- | Fanger hårde sleeps (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | nogle gange | ✅ |
423
- | Kører på sekunder, nul netværkskald under scanning | ✅ | ✅ | — | ✅ |
446
+ Generér PR-workflowet, rådgivende som standard:
424
447
 
425
- \*`eslint-plugin-jest` (`expect-expect`) og `eslint-plugin-playwright`
426
- (`expect-expect`, `no-wait-for-timeout`) dækker dette for deres
427
- respektive frameworks.
448
+ ```bash
449
+ mjolnir ci install
450
+ ```
428
451
 
429
- **Runtime-analyse** er en separat kategori ud over statisk lintning:
452
+ Eller tilføj Marketplace-action'en til et workflow, du allerede har:
430
453
 
431
- | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
432
- | ------------------------------------------------ | :-----------------------: | :-------------------: | :-------------------: |
433
- | Læser ægte kørselsdata til `TRUE-FLAKE`-domme | delvist\* | delvist (tag) | ✅ |
434
- | Flaky-triage-rapport fra eksekveringshistorikken | ❌ | ✅ | ✅ |
435
- | Integrerer med den statiske værdighedsscore | ❌ | ❌ | ✅ |
454
+ ```yaml
455
+ - uses: Sergey-Bar/Mjolnir@v1
456
+ with:
457
+ scope: changed
458
+ fail-on: error
459
+ ```
436
460
 
437
- \*Playwright sporer retries internt, men producerer ikke en selvstændig
438
- flakiness-rapport med domme-etiketter.
461
+ Fastlås `@v1` for at følge major-linjen, eller et præcist tag (`@v0.5.32`) for en reproducerbar gate. [docs/DISTRIBUTION-KIT.md](docs/DISTRIBUTION-KIT.md) dækker Marketplace, Smithery og MCP-registrene.
439
462
 
440
- ---
463
+ Upload SARIF for at få fund ind i GitHub Code Scanning (kræver `security-events: write` på workflow- eller job-niveau):
441
464
 
442
- ## 🤖 Hvorfor ikke bare bruge AI-kodereview?
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
+ ```
443
473
 
444
- Andet problem, andet lag. AI-review kan spotte en mistænkelig
445
- testændring i en diff; det beviser ikke, at verifikationssystemet som
446
- helhed er troværdigt — og det ser kun den diff, du viser det.
474
+ På GitLab skriver `--format codequality` den Code Quality-rapport, som MR-widgetten og diff-annoteringerne læser ([docs/GITLAB-CI.md](docs/GITLAB-CI.md)). Opsætning af editor og pipeline: [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
447
475
 
448
- | | AI-kodereview (Copilot m.fl.) | **Mjölnir** |
449
- | ------------------------------------------- | :-----------------------------------------: | :--------------------------------: |
450
- | Omkostning pr. scan | Tokens (skalerer med diff-størrelse) | **Nul** (lokal, installeret) |
451
- | Ser hele suiten + alle CI-konfigs | Kun den PR-diff, du viser | **Alt, hver gang** |
452
- | Deterministisk (samme input → samme output) | ❌ (ikke-deterministisk) | **✅** |
453
- | Fanger mønstre, der har sovet i måneder | Kun hvis det er i konteksten | **✅** (scanner alle filer) |
454
- | Husker fund mellem kørsler | ❌ (ingen hukommelse på tværs af sessioner) | **✅** (baseline + diff) |
455
- | Kører uden menneskelig udløser | Kræver en PR eller prompt | **✅** (CI-hook, kører i sekunder) |
476
+ ### Tilskrivning i det ændrede scope
456
477
 
457
- **Brug begge.** AI fanger nuance, intention og designfejl, ingen regex
458
- kan finde. Mjölnir fanger de strukturelle mønstre, AI overser, fordi de
459
- ser „intentionelle" ud — et committet `.only`, en opslugt exit-kode,
460
- en `continue-on-error` på et testjob. Det er ikke bugs, der kræver
461
- resonnement; det er fakta, der kræver scanning.
478
+ ```bash
479
+ npx mjolnir-qa@latest --scope changed
480
+ ```
462
481
 
463
- ---
482
+ Fund tilskrives de linjer, din gren har tilføjet, målt mod **merge-base**. Scopet er det samme filsæt, som et fuldt scan finder (TS/JS-specs og adapterkonfigurationer, `test_*.py`, `*Test.java`, `*Tests.cs`, `.github/workflows/*.yml`), plus ucommittede og usporede ændringer, så det virker, før du committer. Basen findes i rækkefølgen `main → master → origin/main → origin/master → origin/HEAD`; tilsidesæt den med `--base <ref>`.
464
483
 
465
- ## 🤖 CI-integration
484
+ Når merge-base ikke kan findes (en shallow clone, et detached HEAD, et mål uden for git), falder fundene tilbage til tilskrivning til hele filen, **og rapporten siger det.** En tavs fallback ville være præcis den slags defekt, dette værktøj findes for at fange.
466
485
 
467
- Én kommando genererer en PR-workflow — vejledende som standard, aldrig
468
- blokerende:
486
+ <br />
469
487
 
470
- ```bash
471
- mjolnir ci install
472
- ```
488
+ ## AI-agenter
473
489
 
474
- Eller kobl den native ind i GitHub Code Scanning via SARIF:
490
+ Fund er kun noget værd, hvis noget handler på dem.
475
491
 
476
- ```yaml
477
- - run: npx mjolnir-qa@latest --format sarif > mjolnir.sarif
478
- - uses: github/codeql-action/upload-sarif@v3
479
- with:
480
- sarif_file: mjolnir.sarif
492
+ ```text
493
+ SCAN → EVIDENCE → HANDOFF → AGENT → RE-SCAN → PROOF
481
494
  ```
482
495
 
483
- Editor- og pipeline-opsætning til SARIF:
484
- [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md).
485
-
486
- ### Changed-scope-dækning
487
-
488
- `--scope changed` tilskriver fund de linjer, din branch tilføjede i
489
- forhold til merge-base med `main`. Den dækker testfiler (`*.spec.*`,
490
- `*.test.*`) plus GitHub-workflowfiler og Playwright-konfigurationer i
491
- diffen. Når merge-base ikke kan resolve — shallow clone, detached HEAD,
492
- ikke-git-mål, anden default-branch — degraderer den ærligt: fund
493
- falder tilbage til hel-fil-attribuering, og rapporten siger det.
494
- Overskriv base-ref'en med `--base <ref>`.
496
+ **AI skriver rettelsen. Mjölnir verificerer den.** Beviset kommer fra det nye scan, aldrig fra agentens egen melding om succes.
495
497
 
496
- ---
498
+ | Kommando | Hvad agenten får |
499
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
500
+ | `mjolnir mcp` | En [MCP](https://modelcontextprotocol.io)-server over stdio. `scan`, `explain` og `diff` bliver til kaldbare værktøjer. |
501
+ | `mjolnir handoff` | En gemt `--json`-rapport bliver til en deterministisk Markdown-plan: hvad der blev fundet, evidensgrænsen for hvert fund, hvad der **ikke** må ændres, og hvordan det verificeres. |
502
+ | `mjolnir install` | Skriver ind i de agentflader, dit repo allerede har (`.claude/`, `.cursor/`, `.kilo/`, `AGENTS.md`), så agenten scanner igen, før den påstår, at den er færdig. |
497
503
 
498
- ## Konfiguration
504
+ Tilføj det til en klient med sin egen CLI:
499
505
 
500
- Mjölnir er zero-config. En valgfri `mjolnir.config.json` (eller
501
- `.mjolnir.json`) i roden af repoet finjusterer severity, gating og
502
- scope — den ændrer aldrig detektionssemantikken.
506
+ ```bash
507
+ claude mcp add mjolnir -- npx -y mjolnir-qa@latest mcp
508
+ ```
503
509
 
504
- | Key | Type | Effekt |
505
- | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
506
- | `exclude` | `string[]` | Ekstra ignore-globs (gitignore-undersæt), oven på de indbyggede defaults |
507
- | `gate` | `"advisory" \| "error" \| "warning"` | Hvilke severities der afslutter med ikke-nul (default `error`; `advisory` blokerer aldrig) |
508
- | `severityOverrides` | `{ "<RULE-ID>": severity }` | Omrangordner en regels fund for dit repo |
509
- | `ignore` | `IgnoreEntry[]` | Undertrykker fund — **`reason` er påkrævet**; indgange udløber efter 90 dage (en eksplicit `expires`-dato, eller config-filens last-modified-tid for indgange uden) |
510
- | `plugins` | `string[]` | Regelpakker fra tredjepart (se [Tillidsmodellen](#tillidsmodellen)) |
510
+ Eller til enhver klient, der tager en `mcpServers`-blok:
511
511
 
512
512
  ```json
513
513
  {
514
- "gate": "error",
515
- "exclude": ["legacy/**"],
516
- "severityOverrides": { "QA-PW-141": "warning" },
517
- "ignore": [
518
- {
519
- "ruleId": "QA-TEST-004",
520
- "files": ["e2e/legacy-login.spec.ts"],
521
- "reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
522
- "expires": "2026-12-31"
523
- }
524
- ]
514
+ "mcpServers": {
515
+ "mjolnir": { "command": "npx", "args": ["-y", "mjolnir-qa@latest", "mcp"] }
516
+ }
525
517
  }
526
518
  ```
527
519
 
528
- - **`.mjolnirignore`** — en enkel gitignore-agtig fil til
529
- sti-udelukkelser, samme dialekt som `exclude`. Brug den til
530
- maskinspecifikt støj; brug `exclude`, når listen hører til i
531
- versionsstyring sammen med resten af konfigurationen.
532
- - **CLI-overrides** — `--strict` (inkluder karantæneregler),
533
- `--width <cols>` og `--ascii` / `--no-ascii` (terminalrendering),
534
- `--tone blunt` (knugere beskeder), `--max-duration <sec>` (begrænset
535
- delvis scanning).
536
- - Regelundertrykkelse og deprecation-levetid:
537
- [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
538
-
539
- `ignore`-indgange driver også den selvstændige kommando
540
- `mjolnir suppressions`, som lister, hvad der i øjeblikket er
541
- undertrykket, og hvornår hver indgang udløber.
542
-
543
- ---
544
-
545
- ## 📐 Exit-koder & kontrakter
546
-
547
- Frosne — trygge at bygge CI-logik på:
548
-
549
- | Exit-kode | Betydning |
550
- | --------- | -------------------------------------------------------------------- |
551
- | `0` | Rent — ingen fund på eller over gaten |
552
- | `1` | Fund på eller over gaten |
553
- | `2` | Delvis scanning (tidsbudget ramt, ulæselige filer) — blokerer aldrig |
554
- | `10` | Brugsfejl (ugyldigt flag, manglende mål) |
555
- | `20` | Intern fejl |
556
-
557
- JSON/SARIF-rapporten er `schemaVersion: 1`. Regel-ID'er
558
- (`QA-<FAMILY>-NNN`) er uforanderlige, når de først er skibet, og
559
- genbruges aldrig.
560
-
561
- ---
562
-
563
- ## Tillidsmodellen
564
-
565
- - **Local-first** — nul netværkskald under scanning. Ever. Nul
566
- telemetri.
567
- - **Ingen falsk bevis** — vi siger hellere „ukendt" end „verificeret".
568
- Et tomt repo får `score: null`, aldrig en falsk 100.
569
- - **Delvis ærlighed** — hvis analysen blev afkortet, siger outputtet
570
- det. Aldrig „complete", når det ikke er.
571
- - **FP-firewall** — detektion kører på et kommentar-/string-frit view
572
- af koden (TypeScript-regler bruger compiler-AST): et mønster inde i
573
- en prosakommentar eller en doc-eksempelstreng er dokumentation, ikke
574
- et fund.
575
- - **Målt, ikke påstået** — kun regler med en false-positive-rate fra
576
- rigtig OSS-kode skiber i overskriftstierne (se
577
- [Hvor meget er målt](#hvor-meget-er-målt)); scan-fodnoten og
578
- `mjolnir rules --unmeasured` fortæller dig, hvilke der er hvad.
579
- - **Plugin-tillid og kørselsport** — plugins er npm-pakker deklareret under
580
- `"plugins"`; JS-moduler bor i `mjolnir-rules/*.mjs`.
581
- Der er **ingen sandbox**: plugin-kode kører med fulde
582
- Node-privilegier, samme tillidsmodel som ESLint- eller
583
- Vitest-plugins. Derfor er kodekørsel **opt-in ved hver scan**: giv
584
- `--enable-plugins` (eller sæt `MJOLNIR_ENABLE_PLUGINS=1`), ellers
585
- indlæses kilderne IKKE — en højlydt stderr-meddelelse lister
586
- præcis, hvad der blev sprunget over. At skanne utroværdig kode
587
- udfører den aldrig. JSON-regelmanifester (`mjolnir-rules/*.json`)
588
- berøres ikke: de deklarerer regex-mønstre og udfører ingen kode af
589
- konstruktion. Core regel-ID-præfikser er reserverede og afvises
590
- fra plugins og eksterne regler mod spoofing.
591
- - **Workspace-lokale eksterne regler** (mappebaserede, nul netværk) —
592
- en `mjolnir-rules/`-mappe ved siden af scan-målet loader brugerdefinerede
593
- regler: JSON-filer deklarerer regex-mønstre (ingen kode eksekveres),
594
- `.mjs`/`.js`-moduler eksporterer `rules` (fuld Node-tillid, som
595
- plugins). Eksterne regler bærer samme trust-metadata som core; de kan
596
- aldrig skibe i core-tieren (core kræver en målt FP-rate fra
597
- corpus-sidecaren — en deklareret `tier: "core"` klemmes til
598
- `extended`), adlyder tier-grænser og tjekkes for drift:
599
- `mjolnir rules --md --external` renderer kataloget fra de loadede
600
- filer (proveniens `external`), og matrixgeneratoren accepterer
601
- `--external <root>`.
602
-
603
- ---
604
-
605
- ## 🏗️ Arkitektur
520
+ **Rækværket betyder mere end bekvemmeligheden.** Hvert fund i en overdragelse bærer sin grænse. **E2** siger _deterministisk: tjek placeringen, og anvend rettelsen_. **E1** siger _KRÆVER BEKRÆFTELSE: observationen alene beviser ikke defekten_. En agent, der retter E1 i blinde, undertrykker en regel eller redigerer en regel for at hæve scoren, gør præcis det, dette værktøj findes for at fange, så overdragelsen siger det i prompten, lige ved siden af fundet.
606
521
 
607
- <details>
608
- <summary>Udvid træet</summary>
522
+ <br />
609
523
 
610
- ```
611
- mjolnir/
612
- ├── src/
613
- │ ├── engine/ # LanguageAdapter interface + rule runner
614
- │ ├── adapters/ # typescript · python · java · csharp · github-actions
615
- │ ├── rules/ # rules across 8 families + the measured-FP table
616
- │ ├── playwright/ # Selector Health Score engine
617
- │ ├── discovery/ # workspace, frameworks, ignore resolution
618
- │ ├── scope/ # git merge-base changed-scope engine
619
- │ ├── scorer/ # transparent deduction table + prioritization
620
- │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
621
- │ ├── forensics/ # run-data ingestion · flake verdicts · triage
622
- │ ├── config/ # mjolnir.config.json + suppressions
623
- │ ├── plugins/ # third-party rule loading (no sandbox)
624
- │ └── commands/ # every subcommand
625
- └── tests/
626
- ├── fixtures/ # must-fire / must-not-fire per rule
627
- └── golden/ # frozen score regression locks
628
- ```
524
+ ## Tillid og sikkerhed
629
525
 
630
- </details>
526
+ **Local-first, nul telemetri.** Der findes ingen netværkskapabel API (`fetch`, `http`, `https`, `net`, `dns`, `dgram`, WebSocket) nogen steder i `src/`, og [`privacy-network-isolation.spec.ts`](tests/contract/privacy-network-isolation.spec.ts) får buildet til at fejle, hvis der dukker en op. Den forbyder også `eval` og `new Function`. At scanne kode, du ikke stoler på, udfører den aldrig: statisk analyse læser kildetekst, og kørselsanalysen parser rapportfiler, der allerede ligger på disken.
527
+
528
+ To forbehold: `npx` henter selv pakken, før noget kører, og garantien dækker `src/`, ikke tredjeparts-plugins.
529
+
530
+ **Plugins kører ikke i en sandbox.** JS-plugins (`mjolnir-rules/*.mjs` eller npm-pakker angivet under `"plugins"`) kører med fulde Node-rettigheder, samme tillidsmodel som ESLint- eller Vitest-plugins. At indlæse dem er et tilvalg **pr. scan**: uden `--enable-plugins` (eller `MJOLNIR_ENABLE_PLUGINS=1`) indlæses deres kilder aldrig, og en besked på stderr lister, hvad der blev sprunget over. JSON-regelmanifester udfører ingen kode, og præfikserne for core-regel-ID'er er reserverede, så et plugin ikke kan udgive sig for at være en af dem. Rapportér sårbarheder via [SECURITY.md](SECURITY.md).
531
+
532
+ **Det kører på sig selv.** En verification trust engine har ingen troværdighed, medmindre den selv kan verificeres. Hver CI-kørsel scanner dette repository med det build, samme kørsel producerede. Gaten fejler ved ethvert fund med alvorligheden error, og også ved et **delvist** scan eller en **regel, der gik ned**, fordi et afkortet selvscan, der ikke rapporterer noget, er præcis den falske grønne farve, dette projekt findes for at fange. `mjolnir doctor` reviderer regelbasen igen i samme kørsel (fixture-firewall, ærlige niveauer, loftet for core-niveauet), og et INCONCLUSIVE-tjek fejler præcis som et fejlende tjek. Begge rapporter uploades som build-artefakter.
533
+
534
+ ### Exitkoder og maskinkontrakten
631
535
 
632
- - **Regler er rene funktioner** — `(SourceFileContext) → Finding[]`,
633
- ingen I/O, ingen globals. Nyt økosystem = én adapter + dens regler.
634
- - **TypeScript/Playwright bruger compiler-AST** (ts-morph). Python,
635
- Java og C# kører på et delt regex-lag med maskerede kommentare/strenge.
636
- - Et tree-sitter WASM AST-lag til Java og C# findes og er næste
637
- præcisionsskridt — det er endnu ikke koblet på den synkrone
638
- scan-pipeline.
536
+ Fastfrosne, så du kan bygge CI-logik på dem:
639
537
 
640
- ---
538
+ | Exitkode | Betydning |
539
+ | -------- | -------------------------------------------------------------------- |
540
+ | `0` | Rent: ingen fund på eller over gaten |
541
+ | `1` | Fund på eller over gaten |
542
+ | `2` | Delvist scan (tidsbudget opbrugt, ulæselige filer). Blokerer aldrig. |
543
+ | `10` | Brugsfejl (forkert flag, manglende mål) |
544
+ | `20` | Intern fejl |
641
545
 
642
- ## 📚 Dokumentation
546
+ `2` er bevidst forskellig fra `0`: et scan, der ikke blev færdigt, har ikke fundet ingenting. Det er bare ikke færdigt med at lede.
643
547
 
644
- | Dokument | Hvad der er i det |
645
- | ------------------------------------------------------ | ------------------------------------------- |
646
- | [docs/SCORING.md](docs/SCORING.md) | Score-normalisering + bevisvægtning |
647
- | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Målte false-positive-rater + metode |
648
- | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Regeltilstande, undertrykkelse, deprecation |
649
- | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF-output + editor/CI-opsætning |
650
- | [docs/rules/](docs/rules/) | Genereret katalog pr. regel |
651
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Dev-opsætning + bidrag workflow |
652
- | [CHANGELOG.md](CHANGELOG.md) | Releasehistorik |
653
- | [SECURITY.md](SECURITY.md) | Sårbarhedsrapportering |
548
+ Alt, hvad en maskine forbruger (MCP-værktøjsresultater, `--json`, SARIF 2.1), kommer fra ét kanonisk resultat under et versioneret, **kun additivt** skema (`schemaVersion: 1`, `contractVersion: 1`), så ingen forbruger behøver at genskabe betydningen ud fra renderet tekst. Se [maskinkontrakten](docs/machine-contract.md). Regel-ID'er (`QA-<FAMILY>-NNN`) kan ikke ændres, når de er udgivet, og genbruges aldrig.
654
549
 
655
- ---
550
+ <br />
656
551
 
657
- ## 📈 Status
552
+ ## Hvad Mjölnir ikke kan fortælle dig
658
553
 
659
- **v0.5.x · åben beta.** JSON-skemaet og exit-koderne er frosne
660
- kontrakter. TypeScript og Python har den bredeste målte dækning; Java
661
- og C# er nyere — læs dem gennem
662
- [tiers-tabellen](#regel-tiers-og-sproglig-modenhed).
554
+ - **Det kører ikke dine tests.** Et rent scan er ikke en bestået suite.
555
+ - **Det kan ikke fortælle dig, at en assertion er _forkert_.** `expect(total).toBe(41)` ser sund ud. Mjölnir finder tests, der _ikke kan fejle_, og pipelines, der _ikke kan blive røde_, ikke tests, der tjekker det forkerte.
556
+ - **Det beviser ikke forretningsmæssig korrekthed.** Intet her siger, at dit produkt gør, hvad kravet bad om.
557
+ - **100 er ikke bevis for en god suite.** Om din suite dækker din reelle risiko, er et andet spørgsmål, og det besvarer dette værktøj ikke.
558
+ - **5 af 79 regler bygger på et skøn**, ikke en målt rate. Hver af dem siger det på sit eget fund.
559
+ - **E1 er ikke E2.** Heuristiske fund er værd at læse, ikke værd at anvende i blinde.
560
+ - **Et tomt repo får `null`, aldrig 100.**
561
+ - **En fil ved navn `*.spec.ts` uden testdeklarationer tæller ikke som dækning.** Et repo, hvis eneste spec-filer indeholder imports eller typer (nul `it`/`test`-kald), får `null`, ikke 100.
663
562
 
664
- ---
563
+ <br />
665
564
 
666
- ## 🤝 Bidrag
565
+ ## Dokumentation
667
566
 
668
- Nye regler er den nemmeste første bidrag — én kommando scaffolder
669
- reglen plus dens must-fire- **og** must-not-fire-fixtures (den
670
- genererede regel fejler bevidst dens fixtures, indtil du implementerer
671
- rigtig detektion — en stub kan ikke skibes):
567
+ Det fulde dokumentationssite ligger på <https://sergey-bar.github.io/Mjolnir/>.
568
+
569
+ | Dokument | Hvad det indeholder |
570
+ | ------------------------------------------------------ | --------------------------------------------------- |
571
+ | [docs/SCORING.md](docs/SCORING.md) | Normalisering af scoren og vægtning af evidens |
572
+ | [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Kanonisk ordforråd: ét ord pr. begreb |
573
+ | [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Målte falsk-positiv-rater og metoden |
574
+ | [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Regeltilstande, niveauer, undertrykkelse, udfasning |
575
+ | [docs/VERSIONING.md](docs/VERSIONING.md) | Semver-politik, fastfrosne flader, udfasningscyklus |
576
+ | [docs/machine-contract.md](docs/machine-contract.md) | Det kanoniske maskinlæsbare resultat |
577
+ | [docs/SARIF-INTEGRATION.md](docs/SARIF-INTEGRATION.md) | SARIF-output og opsætning af editor eller CI |
578
+ | [docs/GITLAB-CI.md](docs/GITLAB-CI.md) | GitLab: Code Quality-rapport, MR-opskrift, gate |
579
+ | [docs/rules/](docs/rules/) | Genereret katalog pr. regel |
580
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Udviklingsmiljø og bidragsproces |
581
+ | [SUPPORT.md](SUPPORT.md) | Hvor du kan spørge, rapportere og få hjælp |
582
+ | [SECURITY.md](SECURITY.md) | Rapportering af sårbarheder |
583
+ | [CHANGELOG.md](CHANGELOG.md) | Udgivelseshistorik |
584
+
585
+ ### Status
586
+
587
+ **Version 1.** JSON-skemaet og exitkoderne er fastfrosne kontrakter. TypeScript og Python har den bredeste målte dækning. Java og C# er nyere; læs dem gennem [modenhedstabellen](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle). Hvad der kommer næste gang, uden opdigtede datoer: [den offentlige roadmap](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
588
+
589
+ ### Bidrag
590
+
591
+ Nye regler er det nemmeste første bidrag. Én kommando opretter skelettet til reglen med dens must-fire- **og** must-not-fire-fixtures. Den genererede regel fejler bevidst sine egne fixtures, indtil der er skrevet rigtig detektion, fordi en stub, der bliver leveret, er en regel, ingen har målt:
672
592
 
673
593
  ```bash
674
594
  mjolnir create-rule QA-PW-140 --title "Screenshot without diff bound"
675
595
  ```
676
596
 
677
- Fuld dev-opsætning, standing-gate-kommandoerne og anti-creep- /
678
- fixture-firewall-lovene er i [CONTRIBUTING.md](CONTRIBUTING.md).
597
+ Udviklingsmiljøet, kommandoerne til de faste gates samt anti-creep- og fixture-firewall-lovene står i [CONTRIBUTING.md](CONTRIBUTING.md).
679
598
 
680
- ---
599
+ <br />
681
600
 
682
601
  <div align="center">
683
602
 
684
- **Stop med at skibe tests, du ikke kan stole på.**
603
+ <img src="assets/readme/closing.svg" alt="Kør det på dit repo." width="100%" />
685
604
 
686
605
  ```bash
687
606
  npx mjolnir-qa@latest
688
607
  ```
689
608
 
690
- **Star ⭐ · Watch 👀 · Contribute 🤝**
609
+ [Læs guiden](https://sergey-bar.github.io/Mjolnir/guide/getting-started) · [Dokumentationssite](https://sergey-bar.github.io/Mjolnir/) · [npm](https://www.npmjs.com/package/mjolnir-qa)
610
+
611
+ <br />
612
+
613
+ Spørg ikke, om testene bestod.<br />
614
+ Spørg, om evidensen beviser, at de fortjener tillid.
691
615
 
692
- Bygget af [Sergey Bar](https://www.linkedin.com/in/sergeybar/)
616
+ <sub>Bygget af [Sergey Bar](https://www.linkedin.com/in/sergeybar/) · MIT-licens</sub>
693
617
 
694
618
  </div>