mjolnir-qa 0.4.0 → 0.5.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.md CHANGED
@@ -1,23 +1,22 @@
1
1
  <div align="center">
2
2
 
3
- <pre>
4
- ╔═══════════╗
5
- ║ ║
6
- ╠═══════════╣ M J Ö L N I R
7
- ║ ║ ║
8
- ╚═════╩═════╝ VERIFICATION TRUST ENGINE
9
-
10
-
11
- </pre>
3
+ <img src="assets/readme/logo.png" alt="Mjölnir — Verification Trust Engine" width="800" />
12
4
 
13
5
  ### Your tests are lying to you. We prove it.
14
6
 
15
7
  **Verification Trust Engine for QA.** Audits test suites and CI pipelines,
16
8
  reports a worthiness score, and shows exactly where trust breaks.
17
9
 
10
+ <sub>"We prove it" is literal for the deterministic findings — a committed `.only`,
11
+ a `continue-on-error` on a test job. The pattern-based rules are labelled
12
+ heuristic, and **15 of 91 carry a false-positive rate measured against real
13
+ OSS code**; `mjolnir rules --unmeasured` names the rest. See
14
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md).</sub>
15
+
16
+ [![npm](https://img.shields.io/npm/v/mjolnir-qa.svg?style=flat-square&color=B45309&labelColor=0D0D0D)](https://www.npmjs.com/package/mjolnir-qa)
17
+ [![ci](https://img.shields.io/github/actions/workflow/status/Sergey-Bar/Mjolnir/ci.yml?branch=main&style=flat-square&label=ci&labelColor=0D0D0D)](https://github.com/Sergey-Bar/Mjolnir/actions/workflows/ci.yml)
18
18
  [![license](https://img.shields.io/badge/license-MIT-B45309.svg?style=flat-square&labelColor=0D0D0D)](LICENSE)
19
19
  [![node](https://img.shields.io/badge/node-%E2%89%A5%2022.18-146A8C.svg?style=flat-square&labelColor=0D0D0D)](https://nodejs.org)
20
- [![status](https://img.shields.io/badge/status-●_ONLINE-15803D.svg?style=flat-square&labelColor=0D0D0D)](#-quickstart)
21
20
 
22
21
  ```bash
23
22
  npx mjolnir-qa@latest
@@ -33,14 +32,14 @@ npx mjolnir-qa@latest
33
32
 
34
33
  ## 🔨 What is Mjölnir?
35
34
 
36
- | | |
37
- | --- | -------------------------------------------------------------------------------------------------------------- |
38
- | ⚖️ | **Worthiness Score** — one number, transparent deduction table, no black box |
39
- | 🎭 | **Selector Health Score** — grades your Playwright locators, not just your pass rate |
40
- | 🔬 | **Runtime forensics** — reads real Playwright/JUnit run data to catch `TRUE-FLAKE`, not just static guesses |
41
- | 🚨 | **CI-integrity rules** — catches `continue-on-error`, `\|\| true`, and other false-green tricks |
42
- | 🐍 | **Multi-language** — TypeScript/Playwright, Python/pytest, Java, and C#/.NET today, one adapter away from more |
43
- | 🔒 | **Local-first** — zero network calls while scanning, zero telemetry, runs in seconds |
35
+ | | |
36
+ | --- | ----------------------------------------------------------------------------------------------------------------- |
37
+ | ⚖️ | **Worthiness Score** — one number, transparent deduction table, no black box |
38
+ | 🎭 | **Selector Health Score** — grades your Playwright locators, not just your pass rate |
39
+ | 🔬 | **Runtime forensics** — reads real Playwright/JUnit run data to catch `TRUE-FLAKE`, not just static guesses |
40
+ | 🚨 | **CI-integrity rules** — catches `continue-on-error`, `\|\| true`, and other false-green tricks |
41
+ | 🐍 | **All four Playwright bindings** — TypeScript, Python, Java, C#/.NET — plus pytest, JUnit/TestNG and CI workflows |
42
+ | 🔒 | **Local-first** — zero network calls while scanning, zero telemetry, runs in seconds |
44
43
 
45
44
  ---
46
45
 
@@ -49,65 +48,128 @@ npx mjolnir-qa@latest
49
48
  Linters tell you whether code follows rules.
50
49
  Mjölnir tells you whether your verification can be trusted.
51
50
 
52
- | | ESLint / SonarQube | Coverage tools | Manual review | **Mjölnir** |
53
- | ---------------------------------------------------------- | :----------------: | :------------: | :-----------: | :---------: |
54
- | Catches syntax & style bugs | | ❌ | (slow) | |
55
- | Flags tests with no real assertions | ❌ | ❌ | sometimes | ✅ |
56
- | Catches CI false-greens (`\|\| true`, `continue-on-error`) | ❌ | ❌ | rarely | ✅ |
57
- | Reads **real** run data for `TRUE-FLAKE` verdicts | ❌ | ❌ || ✅ |
58
- | Grades Playwright locator resilience || ❌ | rarely | ✅ |
59
- | Runs in seconds, zero network calls | ✅ | ✅ | — | ✅ |
51
+ | | ESLint / SonarQube | Coverage tools | Manual review | **Mjölnir** |
52
+ | -------------------------------------------------------- | :----------------: | :------------: | :-----------: | :---------: |
53
+ | CI workflow integrity (`continue-on-error`, `\|\| true`) | | ❌ | rarely | ✅ |
54
+ | Cross-language (TS, Python, Java, C#) from one tool | ❌ | ❌ || ✅ |
55
+ | Grades Playwright locator resilience (Selector Health) | ❌ | ❌ | rarely | ✅ |
56
+ | Flags tests with no real assertions | ✅ (plugin)\* | ❌ | sometimes | ✅ |
57
+ | Catches hard sleeps (`waitForTimeout`, `time.sleep`) | ✅ (plugin)\* | ❌ | sometimes | ✅ |
58
+ | Runs in seconds, zero network calls while scanning | ✅ | ✅ | — | ✅ |
59
+
60
+ \*`eslint-plugin-jest` (`expect-expect`) and `eslint-plugin-playwright` (`expect-expect`, `no-wait-for-timeout`) cover these for their respective frameworks.
61
+
62
+ **Runtime Analysis** — a separate category from static linting:
63
+
64
+ | | Playwright retry reporter | Allure / ReportPortal | **Mjölnir forensics** |
65
+ | --------------------------------------------- | :-----------------------: | :-------------------: | :-------------------: |
66
+ | Reads real run data for `TRUE-FLAKE` verdicts | partial\* | partial (tag) | ✅ |
67
+ | Flaky-triage report from execution history | ❌ | ✅ | ✅ |
68
+ | Integrates with static worthiness score | ❌ | ❌ | ✅ |
69
+
70
+ \*Playwright tracks retries internally but does not produce a standalone flakiness report with verdict labels.
71
+
72
+ ---
73
+
74
+ ## 🤖 Why not just use AI code review?
75
+
76
+ AI reviewers are powerful — but they're expensive, intermittent, and reactive.
77
+ Mjölnir is cheap, continuous, and structural. They complement each other, but
78
+ only one of them catches the `continue-on-error` that's been silently hiding
79
+ failures since the repo was created.
80
+
81
+ | | AI code review (Copilot, etc.) | **Mjölnir** |
82
+ | ------------------------------------- | :----------------------------: | :-------------------------: |
83
+ | Cost per scan | Tokens (scales with diff size) | **Zero** (local, installed) |
84
+ | Sees the whole suite + all CI configs | Only the PR diff you show it | **Everything, every time** |
85
+ | Deterministic (same input → same out) | ❌ (non-deterministic) | **✅** |
86
+ | Catches patterns dormant for months | Only if it's in the context | **✅** (scans all files) |
87
+ | Remembers findings between runs | ❌ (no memory across sessions) | **✅** (baseline + diff) |
88
+ | Runs without human triggering | Needs a PR or prompt | **✅** (CI hook, 3 seconds) |
89
+ | Attention budget | Limited by context window | **Unlimited** (every file) |
90
+
91
+ **The honest answer:** Use both. AI catches nuance, intent, and design flaws
92
+ that no regex can find. Mjölnir catches the structural patterns that AI
93
+ overlooks because they look "intentional" — a committed `.only`, a swallowed
94
+ exit code, a `continue-on-error` on a test job. Those aren't bugs that need
95
+ reasoning; they're facts that need scanning. And scanning is what a
96
+ deterministic tool does better, faster, and for free.
60
97
 
61
98
  ---
62
99
 
63
100
  ## ⚡ Quickstart
64
101
 
102
+ **The product is one command in CI:**
103
+
104
+ ```bash
105
+ npx mjolnir-qa@latest --scope changed
106
+ ```
107
+
108
+ It scans the test files, Playwright configs and CI workflows touched in the
109
+ branch, and exits non-zero when it finds new problems. Drop that in a PR check
110
+ (`mjolnir ci install` writes the workflow) and you're done. Everything below is
111
+ optional.
112
+
113
+ Run it with no flags for a full-repo report and a WORTHINESS score:
114
+
65
115
  ```bash
66
116
  npx mjolnir-qa@latest
67
117
  ```
68
118
 
69
- That's it. Zero configuration — Mjölnir detects your frameworks, finds your
70
- tests, and reports.
71
-
72
- | Command | What it does |
73
- | ------------------------------------------------------ | ------------------------------------------------------------------- |
74
- | `npx mjolnir-qa@latest --scope changed` | Only findings introduced by **your** changes — perfect for PRs |
75
- | `npx mjolnir-qa@latest --json` | Machine-readable output |
76
- | `npx mjolnir-qa@latest --format sarif > mjolnir.sarif` | GitHub Code Scanning integration |
77
- | `npx mjolnir-qa@latest --format mermaid` | Test-architecture diagram — paste into a GitHub comment or a slide |
78
- | `mjolnir doctor:playwright` | Playwright-only deep scan + Selector Health Score |
79
- | `mjolnir forensics ./test-results/` | Runtime evidence — retries, true flakes, `FLAKY.md` artifact |
80
- | `mjolnir triage ./test-results/` | The flaky-triage meeting, in 10 minutes instead of 45 |
81
- | `mjolnir fix --dry-run` / `fix` | Safe auto-fixes with proof (dry-run first) |
82
- | `mjolnir debt` | Test debt register — presentable to management |
83
- | `mjolnir handover` | New-QA-onboarding map of the suite |
84
- | `mjolnir pw-report ./test-results/` | Playwright run summary — retries / flakes / slowest |
85
- | `mjolnir badge` | Evidentiary badge (shields.io endpoint JSON) |
86
- | `mjolnir doctor` | Self-audit — prove Mjölnir's own rule base is worthy |
87
- | `mjolnir rules` / `rules --md` | Rule catalog with trust metadata (JSON or markdown) |
88
- | `mjolnir explain <RULE-ID>` | What/why/fix for one rule, with a real example from its own fixture |
89
- | `mjolnir impact [--since <ref>]` | What changed since a prior commit — fixes and new debt |
119
+ ### Everyday
90
120
 
91
- ---
121
+ | Command | What it does |
122
+ | ----------------------------------- | ------------------------------------------------ |
123
+ | `mjolnir --scope changed` | Only what your branch introduced — the CI form |
124
+ | `mjolnir` | Full-repo scan + WORTHINESS score |
125
+ | `mjolnir --json` / `--format sarif` | Machine-readable / GitHub Code Scanning |
126
+ | `mjolnir ci install` | Generate the advisory PR workflow |
127
+ | `mjolnir explain <RULE-ID>` | What/why/fix + measured FP rate for one rule |
128
+ | `mjolnir rules --unmeasured` | The rules running on assumption, not measurement |
129
+ | `mjolnir --strict` | Also run quarantine-tier rules (higher FP risk) |
92
130
 
93
- ## ⚖️ Worthiness Score
131
+ ### When something's flaky
94
132
 
95
- ```text
96
- 🔨 MJÖLNIR
133
+ | Command | What it does |
134
+ | ----------------------------------- | --------------------------------------------------- |
135
+ | `mjolnir forensics ./test-results/` | Real run data → `TRUE-FLAKE` verdicts, `FLAKY.md` |
136
+ | `mjolnir triage ./test-results/` | Quarantine proposal from execution history |
137
+ | `mjolnir pw-report ./test-results/` | Playwright run summary — retries / flakes / slowest |
138
+ | `mjolnir doctor:playwright` | Playwright-only deep scan + Selector Health Score |
97
139
 
98
- WORTHINESS 80/100 — WORTHY
99
- ████████████████████████░░░░░░
140
+ <details>
141
+ <summary><strong>Occasional / reporting</strong></summary>
142
+
143
+ | Command | What it does |
144
+ | -------------------------------- | ------------------------------------------------ |
145
+ | `mjolnir fix --dry-run` / `fix` | Safe auto-fixes with proof |
146
+ | `mjolnir baseline` / `diff` | Snapshot findings, then report only new/worsened |
147
+ | `mjolnir impact [--since <ref>]` | What changed since a prior commit |
148
+ | `mjolnir debt` | Test-debt register with a cost model |
149
+ | `mjolnir handover` | New-QA onboarding map of the suite |
150
+ | `mjolnir stats` | Local all-time counters of fixes seen |
151
+ | `mjolnir badge` | shields.io endpoint JSON + snippet |
152
+ | `mjolnir rules` / `rules --md` | Full rule catalog (JSON or Markdown) |
153
+ | `mjolnir doctor` | Self-audit of Mjölnir's own rule base |
154
+ | `mjolnir create-rule <ID>` | Scaffold a new rule + fixtures |
155
+ | `mjolnir --format mermaid` | Test-architecture diagram for a PR comment |
100
156
 
101
- DETECTED [playwright]
157
+ </details>
102
158
 
103
- ▚ FIX THIS FIRST
104
- +8 pts QA-CI-001 · .github/workflows/ci.yml:48
105
- +3 pts QA-TEST-004 · e2e/checkout.spec.ts:6
106
- ```
159
+ ---
107
160
 
108
- The score is transparent: error −8, warning −3, info −1. Evidence-weighted
109
- deductions mean weak signals cost less. The terminal shows the same discounted
110
- numbers the score uses — no black box.
161
+ ## ⚖️ Worthiness Score
162
+
163
+ <p align="center">
164
+ <img src="assets/readme/terminal-hero.svg" alt="Mjölnir terminal output — WORTHINESS 67/100 NEEDS WORK, a diagnostics-by-category breakdown, and a FIX THIS FIRST list" width="760" />
165
+ </p>
166
+
167
+ <sub>Real output of `npx mjolnir-qa ./examples/demo-repo` in this repo. Regenerated by `npm run docs:hero`; [`tests/hero-asset-reproducibility.spec.ts`](tests/hero-asset-reproducibility.spec.ts) fails CI if it drifts from what the reporter actually prints.</sub>
168
+
169
+ The score is transparent: error −8, warning −3, info −1, then normalized by
170
+ suite exposure (deductions per test declaration). Evidence-weighted deductions
171
+ mean weak signals cost less. The terminal shows the same discounted numbers the
172
+ score uses — no black box.
111
173
 
112
174
  **Verdicts:**
113
175
 
@@ -117,6 +179,29 @@ numbers the score uses — no black box.
117
179
  | 50 – 79 | ⚠ **NEEDS WORK** |
118
180
  | < 50 | ✖ **UNWORTHY** |
119
181
 
182
+ **Evidence Levels:**
183
+
184
+ Every finding carries an evidence level that determines its weight in the score:
185
+
186
+ | Level | Meaning | Score impact | Example |
187
+ | ----- | -------------------- | ---------------- | ---------------------------------------------------------------------------------- |
188
+ | E2 | Deterministic defect | Full deduction | `.only` committed — structurally provable; downgraded to E1 when confidence is low |
189
+ | E1 | Heuristic pattern | Half deduction | Regex-matched `sleep()` — strong signal, not proof |
190
+ | E0 | Observation | Zero (info only) | Reported but never gates CI or deducts |
191
+
192
+ Most rules are **E1** (heuristic). The tagline "we prove it" refers to this
193
+ evidence-level system — deterministic findings (E2) are structural proof;
194
+ heuristic findings (E1) are correctly-positioned warnings, not formal proofs.
195
+
196
+ **How much of this is measured.** 15 of 91 rules carry a false-positive rate
197
+ measured against real OSS code (≥ 10 hand-classified findings each; see
198
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md)). The other 76 ship on the author's
199
+ estimate. Every scan footer tells you how many of the rules that _fired_ are
200
+ measured; `mjolnir rules --unmeasured` lists the ones that aren't; every rule's
201
+ `mjolnir explain` page states its status. We publish the rate even when it's
202
+ ugly — QA-JV-103 audits at 50% and is quarantined for it. Growing that 15
203
+ is the project's main open work.
204
+
120
205
  ---
121
206
 
122
207
  ## 📋 The Rules
@@ -194,9 +279,49 @@ firewall.
194
279
  | QA-PY-010 | Random/time dependence without freeze | warning |
195
280
  | QA-PY-012 | Tautological assertion | error |
196
281
 
282
+ 20 Python rules total (QA-PY-001…012 pytest hygiene + QA-PY-101…108 Playwright-Python).
283
+
197
284
  </details>
198
285
 
199
- > The full live catalog — every rule with confidence, false-positive risk,
286
+ <details>
287
+ <summary><strong>Java / JUnit · TestNG ☕</strong></summary>
288
+
289
+ | ID | Rule | Severity |
290
+ | --------- | ---------------------------------------- | -------- |
291
+ | QA-JV-101 | Disabled test (`@Disabled`) | warning |
292
+ | QA-JV-102 | Hard sleep (`Thread.sleep()`) | warning |
293
+ | QA-JV-103 | Test method with no assertions | error |
294
+ | QA-JV-105 | Playwright `waitForTimeout()` hard sleep | warning |
295
+ | QA-JV-106 | Brittle selector instead of role locator | warning |
296
+ | QA-JV-108 | Hardcoded environment URL in test | warning |
297
+ | QA-JV-111 | Blanket `page.route("**")` mock | warning |
298
+
299
+ </details>
300
+
301
+ <details>
302
+ <summary><strong>C# / .NET — NUnit · xUnit · MSTest 🟣</strong></summary>
303
+
304
+ | ID | Rule | Severity |
305
+ | --------- | ------------------------------------------ | -------- |
306
+ | QA-CS-101 | Skipped test (`[Ignore]`, `[Fact(Skip=)]`) | warning |
307
+ | QA-CS-102 | Hard sleep (`Thread.Sleep` / `Task.Delay`) | warning |
308
+ | QA-CS-103 | Test method with no assertions | error |
309
+ | QA-CS-105 | `WaitForTimeoutAsync()` hard sleep | warning |
310
+ | QA-CS-106 | Brittle selector instead of role locator | warning |
311
+ | QA-CS-108 | Hardcoded environment URL in test | warning |
312
+ | QA-CS-111 | Blanket `page.RouteAsync("**")` mock | warning |
313
+
314
+ </details>
315
+
316
+ **Rule tiers.** Every rule is `core`, `extended`, or `quarantine`, assigned from
317
+ its **measured** false-positive rate ([docs/FP-AUDIT.md](docs/FP-AUDIT.md)):
318
+ `core` ≤ 10 % FP, `extended` ≤ 30 %, `quarantine` above that or not yet measured.
319
+ The default scan runs core + extended; `--strict` adds quarantine. The Java and
320
+ C# families are newer and mostly `extended`/`quarantine` today — they ship,
321
+ they're documented, and they stay out of the headline number until a real
322
+ consumer suite (not a binding-library's own tests) has been audited.
323
+
324
+ > The full live catalog — every rule with tier, confidence, false-positive risk,
200
325
  > and autofix availability — is generated from the registry:
201
326
  >
202
327
  > ```bash
@@ -285,6 +410,10 @@ Or wire it into GitHub Code Scanning natively via SARIF:
285
410
  - **FP firewall** — detection runs on a comment/string-free view of the code
286
411
  (TypeScript rules use the compiler AST): a pattern inside a prose comment
287
412
  or a doc-example string is documentation, not a finding.
413
+ - **Measured, not asserted** — 15 of 91 rules carry a false-positive rate from
414
+ real OSS code; the scan footer, `mjolnir rules --unmeasured`, and every
415
+ `mjolnir explain` page tell you which rules are which.
416
+ [docs/FP-AUDIT.md](docs/FP-AUDIT.md) has the numbers, ugly ones included.
288
417
 
289
418
  ---
290
419
 
@@ -302,10 +431,12 @@ rejected from plugins to prevent spoofing.
302
431
  ## 🔍 Changed-Scope Coverage
303
432
 
304
433
  `--scope changed` attributes findings to lines added in your branch vs the
305
- merge-base with `main`. It covers test files (`*.spec.*`, `*.test.*`) plus
306
- GitHub workflow files and Playwright configs changed in the diff. On shallow
307
- clones or non-git targets it degrades honestly: findings fall back to
308
- full-file attribution and the report says so.
434
+ merge-base with `main` (the base branch is `main`; a repo that uses `master`
435
+ or a shallow clone with no merge-base degrades see below). It covers test
436
+ files (`*.spec.*`, `*.test.*`) plus GitHub workflow files and Playwright
437
+ configs changed in the diff. When the merge-base can't be resolved — shallow
438
+ clone, detached HEAD, non-git target, different default branch — it degrades
439
+ honestly: findings fall back to full-file attribution and the report says so.
309
440
 
310
441
  ---
311
442
 
@@ -326,7 +457,7 @@ mjolnir/
326
457
  │ ├── scorer/ # transparent deduction table
327
458
  │ ├── reporter/ # terminal · JSON · SARIF 2.1 · Mermaid
328
459
  │ ├── forensics/ # run-data ingestion · flake verdicts · triage
329
- │ ├── commands/ # fix · badge · debt · handover · init · create-rule · doctor · rules-catalog · explain · impact · baseline · diff · pr-comment · stats
460
+ │ ├── commands/ # badge · baseline+diff · create-rule · debt · doctor · explain · fix · handover · impact · init · pr-comment · pw-report · rules · stats
330
461
  │ └── integrations/ # CI workflow generator
331
462
  └── tests/
332
463
  ├── fixtures/ # must-fire / must-not-fire per rule
@@ -335,10 +466,16 @@ mjolnir/
335
466
 
336
467
  </details>
337
468
 
338
- Multi-language by design: adding a language = one adapter + its rules.
339
- TypeScript/Playwright and Python are the most mature; Java and C#/.NET
340
- adapters ship a regex-based core rule family with tree-sitter WASM AST
341
- precision as the next step.
469
+ Multi-language by design: adding an ecosystem (Ruby/RSpec, Go, PHPUnit…) =
470
+ one adapter + its rules. Playwright itself has exactly four official
471
+ language bindings TypeScript, Python, Java, .NET and all four have an
472
+ adapter here.
473
+ TypeScript/Playwright uses the compiler AST (ts-morph); Python, Java, and
474
+ C#/.NET run on a shared comment/string-masked regex layer. A tree-sitter
475
+ WASM AST layer for Java and C# exists (`src/engine/tree-sitter-ast.ts`) and
476
+ is the next precision step — it is not yet wired into the synchronous scan
477
+ pipeline. TypeScript/Playwright and Python are the most battle-tested by
478
+ measured false-positive rate; Java and C# are newer (see **Rule tiers** above).
342
479
 
343
480
  ---
344
481
 
@@ -352,7 +489,7 @@ npx mjolnir-qa@latest
352
489
  npm i -g mjolnir-qa
353
490
  ```
354
491
 
355
- Requires Node.js ≥ 22.18. Works on Windows, macOS, and Linux.
492
+ Requires Node.js ≥ 22.18 (required by the tsdown bundler for native ESM support). Works on Windows, macOS, and Linux.
356
493
 
357
494
  ---
358
495
 
package/dist/cli.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/types.d.ts
2
2
  /**
3
- * QA Doctor — canonical types (JSON contract v1, schemaVersion 1).
3
+ * Mjolnir — canonical types (JSON contract v1, schemaVersion 1).
4
4
  *
5
5
  * STABILITY: This file is public API. Per Product-MVP.txt §24.2:
6
6
  * additive changes only within schemaVersion 1; removing or renaming
@@ -48,6 +48,15 @@ interface Finding {
48
48
  * Optional in the JSON contract (additive within schemaVersion 1).
49
49
  */
50
50
  evidenceLevel?: EvidenceLevel;
51
+ /**
52
+ * Measured false-positive rate (0..1) for the rule that produced this
53
+ * finding, from hand-classified corpus verdicts — present only when the
54
+ * rule has ≥ 10 classified verdicts. Absent means the rule ships on
55
+ * assumption. Additive within schemaVersion 1.
56
+ */
57
+ measuredFpRate?: number;
58
+ /** Classified (TP+FP) verdicts behind `measuredFpRate`. */
59
+ measuredFpN?: number;
51
60
  /** Repo-relative path with forward slashes, regardless of OS. */
52
61
  file: string;
53
62
  /** 1-based. */
@@ -84,6 +93,14 @@ interface ScanResult {
84
93
  frameworkDetectionUnknown: boolean;
85
94
  dimensions: DimensionScore[];
86
95
  findings: Finding[];
96
+ /** Number of test files scanned (Phase 5 — reporting only). */
97
+ testFileCount?: number;
98
+ /** Test declarations found — the normalization denominator (Phase 5). */
99
+ testDeclarationCount?: number;
100
+ /** Raw deduction total before normalization (Phase 5 — transparency). */
101
+ rawDeductions?: number;
102
+ /** Number of findings suppressed by active config entries (suppression transparency). */
103
+ suppressionCount?: number;
87
104
  analysisStatus: {
88
105
  discovery: AnalysisStatus;
89
106
  rules: AnalysisStatus;
@@ -93,6 +110,17 @@ interface ScanResult {
93
110
  }
94
111
  //#endregion
95
112
  //#region src/cli.d.ts
113
+ /**
114
+ * Tool version for `mjolnir --version`.
115
+ *
116
+ * A literal, not a package.json read: the shipped artifact is a single
117
+ * bundled `dist/cli.mjs`, so resolving package.json at runtime depends on
118
+ * where the file happens to sit after install. This follows the same
119
+ * discipline as SARIF's `driver.version` — kept in sync by
120
+ * `scripts/sync-sarif-version.cjs` on release and guarded by
121
+ * `tests/version-consistency.spec.ts` locally.
122
+ */
123
+ declare const CLI_VERSION = "0.5.0";
96
124
  interface CliArgs {
97
125
  target: string;
98
126
  json: boolean;
@@ -106,6 +134,8 @@ interface CliArgs {
106
134
  ascii?: boolean;
107
135
  /** --tone blunt: opt-in blunter messages (Sprint 9 Task 40). */
108
136
  tone?: "blunt";
137
+ /** --strict: include quarantine-tier rules in the scan (Phase 4). */
138
+ strict?: boolean;
109
139
  }
110
140
  declare function parseArgs(argv: string[]): CliArgs | null;
111
141
  declare function runScan(args: CliArgs): ScanResult;
@@ -231,4 +261,4 @@ declare function runPwReportCommand(argv: string[], io?: {
231
261
  declare function main(argv?: string[]): number;
232
262
  declare function isEntryPoint(): boolean;
233
263
  //#endregion
234
- export { Output, isEntryPoint, main, parseArgs, pathMatchesGlob, runBadgeCommand, runBaselineCommand, runCiInstall, runCreateRuleCommand, runDebtCommand, runDiffCommand, runDoctorCommand, runDoctorPlaywright, runExplainCommand, runFixCommand, runForensicsCommand, runHandoverCommand, runImpactCommand, runInitCommand, runPrCommentCommand, runPwReportCommand, runRulesCommand, runScan, runScanCommand, runStatsCommand, runSuppressions, runTriageCommand };
264
+ export { CLI_VERSION, Output, isEntryPoint, main, parseArgs, pathMatchesGlob, runBadgeCommand, runBaselineCommand, runCiInstall, runCreateRuleCommand, runDebtCommand, runDiffCommand, runDoctorCommand, runDoctorPlaywright, runExplainCommand, runFixCommand, runForensicsCommand, runHandoverCommand, runImpactCommand, runInitCommand, runPrCommentCommand, runPwReportCommand, runRulesCommand, runScan, runScanCommand, runStatsCommand, runSuppressions, runTriageCommand };