mjolnir-qa 1.0.8 → 2.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
@@ -179,6 +179,25 @@ interface Finding {
179
179
  * Additive within schemaVersion 1.
180
180
  */
181
181
  fixGroupId?: string;
182
+ /**
183
+ * ENGINE-002: disambiguated finding identity (fingerprint + line +
184
+ * column). Unique per occurrence when the same rule fires multiple
185
+ * times in one file. Additive within schemaVersion 1.
186
+ */
187
+ findingId?: string;
188
+ /**
189
+ * ENGINE-002: root-cause identity — initially the same as the
190
+ * fingerprint (ruleId + file + message). Future rules may coarsen
191
+ * this to group findings by underlying cause. Additive within
192
+ * schemaVersion 1.
193
+ */
194
+ rootCauseId?: string;
195
+ /**
196
+ * ENGINE-002: deduplication group — initially the same as
197
+ * rootCauseId. Future rules may further coarsen this. Additive
198
+ * within schemaVersion 1.
199
+ */
200
+ deduplicationGroup?: string;
182
201
  }
183
202
  /**
184
203
  * Mutation evidence (master plan P5, plan 1788853205786 — flag 6,
@@ -268,6 +287,15 @@ interface ScanResult {
268
287
  name: string;
269
288
  rules: number;
270
289
  }>;
290
+ /**
291
+ * Rules whose suiteInvalidating findings capped the score (e.g.
292
+ * QA-TEST-001 when `.only` is committed). Additive within
293
+ * schemaVersion 1; absent when no suite-invalidating rule fired.
294
+ * Present to explain why the score may be 49 even when all findings
295
+ * are advisory (E0/quarantine) — the factual state (suite did not
296
+ * fully execute) prevents a perfect score regardless of severity.
297
+ */
298
+ suiteInvalidatedBy?: string[];
271
299
  /**
272
300
  * Agentic Trust Profile (plan §17): per-scan provenance metadata —
273
301
  * share of test files carrying detected generative markers and the
@@ -304,6 +332,13 @@ interface ScanResult {
304
332
  */
305
333
  rulesCrashed?: number;
306
334
  };
335
+ /**
336
+ * Scoring model version stamped into the result (ENGINE-001). Allows
337
+ * consumers to identify which scoring formula produced the score.
338
+ * Additive within schemaVersion 1; absent when the producer predates
339
+ * this field.
340
+ */
341
+ scoringModelVersion?: string;
307
342
  /**
308
343
  * Scope Integrity block (product-gap master plan §7, R4c): the
309
344
  * claimed-vs-analyzed accounting. `scopeVerdict` is PROVEN only when
@@ -340,6 +375,9 @@ interface ScanResult {
340
375
  rulesDigest: string;
341
376
  configFingerprint: string;
342
377
  engineVersion: string;
378
+ trustModelVersion?: string;
379
+ scoringModelVersion?: string;
380
+ frameworkSupportMatrixVersion?: string;
343
381
  };
344
382
  /**
345
383
  * Evidence Graph (R4c): the chain-law links (VERDICT ← EVIDENCE ←
@@ -385,6 +423,48 @@ interface ScanResult {
385
423
  * src/engine/trust-summary.ts — the single definition site.
386
424
  */
387
425
  trustSummary?: TrustSummary;
426
+ /**
427
+ * Cross-Rule Evidence Correlation Engine (INTEL-005) conclusions.
428
+ * Produced by `correlateFindings()` after scoring; purely additive —
429
+ * findings are never modified. Present on every completed scan with
430
+ * ≥ 1 finding. Additive within schemaVersion 1.
431
+ */
432
+ correlationConclusions?: Array<{
433
+ conclusionType: "CONVERGENT" | "CORROBORATED" | "CONTRADICTED" | "AMPLIFIED" | "INDEPENDENT";
434
+ certainty: "NONE" | "SUPPORTING" | "STRONG";
435
+ corroboration: string;
436
+ sourceCount: number;
437
+ findingIds: string[];
438
+ }>;
439
+ /**
440
+ * Dependency graph metadata (ECO-005). Present when a dependency
441
+ * manifest (package.json, pyproject.toml, pom.xml) was found.
442
+ * Additive within schemaVersion 1.
443
+ */
444
+ dependencyGraph?: {
445
+ /** Number of nodes in the dependency graph. */
446
+ nodes: number;
447
+ /** Number of edges (total dependency references). */
448
+ edges: number;
449
+ };
450
+ /**
451
+ * Monorepo analysis results (ECO-003). Present when --monorepo was
452
+ * requested and multiple packages were found. Additive within
453
+ * schemaVersion 1.
454
+ */
455
+ monorepoAnalysis?: {
456
+ packages: Array<{
457
+ packageName: string;
458
+ path: string;
459
+ findings: number;
460
+ score: number | null;
461
+ verdict: "pass" | "warn" | "fail";
462
+ }>;
463
+ overallScore: number | null;
464
+ overallVerdict: "pass" | "warn" | "fail";
465
+ strategy: "worst-package" | "average" | "configurable";
466
+ blockerPackage?: string;
467
+ };
388
468
  }
389
469
  /** Trust summary metric block (plan §6 — measurement, not contract). */
