mjolnir-qa 3.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.mts CHANGED
@@ -1,3 +1,77 @@
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
1
75
  //#region src/types.d.ts
2
76
  /**
3
77
  * Mjolnir — canonical types (JSON contract v1, schemaVersion 1).
@@ -235,6 +309,68 @@ interface DimensionScore {
235
309
  infos: number;
236
310
  }
237
311
  type AnalysisStatus = "complete" | "partial";
312
+ /**
313
+ * The normalized per-test runtime evidence record as it appears in a report
314
+ * (plan V5-011). Structurally the `EvidenceRecord` from
315
+ * `src/engine/evidence-core.ts`, restated here so the report schema does not
316
+ * depend on an engine module's internals — the wire shape is a contract.
317
+ */
318
+ interface EvidenceRecordShape {
319
+ source: string;
320
+ artifact: string;
321
+ file: string;
322
+ title: string;
323
+ line?: number;
324
+ status: {
325
+ final: string;
326
+ failed: boolean;
327
+ retried: boolean;
328
+ passedOnRetry: boolean;
329
+ skipped: boolean;
330
+ timedOut: boolean;
331
+ };
332
+ attempts: number;
333
+ durationMs: number;
334
+ errors: string[];
335
+ attachments: string[];
336
+ provenance: {
337
+ core: string;
338
+ ingest: string;
339
+ };
340
+ }
341
+ /**
342
+ * Candidate binding (plan V5-010).
343
+ *
344
+ * The immutable identity of the release candidate a run was produced against.
345
+ * It is what makes a claim replayable: "this verdict belongs to candidate
346
+ * X, built from commit Y with lockfile Z" can be re-checked; "this verdict
347
+ * belongs to whatever was on the machine" cannot.
348
+ *
349
+ * Every field is required. A partial binding is worse than none, because a
350
+ * consumer cannot tell which link is missing — so an unbound run omits the
351
+ * whole object rather than filling in what it has.
352
+ */
353
+ interface CandidateBinding {
354
+ /** The candidate manifest's own id. */
355
+ manifestId: string;
356
+ /** WORKING_CANDIDATE | RELEASE_CANDIDATE. */
357
+ state: "WORKING_CANDIDATE" | "RELEASE_CANDIDATE";
358
+ /**
359
+ * The immutable commit this candidate is bound to, or null for a working
360
+ * candidate — which by definition has no commit yet.
361
+ */
362
+ candidateSha: string | null;
363
+ /** The commit the candidate was branched from. */
364
+ baseSha: string;
365
+ /** sha256 of package.json as published for the candidate. */
366
+ packageSha256: string;
367
+ /** sha256 of the lockfile as published for the candidate. */
368
+ lockfileSha256: string;
369
+ /** Who owns the candidate. "UNASSIGNED" is a real, reportable value. */
370
+ owner: string;
371
+ /** NOT_AUTHORIZED | AUTHORIZED. Never inferred from evidence. */
372
+ releaseAuthorizationState: "NOT_AUTHORIZED" | "AUTHORIZED";
373
+ }
238
374
  interface ScanResult {
239
375
  schemaVersion: typeof SCHEMA_VERSION;
240
376
  /** False when budget expired or files were skipped (§18.3). */
@@ -321,12 +457,42 @@ interface ScanResult {
321
457
  durationMs: number;
322
458
  /**
323
459
  * Named reasons the scan stopped early (audit H-8): "deadline",
324
- * "file-cap:<adapter>", "rule-loop-deadline". Present only when
325
- * truncation actually happened — absence means the scan is whole.
460
+ * "file-cap:<adapter>", "rule-loop-deadline", "ast-budget-fallback".
461
+ * Present only when truncation actually happened — absence means the
462
+ * scan is whole.
463
+ *
464
+ * Open `string[]`, NOT a closed union: a producer may add a member
465
+ * without a schema bump. The cost of that choice is that a consumer
466
+ * asserting the EXACT set breaks on every new reason, so assertions
467
+ * here are containment assertions, never `toEqual([...])`.
468
+ *
469
+ * "ast-budget-fallback" specifically means the scan ran out of time
470
+ * before a file could be analyzed through its adapter's AST stage, and
471
+ * that file was analyzed by regex instead. It is a capability loss, not
472
+ * a speed trade, and the count of affected files is in `reasons` as
473
+ * "ast-budget-fallback-files:<n>".
326
474
  */
327
475
  truncationReasons?: string[];
328
476
  /** Canonical completion reasons, including scope and parser degradation. */
329
477
  reasons?: string[];
478
+ /**
479
+ * Per-reason counts of capability lost inside a `catch` that returned a
480
+ * clean default — the degradation ledger (`src/engine/degradation-ledger.ts`).
481
+ *
482
+ * Distinct from `truncationReasons`, which means the scan STOPPED, and
483
+ * from `reasons`, which is a flat string set. This one is structured and
484
+ * reason-coded so "the AST mask was unavailable for 3 files" and "3 files
485
+ * were skipped" cannot be confused. Each entry is ALSO surfaced in
486
+ * `reasons` as `degraded:<reason>:<count>`.
487
+ *
488
+ * Present only when at least one degradation happened; absence means the
489
+ * scan lost no capability to a swallowed error, OR the producer predates
490
+ * the ledger. A non-empty value always forces `partial: true`.
491
+ */
492
+ degradations?: Array<{
493
+ reason: string;
494
+ count: number;
495
+ }>;
330
496
  /**
331
497
  * Rule executions that threw and were swallowed by crash isolation
332
498
  * (audit R-9). 0 means no rule silently failed; absence means the
@@ -367,6 +533,18 @@ interface ScanResult {
367
533
  /** The named scope reasons, present only when PARTIAL. */
368
534
  reasons?: string[];
369
535
  };
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;
370
548
  /**
371
549
  * Run Identity (R4c): the deterministic anchor binding verdict ←
372
550
  * evidence ← execution ← scope ← source ← rule(rev). Present when the
@@ -382,6 +560,29 @@ interface ScanResult {
382
560
  scoringModelVersion?: string;
383
561
  frameworkSupportMatrixVersion?: string;
384
562
  commit?: string;
563
+ /** The worktree tree hash the run was bound to, when known. */
564
+ tree?: string;
565
+ /** The lockfile digest the run was bound to, when known. */
566
+ lockfile?: string;
567
+ /**
568
+ * Which identity links this run actually bound (plan V5-010). Derived, not
569
+ * asserted by each consumer: a claim is replayable only when every link it
570
+ * depends on is present.
571
+ *
572
+ * OPTIONAL on purpose. The machine contract is v1 additive-only, so a
573
+ * report written before this field existed has none, and a reader that
574
+ * required it would reject exactly the older artifacts it must tolerate.
575
+ * Absence means "produced by a build that did not record bindings" — never
576
+ * "bound to everything".
577
+ */
578
+ boundLinks?: Array<"input" | "rules" | "config" | "engine" | "commit" | "tree" | "lockfile" | "candidate">;
579
+ /**
580
+ * The release candidate this run is bound to (plan V5-010). Absent for an
581
+ * ordinary scan of a working tree — a run is not a release candidate, and
582
+ * saying otherwise would let a local scan stand in for authorized
583
+ * evidence.
584
+ */
585
+ candidate?: CandidateBinding;
385
586
  };
