mjolnir-qa 5.0.0 → 6.0.1

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/dist/cli.d.mts CHANGED
@@ -1,78 +1,3 @@
1
- //#region src/v6/test-doubles.d.ts
2
- /** How serious the pattern is. Closed enum, so a renderer cannot invent a
3
- * fourth severity at a call site. */
4
- declare const DOUBLE_RISKS: readonly ["NO_ASSERTION", "DOUBLE_ONLY", "DOUBLE_MIXED", "DOUBLE_SUBJECT"];
5
- type DoubleRisk = (typeof DOUBLE_RISKS)[number];
6
- interface DoubleFinding {
7
- risk: DoubleRisk;
8
- /** How many assertions there are in total. */
9
- assertions: number;
10
- /** How many are about a double. */
11
- doubleAssertions: number;
12
- /** How many carry real evidence about the code under test. */
13
- substantiatingAssertions: number;
14
- /** 0..1 — the share of assertions that prove nothing. */
15
- hollowRatio: number;
16
- /** The one-line statement a surface can render verbatim. */
17
- statement: string;
18
- }
19
- /**
20
- * A roll-up across a suite. A suite is hollow when *no* test in it
21
- * substantiates anything — which is a different and stronger statement
22
- * than "some tests are hollow", and the one a user needs at the top of a
23
- * report.
24
- */
25
- declare function assessSuite(findings: readonly DoubleFinding[]): {
26
- tests: number;
27
- hollow: number;
28
- substantiating: number;
29
- hollowRatio: number;
30
- verdict: "SUBSTANTIATED" | "PARTIALLY_HOLLOW" | "HOLLOW";
31
- };
32
- //#endregion
33
- //#region src/v6/tool-coverage.d.ts
34
- /** What the engine can do with a detected tool. Closed enum, so a
35
- * renderer cannot invent a fifth state. */
36
- declare const TOOL_COVERAGE_STATES: readonly ["UNDERSTOOD", "DECLARED", "OUT_OF_SCOPE", "IGNORED"];
37
- type ToolCoverageState = (typeof TOOL_COVERAGE_STATES)[number];
38
- interface ToolCoverageEntry {
39
- name: string;
40
- state: ToolCoverageState;
41
- /** The census category, when one applies. */
42
- category: string | null;
43
- /** The census entry id, when the engine has one. */
44
- censusId: string | null;
45
- /** What the cost of not handling it is, in one sentence. */
46
- consequence: string;
47
- }
48
- interface BlindSpot {
49
- id: string;
50
- /** What the engine cannot see. */
51
- blindTo: string;
52
- /** Why it matters, in one sentence. */
53
- consequence: string;
54
- }
55
- interface ToolCoverage {
56
- /** Every QA tool the probe saw, with what the engine does about it. */
57
- tools: readonly ToolCoverageEntry[];
58
- counts: Readonly<Record<ToolCoverageState, number>>;
59
- /** Tools the engine sees and cannot act on. */
60
- unhandled: readonly ToolCoverageEntry[];
61
- /**
62
- * What the engine cannot see **through**, whether or not the tool is
63
- * known. This is the part a user cannot infer, and the part that makes
64
- * a green run mean something.
65
- */
66
- blindSpots: readonly BlindSpot[];
67
- /** The double-analysis roll-up, when the caller supplied test data. */
68
- suite: ReturnType<typeof assessSuite> | null;
69
- /** One line a surface may render verbatim. */
70
- statement: string;
71
- /** Never a claim of completeness. */
72
- complete: false;
73
- }
74
- //#endregion
75
- //#region src/types.d.ts
76
1
  /**
77
2
  * Mjolnir — canonical types (JSON contract v1, schemaVersion 1).
78
3
  *
@@ -134,6 +59,23 @@ type RuleCategory = (typeof RULE_CATEGORIES)[number];
134
59
  * retried, or timed out in the report).
135
60
  * INVARIANT (structurally enforced): L3–L5 require runtime
136
61
  * corroboration — a static-only finding can never claim L4/L5.
62
+ *
63
+ * THREE LADDER VOCABULARIES now share this shape, and the letters are the
64
+ * only thing distinguishing them:
65
+ *
66
+ * `L0`–`L5` RUNTIME TRUST — how much a consumer can believe one finding.
67
+ * Here.
68
+ * `F0`–`F5` FRAMEWORK SUPPORT — a hand-declared target per framework.
69
+ * `FrameworkSupportLevel`, src/frameworks/framework-inventory.ts
70
+ * `M0`–`M5` CAPABILITY MATURITY — DERIVED from evidence, and the only
71
+ * one of the three that is a measurement.
72
+ * `Maturity`, src/v6/maturity.ts
73
+ *
74
+ * `tests/v6/maturity-vocabulary.spec.ts` holds the two vocabulary sets apart
75
+ * and fails if they ever share a member. `M` is the only DERIVED of the three,
76
+ * and that is the distinction worth remembering when reading a level out of a
77
+ * report: `F` and `L` are declarations about the world, `M` is a function of
78
+ * what this repository can demonstrate.
137
79
  */
138
80
  declare const TRUST_ORDER: readonly ["L0", "L1", "L2", "L3", "L4", "L5"];
139
81
  type TrustLevel = (typeof TRUST_ORDER)[number];
@@ -154,7 +96,13 @@ interface RuntimeCorroboration {
154
96
  * constrains TRUE-FLAKE derivation, which lives in the analysis, not
155
97
  * in the provenance label.
156
98
  */
157
- source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har";
99
+ source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har" |
100
+ /**
101
+ * Visual Studio TRX (`dotnet test`). Added so .NET runs can corroborate at
102
+ * all — previously the union had no member a TRX source could inhabit, so a
103
+ * C# run was unrepresentable rather than merely unrecognised.
104
+ */
105
+ "dotnet-trx";
158
106
  /** Number of tests executed in the finding's file (any level). */
159
107
  testsExecuted: number;
160
108
  /**
@@ -276,14 +224,16 @@ interface Finding {
276
224
  /**
277
225
  * Mutation evidence (master plan P5, plan 1788853205786 — flag 6,
278
226
  * decision 8): provenance from a mutation-testing report
279
- * (`mjolnir mutation <report>`), stamped on matching findings.
227
+ * (a Stryker or Mutmut report — the `mutation` verb is retired in the
228
+ * v6 carve), stamped on matching findings.
280
229
  *
281
230
  * PROVENANCE IS NOT TRUTH: a survived mutant is code the suite would
282
231
  * not notice changing — evidence FOR a nearby finding, never a claim
283
- * the finding is "proven real". The E1→E2 consolidation is BY
284
- * DERIVATION and lives in src/mutation/derive.ts (documented in
285
- * docs/RULE-LIFECYCLE.md + the machine-contract docs). trustLevel never
286
- * rises from mutation evidence alone: nothing ran.
232
+ * the finding is "proven real". The E1→E2 consolidation was BY
233
+ * DERIVATION, and lived with the `mutation` verb the v6 carve removed;
234
+ * the rule is documented in docs/RULE-LIFECYCLE.md and the
235
+ * machine-contract docs. trustLevel never rises from mutation evidence
236
+ * alone: nothing ran.
287
237
  */
288
238
  interface MutationEvidence {
289
239
  /** Which mutation tool produced the report. */
@@ -500,6 +450,45 @@ interface ScanResult {
500
450
  */
501
451
  rulesCrashed?: number;
502
452
  parseFallbacks?: number;
453
+ /**
454
+ * How much of the rule registry this scan could see.
455
+ *
456
+ * ORTHOGONAL TO `partial`, and that is the contract. `partial` means
457
+ * "this run lost something while doing it" and is what `scanExitCode`,
458
+ * SARIF `executionSuccessful` and generated-CI gating read. A rule
459
+ * removed from the set before the walk started never ran, so nothing
460
+ * about it was lost mid-run — and folding it into `partial` would make
461
+ * every ordinary non-`--strict` scan exit 2, at which point nobody
462
+ * reads the field.
463
+ *
464
+ * `PARTIAL` means at least one quarantined rule was withheld. It is the
465
+ * disclosure that `analysisStatus.rules: "complete"` cannot make: a
466
+ * scan can read every file it was given, parse all of them, crash no
467
+ * rule, and still have run 45 of 79 detectors.
468
+ */
469
+ coverageState?: "COMPLETE" | "PARTIAL";
470
+ /**
471
+ * Rules that ran, and rules withheld from the run. Their sum is the
472
+ * rule set this scan was offered; the registry total is in
473
+ * `coverageState`'s reason (`coverage:quarantine:<n>` in `reasons`).
474
+ */
475
+ rulesApplied?: number;
476
+ rulesWithheld?: number;
477
+ /**
478
+ * WHICH rules were withheld, when the count is non-zero.
479
+ *
480
+ * The count answers "how much of the registry did this scan not see";
481
+ * this answers "which". It exists because a consumer that has to
482
+ * classify one finding — lifecycle resolution must, because a
483
+ * quarantined rule that never ran cannot have fixed anything — cannot
484
+ * answer from a number. The count alone forced those consumers to
485
+ * claim what they could not know.
486
+ *
487
+ * Absent means the producer did not report the set. It does NOT mean
488
+ * nothing was withheld: `rulesWithheld` is the authority on that, and
489
+ * this field is only the disambiguator.
490
+ */
491
+ withheldRuleIds?: string[];
503
492
  };
504
493
  /**
505
494
  * Scoring model version stamped into the result (ENGINE-001). Allows
@@ -508,6 +497,22 @@ interface ScanResult {
508
497
  * this field.
509
498
  */
510
499
  scoringModelVersion?: string;
500
+ /**
501
+ * Why `score` is 99 when the scorer computed 100 (contract v2).
502
+ *
503
+ * Two sites clamp: a degraded scope, and a partial scan. The clamp itself
504
+ * is right — 100 is the strongest claim the scorer makes and neither
505
+ * condition permits it — but the RESULT was byte-identical to a genuine 99,
506
+ * so a reader could not distinguish "withheld to 99" from "scored 99"
507
+ * without reading the pipeline. The presentation surfaces render the
508
+ * reason rather than the bare number.
509
+ *
510
+ * Absent means the score is exactly what the scorer computed. The field
511
+ * lives on `ScanResult`, NOT inside `runIdentity`: it describes the score,
512
+ * and `runIdentity` is the machine anchor whose digest must not change
513
+ * because a presentation reason was added.
514
+ */
515
+ scoreClampReason?: "scope-degraded" | "partial-scan";
511
516
  /**
512
517
  * Scope Integrity block (product-gap master plan §7, R4c): the
513
518
  * claimed-vs-analyzed accounting. `scopeVerdict` is PROVEN only when
@@ -533,18 +538,6 @@ interface ScanResult {
533
538
  /** The named scope reasons, present only when PARTIAL. */
534
539
  reasons?: string[];
535
540
  };
536
- /**
537
- * Tool coverage (v6): which QA tooling this scan actually saw, what the
538
- * engine does about it, and what it is blind to.
539
- *
540
- * Present when the scan ran the ecosystem probe. Additive within
541
- * schemaVersion 1; absent when the producer predates this field.
542
- *
543
- * complete is always false. A scan that could not see through a test
544
- * double must never render as a scan with no blind spots, and that is
545
- * the one claim a verification tool cannot make.
546
- */
547
- toolCoverage?: ToolCoverage;
548
541
  /**
549
542
  * Run Identity (R4c): the deterministic anchor binding verdict ←
550
543
  * evidence ← execution ← scope ← source ← rule(rev). Present when the
@@ -745,22 +738,16 @@ interface ForensicVerdictSummary {
745
738
  /** INCONCLUSIVE outcome count — a first-class result, not a failure. */
746
739
  inconclusive: number;
747
740
  }