390
470
  interface TrustSummary {
@@ -425,6 +505,106 @@ interface OverlapMeta {
425
505
  order?: number;
426
506
  }
427
507
  //#endregion
508
+ //#region src/forensics/classify.d.ts
509
+ declare const FORENSIC_VERDICTS: readonly ["likely-real-defect", "environmental-failure", "infrastructure-failure", "flaky", "retry-dependent", "unstable-construction", "inconclusive"];
510
+ type ForensicVerdict = (typeof FORENSIC_VERDICTS)[number];
511
+ declare const EVIDENCE_STATES: readonly ["exists", "absent", "unsupported", "contradictory", "insufficient"];
512
+ type ForensicEvidenceState = (typeof EVIDENCE_STATES)[number];
513
+ interface ForensicClassification {
514
+ verdict: ForensicVerdict;
515
+ evidenceState: ForensicEvidenceState;
516
+ /** Which signal family won (audit trail for the classification). */
517
+ signals: {
518
+ environmental: number;
519
+ infrastructure: number;
520
+ construction: number;
521
+ };
522
+ }
523
+ //#endregion
524
+ //#region src/forensics/types.d.ts
525
+ type RunStatus = "passed" | "failed" | "timedOut" | "skipped" | "interrupted";
526
+ /** Derived per-test reliability facts. */
527
+ interface TestVerdict {
528
+ file: string;
529
+ title: string;
530
+ attempts: number;
531
+ finalStatus: RunStatus;
532
+ totalDurationMs: number;
533
+ /**
534
+ * TRUE-FLAKE: passed only on attempt >= 2 — not a passing test,
535
+ * a lucky test.
536
+ */
537
+ passedOnRetry: boolean;
538
+ /** Failed at least once across attempts. */
539
+ everFailed: boolean;
540
+ skipped: boolean;
541
+ /**
542
+ * 1-based spec declaration line when known (plan §16). Enables
543
+ * test-level runtime corroboration: a finding between this line and
544
+ * the next declared test's line in the same file belongs to this
545
+ * test. Undefined when the report format omits locations (JUnit).
546
+ */
547
+ line?: number;
548
+ /**
549
+ * WI-18 (growth roadmap §6): the forensic verdict classification —
550
+ * deterministic minimum-signal table over machine-visible facts
551
+ * (see forensics/classify.ts). Additive within schemaVersion.
552
+ */
553
+ forensic?: ForensicClassification;
554
+ }
555
+ interface ForensicsReport {
556
+ forensicsSchemaVersion: number;
557
+ /**
558
+ * Which report family the records came from. P4 (plan 1788853205786)
559
+ * widens the union additively: `jest-json` and `vitest-json` ingest
560
+ * with ONE attempt per record (their reports carry no per-attempt
561
+ * history), so TRUE-FLAKE can never fire from these sources — honest
562
+ * degradation, see the parsers' module docs. R5 (WI-17) adds
563
+ * `playwright-trace`: one record per trace artifact (the per-test
564
+ * trace convention), action-level facts only.
565
+ */
566
+ source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace";
567
+ totalTests: number;
568
+ failed: number;
569
+ skipped: number;
570
+ retriedTests: number;
571
+ flakyTests: number;
572
+ totalDurationMs: number;
573
+ verdicts: TestVerdict[];
574
+ /**
575
+ * Whether all discovered report files were successfully ingested.
576
+ * FALSE when any files were skipped due to size limits, parse failures,
577
+ * or cumulative size cap.
578
+ */
579
+ analysisComplete: boolean;
580
+ /** Number of report files skipped (size limit, corrupt, unsupported). */
581
+ skippedReports: number;
582
+ /** Named reasons for incompleteness (e.g. "size-limit", "cumulative-size-limit", "parse-failure"). */
583
+ incompleteReasons: string[];
584
+ }
585
+ //#endregion
586
+ //#region src/engine/dependency-graph.d.ts
587
+ /**
588
+ * Dependency Graph (ECO-005).
589
+ *
590
+ * Parses package manifests (package.json, pyproject.toml, pom.xml) to
591
+ * build a dependency graph. Supports transitive dependency resolution,
592
+ * dependent lookup, and reachable-file computation from test files.
593
+ */
594
+ interface DependencyNode {
595
+ path: string;
596
+ dependencies: string[];
597
+ }
598
+ declare class DependencyGraph {
599
+ private readonly nodes;
600
+ addNode(node: DependencyNode): void;
601
+ getDependencies(path: string): string[];
602
+ getTransitiveDependencies(path: string): string[];
603
+ getDependents(path: string): string[];
604
+ get allPaths(): string[];
605
+ get size(): number;
606
+ }
607
+ //#endregion
428
608
  //#region src/discovery/workspace.d.ts
429
609
  /**
430
610
  * Repository discovery (Sprint-Plan W1-03).
@@ -440,6 +620,15 @@ interface Workspace {
440
620
  workspaceGlobs: string[];
441
621
  }
442
622
  //#endregion
623
+ //#region src/discovery/frameworks.d.ts
624
+ type TestFramework = "jest" | "vitest" | "playwright";
625
+ interface FrameworkInfo$1 {
626
+ frameworks: TestFramework[];
627
+ /** True when no config evidence was found at all. */
628
+ unknown: boolean;
629
+ }
630
+ declare function detectFrameworks(ws: Workspace): FrameworkInfo$1;
631
+ //#endregion
443
632
  //#region src/rules/rule.d.ts
444
633
  /**
445
634
  * How the rule's primary detection decision is made (Verification Trust
@@ -669,6 +858,12 @@ interface QADoctorRule extends RuleMeta {
669
858
  run: RuleFn;
670
859
  }
671
860
  //#endregion
861
+ //#region src/discovery/ignores.d.ts
862
+ /** An immutable, per-root resolved ignore matcher (audit R-8). */
863
+ interface IgnoreMatcher {
864
+ isIgnored(relPath: string): boolean;
865
+ }
866
+ //#endregion
672
867
  //#region src/engine/adapter.d.ts
