mjolnir-qa 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.ar.md +21 -15
- package/README.bn.md +17 -9
- package/README.br.md +25 -16
- package/README.bs.md +23 -15
- package/README.da.md +23 -16
- package/README.de.md +24 -16
- package/README.es.md +25 -16
- package/README.fr.md +25 -16
- package/README.gr.md +24 -16
- package/README.he.md +22 -15
- package/README.it.md +25 -16
- package/README.ja.md +16 -9
- package/README.ko.md +23 -15
- package/README.md +124 -225
- package/README.no.md +23 -16
- package/README.pl.md +17 -9
- package/README.ru.md +26 -17
- package/README.th.md +16 -9
- package/README.tr.md +23 -16
- package/README.uk.md +24 -16
- package/README.vi.md +22 -15
- package/README.zh.md +21 -15
- package/README.zht.md +21 -15
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +84 -40
- package/dist/mcp/stdio.mjs +84 -40
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -12,15 +12,23 @@ pipelines, reports a worthiness score, and shows exactly where trust breaks.
|
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://nodejs.org)
|
|
14
14
|
|
|
15
|
-
English | [简体中文](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](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)
|
|
16
|
-
|
|
17
15
|
```bash
|
|
18
16
|
npx mjolnir-qa@latest
|
|
19
17
|
```
|
|
20
18
|
|
|
21
19
|
**Are your tests worthy of trust?**
|
|
22
20
|
|
|
23
|
-
[See it work](#-see-it-work) · [Quickstart](#-quickstart) · [What it
|
|
21
|
+
[See it work](#-see-it-work) · [Quickstart](#-quickstart) · [Who it's for](#-who-is-this-for) · [Why not a linter](#-mjölnir-is-not-another-linter) · [What it verifies](#-what-mjölnir-verifies) · [Scoring](#-how-the-score-works) · [Runtime evidence](#-runtime-evidence) · [CI](#-ci-integration) · [Exit codes](#-exit-codes--contracts) · [Docs](#-documentation) · [Contributing](#-contributing)
|
|
22
|
+
|
|
23
|
+
<details>
|
|
24
|
+
<summary>Read this in another language — 22 translations</summary>
|
|
25
|
+
|
|
26
|
+
English | [简体中文](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](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)
|
|
27
|
+
|
|
28
|
+
English is canonical. Translations are machine-assisted and may lag behind
|
|
29
|
+
it; `npm run docs:translations` reports how far.
|
|
30
|
+
|
|
31
|
+
</details>
|
|
24
32
|
|
|
25
33
|
</div>
|
|
26
34
|
|
|
@@ -29,14 +37,38 @@ npx mjolnir-qa@latest
|
|
|
29
37
|
## 🎬 See it work
|
|
30
38
|
|
|
31
39
|
<p align="center">
|
|
32
|
-
<
|
|
40
|
+
<a href="assets/video/mjolnir-demo.mp4">
|
|
41
|
+
<img src="assets/video/mjolnir-demo-poster.png" alt="Mjölnir scanning a demo repo: the hammer instrument at [STRAINED] and WORTHINESS 75/100 NEEDS WORK" width="900" />
|
|
42
|
+
</a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<strong><a href="assets/video/mjolnir-demo.mp4">▶ Watch the 42-second demo</a></strong> —
|
|
47
|
+
one false-green CI gate: found, fixed, and re-proved.
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<sub>Every frame is real CLI output. The 75 → 90 score change is a real
|
|
51
|
+
re-scan after applying the fix the tool itself printed — never a mockup.
|
|
52
|
+
Rendered by `npm run docs:video` from
|
|
53
|
+
[`assets/video/script.demo.json`](assets/video/script.demo.json);
|
|
54
|
+
[`tests/contract/video-script.spec.ts`](tests/contract/video-script.spec.ts)
|
|
55
|
+
fails CI if that script stops matching what the CLI prints, or if the
|
|
56
|
+
findings the video shows as fixed turn out to still be there.</sub>
|
|
57
|
+
|
|
58
|
+
<details>
|
|
59
|
+
<summary><strong>Prefer it inline?</strong> The full <code>--verbose</code> report, as an animated SVG</summary>
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
<img src="assets/readme/demo.svg" alt="Mjölnir's full --verbose report on a demo repo: WORTHINESS 75/100 NEEDS WORK, a diagnostics-by-category breakdown, a FIX THIS FIRST list, and every finding with its rule ID and line number" width="900" />
|
|
33
63
|
</p>
|
|
34
64
|
|
|
35
65
|
<sub>The complete `npx mjolnir-qa ./examples/demo-repo --verbose` output,
|
|
36
66
|
rendered from the actual reporter — nothing trimmed. Regenerated by
|
|
37
67
|
`npm run docs:demo`;
|
|
38
|
-
[`tests/demo-asset-reproducibility.spec.ts`](tests/demo-asset-reproducibility.spec.ts)
|
|
39
|
-
fails CI if it drifts
|
|
68
|
+
[`tests/contract/demo-asset-reproducibility.spec.ts`](tests/contract/demo-asset-reproducibility.spec.ts)
|
|
69
|
+
fails CI if it drifts.</sub>
|
|
70
|
+
|
|
71
|
+
</details>
|
|
40
72
|
|
|
41
73
|
**What just happened:**
|
|
42
74
|
|
|
@@ -48,27 +80,48 @@ fails CI if it drifts from what the tool prints.</sub>
|
|
|
48
80
|
3. It turned each into a concrete finding with a rule ID, a location and a
|
|
49
81
|
fix — and a single score you can gate a PR on.
|
|
50
82
|
|
|
83
|
+
There's also an 89-second tour covering `explain`, `forensics`, and the
|
|
84
|
+
rest of the walkthrough below — same pipeline, same guarantee (every
|
|
85
|
+
frame is real CLI output). It's built as
|
|
86
|
+
[`assets/video/script.tour.json`](assets/video/script.tour.json) but not
|
|
87
|
+
committed as an MP4 (it's ~16MB; every clone shouldn't pay for a video
|
|
88
|
+
most readers won't open) — run `npm run docs:video` to render it, or
|
|
89
|
+
check the repo's [Releases](../../releases) for a published copy.
|
|
90
|
+
|
|
51
91
|
### One finding, up close
|
|
52
92
|
|
|
53
93
|
Run `mjolnir explain QA-CI-001` on the first finding above and you get:
|
|
54
94
|
|
|
55
95
|
```text
|
|
56
|
-
▚ QA-CI-001 — continue-on-error masks a failing verification gate
|
|
96
|
+
▚ QA-CI-001 — continue-on-error masks a failing verification gate
|
|
57
97
|
|
|
58
98
|
Severity: error
|
|
59
99
|
Confidence: high
|
|
100
|
+
Tier: quarantine
|
|
60
101
|
Evidence: E2
|
|
61
|
-
|
|
102
|
+
QA impact: False-green risk (FALSE-GREEN)
|
|
103
|
+
Measured FP: 11% (19 hand-classified corpus verdicts)
|
|
104
|
+
FP risk: low (author estimate)
|
|
105
|
+
Languages: yaml
|
|
106
|
+
Frameworks: github-actions
|
|
62
107
|
|
|
63
108
|
WHAT WAS FOUND (real detector output, not a mockup)
|
|
64
109
|
Job `security-scan` runs a verification gate under `continue-on-error: true`.
|
|
65
110
|
|
|
66
111
|
WHY IT MATTERS
|
|
67
|
-
This job can fail every day and CI will still show green. The checkmark
|
|
68
|
-
|
|
112
|
+
This job can fail every day and CI will still show green. The checkmark on
|
|
113
|
+
this workflow cannot be trusted.
|
|
69
114
|
|
|
70
115
|
HOW TO FIX
|
|
71
116
|
Remove continue-on-error, or scope it to individual non-blocking steps only.
|
|
117
|
+
|
|
118
|
+
Example from this rule's own must-fire fixture: QA-CI-001/must-fire/masked.yml
|
|
119
|
+
|
|
120
|
+
HOW TO VERIFY THE FIX
|
|
121
|
+
Re-run `mjolnir` on the changed file(s) — this finding should no longer
|
|
122
|
+
appear. `mjolnir --scope changed` scopes the check to just what you touched.
|
|
123
|
+
|
|
124
|
+
Docs: mjolnir rules --md (full catalog, this rule included)
|
|
72
125
|
```
|
|
73
126
|
|
|
74
127
|
That is the unit of value: not a style nit, but a place where your CI is
|
|
@@ -153,7 +206,35 @@ Requires Node.js ≥ 22.18. Works on Windows, macOS, and Linux.
|
|
|
153
206
|
|
|
154
207
|
---
|
|
155
208
|
|
|
156
|
-
##
|
|
209
|
+
## ⚡ Mjölnir is not another linter
|
|
210
|
+
|
|
211
|
+
Linters tell you whether code follows rules. Mjölnir tells you whether your
|
|
212
|
+
verification can be trusted.
|
|
213
|
+
|
|
214
|
+
| | ESLint / SonarQube | Coverage tools | AI code review | **Mjölnir** |
|
|
215
|
+
| -------------------------------------------------------- | :----------------: | :------------: | :------------: | :--------------: |
|
|
216
|
+
| CI workflow integrity (`continue-on-error`, `\|\| true`) | ❌ | ❌ | only the diff | ✅ |
|
|
217
|
+
| Cross-language (TS, Python, Java, C#) from one tool | ❌ | ❌ | ❌ | ✅ |
|
|
218
|
+
| Grades Playwright locator resilience (Selector Health) | ❌ | ❌ | ❌ | ✅ |
|
|
219
|
+
| Flags tests with no real assertions | ✅ (plugin)\* | ❌ | sometimes | ✅ |
|
|
220
|
+
| Catches hard sleeps (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | sometimes | ✅ |
|
|
221
|
+
| Reads real run data for `TRUE-FLAKE` verdicts | ❌ | ❌ | ❌ | ✅ |
|
|
222
|
+
| Deterministic (same input → same output) | ✅ | ✅ | ❌ | ✅ |
|
|
223
|
+
| Cost per scan | free | free | tokens | **zero** (local) |
|
|
224
|
+
|
|
225
|
+
\*`eslint-plugin-jest` (`expect-expect`) and `eslint-plugin-playwright`
|
|
226
|
+
(`expect-expect`, `no-wait-for-timeout`) cover these for their respective
|
|
227
|
+
frameworks.
|
|
228
|
+
|
|
229
|
+
**Use AI review too.** It catches nuance, intent, and design flaws no regex
|
|
230
|
+
can find. Mjölnir catches the structural patterns AI overlooks because they
|
|
231
|
+
look "intentional" — a committed `.only`, a swallowed exit code, a
|
|
232
|
+
`continue-on-error` on a test job. Those aren't bugs that need reasoning;
|
|
233
|
+
they're facts that need scanning.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 🔨 What Mjölnir verifies
|
|
157
238
|
|
|
158
239
|
| | |
|
|
159
240
|
| --- | ----------------------------------------------------------------------------------------------------------------- |
|
|
@@ -164,12 +245,15 @@ Requires Node.js ≥ 22.18. Works on Windows, macOS, and Linux.
|
|
|
164
245
|
| 🐍 | **All four Playwright bindings** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG and CI workflows |
|
|
165
246
|
| 🔒 | **Local-first** — zero network calls while scanning, zero telemetry, runs in seconds |
|
|
166
247
|
|
|
167
|
-
### The rules
|
|
168
|
-
|
|
169
248
|
Every rule ships with must-fire **and** must-not-fire fixtures. A rule that
|
|
170
249
|
fires on its own negative fixture cannot ship — that's the false-positive
|
|
171
250
|
firewall.
|
|
172
251
|
|
|
252
|
+
**The rule catalog.** Every family is collapsed below; the generated
|
|
253
|
+
full catalog lives in [`docs/rules/`](docs/rules/),
|
|
254
|
+
[what it checks](https://sergey-bar.github.io/Mjolnir/guide/what-it-checks),
|
|
255
|
+
or `mjolnir rules --md`.
|
|
256
|
+
|
|
173
257
|
<details>
|
|
174
258
|
<summary><strong>Test Hygiene</strong></summary>
|
|
175
259
|
|
|
@@ -285,7 +369,7 @@ firewall.
|
|
|
285
369
|
### How much of this is measured
|
|
286
370
|
|
|
287
371
|
**78 of 99 rules carry a false-positive rate measured against real OSS code** (≥ 10 hand-classified findings each; see
|
|
288
|
-
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). The other
|
|
372
|
+
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). The other 21 ship on the author's
|
|
289
373
|
estimate. Every scan footer tells you how many of the rules that _fired_
|
|
290
374
|
are measured; `mjolnir rules --unmeasured` lists the ones that aren't;
|
|
291
375
|
every rule's `mjolnir explain` page states its status. We publish the rate
|
|
@@ -295,82 +379,42 @@ Growing that number is the project's continuing work.
|
|
|
295
379
|
### Rule tiers and language maturity
|
|
296
380
|
|
|
297
381
|
Every rule is `core`, `extended`, or `quarantine`, assigned from its
|
|
298
|
-
**measured** false-positive rate
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
| ------------ | ---------------------------------------- | :----------: | :--------: |
|
|
302
|
-
| `core` | ≤ 10 % measured FP | ✅ | ✅ |
|
|
303
|
-
| `extended` | ≤ 30 % measured FP | ✅ | ✅ |
|
|
304
|
-
| `quarantine` | above 30 %, or not yet measured (n < 10) | ❌ | ✅ |
|
|
305
|
-
|
|
306
|
-
| Language | Adapter | Coverage today |
|
|
307
|
-
| --------------- | ------------ | -------------------------------------------------- |
|
|
308
|
-
| TypeScript / JS | compiler AST | broadest, most measured — mostly `core`/`extended` |
|
|
309
|
-
| Python / pytest | regex layer | broad, corpus-audited — mostly `core`/`extended` |
|
|
310
|
-
| Java | regex layer | newer — mostly `extended`/`quarantine` |
|
|
311
|
-
| C# / .NET | regex layer | newer — mostly `extended`/`quarantine` |
|
|
312
|
-
|
|
313
|
-
TypeScript and Python have the broadest measured coverage. Java and C# ship,
|
|
314
|
-
are documented, and stay out of the headline number until a real consumer
|
|
315
|
-
suite (not a binding library's own tests) has been audited.
|
|
382
|
+
**measured** false-positive rate — quarantine rules only run under
|
|
383
|
+
`--strict`. Tiers, language maturity and the promotion/demotion rules:
|
|
384
|
+
[rule lifecycle](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
|
|
316
385
|
|
|
317
386
|
---
|
|
318
387
|
|
|
319
|
-
## How the score works
|
|
388
|
+
## 📊 How the score works
|
|
320
389
|
|
|
321
390
|
<p align="center">
|
|
322
391
|
<img src="assets/readme/terminal-hero.svg" alt="Mjölnir terminal output — WORTHINESS 75/100 NEEDS WORK, a diagnostics-by-category breakdown, and a FIX THIS FIRST list" width="820" />
|
|
323
392
|
</p>
|
|
324
393
|
|
|
325
394
|
<sub>Regenerated by `npm run docs:hero`;
|
|
326
|
-
[`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts)
|
|
395
|
+
[`tests/contract/hero-asset-reproducibility.spec.ts`](tests/contract/hero-asset-reproducibility.spec.ts)
|
|
327
396
|
fails CI if it drifts from what the reporter actually prints.</sub>
|
|
328
397
|
|
|
329
398
|
The score is transparent: **error −8, warning −3, info −1**, then normalized
|
|
330
399
|
by suite exposure (deductions per test declaration). Evidence-weighted
|
|
331
400
|
deductions mean weak signals cost less. The terminal shows the same
|
|
332
|
-
discounted numbers the score uses — no black box.
|
|
333
|
-
[docs/SCORING.md](docs/SCORING.md).
|
|
334
|
-
|
|
335
|
-
**Verdicts**
|
|
401
|
+
discounted numbers the score uses — no black box.
|
|
336
402
|
|
|
337
|
-
| Score | Verdict |
|
|
338
|
-
| ------- | ---------------- |
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
|
|
|
342
|
-
|
|
343
|
-
**Evidence levels** — every finding carries one; it sets the finding's
|
|
344
|
-
weight in the score:
|
|
345
|
-
|
|
346
|
-
| Level | Meaning | Score impact | Example |
|
|
347
|
-
| ----- | -------------------- | ---------------- | -------------------------------------------------- |
|
|
348
|
-
| E2 | Deterministic defect | Full deduction | `.only` committed — structurally provable |
|
|
349
|
-
| E1 | Heuristic pattern | Half deduction | Regex-matched `sleep()` — strong signal, not proof |
|
|
350
|
-
| E0 | Observation | Zero (info only) | Reported but never gates CI or deducts |
|
|
403
|
+
| Score | Verdict | | Level | Evidence | Score impact |
|
|
404
|
+
| ------- | ---------------- | --- | ----- | -------------------- | ---------------- |
|
|
405
|
+
| 100 | ⚡ **FORGED** | | E2 | Deterministic defect | Full deduction |
|
|
406
|
+
| ≥ 80 | ✓ **WORTHY** | | E1 | Heuristic pattern | Half deduction |
|
|
407
|
+
| 50 – 79 | ⚠ **NEEDS WORK** | | E0 | Observation | Zero (info only) |
|
|
408
|
+
| < 50 | ✖ **UNWORTHY** | | | | |
|
|
351
409
|
|
|
352
410
|
Most rules are **E1**. The tagline "we prove it" refers to this system:
|
|
353
411
|
E2 findings are structural proof; E1 findings are correctly-positioned
|
|
354
412
|
warnings, not formal proofs.
|
|
355
413
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
## 🎭 Selector Health Score
|
|
361
|
-
|
|
362
|
-
The headline metric for Playwright suites — how resilient your locators are:
|
|
363
|
-
|
|
364
|
-
```text
|
|
365
|
-
▚ SELECTOR HEALTH — e2e/checkout.spec.ts
|
|
366
|
-
|
|
367
|
-
[█████████████████░░░] 83 / 100
|
|
368
|
-
role/text: 2 · testid: 1 · css-chains: 1 ⚠ · xpath: 0
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
Role-based locators score full credit. CSS class chains and XPath tank the
|
|
372
|
-
score — they break on any DOM refactor without telling you which behavior
|
|
373
|
-
regressed.
|
|
414
|
+
**No false proof.** We'd rather say "unknown" than "verified" — an empty
|
|
415
|
+
repo scores `null`, never a fake 100. Full method:
|
|
416
|
+
[docs/SCORING.md](docs/SCORING.md) ·
|
|
417
|
+
[scoring guide](https://sergey-bar.github.io/Mjolnir/guide/scoring).
|
|
374
418
|
|
|
375
419
|
---
|
|
376
420
|
|
|
@@ -399,60 +443,6 @@ test. It gets flagged `TRUE-FLAKE` regardless of the final green checkmark.
|
|
|
399
443
|
|
|
400
444
|
---
|
|
401
445
|
|
|
402
|
-
## ⚡ Mjölnir is not another linter
|
|
403
|
-
|
|
404
|
-
Linters tell you whether code follows rules. Mjölnir tells you whether your
|
|
405
|
-
verification can be trusted.
|
|
406
|
-
|
|
407
|
-
| | ESLint / SonarQube | Coverage tools | Manual review | **Mjölnir** |
|
|
408
|
-
| -------------------------------------------------------- | :----------------: | :------------: | :-----------: | :---------: |
|
|
409
|
-
| CI workflow integrity (`continue-on-error`, `\|\| true`) | ❌ | ❌ | rarely | ✅ |
|
|
410
|
-
| Cross-language (TS, Python, Java, C#) from one tool | ❌ | ❌ | ❌ | ✅ |
|
|
411
|
-
| Grades Playwright locator resilience (Selector Health) | ❌ | ❌ | rarely | ✅ |
|
|
412
|
-
| Flags tests with no real assertions | ✅ (plugin)\* | ❌ | sometimes | ✅ |
|
|
413
|
-
| Catches hard sleeps (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | sometimes | ✅ |
|
|
414
|
-
| Runs in seconds, zero network calls while scanning | ✅ | ✅ | — | ✅ |
|
|
415
|
-
|
|
416
|
-
\*`eslint-plugin-jest` (`expect-expect`) and `eslint-plugin-playwright`
|
|
417
|
-
(`expect-expect`, `no-wait-for-timeout`) cover these for their respective
|
|
418
|
-
frameworks.
|
|
419
|
-
|
|
420
|
-
**Runtime analysis** is a separate category from static linting:
|
|
421
|
-
|
|
422
|
-
| | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
|
|
423
|
-
| --------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
|
|
424
|
-
| Reads real run data for `TRUE-FLAKE` verdicts | partial\* | partial (tag) | ✅ |
|
|
425
|
-
| Flaky-triage report from execution history | ❌ | ✅ | ✅ |
|
|
426
|
-
| Integrates with static worthiness score | ❌ | ❌ | ✅ |
|
|
427
|
-
|
|
428
|
-
\*Playwright tracks retries internally but does not produce a standalone
|
|
429
|
-
flakiness report with verdict labels.
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
## 🤖 Why not just use AI code review?
|
|
434
|
-
|
|
435
|
-
Different problem, different layer. AI review can spot a suspicious test
|
|
436
|
-
change in a diff; it does not prove the verification system as a whole is
|
|
437
|
-
trustworthy — and it only sees the diff you show it.
|
|
438
|
-
|
|
439
|
-
| | AI code review (Copilot, etc.) | **Mjölnir** |
|
|
440
|
-
| ------------------------------------- | :----------------------------: | :-------------------------: |
|
|
441
|
-
| Cost per scan | Tokens (scales with diff size) | **Zero** (local, installed) |
|
|
442
|
-
| Sees the whole suite + all CI configs | Only the PR diff you show it | **Everything, every time** |
|
|
443
|
-
| Deterministic (same input → same out) | ❌ (non-deterministic) | **✅** |
|
|
444
|
-
| Catches patterns dormant for months | Only if it's in the context | **✅** (scans all files) |
|
|
445
|
-
| Remembers findings between runs | ❌ (no memory across sessions) | **✅** (baseline + diff) |
|
|
446
|
-
| Runs without human triggering | Needs a PR or prompt | **✅** (CI hook, 3 seconds) |
|
|
447
|
-
|
|
448
|
-
**Use both.** AI catches nuance, intent, and design flaws no regex can
|
|
449
|
-
find. Mjölnir catches the structural patterns AI overlooks because they
|
|
450
|
-
look "intentional" — a committed `.only`, a swallowed exit code, a
|
|
451
|
-
`continue-on-error` on a test job. Those aren't bugs that need reasoning;
|
|
452
|
-
they're facts that need scanning.
|
|
453
|
-
|
|
454
|
-
---
|
|
455
|
-
|
|
456
446
|
## 🤖 CI integration
|
|
457
447
|
|
|
458
448
|
One command generates a PR workflow — advisory by default, never blocking:
|
|
@@ -484,49 +474,6 @@ ref with `--base <ref>`.
|
|
|
484
474
|
|
|
485
475
|
---
|
|
486
476
|
|
|
487
|
-
## Configuration
|
|
488
|
-
|
|
489
|
-
Mjölnir is zero-config. An optional `mjolnir.config.json` (or
|
|
490
|
-
`.mjolnir.json`) at the repo root tunes severity, gating and scope — it
|
|
491
|
-
never changes detection semantics.
|
|
492
|
-
|
|
493
|
-
| Key | Type | Effect |
|
|
494
|
-
| ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
495
|
-
| `exclude` | `string[]` | Extra ignore globs (gitignore subset), on top of the built-in defaults |
|
|
496
|
-
| `gate` | `"advisory" \| "error" \| "warning"` | Which severities exit non-zero (default `error`; `advisory` never blocks) |
|
|
497
|
-
| `severityOverrides` | `{ "<RULE-ID>": severity }` | Re-rank a rule's findings for your repo |
|
|
498
|
-
| `ignore` | `IgnoreEntry[]` | Suppress findings — **`reason` is required**; entries expire after 90 days (an explicit `expires` date, or the config file's last-modified time for entries without one) |
|
|
499
|
-
| `plugins` | `string[]` | Third-party rule packages (see [Trust model](#trust-model)) |
|
|
500
|
-
|
|
501
|
-
```json
|
|
502
|
-
{
|
|
503
|
-
"gate": "error",
|
|
504
|
-
"exclude": ["legacy/**"],
|
|
505
|
-
"severityOverrides": { "QA-PW-118": "warning" },
|
|
506
|
-
"ignore": [
|
|
507
|
-
{
|
|
508
|
-
"ruleId": "QA-TEST-004",
|
|
509
|
-
"files": ["e2e/legacy-login.spec.ts"],
|
|
510
|
-
"reason": "Third-party widget needs a settle delay; tracked in JIRA-4821",
|
|
511
|
-
"expires": "2026-12-31"
|
|
512
|
-
}
|
|
513
|
-
]
|
|
514
|
-
}
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
- **`.mjolnirignore`** — a plain gitignore-style file for path exclusions,
|
|
518
|
-
same dialect as `exclude`. Use it for machine-wide noise; use `exclude`
|
|
519
|
-
when the list belongs in version control alongside the rest of the config.
|
|
520
|
-
- **CLI overrides** — `--strict` (include quarantine rules), `--width <cols>`
|
|
521
|
-
and `--ascii` / `--no-ascii` (terminal rendering), `--tone blunt`
|
|
522
|
-
(blunter messages), `--max-duration <sec>` (bounded partial scan).
|
|
523
|
-
- Rule suppression and deprecation lifecycle: [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md).
|
|
524
|
-
|
|
525
|
-
`ignore` entries also power the standalone `mjolnir suppressions` command,
|
|
526
|
-
which lists what's currently suppressed and when each entry expires.
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
477
|
## 📐 Exit codes & contracts
|
|
531
478
|
|
|
532
479
|
Frozen — safe to build CI logic on:
|
|
@@ -546,18 +493,11 @@ are immutable once shipped and never reused.
|
|
|
546
493
|
|
|
547
494
|
## Trust model
|
|
548
495
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
- **FP firewall** — detection runs on a comment/string-free view of the code
|
|
555
|
-
(TypeScript rules use the compiler AST): a pattern inside a prose comment
|
|
556
|
-
or a doc-example string is documentation, not a finding.
|
|
557
|
-
- **Measured, not asserted** — only rules with a false-positive rate from
|
|
558
|
-
real OSS code ship in the headline tiers (see
|
|
559
|
-
[How much of this is measured](#how-much-of-this-is-measured)); the scan
|
|
560
|
-
footer and `mjolnir rules --unmeasured` tell you which is which.
|
|
496
|
+
**Local-first, zero telemetry, no false proof** — full detail in
|
|
497
|
+
[docs/SCORING.md](docs/SCORING.md) and
|
|
498
|
+
[docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md). The one piece worth
|
|
499
|
+
stating here because it changes how you invoke the tool:
|
|
500
|
+
|
|
561
501
|
- **Plugin trust & execution gate** — plugins are npm packages declared
|
|
562
502
|
under `"plugins"`; JS modules live in `mjolnir-rules/*.mjs`. There is
|
|
563
503
|
**no sandbox**: plugin code runs with full Node privileges, the same
|
|
@@ -569,52 +509,10 @@ are immutable once shipped and never reused.
|
|
|
569
509
|
unaffected: they declare regex patterns and execute no code by design.
|
|
570
510
|
Core rule-ID prefixes are reserved and rejected from plugins and
|
|
571
511
|
external rules to prevent spoofing.
|
|
572
|
-
- **Workspace-local external rules** (folder-based, zero network) — a
|
|
573
|
-
`mjolnir-rules/` directory next to the scan target loads custom rules:
|
|
574
|
-
JSON files declare regex patterns (no code executed), `.mjs`/`.js`
|
|
575
|
-
modules export `rules` (full-Node trust, same as plugins). External
|
|
576
|
-
rules carry the same trust metadata as core; they can never ship in
|
|
577
|
-
the core tier (core requires a measured FP rate from the corpus
|
|
578
|
-
sidecar — a declared `tier: "core"` is clamped to `extended`), obey
|
|
579
|
-
tier caps, and are drift-checked: `mjolnir rules --md --external`
|
|
580
|
-
renders the catalog from the loaded files (provenance `external`),
|
|
581
|
-
and the matrix generator accepts `--external <root>`.
|
|
582
|
-
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
## 🏗️ Architecture
|
|
586
|
-
|
|
587
|
-
<details>
|
|
588
|
-
<summary>Expand tree</summary>
|
|
589
|
-
|
|
590
|
-
```
|
|
591
|
-
mjolnir/
|
|
592
|
-
├── src/
|
|
593
|
-
│ ├── engine/ # LanguageAdapter interface + rule runner
|
|
594
|
-
│ ├── adapters/ # typescript · python · java · csharp · github-actions
|
|
595
|
-
│ ├── rules/ # rules across 8 families + the measured-FP table
|
|
596
|
-
│ ├── playwright/ # Selector Health Score engine
|
|
597
|
-
│ ├── discovery/ # workspace, frameworks, ignore resolution
|
|
598
|
-
│ ├── scope/ # git merge-base changed-scope engine
|
|
599
|
-
│ ├── scorer/ # transparent deduction table + prioritization
|
|
600
|
-
│ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
|
|
601
|
-
│ ├── forensics/ # run-data ingestion · flake verdicts · triage
|
|
602
|
-
│ ├── config/ # mjolnir.config.json + suppressions
|
|
603
|
-
│ ├── plugins/ # third-party rule loading (no sandbox)
|
|
604
|
-
│ └── commands/ # every subcommand
|
|
605
|
-
└── tests/
|
|
606
|
-
├── fixtures/ # must-fire / must-not-fire per rule
|
|
607
|
-
└── golden/ # frozen score regression locks
|
|
608
|
-
```
|
|
609
|
-
|
|
610
|
-
</details>
|
|
611
512
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
and C# run on a shared comment/string-masked regex layer.
|
|
616
|
-
- A tree-sitter WASM AST layer for Java and C# exists and is the next
|
|
617
|
-
precision step — it is not yet wired into the synchronous scan pipeline.
|
|
513
|
+
Architecture, the full rule catalog, and the tree-sitter roadmap live in
|
|
514
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) and the
|
|
515
|
+
[docs site](https://sergey-bar.github.io/Mjolnir/).
|
|
618
516
|
|
|
619
517
|
---
|
|
620
518
|
|
|
@@ -623,6 +521,7 @@ mjolnir/
|
|
|
623
521
|
| Document | What's in it |
|
|
624
522
|
| ------------------------------------------------------ | ------------------------------------------------- |
|
|
625
523
|
| [docs/SCORING.md](docs/SCORING.md) | Score normalization + evidence weighting |
|
|
524
|
+
| [docs/TERMINOLOGY.md](docs/TERMINOLOGY.md) | Canonical vocabulary — one word per concept |
|
|
626
525
|
| [docs/FP-AUDIT.md](docs/FP-AUDIT.md) | Measured false-positive rates + method |
|
|
627
526
|
| [docs/RULE-LIFECYCLE.md](docs/RULE-LIFECYCLE.md) | Rule states, suppression, deprecation |
|
|
628
527
|
| [docs/VERSIONING.md](docs/VERSIONING.md) | Semver policy, frozen surfaces, deprecation cycle |
|
|
@@ -639,7 +538,7 @@ mjolnir/
|
|
|
639
538
|
|
|
640
539
|
**v0.5.x · open beta.** The JSON schema and exit codes are frozen contracts.
|
|
641
540
|
TypeScript and Python have the broadest measured coverage; Java and C# are
|
|
642
|
-
newer — read them through the [maturity table](
|
|
541
|
+
newer — read them through the [maturity table](https://sergey-bar.github.io/Mjolnir/reference/rule-lifecycle).
|
|
643
542
|
Honest scope, no invented dates: the [public roadmap](https://sergey-bar.github.io/Mjolnir/reference/roadmap).
|
|
644
543
|
|
|
645
544
|
---
|
package/README.no.md
CHANGED
|
@@ -15,7 +15,7 @@ tilliten bryter sammen.
|
|
|
15
15
|
|
|
16
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
17
|
|
|
18
|
-
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-
|
|
18
|
+
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-07.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx mjolnir-qa@latest
|
|
@@ -296,14 +296,14 @@ det er false-positive-brannmuren.
|
|
|
296
296
|
|
|
297
297
|
### Hvor mye er målt
|
|
298
298
|
|
|
299
|
-
**
|
|
299
|
+
**78 av 99 regler bærer en false-positive-rate målt mot ekte OSS-kode**
|
|
300
300
|
(≥ 10 håndklassifiserte funn hver; se
|
|
301
|
-
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). De andre
|
|
301
|
+
[docs/FP-AUDIT.md](docs/FP-AUDIT.md)). De andre 21 skiper på
|
|
302
302
|
forfatterens estimat. Hver skann-fotnote forteller hvor mange av de
|
|
303
303
|
_utløste_ reglene som er målt; `mjolnir rules --unmeasured` lister de
|
|
304
304
|
umålte; hver regels `mjolnir explain`-side angir statusen. Vi publiserer
|
|
305
305
|
raten selv når den er stygg — QA-CS-103 auditeres til 95 % og er satt i
|
|
306
|
-
karantene for det. Å få
|
|
306
|
+
karantene for det. Å få det tallet til å vokse er prosjektets fortsatte
|
|
307
307
|
arbeid.
|
|
308
308
|
|
|
309
309
|
### Regel-tiers og språkmodenhet
|
|
@@ -454,14 +454,14 @@ Annet problem, annet lag. AI-review kan spotte en mistenkelig
|
|
|
454
454
|
testendring i en diff; det beviser ikke at verifiseringssystemet som
|
|
455
455
|
helhet er troverdig — og det ser bare diffen du viser det.
|
|
456
456
|
|
|
457
|
-
| | AI-kodereview (Copilot m.fl.) |
|
|
458
|
-
| ------------------------------------------- | :-------------------------------------: |
|
|
459
|
-
| Kostnad per skann | Tokens (skalerer med diffstørrelsen) |
|
|
460
|
-
| Ser hele suiten + alle CI-konfigs | Bare PR-diffen du viser |
|
|
461
|
-
| Deterministisk (samme input → samme output) | ❌ (ikke-deterministisk) |
|
|
462
|
-
| Fanger mønstre som har sovet i måneder | Bare hvis det er i konteksten |
|
|
463
|
-
| Husker funn mellom kjøringer | ❌ (ingen hukommelse på tvers av økter) |
|
|
464
|
-
| Kjører uten menneskelig utløser | Krever en PR eller prompt | **✅** (CI-hook,
|
|
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, kjører på sekunder) |
|
|
465
465
|
|
|
466
466
|
**Bruk begge.** AI fanger nyanse, intensjon og designfeil ingen regex
|
|
467
467
|
kan finne. Mjölnir fanger de strukturelle mønstrene AI overser fordi de
|
|
@@ -585,11 +585,18 @@ aldri.
|
|
|
585
585
|
ekte OSS-kode skiper i overskriftstierne (se
|
|
586
586
|
[Hvor mye er målt](#hvor-mye-er-målt)); skann-fotnoten og
|
|
587
587
|
`mjolnir rules --unmeasured` forteller deg hvilke som er hva.
|
|
588
|
-
- **Plugin-tillit** — plugins er npm-pakker deklarert under
|
|
589
|
-
`"plugins"
|
|
588
|
+
- **Plugin-tillit og kjøringsport** — plugins er npm-pakker deklarert under
|
|
589
|
+
`"plugins"`; JS-moduler bor i `mjolnir-rules/*.mjs`.
|
|
590
|
+
Det er **ingen sandbox**: plugin-kode kjører med fulle
|
|
590
591
|
Node-privilegier, samme tillitsmodell som ESLint- eller
|
|
591
|
-
Vitest-plugins.
|
|
592
|
-
plugins
|
|
592
|
+
Vitest-plugins. Derfor er kodekjøring **opt-in ved hver skann**: gi
|
|
593
|
+
`--enable-plugins` (eller sett `MJOLNIR_ENABLE_PLUGINS=1`), ellers
|
|
594
|
+
lastes kildene IKKE — et høylig stderr-varsel lister nøyaktig hva som
|
|
595
|
+
ble hoppet over. Å skanne ukjent kode kjører den aldri. JSON-regelmanifest
|
|
596
|
+
(`mjolnir-rules/*.json`) berøres ikke: de deklarerer regex-mønstre og
|
|
597
|
+
kjører ingen kode av konstruksjon. Core regel-ID-prefiks er reservert
|
|
598
|
+
og avvises fra
|
|
599
|
+
plugins og eksterne regler mot spoofing.
|
|
593
600
|
- **Workspace-lokale eksterne regler** (mappebaserte, null nettverk) —
|
|
594
601
|
en `mjolnir-rules/`-mappe ved siden av skannemålet loader
|
|
595
602
|
tilpassede regler: JSON-filer deklarerer regex-mønstre (ingen kode
|
package/README.pl.md
CHANGED
|
@@ -15,7 +15,7 @@ gdzie zaufanie się łamie.
|
|
|
15
15
|
|
|
16
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.ru.md) | [Norsk](README.no.md) | [Português (Brasil)](README.br.md) | [ไทย](README.th.md) | [Türkçe](README.tr.md) | [Українська](README.uk.md) | [বাংলা](README.bn.md) | [Ελληνικά](README.gr.md) | [Tiếng Việt](README.vi.md) | [עברית](README.he.md) | [العربية](README.ar.md) | [Bosanski](README.bs.md)
|
|
17
17
|
|
|
18
|
-
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-
|
|
18
|
+
> 🤖 Machine-assisted translation. The [English README](README.md) is canonical. Last synced: 2026-09-07.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx mjolnir-qa@latest
|
|
@@ -297,14 +297,14 @@ nie może się wydać — to zapora na fałszywe pozytywy.
|
|
|
297
297
|
|
|
298
298
|
### Ile z tego jest zmierzone
|
|
299
299
|
|
|
300
|
-
**
|
|
300
|
+
**78 z 99 reguł niesie stopę fałszywych pozytywów zmierzoną na
|
|
301
301
|
prawdziwym kodzie OSS** (≥ 10 ręcznie zaklasyfikowanych znalezisk każda;
|
|
302
|
-
zob. [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Pozostałe
|
|
302
|
+
zob. [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). Pozostałe 21 wychodzi na
|
|
303
303
|
oszacowaniu autora. Stopka każdego skanu mówi, ile z _odpalonych_
|
|
304
304
|
reguł jest zmierzonych; `mjolnir rules --unmeasured` wypisuje
|
|
305
305
|
niezmierzone; strona `mjolnir explain` każdej reguły deklaruje jej
|
|
306
306
|
status. Publikujemy stopę, nawet gdy jest brzydka — QA-CS-103 audytuje
|
|
307
|
-
się na 95 % i za to trafia do kwarantanny. Powiększanie tej
|
|
307
|
+
się na 95 % i za to trafia do kwarantanny. Powiększanie tej liczby to
|
|
308
308
|
stale trwająca praca projektu.
|
|
309
309
|
|
|
310
310
|
### Tiery reguł i dojrzałość językowa
|
|
@@ -465,7 +465,7 @@ godny zaufania — i widzi tylko diff, który mu pokażesz.
|
|
|
465
465
|
| Deterministyczny (ten sam input → ten sam output) | ❌ (niedeterministyczny) | **✅** |
|
|
466
466
|
| Łapie wzorce śpiące miesiącami | Tylko jeśli jest w kontekście | **✅** (skanuje wszystkie pliki) |
|
|
467
467
|
| Pamięta znaleziska między przebiegami | ❌ (brak pamięci między sesjami) | **✅** (baseline + diff) |
|
|
468
|
-
| Działa bez ludzkiego wyzwalacza | Potrzebuje PR-a albo promptu |
|
|
468
|
+
| Działa bez ludzkiego wyzwalacza | Potrzebuje PR-a albo promptu | **✅** (hak CI, działa sekundy) |
|
|
469
469
|
|
|
470
470
|
**Używaj obu.** AI łapie niuans, intencję i wady projektowe, których
|
|
471
471
|
żaden regex nie znajdzie. Mjölnir łapie strukturalne wzorce, które AI
|
|
@@ -587,11 +587,19 @@ są niezmienne po wydaniu i nigdy nie są używane ponownie.
|
|
|
587
587
|
reguły ze stopą fałszywych pozytywów z prawdziwego kodu OSS (zob.
|
|
588
588
|
[Ile z tego jest zmierzone](#ile-z-tego-jest-zmierzone)); stopka
|
|
589
589
|
skanu i `mjolnir rules --unmeasured` powiedzą ci, które które.
|
|
590
|
-
- **Zaufanie do pluginów** — pluginy to pakiety npm
|
|
591
|
-
|
|
590
|
+
- **Zaufanie do pluginów i brama wykonania** — pluginy to pakiety npm
|
|
591
|
+
deklarowane pod
|
|
592
|
+
`"plugins"`; moduły JS żyją w `mjolnir-rules/*.mjs`.
|
|
593
|
+
**Nie ma sandboxa**: kod pluginu działa z pełnymi
|
|
592
594
|
uprawnieniami Node, ten sam model zaufania co pluginy ESLint czy
|
|
593
|
-
Vitest.
|
|
594
|
-
|
|
595
|
+
Vitest. Dlatego wykonanie kodu jest **opt-in przy każdym skanie**:
|
|
596
|
+
przekaż `--enable-plugins` (albo ustaw `MJOLNIR_ENABLE_PLUGINS=1`),
|
|
597
|
+
inaczej źródła NIE są ładowane — głośny komunikat na stderr wylicza
|
|
598
|
+
dokładnie, co pominięto. Skanowanie niezaufanego kodu nigdy go nie
|
|
599
|
+
wykonuje. Manifesty reguł JSON (`mjolnir-rules/*.json`) nie są
|
|
600
|
+
dotknięte: deklarują wzorce regex i z założenia nie wykonują kodu.
|
|
601
|
+
Prefiksy ID reguł core są zarezerwowane i odrzucane od
|
|
602
|
+
pluginów i zewnętrznych reguł przeciw spoofingowi.
|
|
595
603
|
- **Zewnętrzne reguły lokalne wobec workspace'u** (folderowe, zero
|
|
596
604
|
sieci) — katalog `mjolnir-rules/` obok celu skanu ładuje własne
|
|
597
605
|
reguły: pliki JSON deklarują wzorce regex (żaden kod nie jest
|