386
587
  /**
387
588
  * Evidence Graph (R4c): the chain-law links (VERDICT ← EVIDENCE ←
@@ -401,6 +602,34 @@ interface ScanResult {
401
602
  configFingerprint: string;
402
603
  engineVersion: string;
403
604
  };
605
+ /** The release candidate the verdict belongs to, when bound (V5-010). */
606
+ candidate?: {
607
+ manifestId: string;
608
+ candidateSha: string | null;
609
+ };
610
+ };
611
+ /**
612
+ * The normalized evidence core (plan V5-011).
613
+ *
614
+ * Present whenever the run ingested a runtime report. `records` is the
615
+ * canonical, deterministically-ordered set that every downstream consumer
616
+ * reads — before this, the core was computed and discarded inside the
617
+ * pipeline, so a re-derivation had to re-parse the report and could disagree
618
+ * with the verdict it was supposed to explain.
619
+ *
620
+ * Empty `records` with a null `artifact` means no runtime evidence was
621
+ * found, which is different from a run that found an empty report.
622
+ */
623
+ evidence?: {
624
+ records: Array<EvidenceRecordShape>;
625
+ counts: {
626
+ total: number;
627
+ failed: number;
628
+ flaky: number;
629
+ skipped: number;
630
+ timedOut: number;
631
+ };
632
+ artifact: string | null;
404
633
  };