673
868
  /** Semantic operations a parsed file exposes to rules. */
674
869
  interface ParsedFile {
@@ -693,6 +888,87 @@ interface ParsedFile {
693
888
  */
694
889
  frameworkTags?: readonly string[];
695
890
  }
891
+ interface FrameworkInfo {
892
+ frameworks: string[];
893
+ unknown: boolean;
894
+ }
895
+ /**
896
+ * The result of the async parse stage (Verification Trust Evolution Plan
897
+ * Phase 0.5, §10): an AST plus its explicit disposal path. The scan
898
+ * pipeline calls `dispose()` exactly once per file, in a
899
+ * finally-equivalent position that runs whether or not rules completed —
900
+ * normal completion, rule crash, per-file budget expiry, or scan abort.
901
+ */
902
+ interface ParsedAst {
903
+ /** Consumed synchronously by rules via `ParsedFile.ast`. */
904
+ ast: unknown;
905
+ /** Release WASM/AST resources (`tree.delete()` for tree-sitter). */
906
+ dispose(): void;
907
+ }
908
+ interface ScanContext {
909
+ workspace: Workspace;
910
+ /** Absolute paths of discovered test files. */
911
+ testFiles: string[];
912
+ deadline: number;
913
+ /** Resolved per-scan ignore matcher (audit R-8) — no module globals. */
914
+ ignoreMatcher: IgnoreMatcher;
915
+ /** Called once per file that could not be read/analyzed. */
916
+ onSkippedFile: (reason: string) => void;
917
+ /**
918
+ * Called when discovery stops early (audit H-8): deadline expiry or
919
+ * the per-adapter file cap. The reason is named in analysisStatus.
920
+ */
921
+ onDiscoveryTruncated: (reason: string) => void;
922
+ /** Per-adapter discovery budget (audit H-8). One language can no
923
+ * longer consume the whole list and starve the others. */
924
+ maxFiles: number;
925
+ /** R4c Scope Integrity: counted matcher exclusions (optional — adapters
926
+ * whose discovery walks sharedWalk pass this through to the counters). */
927
+ onIgnored?: () => void;
928
+ /** R4c Scope Integrity: counted files no adapter claims (optional). */
929
+ onUnrecognized?: () => void;
930
+ /**
931
+ * Called when a rule throws on a file (audit R-9): crash isolation
932
+ * stays silent by default, but the scan counts it and `--debug`
933
+ * surfaces it.
934
+ */
935
+ onRuleCrash?: (ruleId: string, file: string, error: unknown) => void;
936
+ }
937
+ interface LanguageAdapter {
938
+ readonly id: string;
939
+ readonly extensions: readonly string[];
940
+ /** Human-readable patterns this adapter searches for (empty-state UX). */
941
+ readonly testFileGlobs: readonly string[];
942
+ /** Dependency/output directory names this adapter never enters. */
943
+ readonly dirSkips: readonly string[];
944
+ /** Canonical test-file check per ecosystem conventions. */
945
+ isTestFile(path: string): boolean;
946
+ detectFrameworks(root: string): FrameworkInfo;
947
+ discoverTestFiles(ctx: ScanContext): void;
948
+ /**
949
+ * Async parse stage (Verification Trust Evolution Plan Phase 0.5, §10):
950
+ * awaited by the scan pipeline between discovery and rule execution.
951
+ * `runRules` and every rule stay synchronous and consume the produced
952
+ * tree via `ParsedFile.ast` — the engine is NOT async end-to-end, only
953
+ * this one seam is (WASM grammar load is inherently async).
954
+ *
955
+ * Contract: resolve to a ParsedAst on success, `undefined` when this
956
+ * adapter has no AST layer (or parsing failed — rules fall back to the
957
+ * regex path either way). Never throws.
958
+ */
959
+ parseAst?(file: ParsedFile): Promise<ParsedAst | undefined>;
960
+ /**
961
+ * Run all rules this adapter hosts against one file. `onCrash` is
962
+ * invoked when a rule throws (audit R-9) — the crash is still
963
+ * isolated, but never invisible. `budget` (audit P-1) stops rule
964
+ * execution mid-file when a single file is too expensive to analyze
965
+ * in full; the caller reports the skip honestly.
966
+ */
967
+ runRules(rules: readonly UniversalRule[], file: ParsedFile, emit: (f: Omit<Finding, "ruleId" | "category">, ruleId: string, category: string) => void, onCrash?: (ruleId: string, error: unknown) => void, budget?: {
968
+ deadline: number;
969
+ onExceeded: () => void;
970
+ }): void;
971
+ }
696
972
  /**
697
973
  * A rule that declares which adapters it supports. Backward compatible:
698
974
  * legacy 'test-files' maps to ['typescript'], 'ci-workflows' to
@@ -733,7 +1009,62 @@ interface UniversalRule {
733
1009
  run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
734
1010
  }
735
1011
  //#endregion
736
- //#region src/engine/scan-pipeline.d.ts
1012
+ //#region src/engine/provenance.d.ts
1013
+ type FileProvenance = "unmarked" | "generated-marked" | "codegen-like";
1014
+ /** Classify one file's provenance. Pure. */
1015
+ declare function classifyProvenance(file: {
1016
+ text: string;
1017
+ codeText?: string;
1018
+ }): FileProvenance;
1019
+ //#endregion
1020
+ //#region src/engine/scan-cache.d.ts
1021
+ interface CacheStats {
1022
+ hits: number;
1023
+ misses: number;
1024
+ /** Where the cache lives — reported so `--json` consumers can audit it. */
1025
+ file: string;
1026
+ }
1027
+ interface ScanCache {
1028
+ readonly stats: CacheStats;
1029
+ lookup(key: string): Finding[] | undefined;
1030
+ store(key: string, findings: Finding[], fileBudgetExceeded: boolean): void;
1031
+ persist(): void;
1032
+ }
1033
+ //#endregion
1034
+ //#region src/config/config.d.ts
1035
+ interface IgnoreEntry {
1036
+ ruleId: string;
1037
+ files?: string[];
1038
+ reason: string;
1039
+ /** ISO date; defaults to 90 days from creation (S11). */
1040
+ expires?: string;
1041
+ }
1042
+ interface QADoctorConfig {
1043
+ gate?: "advisory" | "error" | "warning";
1044
+ /** Path globs to skip during discovery (see DEFAULT_IGNORES dialect). */
1045
+ exclude?: string[];
1046
+ severityOverrides?: Record<string, Severity>;
1047
+ ignore?: IgnoreEntry[];
1048
+ }
1049
+ declare function loadConfig(root: string, options?: {
1050
+ knownRuleIds?: ReadonlySet<string>;
1051
+ }): {
1052
+ config: QADoctorConfig;
1053
+ path: string | null;
1054
+ /** Non-fatal notes (e.g. unknown rule IDs in severityOverrides). */
1055
+ warnings: string[];
1056
+ };
1057
+ declare namespace scan_pipeline_d_exports {
1058
+ 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 };
1059
+ }
1060
+ /** Registered rule IDs — used to warn on unknown severityOverrides keys (M4). */
1061
+ declare const KNOWN_RULE_IDS$1: ReadonlySet<string>;
1062
+ /**
1063
+ * R6 (Bug Map M-02): per-rule overlap metadata, built from RULES the
1064
+ * same way tierByRuleId is — `asUniversal` drops `overlapWith`, so the
1065
+ * dedup map must come from the registry directly.
1066
+ */
1067
+ declare const OVERLAP_META_BY_RULE_ID$1: ReadonlyMap<string, OverlapMeta>;
737
1068
  declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