748
- //#endregion
749
- //#region src/engine/tier-policy.d.ts
750
741
  type Tier = "core" | "extended" | "quarantine";
751
- //#endregion
752
- //#region src/engine/overlap-dedup.d.ts
753
742
  /** Per-rule metadata the dedup pass needs, built from RULES. */
754
743
  interface OverlapMeta {
755
744
  /** Rule IDs this rule can double-report on the same root cause. */
756
745
  overlapWith?: string[];
757
- /** Tier rank for survivor resolution (missing = core). */
746
+ /** Tier rank for survivor resolution (missing = extended, see below). */
758
747
  tier?: Tier;
759
748
  /** RULES registry order for final tie-breaking. */
760
749
  order?: number;
761
750
  }
762
- //#endregion
763
- //#region src/forensics/classify.d.ts
764
751
  declare const FORENSIC_VERDICTS: readonly ["likely-real-defect", "environmental-failure", "infrastructure-failure", "flaky", "retry-dependent", "unstable-construction", "inconclusive"];
765
752
  type ForensicVerdict = (typeof FORENSIC_VERDICTS)[number];
766
753
  declare const EVIDENCE_STATES: readonly ["exists", "absent", "unsupported", "contradictory", "insufficient"];
@@ -775,8 +762,6 @@ interface ForensicClassification {
775
762
  construction: number;
776
763
  };
777
764
  }
778
- //#endregion
779
- //#region src/forensics/types.d.ts
780
765
  type RunStatus = "passed" | "failed" | "timedOut" | "skipped" | "interrupted";
781
766
  /** Derived per-test reliability facts. */
782
767
  interface TestVerdict {
@@ -825,7 +810,7 @@ interface ForensicsReport {
825
810
  * `playwright-trace`: one record per trace artifact (the per-test
826
811
  * trace convention), action-level facts only.
827
812
  */
828
- source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har";
813
+ source: "playwright-json" | "junit-xml" | "dotnet-trx" | "jest-json" | "vitest-json" | "playwright-trace" | "har";
829
814
  totalTests: number;
830
815
  failed: number;
831
816
  skipped: number;
@@ -847,80 +832,6 @@ interface ForensicsReport {
847
832
  /** Named reasons for incompleteness (e.g. "size-limit", "cumulative-size-limit", "parse-failure"). */
848
833
  incompleteReasons: string[];
849
834
  }
850
- //#endregion
851
- //#region src/engine/dependency-graph.d.ts
852
- /**
853
- * Dependency Graph (ECO-005).
854
- *
855
- * Parses package manifests (package.json, pyproject.toml, pom.xml) to
856
- * build a dependency graph. Supports transitive dependency resolution,
857
- * dependent lookup, and reachable-file computation from test files.
858
- */
859
- interface DependencyNode {
860
- path: string;
861
- dependencies: string[];
862
- }
863
- declare class DependencyGraph {
864
- private readonly nodes;
865
- addNode(node: DependencyNode): void;
866
- getDependencies(path: string): string[];
867
- getTransitiveDependencies(path: string): string[];
868
- getDependents(path: string): string[];
869
- get allPaths(): string[];
870
- get size(): number;
871
- /** Whether the graph knows this exact key. Reachability must not
872
- * treat "not in the graph" as "nothing to traverse" without saying so. */
873
- has(path: string): boolean;
874
- }
875
- //#endregion
876
- //#region src/engine/degradation-ledger.d.ts
877
- /**
878
- * One ledger for every silent capability loss.
879
- *
880
- * The defect class this exists to kill is not a deprecation warning. It is a
881
- * `catch` in a DETECTION path that swallows an error and returns a clean
882
- * default, so the scan still reports `analysisComplete` while quietly running
883
- * with less capability than the reader was promised. Fifteen such sites were
884
- * verified in this tree; the AST-layer ones are the worst, because the layer
885
- * they degrade is the one that keeps prose comments and sample strings from
886
- * becoming false-positive findings.
887
- *
888
- * Two shapes, one rule. The precedent already in the tree is
889
- * `parserRetryDegradationCount` (`src/engine/tree-sitter-ast.ts`): a counted,
890
- * readable number rather than a swallowed exception. This generalizes it —
891
- * every site records a REASON from the closed set below, and the scan result
892
- * carries the per-reason counts, so "the parser failed" becomes
893
- * "the AST mask was unavailable for 3 files".
894
- *
895
- * The ledger is APPEND-ONLY and process-scoped, which has one consequence
896
- * worth stating because it is the security-relevant direction: `beginWindow`
897
- * returns a MARKER, not an ownership claim. Two concurrent `runScan` calls in
898
- * one process (there is a test that does exactly this,
899
- * `tests/stress/concurrent-same-process.spec.ts`) each read the records
900
- * appended after their own marker, so a degradation one scan records may be
901
- * counted by the other. That can only ever OVER-count — a scan reporting a
902
- * degradation that happened to a sibling is more cautious than the truth, and
903
- * never less — so the failure direction is the honest one. The alternative,
904
- * a sink threaded through every rule context, is a diff across every call site
905
- * in the tree to fix a problem that cannot make a scan look better than it is.
906
- */
907
- /**
908
- * Every reason a detection path may lose capability.
909
- *
910
- * Closed on purpose: a `catch` site that wants a new reason has to add it
911
- * here, which is the moment a reviewer sees the claim. `tests/contract/
912
- * no-uncounted-degradation.spec.ts` reads this list and fails on any `catch`
913
- * in a detection path that neither rethrows, records one of these, nor
914
- * appears in its allowlist with a stated reason.
915
- */
916
- declare const DEGRADATION_REASONS: readonly ["ast-parse-failed", "ast-mask-unavailable", "ast-range-scan-failed", "ci-workflow-listing-unreadable", "workspace-manifest-unreadable", "java-build-listing-unreadable", "rules-tree-listing-unreadable", "trend-record-unparseable", "explain-fixture-unreadable", "explain-workflow-parse-failed", "explain-rule-crash", "rule-doc-rule-crash", "rule-doc-fixture-unreadable", "rule-doc-workflow-parse-failed"];
917
- type DegradationReason = (typeof DEGRADATION_REASONS)[number];
918
- interface DegradationCount {
919
- readonly reason: DegradationReason;
920
- readonly count: number;
921
- }
922
- //#endregion
923
- //#region src/discovery/workspace.d.ts
924
835
  /**
925
836
  * Repository discovery (Sprint-Plan W1-03).
926
837
  * Finds project root, parses package.json, detects npm/yarn/pnpm workspaces.
@@ -934,26 +845,6 @@ interface Workspace {
934
845
  /** Glob patterns from the root package.json workspaces field. */
935
846
  workspaceGlobs: string[];
936
847
  }
937
- //#endregion
938
- //#region src/discovery/frameworks.d.ts
939
- /**
940
- * The catalogued frameworks whose detection is implemented today.
941
- *
942
- * Every entry must exist in the inventory — enforced by the parity spec, not
943
- * by comment.
944
- */
945
- declare const DETECTABLE_TEST_FRAMEWORKS: readonly ["jest", "vitest", "playwright"];
946
- /** The detection output type, in the inventory's vocabulary. */
947
- type DetectedFramework = (typeof DETECTABLE_TEST_FRAMEWORKS)[number];
948
- interface FrameworkInfo$1 {
949
- /** Detected frameworks, as inventory ids. */
950
- frameworks: DetectedFramework[];
951
- /** True when no config evidence was found at all. */
952
- unknown: boolean;
953
- }
954
- declare function detectFrameworks(ws: Workspace): FrameworkInfo$1;
955
- //#endregion
956
- //#region src/rules/rule.d.ts
957
848
  /**
958
849
  * How the rule's primary detection decision is made (Verification Trust
959
850
  * Evolution Plan §09.6/§12.1 — the enforced D6 enum, replacing free text):
@@ -1014,6 +905,58 @@ interface StrategyJustification {
1014
905
  */
1015
906
  detail: string;
1016
907
  }
