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/README.md CHANGED
@@ -12,15 +12,23 @@ pipelines, reports a worthiness score, and shows exactly where trust breaks.
12
12
  [![license](https://img.shields.io/badge/license-MIT-C9A227.svg?style=flat-square&labelColor=0B0F17)](LICENSE)
13
13
  [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-2E8C7F.svg?style=flat-square&labelColor=0B0F17)](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 checks](#-what-mjölnir-checks) · [Scoring](#how-the-score-works) · [CI](#-ci-integration) · [Configuration](#configuration) · [Docs](#-documentation)
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
- <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 across CI, Playwright, test-hygiene and Python rules" width="900" />
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 from what the tool prints.</sub>
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
- Measured FP: not yet measured — this rule ships on assumption (see docs/FP-AUDIT.md)
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
- on this workflow cannot be trusted.
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
- ## 🔨 What Mjölnir checks
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 23 ship on the author's
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
- | Tier | Meaning | Default scan | `--strict` |
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. Full method:
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
- | ≥ 80 | ✓ **WORTHY** |
340
- | 50 – 79 | ⚠ **NEEDS WORK** |
341
- | < 50 | ✖ **UNWORTHY** |
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
- An empty repo scores `null`, never a fake 100 — see [Trust model](#trust-model).
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
- - **Local-first** — zero network calls during scanning. Ever. Zero telemetry.
550
- - **No false proof** — we'd rather say "unknown" than "verified". An empty
551
- repo gets `score: null`, never a fake 100.
552
- - **Partial honesty** — if analysis was cut short, the output says so.
553
- Never "complete" when it isn't.
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
- - **Rules are pure functions** — `(SourceFileContext) → Finding[]`, no I/O,
613
- no globals. Adding an ecosystem = one adapter + its rules.
614
- - **TypeScript/Playwright uses the compiler AST** (ts-morph). Python, Java
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](#rule-tiers-and-language-maturity).
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-04.
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
- **74 av 99 regler bærer en false-positive-rate målt mot ekte OSS-kode**
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 19 skiper på
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å de 78 til å vokse er prosjektets fortsatte
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.) | **Mjölnir** |
458
- | ------------------------------------------- | :-------------------------------------: | :--------------------------: |
459
- | Kostnad per skann | Tokens (skalerer med diffstørrelsen) | **Null** (lokal, installert) |
460
- | Ser hele suiten + alle CI-konfigs | Bare PR-diffen du viser | **Alt, hver gang** |
461
- | Deterministisk (samme input → samme output) | ❌ (ikke-deterministisk) | **✅** |
462
- | Fanger mønstre som har sovet i måneder | Bare hvis det er i konteksten | **✅** (skanner alle filer) |
463
- | Husker funn mellom kjøringer | ❌ (ingen hukommelse på tvers av økter) | **✅** (baseline + diff) |
464
- | Kjører uten menneskelig utløser | Krever en PR eller prompt | **✅** (CI-hook, 3 sekunder) |
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"`. Det er **ingen sandbox**: plugin-kode kjører med fulle
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. Core regel-ID-prefiks er reservert og avvises fra
592
- plugins mot spoofing.
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-04.
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
- **74 z 99 reguł niesie stopę fałszywych pozytywów zmierzoną na
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 19 wychodzi na
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 78-ki to
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 | **✅** (hak CI, 3 sekundy) |
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 deklarowane pod
591
- `"plugins"`. **Nie ma sandboxa**: kod pluginu działa z pełnymi
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. Prefiksy ID reguł core są zarezerwowane i odrzucane od
594
- pluginów przeciw spoofingowi.
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