738
1069
  /** CLI trust-gate flag (`--enable-plugins`) for this scan. */
739
1070
  enablePlugins?: boolean;
@@ -749,6 +1080,13 @@ declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
749
1080
  }>;
750
1081
  externalRules: QADoctorRule[];
751
1082
  }>;
1083
+ /** Rule-declared evidence-level overrides (Honesty Core). */
1084
+ declare const EVIDENCE_OVERRIDES$1: ReadonlyMap<string, string>;
1085
+ /**
1086
+ * Rules whose findings void the suite's pass claim (RuleMeta.suiteInvalidating).
1087
+ * Built from the registry so the scorer never has to import it.
1088
+ */
1089
+ declare const SUITE_INVALIDATING_RULE_IDS$1: ReadonlySet<string>;
752
1090
  interface CliArgs {
753
1091
  target: string;
754
1092
  json: boolean;
@@ -824,6 +1162,13 @@ interface CliArgs {
824
1162
  * Report is the hero output.
825
1163
  */
826
1164
  classic?: boolean;
1165
+ /**
1166
+ * --monorepo: per-package trust analysis (ECO-003). When enabled, the
1167
+ * scan discovers packages and computes per-package scores with
1168
+ * configurable aggregation. Additive flag; absent means "single-project
1169
+ * mode" (no monorepo analysis).
1170
+ */
1171
+ monorepo?: boolean;
827
1172
  }
828
1173
  interface ScanHooks {
829
1174
  /** Invoked when a rule throws on a file (audit R-9). */
@@ -877,11 +1222,14 @@ declare function pathMatchesGlob$1(path: string, glob: string): boolean;
877
1222
  * accepts. Zero-config search over conventional artifact names at
878
1223
  * depth ≤ 2 (src/discovery/evidence-discovery.ts); the FIRST parsable
879
1224
  * candidate wins (priority: mjolnir-report > playwright-json >
880
- * test-results-dir > junit-file). Returns the path for `runForensics`,
881
- * or undefined when no convention is present ("no runtime evidence" —
882
- * never guessed; the CLI surfaces the missing-evidence message).
1225
+ * test-results-dir > junit-file). Validates each candidate by attempting
1226
+ * to parse it and checking that it produces at least one test. Returns
1227
+ * the parsed report alongside the path to avoid double-parsing.
883
1228
  */
884
- declare function discoverRuntimeReport$1(scanRoot: string): string | undefined;
1229
+ declare function discoverAndParseRuntimeReport$1(scanRoot: string): {
1230
+ path: string;
1231
+ report: ForensicsReport;
1232
+ } | undefined;
885
1233
  /**
886
1234
  * Audit W10 — runtime shape validation at the rule→Finding boundary.
887
1235
  * A finding record coming out of a rule (plugin/JSON-manifest rules are
@@ -892,6 +1240,84 @@ declare function discoverRuntimeReport$1(scanRoot: string): string | undefined;
892
1240
  * caller) — never silently scored.
893
1241
  */
894
1242
  declare function isValidFindingRecord$1(f: unknown): f is Omit<Finding, "ruleId" | "category">;
1243
+ /**
1244
+ * Replace 7-way nested ternary with a linear probe.
1245
+ * Returns the adapter whose `isTestFile` matches the path, falling
1246
+ * back to the TypeScript adapter (the default language).
1247
+ */
1248
+ declare function selectAdapter(path: string): LanguageAdapter;
1249
+ interface DiscoveryResult {
1250
+ testFiles: string[];
1251
+ stagedSurface: boolean;
1252
+ }
1253
+ declare function discoverTestFilesPhase(workspace: Workspace, args: CliArgs, hooks: ScanHooks, scanRoot: Workspace, ctx: ScanContext): DiscoveryResult;
1254
+ interface FileAnalysisResult {
1255
+ skippedFiles: number;
1256
+ testFileCount: number;
1257
+ testDeclarationCount: number;
1258
+ rulesPartial: boolean;
1259
+ parseFailed: number;
1260
+ scanned: number;
1261
+ }
1262
+ 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<{
1263
+ path: string;
1264
+ provenance: ReturnType<typeof classifyProvenance>;
1265
+ }>, onRuleCrash: (ruleId: string, file: string, error: unknown) => void): Promise<FileAnalysisResult>;
1266
+ interface PostScanResult {
1267
+ testDeclarationCount: number;
1268
+ scopeInfo: {
1269
+ scope: "all" | "changed";
1270
+ degraded?: string | undefined;
1271
+ };
1272
+ suppressionCount: number;
1273
+ frameworks: ReturnType<typeof detectFrameworks>;
1274
+ runtimeReportPath: string | undefined;
1275
+ config: ReturnType<typeof loadConfig>["config"];
1276
+ }
1277
+ 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;
1278
+ interface AssembleScanResultInput {
1279
+ findings: Finding[];
1280
+ testFileCount: number;
1281
+ testDeclarationCount: number;
1282
+ declarationsByFile: Map<string, number>;
1283
+ skippedFiles: number;
1284
+ rulesCrashed: number;
1285
+ truncationReasons: Set<string>;
1286
+ discoveryTruncated: boolean;
1287
+ rulesPartial: boolean;
1288
+ scopeIgnored: number;
1289
+ scopeUnrecognized: number;
1290
+ parseFailed: number;
1291
+ scanned: number;
1292
+ testFiles: string[];
1293
+ workspace: Workspace;
1294
+ scanRoot: Workspace;
1295
+ args: CliArgs;
1296
+ hooks: ScanHooks;
1297
+ cache: ScanCache;
1298
+ REVISION_BY_RULE_ID: Map<string, number>;
1299
+ pluginsLoaded: Array<{
1300
+ name: string;
1301
+ rules: number;
1302
+ }>;
1303
+ scopeInfo: {
1304
+ scope: "all" | "changed";
1305
+ degraded?: string | undefined;
1306
+ };
1307
+ suppressionCount: number;
1308
+ frameworks: ReturnType<typeof detectFrameworks>;
1309
+ runtimeReportPath: string | undefined;
1310
+ config: ReturnType<typeof loadConfig>["config"];
1311
+ fileProvenance: Array<{
1312
+ path: string;
1313
+ provenance: ReturnType<typeof classifyProvenance>;
1314
+ }>;
1315
+ started: number;
1316
+ stagedSurface: boolean;
1317
+ /** ECO-005: dependency graph built from project manifests. */
1318
+ dependencyGraph?: DependencyGraph;
1319
+ }
1320
+ declare function assembleScanResult(o: AssembleScanResultInput): ScanResult;
895
1321
  /**
896
1322
  * Testable default scan path core. `hooks` lets callers observe
897
1323
  * normally-invisible events (swallowed rule crashes) without changing
@@ -913,7 +1339,7 @@ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult
913
1339
  * scripts/sync-sarif-version.cjs and guarded by the version-consistency
914
1340
  * spec. cli.ts re-exports this as CLI_VERSION.
915
1341
  */