908
+ /**
909
+ * A declared, owned, expiring grant of core status.
910
+ *
911
+ * The 6.0 demotion emptied the core tier: no rule's 95% Wilson interval
912
+ * clears the 10% ceiling, the smallest observed `ciHigh` across 73 measured
913
+ * rules being 0.138. That is the corpus being too small, not the ceiling being
914
+ * wrong — moving 0.1 to 0.15 would admit a rule on 24 samples, which is tuning
915
+ * a threshold to fit data.
916
+ *
917
+ * So core is, for now, reachable only by a maintainer saying so in the open.
918
+ * This is that record. It is deliberately not a boolean: `corePromotion: true`
919
+ * would be indistinguishable from the silent override it replaces, and would
920
+ * carry no date on which the claim lapses.
921
+ */
922
+ interface CorePromotion {
923
+ /**
924
+ * Why this rule is trusted before the corpus can support it. A structural
925
+ * argument ("a shared mutable across tests is a defect regardless of
926
+ * frequency") is legitimate; "seemed fine" is not, and the registry
927
+ * check rejects a rationale shorter than a sentence.
928
+ */
929
+ rationale: string;
930
+ /**
931
+ * A named human who owns this claim. Per P9: an unowned claim is not a
932
+ * claim, it is a default nobody chose.
933
+ */
934
+ owner: string;
935
+ /** ISO-8601 date the grant was made. */
936
+ grantedAt: string;
937
+ /**
938
+ * ISO-8601 date the grant lapses. On this date the rule resolves to
939
+ * `extended` unless the grant is re-justified — the record has to be
940
+ * renewed by a person, not held in place by not being looked at.
941
+ */
942
+ expiresOn: string;
943
+ /**
944
+ * What backs the rationale, beyond the rationale: corpus verdict ids, a
945
+ * contract path, an ADR.
946
+ *
947
+ * An empty list is ALLOWED, and the registry check does not demand one —
948
+ * because a genuine structural premise ("a mutable shared across tests is a
949
+ * defect regardless of frequency") has no corpus verdict behind it, and
950
+ * inventing a citation for one would make the field decorative.
951
+ *
952
+ * What an empty list is NOT is invisible: `docs/CORE-READINESS.md` reports a
953
+ * promotion with no evidence reference as `NEEDS-SAMPLES` in its note, so a
954
+ * human sees an uncited grant next to a rule that still owes the corpus
955
+ * samples. That is the honest handling — a judgement the tree cannot verify
956
+ * is labelled as one — rather than a field the check pretends to verify.
957
+ */
958
+ evidenceRefs: string[];
959
+ }
1017
960
  interface RuleMeta {
1018
961
  /** Frozen public API — never reused (§18.4). */
1019
962
  id: string;
@@ -1048,7 +991,7 @@ interface RuleMeta {
1048
991
  /**
1049
992
  * How detection works, as an enforced enum (plan §09.6/§12.1 — D6
1050
993
  * closed). Free-text declarations were migrated to the enum in
1051
- * Phase 2; the registry ratchet (tests/rules.registry.spec.ts) makes
994
+ * Phase 2; the registry ratchet (tests/rules/registry.spec.ts) makes
1052
995
  * omission or a bad value a CI failure, so new rules must declare it.
1053
996
  */
1054
997
  detectionStrategy?: DetectionStrategy;
@@ -1062,17 +1005,99 @@ interface RuleMeta {
1062
1005
  /** First released version (semver). Immutable once set. */
1063
1006
  introduced?: string;
1064
1007
  /**
1065
- * Tier assignment (Phase 4 — Tempering Plan; measurement-dependent
1066
- * default per Verification Trust Evolution Plan §11.2 Step 2).
1067
- * - "core": ships in the default report (≤10% measured FP rate)
1068
- * - "extended": included by default, lower confidence (≤30% FP)
1069
- * - "quarantine": opt-in only via --strict (>30% FP or unmeasured)
1008
+ * Tier assignment — a declared floor that may only ever TIGHTEN.
1009
+ *
1010
+ * Three floors decide a rule's effective tier, and they are not
1011
+ * interchangeable. Measured over all 79 live rules (B0 dry-run, 2026-10-01):
1012
+ *
1013
+ * - POINT ESTIMATE — `fpRate <= 30%` -> extended, else quarantine.
1014
+ * The noise floor. Answers "does this ship by default?"
1015
+ * - INTERVAL — `ciHigh <= 10%` -> core, `ciLow >= 50%` -> quarantine,
1016
+ * else extended. The evidence floor. Answers "does the evidence place it?"
1017
+ * - DECLARED (this field) — overrides both.
1018
+ *
1019
+ * B0 measured what the first two do to this registry, and the result is the
1020
+ * reason this field exists rather than being deleted:
1021
+ *
1022
+ * - The INTERVAL floor quarantines **exactly one** of the 79 rules:
1023
+ * `QA-ENV-001`, which was wrong 20 times out of 20. `ciLow >= 50%`
1024
+ * needs 15 of 20 false positives to fire, so the worst of the rest
1025
+ * (`QA-TEST-002`, 62%) clears neither bound.
1026
+ * - No rule derives CORE. `ciHigh <= 10%` needs n >= 35 with zero false
1027
+ * positives and the sampler caps at 20, where even 0/20 reads 16.1%.
1028
+ * The arithmetic is in `tests/rules/core-tier-reachability.spec.ts`.
1029
+ * - **34 rules declare `quarantine`, and 33 of them would be PROMOTED into
1030
+ * the default scan if this field were deleted** — every one of the 33
1031
+ * moves in the promoting direction and none moves the other way.
1032
+ *
1033
+ * So this field is a THIRD floor, stricter than both derivations, and for 33
1034
+ * of the 34 quarantined rules it is the ONLY thing holding them out of a
1035
+ * default scan. That is why deleting the declared tiers is not a refactor: it
1036
+ * ships 33 quarantined detectors — including the six CI rules the README's
1037
+ * caught-by-default table marks advisory — as ordinary findings.
1038
+ *
1039
+ * THE INVARIANT, enforced by the tightening law in
1040
+ * `tests/rules/registry-ratchet.spec.ts` (not by
1041
+ * `checkDeclaredTierMayOnlyTighten`, which does not exist — the first
1042
+ * version of this comment named a function that was never written, which is
1043
+ * the same defect as a spec pointing at a deleted file): a declared
1044
+ * tier may only ever hold a rule DOWN. Quarantining a well-measured rule is
1045
+ * permitted (conservative, costs a reader a `--strict` flag); shipping a
1046
+ * badly-measured rule is not (it hands a user a finding the corpus says is
1047
+ * wrong). B0 found zero rules loosening, but nothing made that structural —
1048
+ * one hand-edit could have released `QA-TEST-002` at 62% observed FP, and
1049
+ * every derived number in the tree would have agreed with it.
1050
+ *
1070
1051
  * When omitted, the tier resolves measurement-dependently via
1071
- * `effectiveTier` (src/rules/measurement.ts): core for rules with a
1072
- * valid corpus measurement, extended (displayed PROVISIONAL)
1073
- * otherwise — an unmeasured rule can never default into core.
1052
+ * `effectiveTier` (src/rules/measurement.ts) — extended when there is no
1053
+ * valid measurement, so an unmeasured rule can never default into core.
1074
1054
  */
1075
1055
  tier?: "core" | "extended" | "quarantine";
1056
+ /**
1057
+ * A declared, OWNED, EXPIRING grant of core status for a rule the corpus
1058
+ * cannot yet support.
1059
+ *
1060
+ * Required by `checkRegistry` for any rule declaring `tier: "core"` without
1061
+ * a valid measurement — the anti-creep law's counterpart to the measurement
1062
+ * criterion. Without it, a core claim is a human assertion rendered with a
1063
+ * measurement's appearance, which is precisely the defect the 6.0 demotion
1064
+ * removed: nineteen rules showed `tier: "core"`, `measured: true`,
1065
+ * `fpRate: 0` while their 95% Wilson upper bounds ran 13.8%–40.4%.
1066
+ *
1067
+ * The fields are load-bearing:
1068
+ *
1069
+ * - `owner` — a named human. Per P9 an unowned claim is not a claim.
1070
+ * - `expiresOn` — the claim decays to `extended` when it lapses. An
1071
+ * assertion that never expires is a tier, which is what this exists to
1072
+ * avoid.
1073
+ * - `evidenceRefs` — corpus IDs, contract paths, or an ADR. A rationale
1074
+ * with nothing behind it is a preference.
1075
+ *
1076
+ * A rule that HAS earned core by measurement does not need one: the
1077
+ * measurement is the record, and a hand-written rationale beside it is a
1078
+ * second source of truth for the same fact.
1079
+ */
1080
+ corePromotion?: CorePromotion;
1081
+ /**
1082
+ * A declared, OWNED, EXPIRING commitment about a QUARANTINED rule.
1083
+ *
1084
+ * Same shape as `corePromotion`, and for the same reason: 34 quarantined
1085
+ * rules carry no `owner`, no `rationale` and no `expiresOn`, so nothing in
1086
+ * the tree says who will re-measure them or when the quarantine lapses.
1087
+ * The expiry date IS the exit condition — `CorePromotion` has no separate
1088
+ * exit-condition field, and a message naming one would send the next
1089
+ * person looking for a field the contract does not have.
1090
+ * `src/rules/tier-evidence.ts` is the worked example of the pattern for the
1091
+ * nineteen rules demoted in 6.0, and `docs/QUARANTINE-REMEDIATION.md` is the
1092
+ * ledger for the rest — but a ledger nobody must update is a report.
1093
+ *
1094
+ * The difference from `corePromotion` is the direction. `corePromotion` says
1095
+ * "this rule is trusted before the corpus can show it"; this says "this rule
1096
+ * is NOT trusted yet, here is when that is re-decided". A rule in quarantine
1097
+ * WITHOUT one is a rule whose quarantine is permanent by default, which is
1098
+ * the outcome the tier exists to avoid.
1099
+ */
1100
+ quarantinePromotion?: CorePromotion;
1076
1101
  /**
1077
1102
  * Detector implementation revision (Verification Trust Evolution Plan
1078
1103
  * §07). Increment on ANY detection-logic change — pattern, scoping,
@@ -1181,14 +1206,6 @@ interface QADoctorRule extends RuleMeta {
1181
1206
  astQuery?: AstQueryHook;
1182
1207
  run: RuleFn;
1183
1208
  }
1184
- //#endregion
1185
- //#region src/discovery/ignores.d.ts
1186
- /** An immutable, per-root resolved ignore matcher (audit R-8). */
1187
- interface IgnoreMatcher {
1188
- isIgnored(relPath: string): boolean;
1189
- }
1190
- //#endregion
1191
- //#region src/engine/adapter.d.ts
1192
1209
  /** Semantic operations a parsed file exposes to rules. */
1193
1210
  interface ParsedFile {
1194
1211
  path: string;
@@ -1212,98 +1229,6 @@ interface ParsedFile {
1212
1229
  */
1213
1230
  frameworkTags?: readonly string[];
1214
1231
  }
1215
- interface FrameworkInfo {
1216
- frameworks: string[];
1217
- unknown: boolean;
1218
- }
1219
- /**
1220
- * The result of the async parse stage (Verification Trust Evolution Plan
1221
- * Phase 0.5, §10): an AST plus its explicit disposal path. The scan
1222
- * pipeline calls `dispose()` exactly once per file, in a
1223
- * finally-equivalent position that runs whether or not rules completed —
1224
- * normal completion, rule crash, per-file budget expiry, or scan abort.
1225
- */
1226
- interface ParsedAst {
1227
- /** Consumed synchronously by rules via `ParsedFile.ast`. */
1228
- ast: unknown;
1229
- /** Release WASM/AST resources (`tree.delete()` for tree-sitter). */
1230
- dispose(): void;
1231
- }
1232
- interface ScanContext {
1233
- workspace: Workspace;
1234
- /** Absolute paths of discovered test files. */
1235
- testFiles: string[];
1236
- deadline: number;
1237
- /** Resolved per-scan ignore matcher (audit R-8) — no module globals. */
1238
- ignoreMatcher: IgnoreMatcher;
1239
- /** Called once per file that could not be read/analyzed. */
1240
- onSkippedFile: (reason: string) => void;
1241
- /**
1242
- * Called when discovery stops early (audit H-8): deadline expiry or
1243
- * the per-adapter file cap. The reason is named in analysisStatus.
1244
- */
1245
- onDiscoveryTruncated: (reason: string) => void;
1246
- /** Per-adapter discovery budget (audit H-8). One language can no
1247
- * longer consume the whole list and starve the others. */
1248
- maxFiles: number;
1249
- /** R4c Scope Integrity: counted matcher exclusions (optional — adapters
1250
- * whose discovery walks sharedWalk pass this through to the counters). */
1251
- onIgnored?: (path: string) => void;
1252
- /** R4c Scope Integrity: counted files no adapter claims (optional). */
1253
- onUnrecognized?: (path: string) => void;
1254
- /**
1255
- * Called when a rule throws on a file (audit R-9): crash isolation
1256
- * stays silent by default, but the scan counts it and `--debug`
1257
- * surfaces it.
1258
- */
1259
- onRuleCrash?: (ruleId: string, file: string, error: unknown) => void;
1260
- }
1261
- interface LanguageAdapter {
1262
- readonly id: string;
1263
- readonly extensions: readonly string[];
1264
- /** Human-readable patterns this adapter searches for (empty-state UX). */
1265
- readonly testFileGlobs: readonly string[];
1266
- /** Dependency/output directory names this adapter never enters. */
1267
- readonly dirSkips: readonly string[];
1268
- /** Canonical test-file check per ecosystem conventions. */
1269
- isTestFile(path: string): boolean;
1270
- detectFrameworks(root: string): FrameworkInfo;
1271
- discoverTestFiles(ctx: ScanContext): void;
1272
- /**
1273
- * Async parse stage (Verification Trust Evolution Plan Phase 0.5, §10):
1274
- * awaited by the scan pipeline between discovery and rule execution.
1275
- * `runRules` and every rule stay synchronous and consume the produced
1276
- * tree via `ParsedFile.ast` — the engine is NOT async end-to-end, only
1277
- * this one seam is (WASM grammar load is inherently async).
1278
- *
1279
- * Contract: resolve to a ParsedAst on success, `undefined` when this
1280
- * adapter has no AST layer (or parsing failed — rules fall back to the
1281
- * regex path either way). Never throws.
1282
- *
1283
- * May return the result directly as well as a promise: the ts-morph path
1284
- * (plan V5-021) is synchronous, and forcing it to allocate a promise per
1285
- * file would be a cost the shared seam has no business imposing. The
1286
- * pipeline awaits either form.
1287
- *
1288
- * Declared as a PROPERTY, not a method signature. A method signature
1289
- * promises a `this` binding that no adapter uses, and it makes every
1290
- * consumer that reads `adapter.parseAst` trip the unbound-method rule —
1291
- * which is a lint error that gets suppressed rather than a design that
1292
- * gets fixed, so the shape has to say what it means.
1293
- */
1294
- parseAst?: (file: ParsedFile) => ParsedAst | undefined | Promise<ParsedAst | undefined>;
1295
- /**
1296
- * Run all rules this adapter hosts against one file. `onCrash` is
1297
- * invoked when a rule throws (audit R-9) — the crash is still
1298
- * isolated, but never invisible. `budget` (audit P-1) stops rule
1299
- * execution mid-file when a single file is too expensive to analyze
1300
- * in full; the caller reports the skip honestly.
1301
- */
1302
- runRules(rules: readonly UniversalRule[], file: ParsedFile, emit: (f: Omit<Finding, "ruleId" | "category">, ruleId: string, category: string) => void, onCrash?: (ruleId: string, error: unknown) => void, budget?: {
1303
- deadline: number;
1304
- onExceeded: () => void;
1305
- }): void;
1306
- }
1307
1232
  /**
1308
1233
  * A rule that declares which adapters it supports. Backward compatible:
1309
1234
  * legacy 'test-files' maps to ['typescript'], 'ci-workflows' to
@@ -1343,112 +1268,16 @@ interface UniversalRule {
1343
1268
  detectorRevision?: number;
1344
1269
  run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
1345
1270
  }
1346
- //#endregion
1347
- //#region src/engine/evidence-core.d.ts
1348
- /**
1349
- * The core's own version — stamped into every record's provenance so a
1350
- * consumer can tell which normalization contract produced a record.
1351
- * Bump ONLY on a semantic change to the normalization itself.
1352
- */
1353
- declare const EVIDENCE_CORE_VERSION = "evidence-core@1";
1354
- /** Evidence source formats the core understands today (WI-17 adds trace.zip). */
1355
- type EvidenceSource = ForensicsReport["source"];
1356
- /** Normalized per-test runtime evidence (plan WI-2 field contract). */
1357
- interface EvidenceRecord {
1358
- /** Where this evidence came from (format identity). */
1359
- source: EvidenceSource;
1360
- /** Ingested artifact identity — the report path the records derive from. */
1361
- artifact: string;
1362
- /** Test identity: file + title (+ declaration line when known). */
1363
- file: string;
1364
- title: string;
1365
- /** 1-based declaration line when the source carries one (JUnit: absent). */
1366
- line?: number;
1367
- /** Normalized execution status facts. */
1368
- status: {
1369
- /** Last attempt's outcome. */
1370
- final: RunStatus;
1371
- /** Failed at least once across attempts. */
1372
- failed: boolean;
1373
- /** Executed more than once. */
1374
- retried: boolean;
1375
- /** Passed only on attempt >= 2 (TRUE-FLAKE). */
1376
- passedOnRetry: boolean;
1377
- skipped: boolean;
1378
- /** Final attempt timed out. */
1379
- timedOut: boolean;
1380
- };
1381
- /** Number of attempts (retries = attempts − 1). */
1382
- attempts: number;
1383
- /** Total duration across attempts. */
1384
- durationMs: number;
1385
- /** Error messages, in report order. Empty: not captured by this source yet. */
1386
- errors: string[];
1387
- /** Attachment names/paths. Empty: not captured by this source yet (WI-17). */
1388
- attachments: string[];
1389
- /** Where the record came from and under which normalization contract. */
1390
- provenance: {
1391
- core: typeof EVIDENCE_CORE_VERSION;
1392
- ingest: "mjolnir.forensics";
1393
- };
1394
- }
1395
- //#endregion
1396
- //#region src/engine/provenance.d.ts
1397
- type FileProvenance = "unmarked" | "generated-marked" | "codegen-like";
1398
- /** Classify one file's provenance. Pure. */
1399
- declare function classifyProvenance(file: {
1400
- text: string;
1401
- codeText?: string;
1402
- }): FileProvenance;
1403
- //#endregion
1404
- //#region src/engine/scan-cache.d.ts
1405
- interface CacheStats {
1406
- hits: number;
1407
- misses: number;
1408
- /** Where the cache lives — reported so `--json` consumers can audit it. */
1409
- file: string;
1410
- }
1411
- interface ScanCache {
1412
- readonly stats: CacheStats;
1413
- lookup(key: string): Finding[] | undefined;
1414
- store(key: string, findings: Finding[], fileBudgetExceeded: boolean): void;
1415
- persist(): void;
1416
- }
1417
- //#endregion
1418
- //#region src/config/config.d.ts
1419
- interface IgnoreEntry {
1420
- ruleId: string;
1421
- files?: string[];
1422
- reason: string;
1423
- /** ISO date; omitted means the suppression has no expiry. */
1424
- expires?: string;
1425
- }
1426
- interface QADoctorConfig {
1427
- gate?: "advisory" | "error" | "warning";
1428
- /** Path globs to skip during discovery (see DEFAULT_IGNORES dialect). */
1429
- exclude?: string[];
1430
- severityOverrides?: Record<string, Severity>;
1431
- ignore?: IgnoreEntry[];
1432
- }
1433
- declare function loadConfig(root: string, options?: {
1434
- knownRuleIds?: ReadonlySet<string>;
1435
- }): {
1436
- config: QADoctorConfig;
1437
- path: string | null;
1438
- /** Non-fatal notes (e.g. unknown rule IDs in severityOverrides). */
1439
- warnings: string[];
1440
- };
1441
- declare namespace scan_pipeline_d_exports {
1442
- export { AssembleScanResultInput, CliArgs, DiscoveryResult, EVIDENCE_OVERRIDES$1 as EVIDENCE_OVERRIDES, FileAnalysisResult, KNOWN_RULE_IDS$1 as KNOWN_RULE_IDS, OVERLAP_META_BY_RULE_ID$1 as OVERLAP_META_BY_RULE_ID, PostScanResult, SUITE_INVALIDATING_RULE_IDS$1 as SUITE_INVALIDATING_RULE_IDS, ScanHooks, applyPostScanProcessing, assembleScanResult, buildUniversalRules$1 as buildUniversalRules, discoverAndParseRuntimeReport$1 as discoverAndParseRuntimeReport, discoverTestFilesPhase, fallbackWorkspace$1 as fallbackWorkspace, isValidFindingRecord$1 as isValidFindingRecord, pathMatchesGlob$1 as pathMatchesGlob, runFileAnalysisPhase, runScan$1 as runScan, selectAdapter, summarizeForensicVerdicts };
1443
- }
1444
- /** Registered rule IDs — used to warn on unknown severityOverrides keys (M4). */
1445
- declare const KNOWN_RULE_IDS$1: ReadonlySet<string>;
1446
1271
  /**
1447
- * R6 (Bug Map M-02): per-rule overlap metadata, built from RULES the
1448
- * same way tierByRuleId is — `asUniversal` drops `overlapWith`, so the
1449
- * dedup map must come from the registry directly.
1272
+ * Whether `path` matches `glob`, in the dialect documented above.
1273
+ *
1274
+ * Both sides are normalized to forward slashes first. Finding paths are already
1275
+ * normalized by the walker, but a suppression `files` pattern written on
1276
+ * Windows ("e2e\\x.spec.ts") compiled to a literal-backslash regex that could
1277
+ * never match any finding — the suppression silently never applied, which is
1278
+ * the defect that made the whole dialect worth unifying.
1450
1279
  */
1451
- declare const OVERLAP_META_BY_RULE_ID$1: ReadonlyMap<string, OverlapMeta>;
1280
+ declare function pathMatchesGlob$1(path: string, glob: string): boolean;
1452
1281
  declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
1453
1282
  /** CLI trust-gate flag (`--enable-plugins`) for this scan. */
1454
1283
  enablePlugins?: boolean;
@@ -1463,35 +1292,73 @@ declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
1463
1292
  rules: number;
1464
1293
  }>;
1465
1294
  externalRules: QADoctorRule[];
1295
+ /** Rules that ran, after the quarantine filter. */
1296
+ rulesApplied: number;
1297
+ /** Rules removed by the quarantine filter because `--strict` was absent. */
1298
+ rulesWithheld: number;
1299
+ /** The same rules, named. The count is the disclosure; this is the evidence. */
1300
+ withheldRuleIds: string[];
1466
1301
  }>;
1467
- /** Rule-declared evidence-level overrides (Honesty Core). */
1468
- declare const EVIDENCE_OVERRIDES$1: ReadonlyMap<string, string>;
1469
- /**
1470
- * Rules whose findings void the suite's pass claim (RuleMeta.suiteInvalidating).
1471
- * Built from the registry so the scorer never has to import it.
1472
- */
1473
- declare const SUITE_INVALIDATING_RULE_IDS$1: ReadonlySet<string>;
1474
1302
  interface CliArgs {
1475
1303
  target: string;
1476
1304
  json: boolean;
1477
1305
  verbose: boolean;
1478
1306
  maxDurationMs: number;
1479
1307
  scopeChanged: boolean;
1480
- format: "terminal" | "json" | "sarif" | "mermaid" | "codequality";
1308
+ /**
1309
+ * The output shape.
1310
+ *
1311
+ * `trust-report` | `pr-comment` | `github-summary` are the v6 collapse's
1312
+ * REPLACE arm (plan §3): they were separate verbs whose entire body was
1313
+ * "scan, then render". They are render-only — the scan, the findings, the
1314
+ * score and the exit code are identical under all of them, and under
1315
+ * `terminal`, which is the property that lets a format be a flag.
1316
+ */
1317
+ format: "terminal" | "json" | "sarif" | "mermaid" | "codequality" | "trust-report" | "pr-comment" | "github-summary";
1481
1318
  /** --width override for terminal box/gauge wrapping (Sprint 5 Task 22). */
1482
1319
  width?: number;
1483
1320
  /** --ascii / --no-ascii override for shouldUseAscii()'s heuristic. */
1484
1321
  ascii?: boolean;
1485
1322
  /** --tone blunt: opt-in blunter messages (Sprint 9 Task 40). */
1486
1323
  tone?: "blunt";
1487
- /** --strict: include quarantine-tier rules in the scan (Phase 4). */
1324
+ /**
1325
+ * --include-warn (was --strict): include quarantine-tier rules in the scan
1326
+ * (Phase 4). The internal name predates the 6.0 flag rename and names a scan
1327
+ * option rather than a user-facing promise; renaming it would touch every
1328
+ * call site for a rename no user would ever see.
1329
+ */
1488
1330
  strict?: boolean;
1331
+ /**
1332
+ * Deprecated flag spellings used on this invocation, each with its
1333
+ * replacement. Populated by the parser so the CLI can print the whole
1334
+ * deprecation notice once, in one place, rather than each arm printing its
1335
+ * own warning where a `--json` consumer would have to read stderr to find it.
1336
+ */
1337
+ deprecatedFlags?: Array<{
1338
+ flag: string;
1339
+ replacement: string;
1340
+ }>;
1489
1341
  /** --base <ref>: base ref for --scope changed (audit H-10). */
1490
1342
  base?: string;
1491
1343
  /** --debug: print errors swallowed by crash isolation (audit R-9). */
1492
1344
  debug?: boolean;
1493
1345
  /** --record-milestones: let a scan write .mjolnir/stats.json (audit R-1). */
1494
1346
  recordMilestones?: boolean;
1347
+ /**
1348
+ * --save-baseline: write this scan's findings to `.mjolnir/baseline.json`
1349
+ * so a LATER scan can say RESOLVED rather than reporting the same debt
1350
+ * forever.
1351
+ *
1352
+ * This flag exists because the `baseline` verb was removed in the v6 carve
1353
+ * and nothing replaced it. That left `ci verify` unable to leave its
1354
+ * no-baseline arm for any user, and left three shipped messages — the
1355
+ * verify digest, the MCP `verify` tool, and the agent instruction brief —
1356
+ * naming a command that could not perform the action they described. A
1357
+ * recovery instruction that does not recover is worse than no instruction:
1358
+ * it costs the reader a round trip and teaches them that the tool's own
1359
+ * guidance is unreliable.
1360
+ */
1361
+ saveBaseline?: boolean;
1495
1362
  /**
1496
1363
  * --cache: reuse per-file rule verdicts from the local content-addressed
1497
1364
  * cache (M5.2). Post-loop processing always re-runs; the cache only
@@ -1532,6 +1399,22 @@ interface CliArgs {
1532
1399
  * scan semantics and exit codes are unchanged (plan §5.6).
1533
1400
  */
1534
1401
  scoreOnly?: boolean;
1402
+ /**
1403
+ * `--require-full-coverage`: exit `EXIT_PARTIAL` when the quarantine
1404
+ * filter withheld any rule, even though the scan itself was whole.
1405
+ *
1406
+ * Off by default, and the default is the point. Coverage-PARTIAL is the
1407
+ * state of every ordinary scan — the quarantine tier is non-empty — so
1408
+ * making it gate by default would exit 2 on nearly every run. The fix a
1409
+ * user reaches for is `--strict`, which switches the quarantine ON, and
1410
+ * then the flag has nothing left to check. A flag that inverts its own
1411
+ * remedy is worse than no flag.
1412
+ *
1413
+ * The opt-in is for a project that has decided quarantined detectors must
1414
+ * be live: then a withheld rule is a real loss of coverage and the run is
1415
+ * inconclusive, exactly as a truncated walk is.
1416
+ */
1417
+ requireFullCoverage?: boolean;
1535
1418
  /**
1536
1419
  * Audit C2: --enable-plugins opens the plugin trust gate for THIS
1537
1420
  * invocation — npm-plugin and JS-module rule sources may load (and
@@ -1553,6 +1436,30 @@ interface CliArgs {
1553
1436
  * mode" (no monorepo analysis).
1554
1437
  */
1555
1438
  monorepo?: boolean;
1439
+ /**
1440
+ * --suppressions (v6 collapse, plan §3): print the suppression LEDGER and
1441
+ * stop. A mode, not a verb.
1442
+ *
1443
+ * It is a mode because the ledger is a view of `mjolnir.config.json` and
1444
+ * nothing else — a verb for it would be a command whose whole body is
1445
+ * "read a file and print it", which is what a flag on the command that
1446
+ * enforces the file is for. It reads the CWD, exactly as the retired verb
1447
+ * did, so the one observable behaviour a script can depend on is unchanged.
1448
+ */
1449
+ suppressions?: boolean;
1450
+ /**
1451
+ * --suppression-gate (v6 collapse, plan §3): fail when a suppression is
1452
+ * ungoverned — expired, reasonless, or covering a rule that no longer
1453
+ * exists. Runs the scan first, because a governance judgement about a
1454
+ * suppression needs the findings the suppression would have hidden.
1455
+ */
1456
+ suppressionGate?: boolean;
1457
+ /**
1458
+ * --policy (v6 collapse, plan §3): print the scoring policy in force and
1459
+ * stop. A policy nobody can read is a policy nobody can check, and the
1460
+ * scorer publishes one on purpose.
1461
+ */
1462
+ policy?: boolean;
1556
1463
  }
1557
1464
  interface ScanHooks {
1558
1465
  /** Invoked when a rule throws on a file (audit R-9). */
@@ -1588,20 +1495,6 @@ interface ScanHooks {
1588
1495
  * scanning a filesystem root.
1589
1496
  */
1590
1497
  declare function fallbackWorkspace$1(targetAbs: string): Workspace;
1591
- /**
1592
- * Minimal glob match for suppression `files` patterns, with gitignore
1593
- * `**` semantics (bug-audit M5). Supports:
1594
- * "tests/**" — everything inside tests/
1595
- * "tests" + "/**\/*.spec.ts" — any depth UNDER tests/ (including none) ending in .spec.ts
1596
- * "**" + "/*.spec.ts" — any depth including root-level files
1597
- * "tests/foo.spec.ts" — exact path
1598
- * "*" within a segment never crosses "/".
1599
- *
1600
- * Forward slashes only (findings always use normalized paths). `?`,
1601
- * character classes and `!` negation are not metacharacters here — same
1602
- * as before this rewrite.
1603
- */
1604
- declare function pathMatchesGlob$1(path: string, glob: string): boolean;
1605
1498
  /**
1606
1499
  * Plan §16 + WI-11: locate a runtime run report next to the scan
1607
1500
  * target, using the exact conventions the forensics ingestion already
@@ -1626,121 +1519,6 @@ declare function discoverAndParseRuntimeReport$1(scanRoot: string): {
1626
1519
  * caller) — never silently scored.
1627
1520
  */
1628
1521
  declare function isValidFindingRecord$1(f: unknown): f is Omit<Finding, "ruleId" | "category">;
1629
- /**
1630
- * Replace 7-way nested ternary with a linear probe.
1631
- * Returns the adapter whose `isTestFile` matches the path, falling
1632
- * back to the TypeScript adapter (the default language).
1633
- */
1634
- declare function selectAdapter(path: string): LanguageAdapter;
1635
- interface DiscoveryResult {
1636
- testFiles: string[];
1637
- stagedSurface: boolean;
1638
- }
1639
- declare function discoverTestFilesPhase(workspace: Workspace, args: CliArgs, hooks: ScanHooks, scanRoot: Workspace, ctx: ScanContext): DiscoveryResult;
1640
- interface FileAnalysisResult {
1641
- skippedFiles: number;
1642
- testFileCount: number;
1643
- testDeclarationCount: number;
1644
- rulesPartial: boolean;
1645
- parseFailed: number;
1646
- parseFallbacks: number;
1647
- scanned: number;
1648
- analyzed: number;
1649
- /**
1650
- * Files that HAD an AST stage and lost it to the scan deadline, so they
1651
- * were analyzed by regex alone. Zero for every adapter that declares no
1652
- * `parseAst` — that is a capability envelope, not a downgrade.
1653
- */
1654
- astFallbackFiles: number;
1655
- }
1656
- declare function runFileAnalysisPhase(findings: Finding[], testFiles: string[], workspace: Workspace, activeRules: UniversalRule[], hooks: ScanHooks, cache: ScanCache, rulesDigest: string, deadline: number, truncationReasons: Set<string>, declarationsByFile: Map<string, number>, fileProvenance: Array<{
1657
- path: string;
1658
- provenance: ReturnType<typeof classifyProvenance>;
1659
- }>, onRuleCrash: (ruleId: string, file: string, error: unknown) => void): Promise<FileAnalysisResult>;
1660
- interface PostScanResult {
1661
- testDeclarationCount: number;
1662
- scopeInfo: {
1663
- scope: "all" | "changed";
1664
- degraded?: string | undefined;
1665
- };
1666
- suppressionCount: number;
1667
- frameworks: ReturnType<typeof detectFrameworks>;
1668
- runtimeReportPath: string | undefined;
1669
- runtimeIncomplete: boolean;
1670
- /**
1671
- * The normalized evidence core (V5-011). Persisted rather than discarded,
1672
- * so every post-ingest projection reads records instead of re-parsing a
1673
- * report that may have changed underneath it.
1674
- */
1675
- evidenceRecords: EvidenceRecord[];
1676
- /** Aggregate forensic classifications from the ingested runtime report. */
1677
- forensicVerdicts: ForensicVerdictSummary | undefined;
1678
- config: ReturnType<typeof loadConfig>["config"];
1679
- }
1680
- /**
1681
- * Summarize forensic classifications from an ingested runtime report
1682
- * (plan §10.4, WAVE 5). Returns undefined when no verdict carries a
1683
- * classification — the machine contract slot stays absent rather than
1684
- * reporting all-zero counts.
1685
- */
1686
- declare function summarizeForensicVerdicts(report: ForensicsReport): ForensicVerdictSummary | undefined;
1687
- declare function applyPostScanProcessing(findings: Finding[], workspace: Workspace, args: CliArgs, hooks: ScanHooks, scanRoot: Workspace, declarationsByFile: Map<string, number>, testDeclarationCount: number, tierByRuleId: Map<string, Tier>, REVISION_BY_RULE_ID: Map<string, number>): PostScanResult;
1688
- interface AssembleScanResultInput {
1689
- findings: Finding[];
1690
- testFileCount: number;
1691
- testDeclarationCount: number;
1692
- declarationsByFile: Map<string, number>;
1693
- skippedFiles: number;
1694
- rulesCrashed: number;
1695
- truncationReasons: Set<string>;
1696
- discoveryTruncated: boolean;
1697
- rulesPartial: boolean;
1698
- scopeIgnored: number;
1699
- scopeUnrecognized: number;
1700
- parseFailed: number;
1701
- parseFallbacks?: number;
1702
- /** Files that lost their AST stage to the scan deadline. See completion.ts. */
1703
- astFallbackFiles?: number;
1704
- /**
1705
- * Reason-coded capability losses recorded during this scan's window. See
1706
- * `src/engine/degradation-ledger.ts` for why the window is an index rather
1707
- * than an ownership claim.
1708
- */
1709
- degradations?: readonly DegradationCount[];
1710
- scanned: number;
1711
- analyzed?: number;
1712
- testFiles: string[];
1713
- workspace: Workspace;
1714
- scanRoot: Workspace;
1715
- args: CliArgs;
1716
- hooks: ScanHooks;
1717
- cache: ScanCache;
1718
- REVISION_BY_RULE_ID: Map<string, number>;
1719
- pluginsLoaded: Array<{
1720
- name: string;
1721
- rules: number;
1722
- }>;
1723
- scopeInfo: {
1724
- scope: "all" | "changed";
1725
- degraded?: string | undefined;
1726
- };
1727
- suppressionCount: number;
1728
- frameworks: ReturnType<typeof detectFrameworks>;
1729
- runtimeReportPath: string | undefined;
1730
- runtimeIncomplete?: boolean;
1731
- evidenceRecords?: EvidenceRecord[];
1732
- forensicVerdicts: ForensicVerdictSummary | undefined;
1733
- config: ReturnType<typeof loadConfig>["config"];
1734
- fileProvenance: Array<{
1735
- path: string;
1736
- provenance: ReturnType<typeof classifyProvenance>;
1737
- }>;
1738
- started: number;
1739
- stagedSurface: boolean;
1740
- /** ECO-005: dependency graph built from project manifests. */
1741
- dependencyGraph?: DependencyGraph;
1742
- }
1743
- declare function assembleScanResult(o: AssembleScanResultInput): ScanResult;
1744
1522
  /**
1745
1523
  * Testable default scan path core. `hooks` lets callers observe
1746
1524
  * normally-invisible events (swallowed rule crashes) without changing
@@ -1751,20 +1529,21 @@ declare function assembleScanResult(o: AssembleScanResultInput): ScanResult;
1751
1529
  * inherently async); `runRules` and every rule stay synchronous and
1752
1530
  * consume `ParsedFile.ast`. Callers await the returned promise.
1753
1531
  */
1754
- declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult>;
1755
- //#endregion
1756
- //#region src/engine/version.d.ts
1757
1532
  /**
1758
- * Engine version, as a leaf module: `runIdentity` (R4c) needs the engine
1759
- * version inside scan-pipeline WITHOUT importing cli.ts (a cycle — cli
1760
- * imports the pipeline). The literal follows the same discipline as
1761
- * CLI_VERSION and SARIF's driver.version: kept in sync on release by
1762
- * scripts/sync-sarif-version.cjs and guarded by the version-consistency
1763
- * spec. cli.ts re-exports this as CLI_VERSION.
1533
+ * Run one scan inside its own ts-morph Project scope.
1534
+ *
1535
+ * The wrapper is the whole fix. ts-morph's Project caches SourceFiles by path
1536
+ * and `parseTsFile` calls `replaceWithText` on them, so a Project shared between
1537
+ * two concurrent scans lets one rewrite the file the other is mid-rule on —
1538
+ * which surfaced as three of four concurrent scans reporting
1539
+ * `rulesCrashed: 4` and `rules: "partial"`, and therefore as two different
1540
+ * machine-contract digests from one tree. See `src/engine/ts-ast.ts`.
1541
+ *
1542
+ * Async context propagates across `await`, so the scope covers the whole scan
1543
+ * without threading a Project through every adapter.
1764
1544
  */
1765
- declare const ENGINE_VERSION = "5.0.0";
1766
- //#endregion
1767
- //#region src/cli-io.d.ts
1545
+ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult>;
1546
+ declare const ENGINE_VERSION = "6.0.1";
1768
1547
  /**
1769
1548
  * Shared process IO sinks (certification-audit Phase 5, G6): the variadic
1770
1549
  * console sinks and the internal-error reporter lived in cli.ts, which
@@ -1777,63 +1556,51 @@ type Output = (...parts: unknown[]) => void;
1777
1556
  export declare const out: Output;
1778
1557
  export declare const err: Output;
1779
1558
  export declare function internalErrorMessage(err: unknown, emit: (s: string) => void, debug: boolean): void;
1780
- //#endregion
1781
- //#region src/cli-handlers.d.ts
1559
+ /**
1560
+ * The gate level a command applies to its findings. `advisory` is the
1561
+ * `--blocking none` posture: findings are reported and never gate.
1562
+ */
1563
+ type GateLevel = "advisory" | "error" | "warning";
1564
+ /**
1565
+ * Testable why command core. Returns the process exit code.
1566
+ * Live mode awaits the real scan; saved-report mode is synchronous.
1567
+ */
1568
+ export declare function runWhyCommand(argv: string[], io?: {
1569
+ out: Output;
1570
+ err: Output;
1571
+ }): Promise<number>;
1782
1572
  /**
1783
1573
  * Render scan output in the requested format.
1784
1574
  *
1785
1575
  * Extracted from runScanCommand (Task 6) to reduce cyclomatic complexity.
1786
1576
  * Handles sarif, mermaid, codequality, json, and terminal (trust-report)
1787
1577
  * rendering. The caller handles first-run hint + milestones separately.
1578
+ *
1579
+ * The v6 collapse's REPLACE arm (plan §3) lands here: `trust-report`,
1580
+ * `pr-comment` and `summary` were three verbs whose whole body was this
1581
+ * function with one branch each. The renderers already existed; what was
1582
+ * missing was a way to name them from the one verb a reader already knows.
1583
+ *
1584
+ * `--format trust-report` is the terminal render, which is why the `else`
1585
+ * below is spelled `terminal` and not `trust-report`: naming a default after
1586
+ * a flag that is also its value is a way to make the default untypeable.
1788
1587
  */
1789
1588
  export declare function renderScanOutput(result: Awaited<ReturnType<typeof runScan$1>>, args: CliArgs, target: string, io: {
1790
1589
  out: Output;
1791
1590
  err: Output;
1792
- }): void;
1591
+ }, gate: GateLevel): void;
1793
1592
  export declare function runScanCommand(argv: string[], io?: {
1794
1593
  out: Output;
1795
1594
  err: Output;
1796
1595
  }): Promise<number>;
1797
- export declare function runTriageCommand(argv: string[], io?: {
1798
- out: Output;
1799
- err: Output;
1800
- }): number;
1801
- export declare function runMutationCommand(argv: string[], io?: {
1802
- out: Output;
1803
- err: Output;
1804
- }): Promise<number>;
1805
- export declare function runBadgeCommand(argv: string[], io?: {
1806
- out: Output;
1807
- err: Output;
1808
- }): Promise<number>;
1809
- export declare function runDebtCommand(argv: string[], io?: {
1810
- out: Output;
1811
- err: Output;
1812
- }): Promise<number>;
1813
1596
  export declare function runFixCommand(argv: string[], io?: {
1814
1597
  out: Output;
1815
1598
  err: Output;
1816
1599
  }): Promise<number>;
1817
- export declare function runCreateRuleCommand(argv: string[], io?: {
1818
- out: Output;
1819
- err: Output;
1820
- }): number;
1821
- export declare function runImpactCommand(argv: string[], io?: {
1822
- out: Output;
1823
- err: Output;
1824
- }): Promise<number>;
1825
- export declare function runBaselineCommand(argv: string[], io?: {
1826
- out: Output;
1827
- err: Output;
1828
- }): Promise<number>;
1829
1600
  export declare function runVerifyCommand(argv: string[], io?: {
1830
1601
  out: Output;
1831
1602
  err: Output;
1832
1603
  }): Promise<number>;
1833
- export declare function runDiffCommand(argv: string[], io?: {
1834
- out: Output;
1835
- err: Output;
1836
- }): Promise<number>;
1837
1604
  export declare function runPrCommentCommand(argv: string[], io?: {
1838
1605
  out: Output;
1839
1606
  err: Output;
@@ -1846,22 +1613,67 @@ export declare function runHandoverCommand(argv: string[], io?: {
1846
1613
  out: Output;
1847
1614
  err: Output;
1848
1615
  }): Promise<number>;
1849
- export declare function runInitCommand(argv: string[], io?: {
1850
- out: Output;
1851
- err: Output;
1852
- }): number;
1853
1616
  export declare function runPwReportCommand(argv: string[], io?: {
1854
1617
  out: Output;
1855
1618
  err: Output;
1856
1619
  }): number;
1857
- export declare function runForensicsCommand(argv: string[], io?: {
1858
- out: Output;
1859
- err: Output;
1860
- }): number;
1861
1620
  export declare function runRulesCommand(argv: string[], io?: {
1862
1621
  out: Output;
1863
1622
  err: Output;
1864
1623
  }): Promise<number>;
1624
+ /**
1625
+ * `explain` ARMS — the v6 collapse's MOVE arm, group 2 (plan §3).
1626
+ *
1627
+ * `explain` is the question verb: what does this finding mean, and what else
1628
+ * does it touch. Five capabilities were top-level verbs that all answer a
1629
+ * variation of that question, and each is a flag here:
1630
+ *
1631
+ * --list the rule catalogue (was `mjolnir explain --list`)
1632
+ * <file:line> why this finding (was `mjolnir explain <file:line>` — already
1633
+ * delegated by the subject form)
1634
+ * --callers who calls this symbol (was `mjolnir explain --callers`)
1635
+ * --plan what to do next (was `mjolnir explain --plan`)
1636
+ * --evidence what really ran (was the `triage` verb)
1637
+ * --playwright a Playwright run summary (was `mjolnir explain --playwright`)
1638
+ *
1639
+ * The table is the same shape as `CI_SUBCOMMANDS`, and for the same reason: it
1640
+ * is the single source the dispatcher reads AND the help renders from, so an
1641
+ * arm that exists is documented and one that is documented exists.
1642
+ *
1643
+ * `--playwright` is INTERNAL in the plan and marked here as such. `pw-report`
1644
+ * summarised a Playwright JSON run for a person reading CI output; the same
1645
+ * information arrives through `--evidence` once it reads a report rather than
1646
+ * a specific runner's format, and one flag that means "the run summary"
1647
+ * whichever runner produced it beats two that mean it for one of them.
1648
+ */
1649
+ /**
1650
+ * `explain --evidence` — the runtime-evidence arm.
1651
+ *
1652
+ * The `triage` verb's body, verbatim in behaviour: read a real run, classify
1653
+ * every test's verdict, and render the table plus the meeting artifact. The
1654
+ * arm does not add anything, which is the point — the capability is the same
1655
+ * one `src/forensics/triage.ts` and the MCP server already expose, and the
1656
+ * reason the verb is retired is the reason the flag exists.
1657
+ *
1658
+ * `--json` and `--md` are the verb's own output selectors, preserved so a
1659
+ * script written against them keeps working with one word changed.
1660
+ *
1661
+ * The one behaviour note: the report is written to `--flaky-md`'s path, which
1662
+ * the verb also did, and the arm does not silently write a file into a
1663
+ * directory a reader did not name. `--no-md` is still how you decline it.
1664
+ */
1665
+ export declare function runEvidenceArm(argv: string[], io: {
1666
+ out: Output;
1667
+ err: Output;
1668
+ }): number;
1669
+ export declare const EXPLAIN_ARMS: Record<string, {
1670
+ summary: string;
1671
+ status: "public" | "internal";
1672
+ run: (argv: string[], io: {
1673
+ out: Output;
1674
+ err: Output;
1675
+ }) => Promise<number> | number;
1676
+ }>;
1865
1677
  export declare function runExplainCommand(argv: string[], io?: {
1866
1678
  out: Output;
1867
1679
  err: Output;
@@ -1870,37 +1682,30 @@ export declare function runDoctorPlaywright(argv: string[], io?: {
1870
1682
  out: Output;
1871
1683
  err?: Output;
1872
1684
  }): Promise<number>;
1873
- //#endregion
1874
- //#region src/commands/doctor-run.d.ts
1875
- export declare function runDoctorCommand(argv: string[], io?: {
1685
+ export declare function runFrameworkMaturityCommand(argv: string[], io?: {
1876
1686
  out: Output;
1877
1687
  err: Output;
1878
1688
  }): number;
1879
- //#endregion
1880
- //#region src/commands/summary.d.ts
1881
- /**
1882
- * Testable summary command core. Returns the process exit code.
1883
- * Streams: annotations → stdout (always; GitHub greps them), summary →
1884
- * step-summary file when set unless --stdout. Unknown flags are a
1885
- * usage error (exit 10) — a typo'd --stdout must not silently route
1886
- * the summary to $GITHUB_STEP_SUMMARY.
1887
- */
1888
- export declare function runSummaryCommand(argv: string[], io?: {
1689
+ export declare function runSuppressionGateCommand(argv: string[], io?: {
1889
1690
  out: Output;
1890
1691
  err: Output;
1891
- }): number;
1892
- //#endregion
1893
- //#region src/commands/why.d.ts
1894
- /**
1895
- * Testable why command core. Returns the process exit code.
1896
- * Live mode awaits the real scan; saved-report mode is synchronous.
1897
- */
1898
- export declare function runWhyCommand(argv: string[], io?: {
1692
+ }): Promise<number>;
1693
+ export declare function runCrossFileCommand(argv: string[], io?: {
1899
1694
  out: Output;
1900
1695
  err: Output;
1901
1696
  }): Promise<number>;
1902
- //#endregion
1903
- //#region src/commands/handoff.d.ts
1697
+ export declare function runEvidenceGraphCommand(argv: string[], io?: {
1698
+ out: Output;
1699
+ err: Output;
1700
+ }): Promise<number>;
1701
+ export declare function runCIIntegrityCommand(argv: string[], io?: {
1702
+ out: Output;
1703
+ err: Output;
1704
+ }): Promise<number>;
1705
+ export declare function runDoctorCommand(argv: string[], io?: {
1706
+ out: Output;
1707
+ err: Output;
1708
+ }): number;
1904
1709
  /**
1905
1710
  * Testable handoff command core. Returns the process exit code.
1906
1711
  * `mjolnir handoff [mjolnir.json] [--category <cat>]... [--rules <ids>]`
@@ -1909,8 +1714,6 @@ export declare function runHandoffCommand(argv: string[], io?: {
1909
1714
  out: Output;
1910
1715
  err: Output;
1911
1716
  }): number;
1912
- //#endregion
1913
- //#region src/commands/install-agents.d.ts
1914
1717
  /**
1915
1718
  * Testable install command core. Returns the process exit code.
1916
1719
  * `mjolnir install [--dry-run] [--force]` — probes the given cwd
@@ -1920,16 +1723,8 @@ export declare function runInstallCommand(argv: string[], io?: {
1920
1723
  out: Output;
1921
1724
  err: Output;
1922
1725
  }, cwd?: string): number;
1923
- //#endregion
1924
- //#region src/commands/trust-report.d.ts
1925
- export declare function runTrustReportCommand(argv: string[], io: {
1926
- out: Output;
1927
- err: Output;
1928
- }): Promise<number>;
1929
- //#endregion
1930
- //#region src/commands/release-trust.d.ts
1931
1726
  /**
1932
- * CLI verb: `mjolnir release-trust [--json] [repo-root]`.
1727
+ * CLI verb: `mjolnir ci release-trust [--json] [repo-root]`.
1933
1728
  * Exit contract (frozen set): 0 verdict PASS · 1 verdict non-PASS ·
1934
1729
  * 2 no fixtures root (not an mjolnir checkout — BLOCKED context) ·
1935
1730
  * 10 usage error · 20 internal error.
@@ -1938,111 +1733,18 @@ export declare function runReleaseTrustCommand(argv: string[], io?: {
1938
1733
  out: Output;
1939
1734
  err: Output;
1940
1735
  }): number;
1941
- //#endregion
1942
- //#region src/commands/business-case.d.ts
1943
- /**
1944
- * Entry point for `mjolnir business-case`.
1945
- *
1946
- * Flags:
1947
- * --incident-cost <n> The cost of ONE false-green incident in your
1948
- * organisation. Until you supply it, no dollar
1949
- * figure is printed at all.
1950
- * --strict Include quarantine-tier findings
1951
- *
1952
- * `--history` and `--projected` are GONE. Both promised arithmetic this
1953
- * tool cannot do: `--history` claimed to estimate from actual scan
1954
- * improvements while reading no history at all, and `--projected` divided
1955
- * the total by six and called the quotient a monthly rate. A flag that
1956
- * does not do what its help says is worse than no flag, because the help
1957
- * is the promise.
1958
- */
1959
- export declare function runBusinessCaseCommand(argv: string[], io?: {
1960
- out: Output;
1961
- err?: Output;
1962
- }): Promise<number>;
1963
- //#endregion
1964
- //#region src/commands/release-report.d.ts
1965
- /**
1966
- * CLI verb: `mjolnir release-report [--since <ref>] [--history <json>] [target]`.
1967
- */
1968
- export declare function runReleaseReportCommand(argv: string[], io: {
1969
- out: Output;
1970
- err: Output;
1971
- }): Promise<number>;
1972
- //#endregion
1973
- //#region src/commands/report-playwright.d.ts
1974
- export declare function runReportPlaywrightCommand(argv: string[], io: {
1975
- out: Output;
1976
- err: Output;
1977
- }): Promise<number>;
1978
- //#endregion
1979
- //#region src/commands/trend.d.ts
1980
- export declare function runTrendCommand(argv: string[], io: {
1981
- out: Output;
1982
- err: Output;
1983
- }): Promise<number>;
1984
- //#endregion
1985
- //#region src/commands/exec-report.d.ts
1986
- export declare function runExecReportCommand(argv: string[], io: {
1987
- out: Output;
1988
- err: Output;
1989
- }): Promise<number>;
1990
- //#endregion
1991
- //#region src/commands/policy.d.ts
1992
1736
  export declare function runPolicyCommand(argv: string[], io: {
1993
1737
  out: Output;
1994
1738
  err: Output;
1995
1739
  }): Promise<number>;
1996
- //#endregion
1997
- //#region src/commands/quarantine.d.ts
1998
- export declare function runQuarantineCommand(argv: string[], io: {
1999
- out: Output;
2000
- err: Output;
2001
- }): number;
2002
- //#endregion
2003
- //#region src/commands/analyze.d.ts
2004
1740
  export declare function runAnalyzeCommand(argv: string[], io: {
2005
1741
  out: Output;
2006
1742
  err: Output;
2007
1743
  }): number;
2008
- //#endregion
2009
- //#region src/commands/ci-adapter.d.ts
2010
1744
  export declare function runCiAdapterCommand(argv: string[], io: {
2011
1745
  out: Output;
2012
1746
  err: Output;
2013
1747
  }): number;
2014
- //#endregion
2015
- //#region src/commands/dashboard.d.ts
2016
- export declare function runDashboardCommand(argv: string[], io: {
2017
- out: Output;
2018
- err: Output;
2019
- }): Promise<number>;
2020
- //#endregion
2021
- //#region src/commands/enterprise.d.ts
2022
- export declare function runEnterpriseCommand(argv: string[], io: {
2023
- out: Output;
2024
- err: Output;
2025
- }): number;
2026
- //#endregion
2027
- //#region src/commands/maturity.d.ts
2028
- export declare function runMaturityCommand(argv: string[], io: {
2029
- out: Output;
2030
- err: Output;
2031
- }): number;
2032
- //#endregion
2033
- //#region src/commands/milestone.d.ts
2034
- export declare function runFrameworkMaturityCommand(argv: string[], io?: {
2035
- out: Output;
2036
- err: Output;
2037
- }): number;
2038
- export declare function runSuppressionGateCommand(argv: string[], io?: {
2039
- out: Output;
2040
- err: Output;
2041
- }): Promise<number>;
2042
- export declare function runCrossFileCommand(argv: string[], io?: {
2043
- out: Output;
2044
- err: Output;
2045
- }): Promise<number>;
2046
1748
  export declare function runContractVerifyCommand(argv: string[], io?: {
2047
1749
  out: Output;
2048
1750
  err: Output;
@@ -2051,16 +1753,6 @@ export declare function runTrustTrendCommand(argv: string[], io?: {
2051
1753
  out: Output;
2052
1754
  err: Output;
2053
1755
  }): Promise<number>;
2054
- export declare function runEvidenceGraphCommand(argv: string[], io?: {
2055
- out: Output;
2056
- err: Output;
2057
- }): Promise<number>;
2058
- export declare function runCIIntegrityCommand(argv: string[], io?: {
2059
- out: Output;
2060
- err: Output;
2061
- }): Promise<number>;
2062
- //#endregion
2063
- //#region src/cli.d.ts
2064
1756
  export declare const runScan: typeof runScan$1, buildUniversalRules: typeof buildUniversalRules$1, fallbackWorkspace: typeof fallbackWorkspace$1, pathMatchesGlob: typeof pathMatchesGlob$1, isValidFindingRecord: typeof isValidFindingRecord$1, discoverAndParseRuntimeReport: typeof discoverAndParseRuntimeReport$1, KNOWN_RULE_IDS: ReadonlySet<string>, OVERLAP_META_BY_RULE_ID: ReadonlyMap<string, OverlapMeta>, EVIDENCE_OVERRIDES: ReadonlyMap<string, string>, SUITE_INVALIDATING_RULE_IDS: ReadonlySet<string>;
2065
1757
  /** A usage-error detail: the offending token, when one exists. */
2066
1758
  export interface UsageErrorDetail {
@@ -2109,6 +1801,8 @@ export declare function validateScanTarget(target: string, err: Output): number
2109
1801
  * zero findings is exactly the case that turns a bug into a green build.
2110
1802
  */
2111
1803
  export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1804
+ /** `mjolnir ci --help`, rendered from the table so it cannot drift. */
1805
+ export declare function renderCiSubcommandHelp(): string;
2112
1806
  /** Testable `ci install` handler. Returns the process exit code. */
2113
1807
  export declare function runCiInstall(argv: string[], io?: {
2114
1808
  out: Output;
@@ -2135,6 +1829,5 @@ export declare function runHelpCommand(argv: string[], io?: {
2135
1829
  err: Output;
2136
1830
  }): number;
2137
1831
  export declare function isEntryPoint(): boolean;
2138
- //#endregion
2139
1832
  export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };
2140
1833
  //# sourceMappingURL=cli.d.mts.map