405
634
  /**
406
635
  * Local incremental cache report (Beta-to-Stable plan, M5.2). Present
@@ -639,6 +868,56 @@ declare class DependencyGraph {
639
868
  getDependents(path: string): string[];
640
869
  get allPaths(): string[];
641
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;
642
921
  }
643
922
  //#endregion
644
923
  //#region src/discovery/workspace.d.ts
@@ -657,9 +936,18 @@ interface Workspace {
657
936
  }
658
937
  //#endregion
659
938
  //#region src/discovery/frameworks.d.ts
660
- type TestFramework = "jest" | "vitest" | "playwright";
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];
661
948
  interface FrameworkInfo$1 {
662
- frameworks: TestFramework[];
949
+ /** Detected frameworks, as inventory ids. */
950
+ frameworks: DetectedFramework[];
663
951
  /** True when no config evidence was found at all. */
664
952
  unknown: boolean;
665
953
  }
@@ -991,8 +1279,19 @@ interface LanguageAdapter {
991
1279
  * Contract: resolve to a ParsedAst on success, `undefined` when this
992
1280
  * adapter has no AST layer (or parsing failed — rules fall back to the
993
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.
994
1293
  */
995
- parseAst?(file: ParsedFile): Promise<ParsedAst | undefined>;
1294
+ parseAst?: (file: ParsedFile) => ParsedAst | undefined | Promise<ParsedAst | undefined>;
996
1295
  /**
997
1296
  * Run all rules this adapter hosts against one file. `onCrash` is
998
1297
  * invoked when a rule throws (audit R-9) — the crash is still
@@ -1045,6 +1344,55 @@ interface UniversalRule {
1045
1344
  run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
1046
1345
  }
1047
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
1048
1396
  //#region src/engine/provenance.d.ts
1049
1397
  type FileProvenance = "unmarked" | "generated-marked" | "codegen-like";
1050
1398
  /** Classify one file's provenance. Pure. */
@@ -1072,7 +1420,7 @@ interface IgnoreEntry {
1072
1420
  ruleId: string;
1073
1421
  files?: string[];
1074
1422
  reason: string;
1075
- /** ISO date; defaults to 90 days from creation (S11). */
1423
+ /** ISO date; omitted means the suppression has no expiry. */
1076
1424
  expires?: string;
1077
1425
  }
1078
1426
  interface QADoctorConfig {
@@ -1298,6 +1646,12 @@ interface FileAnalysisResult {
1298
1646
  parseFallbacks: number;
1299
1647
  scanned: number;
1300
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;
1301
1655
  }
1302
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<{
1303
1657
  path: string;
@@ -1313,6 +1667,12 @@ interface PostScanResult {
1313
1667
  frameworks: ReturnType<typeof detectFrameworks>;
1314
1668
  runtimeReportPath: string | undefined;
1315
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[];
1316
1676
  /** Aggregate forensic classifications from the ingested runtime report. */
1317
1677
  forensicVerdicts: ForensicVerdictSummary | undefined;
1318
1678
  config: ReturnType<typeof loadConfig>["config"];
@@ -1339,6 +1699,14 @@ interface AssembleScanResultInput {
1339
1699
  scopeUnrecognized: number;
1340
1700
  parseFailed: number;
1341
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[];
1342
1710
  scanned: number;
1343
1711
  analyzed?: number;
1344
1712
  testFiles: string[];
@@ -1360,6 +1728,7 @@ interface AssembleScanResultInput {
1360
1728
  frameworks: ReturnType<typeof detectFrameworks>;
1361
1729
  runtimeReportPath: string | undefined;
1362
1730
  runtimeIncomplete?: boolean;
1731
+ evidenceRecords?: EvidenceRecord[];
1363
1732
  forensicVerdicts: ForensicVerdictSummary | undefined;
1364
1733
  config: ReturnType<typeof loadConfig>["config"];
1365
1734
  fileProvenance: Array<{
@@ -1393,7 +1762,7 @@ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult
1393
1762
  * scripts/sync-sarif-version.cjs and guarded by the version-consistency
1394
1763
  * spec. cli.ts re-exports this as CLI_VERSION.
1395
1764
  */
1396
- declare const ENGINE_VERSION = "3.0.0";
1765
+ declare const ENGINE_VERSION = "5.0.0";
1397
1766
  //#endregion
1398
1767
  //#region src/cli-io.d.ts
1399
1768
  /**
@@ -1575,11 +1944,17 @@ export declare function runReleaseTrustCommand(argv: string[], io?: {
1575
1944
  * Entry point for `mjolnir business-case`.
1576
1945
  *
1577
1946
  * Flags:
1578
- * --industry <type> Industry cost profile (default/fintech/healthcare/...)
1579
- * --history <months> Use git history for the last N months (estimates from
1580
- * actual scan improvements, not projections)
1581
- * --projected <months> Show projected savings over N months
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.
1582
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.
1583
1958
  */
1584
1959
  export declare function runBusinessCaseCommand(argv: string[], io?: {
1585
1960
  out: Output;
@@ -1623,7 +1998,7 @@ export declare function runPolicyCommand(argv: string[], io: {
1623
1998
  export declare function runQuarantineCommand(argv: string[], io: {
1624
1999
  out: Output;
1625
2000
  err: Output;
1626
- }): Promise<number>;
2001
+ }): number;
1627
2002
  //#endregion
1628
2003
  //#region src/commands/analyze.d.ts
1629
2004
  export declare function runAnalyzeCommand(argv: string[], io: {
@@ -1699,7 +2074,7 @@ export declare const MAX_DURATION_MS = 3600000;
1699
2074
  export declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
1700
2075
  /** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
1701
2076
  export declare function levenshtein(a: string, b: string): number;
1702
- /** Nearest known flags within distance ≤ 2, nearest first. */
2077
+ /** Nearest known flags within distance ≤ 2, nearest first. */
1703
2078
  export declare function nearestFlags(flag: string, max?: number): string[];
1704
2079
  /**
1705
2080
  * Friendly usage error (plan M2, exit 10 preserved): nearest-flag
@@ -1724,9 +2099,14 @@ export declare function parseArgsOrUsage(argv: string[], io: {
1724
2099
  */
1725
2100
  export declare function validateScanTarget(target: string, err: Output): number | null;
1726
2101
  /**
1727
- * Exit-code decision for a finished scan under the given gate level
1728
- * (audit H-7): the previously-dead config.gate field now selects which
1729
- * severities block. Advisory (E0) findings never gate at any level.
2102
+ * Exit-code decision for findings only (audit H-7): the previously-dead
2103
+ * config.gate field selects which severities block. Advisory (E0) findings
2104
+ * never gate at any level.
2105
+ *
2106
+ * This is a findings-only view of the one exit matrix in `claim-evidence`.
2107
+ * Callers that have just finished an ANALYSIS must use `scanExitCode`
2108
+ * instead — this function cannot see `partial`, and a truncated scan with
2109
+ * zero findings is exactly the case that turns a bug into a green build.
1730
2110
  */
1731
2111
  export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1732
2112
  /** Testable `ci install` handler. Returns the process exit code. */
@@ -1756,4 +2136,5 @@ export declare function runHelpCommand(argv: string[], io?: {
1756
2136
  }): number;
1757
2137
  export declare function isEntryPoint(): boolean;
1758
2138
  //#endregion
1759
- export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };
2139
+ export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };
2140
+ //# sourceMappingURL=cli.d.mts.map