916
- declare const ENGINE_VERSION = "1.0.8";
1342
+ declare const ENGINE_VERSION = "2.0.0";
917
1343
  //#endregion
918
1344
  //#region src/cli-io.d.ts
919
1345
  /**
@@ -925,172 +1351,225 @@ declare const ENGINE_VERSION = "1.0.8";
925
1351
  */
926
1352
  /** The variadic IO signature shared by all command handlers. */
927
1353
  type Output = (...parts: unknown[]) => void;
928
- declare const out: Output;
929
- declare const err: Output;
930
- declare function internalErrorMessage(err: unknown, emit: (s: string) => void, debug: boolean): void;
931
- //#endregion
932
- //#region src/commands/doctor-run.d.ts
933
- declare function runDoctorCommand(argv: string[], io?: {
934
- out: Output;
935
- err: Output;
936
- }): number;
1354
+ export declare const out: Output;
1355
+ export declare const err: Output;
1356
+ export declare function internalErrorMessage(err: unknown, emit: (s: string) => void, debug: boolean): void;
937
1357
  //#endregion
938
- //#region src/cli.d.ts
939
- declare const runScan: typeof runScan$1, buildUniversalRules: typeof buildUniversalRules$1, fallbackWorkspace: typeof fallbackWorkspace$1, pathMatchesGlob: typeof pathMatchesGlob$1, isValidFindingRecord: typeof isValidFindingRecord$1, discoverRuntimeReport: typeof discoverRuntimeReport$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>;
940
- /** A usage-error detail: the offending token, when one exists. */
941
- interface UsageErrorDetail {
942
- /** The unknown flag or rejected value (e.g. `--nope`, `loud`). */
943
- token?: string | undefined;
944
- /** The flag whose value was rejected (`--tone` for `--tone loud`). */
945
- flag?: string | undefined;
946
- }
947
- declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
948
- /** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
949
- declare function levenshtein(a: string, b: string): number;
950
- /** Nearest known flags within distance ≤ 2, nearest first. */
951
- declare function nearestFlags(flag: string, max?: number): string[];
1358
+ //#region src/cli-handlers.d.ts
952
1359
  /**
953
- * Friendly usage error (plan M2, exit 10 preserved): nearest-flag
954
- * suggestion, the valid neighbors, and the exact help command. Printed
955
- * to stderr; findings/usage stay on their documented streams.
1360
+ * Render scan output in the requested format.
1361
+ *
1362
+ * Extracted from runScanCommand (Task 6) to reduce cyclomatic complexity.
1363
+ * Handles sarif, mermaid, codequality, json, and terminal (trust-report)
1364
+ * rendering. The caller handles first-run hint + milestones separately.
956
1365
  */
957
- declare function usageErrorMessage(detail: UsageErrorDetail): string;
958
- /** Testable `ci install` handler. Returns the process exit code. */
959
- declare function runCiInstall(argv: string[], io?: {
1366
+ export declare function renderScanOutput(result: Awaited<ReturnType<typeof runScan$1>>, args: CliArgs, target: string, io: {
960
1367
  out: Output;
961
1368
  err: Output;
962
- }): number;
963
- /** Testable `suppressions` handler. */
964
- declare function runSuppressions(io?: {
1369
+ }): void;
1370
+ export declare function runScanCommand(argv: string[], io?: {
965
1371
  out: Output;
966
- err?: Output;
967
- }): number;
968
- /** Testable `forensics` handler. */
969
- declare function runForensicsCommand(argv: string[], io?: {
1372
+ err: Output;
1373
+ }): Promise<number>;
1374
+ export declare function runTriageCommand(argv: string[], io?: {
970
1375
  out: Output;
971
1376
  err: Output;
972
1377
  }): number;
973
- /** Testable `doctor:playwright` handler. */
974
- declare function runDoctorPlaywright(argv: string[], io?: {
1378
+ export declare function runMutationCommand(argv: string[], io?: {
975
1379
  out: Output;
976
- err?: Output;
1380
+ err: Output;
977
1381
  }): Promise<number>;
978
- /** Testable `rules` handler — rule catalog with Trust Metadata. */
979
- declare function runRulesCommand(argv: string[], io?: {
1382
+ export declare function runBadgeCommand(argv: string[], io?: {
980
1383
  out: Output;
981
1384
  err: Output;
982
1385
  }): Promise<number>;
983
- /**
984
- * Testable `explain <RULE-ID>` handler (Plan.md Sprint 1.3,
985
- * Master-Stabilization-Plan Sprint 5 Task 19). Metadata always renders
986
- * offline from the registry; the concrete example is real detector
987
- * output from the rule's own must-fire fixture when one is findable
988
- * (this repo checkout, or --fixtures-root), and honestly omitted
989
- * otherwise — never a fabricated example.
990
- */
991
- declare function runExplainCommand(argv: string[], io?: {
1386
+ export declare function runDebtCommand(argv: string[], io?: {
992
1387
  out: Output;
993
1388
  err: Output;
994
1389
  }): Promise<number>;
995
- declare function runScanCommand(argv: string[], io?: {
1390
+ export declare function runFixCommand(argv: string[], io?: {
996
1391
  out: Output;
997
1392
  err: Output;
998
1393
  }): Promise<number>;
999
- /**
1000
- * Exit-code decision for a finished scan under the given gate level
1001
- * (audit H-7): the previously-dead config.gate field now selects which
1002
- * severities block. Advisory (E0) findings never gate at any level.
1003
- */
1004
- declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1005
- /** Testable `triage` handler (Tier 5 #22). */
1006
- declare function runTriageCommand(argv: string[], io?: {
1394
+ export declare function runCreateRuleCommand(argv: string[], io?: {
1007
1395
  out: Output;
1008
1396
  err: Output;
1009
1397
  }): number;
1010
- /** Testable `mutation` handler (master plan P5, plan 1788853205786).
1011
- * Reads a mutation report (Stryker JSON / mutmut junitxml), renders the
1012
- * survived-mutant leaderboard, and — with `--scan <path>` — re-scans the
1013
- * target, stamps matching findings with `mutationEvidence` (E1→E2 by
1014
- * derivation) and renders the stamped findings. NEVER spawns mutation
1015
- * tools: it reads reports the user already produced. Exit 2 when
1016
- * nothing in the report is recognized — honest no-evidence, not an
1017
- * error. Exit 0 in every other case: the reader is report-only, never a
1018
- * gate. */
1019
- declare function runMutationCommand(argv: string[], io?: {
1398
+ export declare function runImpactCommand(argv: string[], io?: {
1020
1399
  out: Output;
1021
1400
  err: Output;
1022
1401
  }): Promise<number>;
1023
- /** Testable `badge` handler (Tier 1 #5). */
1024
- declare function runBadgeCommand(argv: string[], io?: {
1402
+ export declare function runBaselineCommand(argv: string[], io?: {
1025
1403
  out: Output;
1026
1404
  err: Output;
1027
1405
  }): Promise<number>;
1028
- /** Testable `debt` handler (Tier 5 #27). */
1029
- declare function runDebtCommand(argv: string[], io?: {
1406
+ export declare function runVerifyCommand(argv: string[], io?: {
1030
1407
  out: Output;
1031
1408
  err: Output;
1032
1409
  }): Promise<number>;
1033
- /** Testable `fix` handler (Tier 1 #3) — safe auto-fix with proof. */
1034
- declare function runFixCommand(argv: string[], io?: {
1410
+ export declare function runDiffCommand(argv: string[], io?: {
1035
1411
  out: Output;
1036
1412
  err: Output;
1037
1413
  }): Promise<number>;
1038
- /** Testable `create-rule` handler (Tier 6 #34). */
1039
- declare function runCreateRuleCommand(argv: string[], io?: {
1414
+ export declare function runPrCommentCommand(argv: string[], io?: {
1415
+ out: Output;
1416
+ err: Output;
1417
+ }): Promise<number>;
1418
+ export declare function runStatsCommand(argv: string[], io?: {
1040
1419
  out: Output;
1041
1420
  err: Output;
1042
1421
  }): number;
1043
- /** Testable `impact` handler (Sprint 6 Task 23). */
1044
- declare function runImpactCommand(argv: string[], io?: {
1422
+ export declare function runHandoverCommand(argv: string[], io?: {
1045
1423
  out: Output;
1046
1424
  err: Output;
1047
1425
  }): Promise<number>;
1048
- /** Testable `baseline` handler (Sprint 6 Task 24). */
1049
- declare function runBaselineCommand(argv: string[], io?: {
1426
+ export declare function runInitCommand(argv: string[], io?: {
1050
1427
  out: Output;
1051
1428
  err: Output;
1052
- }): Promise<number>;
1053
- /** Testable `diff` handler (Sprint 6 Task 24) — new/worsened debt only. */
1054
- /** Testable `verify` handler — the agent-loop verb (master plan P7,
1055
- * plan 1788853205786). Scans the target and diffs against the committed
1056
- * baseline, rendering the before/after digest the agent loop consumes
1057
- * (resolved per §15 · new · unchanged by ruleId+location · score delta).
1058
- * Exit semantics are the frozen contract: 0 clean · 1 new error
1059
- * findings · 2 partial scan or no baseline · 20 internal. */
1060
- declare function runVerifyCommand(argv: string[], io?: {
1429
+ }): number;
1430
+ export declare function runPwReportCommand(argv: string[], io?: {
1431
+ out: Output;
1432
+ err: Output;
1433
+ }): number;
1434
+ export declare function runForensicsCommand(argv: string[], io?: {
1435
+ out: Output;
1436
+ err: Output;
1437
+ }): number;
1438
+ export declare function runRulesCommand(argv: string[], io?: {
1061
1439
  out: Output;
1062
1440
  err: Output;
1063
1441
  }): Promise<number>;
1064
- declare function runDiffCommand(argv: string[], io?: {
1442
+ export declare function runExplainCommand(argv: string[], io?: {
1065
1443
  out: Output;
1066
1444
  err: Output;
1067
1445
  }): Promise<number>;
1068
- /** Testable `pr-comment` handler (Sprint 6 Task 25). */
1069
- declare function runPrCommentCommand(argv: string[], io?: {
1446
+ export declare function runDoctorPlaywright(argv: string[], io?: {
1447
+ out: Output;
1448
+ err?: Output;
1449
+ }): Promise<number>;
1450
+ //#endregion
1451
+ //#region src/commands/doctor-run.d.ts
1452
+ export declare function runDoctorCommand(argv: string[], io?: {
1453
+ out: Output;
1454
+ err: Output;
1455
+ }): number;
1456
+ //#endregion
1457
+ //#region src/commands/summary.d.ts
1458
+ /**
1459
+ * Testable summary command core. Returns the process exit code.
1460
+ * Streams: annotations → stdout (always; GitHub greps them), summary →
1461
+ * step-summary file when set unless --stdout. Unknown flags are a
1462
+ * usage error (exit 10) — a typo'd --stdout must not silently route
1463
+ * the summary to $GITHUB_STEP_SUMMARY.
1464
+ */
1465
+ export declare function runSummaryCommand(argv: string[], io?: {
1466
+ out: Output;
1467
+ err: Output;
1468
+ }): number;
1469
+ //#endregion
1470
+ //#region src/commands/why.d.ts
1471
+ /**
1472
+ * Testable why command core. Returns the process exit code.
1473
+ * Live mode awaits the real scan; saved-report mode is synchronous.
1474
+ */
1475
+ export declare function runWhyCommand(argv: string[], io?: {
1070
1476
  out: Output;
1071
1477
  err: Output;
1072
1478
  }): Promise<number>;
1073
- /** Testable `stats` handler (Sprint 6 Task 26). */
1074
- declare function runStatsCommand(argv: string[], io?: {
1479
+ //#endregion
1480
+ //#region src/commands/handoff.d.ts
1481
+ /**
1482
+ * Testable handoff command core. Returns the process exit code.
1483
+ * `mjolnir handoff [mjolnir.json] [--category <cat>]... [--rules <ids>]`
1484
+ */
1485
+ export declare function runHandoffCommand(argv: string[], io?: {
1075
1486
  out: Output;
1076
1487
  err: Output;
1077
1488
  }): number;
1078
- /** Testable `handover` handler (Tier 5 #28). */
1079
- declare function runHandoverCommand(argv: string[], io?: {
1489
+ //#endregion
1490
+ //#region src/commands/install-agents.d.ts
1491
+ /**
1492
+ * Testable install command core. Returns the process exit code.
1493
+ * `mjolnir install [--dry-run] [--force]` — probes the given cwd
1494
+ * (production default: process.cwd()).
1495
+ */
1496
+ export declare function runInstallCommand(argv: string[], io?: {
1497
+ out: Output;
1498
+ err: Output;
1499
+ }, cwd?: string): number;
1500
+ //#endregion
1501
+ //#region src/commands/trust-report.d.ts
1502
+ export declare function runTrustReportCommand(argv: string[], io: {
1080
1503
  out: Output;
1081
1504
  err: Output;
1082
1505
  }): Promise<number>;
1083
- /** Testable `init` handler (Tier 2 #10). */
1084
- declare function runInitCommand(argv: string[], io?: {
1506
+ //#endregion
1507
+ //#region src/commands/release-trust.d.ts
1508
+ /**
1509
+ * CLI verb: `mjolnir release-trust [--json] [repo-root]`.
1510
+ * Exit contract (frozen set): 0 verdict PASS · 1 verdict non-PASS ·
1511
+ * 2 no fixtures root (not an mjolnir checkout — BLOCKED context) ·
1512
+ * 10 usage error · 20 internal error.
1513
+ */
1514
+ export declare function runReleaseTrustCommand(argv: string[], io?: {
1085
1515
  out: Output;
1086
1516
  err: Output;
1087
1517
  }): number;
1088
- /** Testable `pw-report` handler (Tier 2 #9 wedge). */
1089
- declare function runPwReportCommand(argv: string[], io?: {
1518
+ //#endregion
1519
+ //#region src/cli.d.ts
1520
+ 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>;
1521
+ /** A usage-error detail: the offending token, when one exists. */
1522
+ export interface UsageErrorDetail {
1523
+ /** The unknown flag or rejected value (e.g. `--nope`, `loud`). */
1524
+ token?: string | undefined;
1525
+ /** The flag whose value was rejected (`--tone` for `--tone loud`). */
1526
+ flag?: string | undefined;
1527
+ }
1528
+ export declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
1529
+ /** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
1530
+ export declare function levenshtein(a: string, b: string): number;
1531
+ /** Nearest known flags within distance ≤ 2, nearest first. */
1532
+ export declare function nearestFlags(flag: string, max?: number): string[];
1533
+ /**
1534
+ * Friendly usage error (plan M2, exit 10 preserved): nearest-flag
1535
+ * suggestion, the valid neighbors, and the exact help command. Printed
1536
+ * to stderr; findings/usage stay on their documented streams.
1537
+ */
1538
+ export declare function usageErrorMessage(detail: UsageErrorDetail): string;
1539
+ /**
1540
+ * Shared parse-or-report path for scan-backed subcommands: friendly
1541
+ * usage errors on stderr (exit 10), the full overview only for an
1542
+ * explicit help flag. Returns null when the caller must exit 10.
1543
+ */
1544
+ export declare function parseArgsOrUsage(argv: string[], io: {
1545
+ out: Output;
1546
+ err: Output;
1547
+ }): CliArgs | null;
1548
+ /**
1549
+ * Audit H-4 (extended to every scanning subcommand): a nonexistent or
1550
+ * non-directory target is a usage error — a typo'd CI path must be a
1551
+ * loud red, never a silent green. Returns the exit code (10) or null
1552
+ * when the target is valid.
1553
+ */
1554
+ export declare function validateScanTarget(target: string, err: Output): number | null;
1555
+ /**
1556
+ * Exit-code decision for a finished scan under the given gate level
1557
+ * (audit H-7): the previously-dead config.gate field now selects which
1558
+ * severities block. Advisory (E0) findings never gate at any level.
1559
+ */
1560
+ export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1561
+ /** Testable `ci install` handler. Returns the process exit code. */
1562
+ export declare function runCiInstall(argv: string[], io?: {
1090
1563
  out: Output;
1091
1564
  err: Output;
1092
1565
  }): number;
1093
- declare function main(argv?: string[], io?: {
1566
+ /** Testable `suppressions` handler. */
1567
+ export declare function runSuppressions(io?: {
1568
+ out: Output;
1569
+ err?: Output;
1570
+ }): number;
1571
+ export declare function printUsage(print: (s: string) => void): void;
1572
+ export declare function main(argv?: string[], io?: {
1094
1573
  out: Output;
1095
1574
  err: Output;
1096
1575
  }): Promise<number>;
@@ -1100,10 +1579,10 @@ declare function main(argv?: string[], io?: {
1100
1579
  * Two-word verbs (`ci install`) are resolved first via the join of the
1101
1580
  * leading non-flag tokens, then the single-word form.
1102
1581
  */
1103
- declare function runHelpCommand(argv: string[], io?: {
1582
+ export declare function runHelpCommand(argv: string[], io?: {
1104
1583
  out: Output;
1105
1584
  err: Output;
1106
1585
  }): number;
1107
- declare function isEntryPoint(): boolean;
1586
+ export declare function isEntryPoint(): boolean;
1108
1587
  //#endregion
1109
- export { ENGINE_VERSION as CLI_VERSION, type CliArgs, EVIDENCE_OVERRIDES, KNOWN_RULE_IDS, OVERLAP_META_BY_RULE_ID, type Output, SUITE_INVALIDATING_RULE_IDS, type ScanHooks, UsageErrorDetail, buildUniversalRules, discoverRuntimeReport, err, exitForFindings, fallbackWorkspace, internalErrorMessage, isEntryPoint, isValidFindingRecord, levenshtein, main, nearestFlags, out, parseArgs, pathMatchesGlob, runBadgeCommand, runBaselineCommand, runCiInstall, runCreateRuleCommand, runDebtCommand, runDiffCommand, runDoctorCommand, runDoctorPlaywright, runExplainCommand, runFixCommand, runForensicsCommand, runHandoverCommand, runHelpCommand, runImpactCommand, runInitCommand, runMutationCommand, runPrCommentCommand, runPwReportCommand, runRulesCommand, runScan, runScanCommand, runStatsCommand, runSuppressions, runTriageCommand, runVerifyCommand, usageErrorMessage };
1588
+ export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };