mjolnir-qa 2.1.0 → 4.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
@@ -235,6 +235,68 @@ interface DimensionScore {
235
235
  infos: number;
236
236
  }
237
237
  type AnalysisStatus = "complete" | "partial";
238
+ /**
239
+ * The normalized per-test runtime evidence record as it appears in a report
240
+ * (plan V5-011). Structurally the `EvidenceRecord` from
241
+ * `src/engine/evidence-core.ts`, restated here so the report schema does not
242
+ * depend on an engine module's internals — the wire shape is a contract.
243
+ */
244
+ interface EvidenceRecordShape {
245
+ source: string;
246
+ artifact: string;
247
+ file: string;
248
+ title: string;
249
+ line?: number;
250
+ status: {
251
+ final: string;
252
+ failed: boolean;
253
+ retried: boolean;
254
+ passedOnRetry: boolean;
255
+ skipped: boolean;
256
+ timedOut: boolean;
257
+ };
258
+ attempts: number;
259
+ durationMs: number;
260
+ errors: string[];
261
+ attachments: string[];
262
+ provenance: {
263
+ core: string;
264
+ ingest: string;
265
+ };
266
+ }
267
+ /**
268
+ * Candidate binding (plan V5-010).
269
+ *
270
+ * The immutable identity of the release candidate a run was produced against.
271
+ * It is what makes a claim replayable: "this verdict belongs to candidate
272
+ * X, built from commit Y with lockfile Z" can be re-checked; "this verdict
273
+ * belongs to whatever was on the machine" cannot.
274
+ *
275
+ * Every field is required. A partial binding is worse than none, because a
276
+ * consumer cannot tell which link is missing — so an unbound run omits the
277
+ * whole object rather than filling in what it has.
278
+ */
279
+ interface CandidateBinding {
280
+ /** The candidate manifest's own id. */
281
+ manifestId: string;
282
+ /** WORKING_CANDIDATE | RELEASE_CANDIDATE. */
283
+ state: "WORKING_CANDIDATE" | "RELEASE_CANDIDATE";
284
+ /**
285
+ * The immutable commit this candidate is bound to, or null for a working
286
+ * candidate — which by definition has no commit yet.
287
+ */
288
+ candidateSha: string | null;
289
+ /** The commit the candidate was branched from. */
290
+ baseSha: string;
291
+ /** sha256 of package.json as published for the candidate. */
292
+ packageSha256: string;
293
+ /** sha256 of the lockfile as published for the candidate. */
294
+ lockfileSha256: string;
295
+ /** Who owns the candidate. "UNASSIGNED" is a real, reportable value. */
296
+ owner: string;
297
+ /** NOT_AUTHORIZED | AUTHORIZED. Never inferred from evidence. */
298
+ releaseAuthorizationState: "NOT_AUTHORIZED" | "AUTHORIZED";
299
+ }
238
300
  interface ScanResult {
239
301
  schemaVersion: typeof SCHEMA_VERSION;
240
302
  /** False when budget expired or files were skipped (§18.3). */
@@ -325,12 +387,15 @@ interface ScanResult {
325
387
  * truncation actually happened — absence means the scan is whole.
326
388
  */
327
389
  truncationReasons?: string[];
390
+ /** Canonical completion reasons, including scope and parser degradation. */
391
+ reasons?: string[];
328
392
  /**
329
393
  * Rule executions that threw and were swallowed by crash isolation
330
394
  * (audit R-9). 0 means no rule silently failed; absence means the
331
395
  * producer predates the counter.
332
396
  */
333
397
  rulesCrashed?: number;
398
+ parseFallbacks?: number;
334
399
  };
335
400
  /**
336
401
  * Scoring model version stamped into the result (ENGINE-001). Allows
@@ -378,6 +443,30 @@ interface ScanResult {
378
443
  trustModelVersion?: string;
379
444
  scoringModelVersion?: string;
380
445
  frameworkSupportMatrixVersion?: string;
446
+ commit?: string;
447
+ /** The worktree tree hash the run was bound to, when known. */
448
+ tree?: string;
449
+ /** The lockfile digest the run was bound to, when known. */
450
+ lockfile?: string;
451
+ /**
452
+ * Which identity links this run actually bound (plan V5-010). Derived, not
453
+ * asserted by each consumer: a claim is replayable only when every link it
454
+ * depends on is present.
455
+ *
456
+ * OPTIONAL on purpose. The machine contract is v1 additive-only, so a
457
+ * report written before this field existed has none, and a reader that
458
+ * required it would reject exactly the older artifacts it must tolerate.
459
+ * Absence means "produced by a build that did not record bindings" — never
460
+ * "bound to everything".
461
+ */
462
+ boundLinks?: Array<"input" | "rules" | "config" | "engine" | "commit" | "tree" | "lockfile" | "candidate">;
463
+ /**
464
+ * The release candidate this run is bound to (plan V5-010). Absent for an
465
+ * ordinary scan of a working tree — a run is not a release candidate, and
466
+ * saying otherwise would let a local scan stand in for authorized
467
+ * evidence.
468
+ */
469
+ candidate?: CandidateBinding;
381
470
  };
382
471
  /**
383
472
  * Evidence Graph (R4c): the chain-law links (VERDICT ← EVIDENCE ←
@@ -397,6 +486,34 @@ interface ScanResult {
397
486
  configFingerprint: string;
398
487
  engineVersion: string;
399
488
  };
489
+ /** The release candidate the verdict belongs to, when bound (V5-010). */
490
+ candidate?: {
491
+ manifestId: string;
492
+ candidateSha: string | null;
493
+ };
494
+ };
495
+ /**
496
+ * The normalized evidence core (plan V5-011).
497
+ *
498
+ * Present whenever the run ingested a runtime report. `records` is the
499
+ * canonical, deterministically-ordered set that every downstream consumer
500
+ * reads — before this, the core was computed and discarded inside the
501
+ * pipeline, so a re-derivation had to re-parse the report and could disagree
502
+ * with the verdict it was supposed to explain.
503
+ *
504
+ * Empty `records` with a null `artifact` means no runtime evidence was
505
+ * found, which is different from a run that found an empty report.
506
+ */
507
+ evidence?: {
508
+ records: Array<EvidenceRecordShape>;
509
+ counts: {
510
+ total: number;
511
+ failed: number;
512
+ flaky: number;
513
+ skipped: number;
514
+ timedOut: number;
515
+ };
516
+ artifact: string | null;
400
517
  };
401
518
  /**
402
519
  * Local incremental cache report (Beta-to-Stable plan, M5.2). Present
@@ -635,6 +752,9 @@ declare class DependencyGraph {
635
752
  getDependents(path: string): string[];
636
753
  get allPaths(): string[];
637
754
  get size(): number;
755
+ /** Whether the graph knows this exact key. Reachability must not
756
+ * treat "not in the graph" as "nothing to traverse" without saying so. */
757
+ has(path: string): boolean;
638
758
  }
639
759
  //#endregion
640
760
  //#region src/discovery/workspace.d.ts
@@ -653,9 +773,18 @@ interface Workspace {
653
773
  }
654
774
  //#endregion
655
775
  //#region src/discovery/frameworks.d.ts
656
- type TestFramework = "jest" | "vitest" | "playwright";
776
+ /**
777
+ * The catalogued frameworks whose detection is implemented today.
778
+ *
779
+ * Every entry must exist in the inventory — enforced by the parity spec, not
780
+ * by comment.
781
+ */
782
+ declare const DETECTABLE_TEST_FRAMEWORKS: readonly ["jest", "vitest", "playwright"];
783
+ /** The detection output type, in the inventory's vocabulary. */
784
+ type DetectedFramework = (typeof DETECTABLE_TEST_FRAMEWORKS)[number];
657
785
  interface FrameworkInfo$1 {
658
- frameworks: TestFramework[];
786
+ /** Detected frameworks, as inventory ids. */
787
+ frameworks: DetectedFramework[];
659
788
  /** True when no config evidence was found at all. */
660
789
  unknown: boolean;
661
790
  }
@@ -956,9 +1085,9 @@ interface ScanContext {
956
1085
  maxFiles: number;
957
1086
  /** R4c Scope Integrity: counted matcher exclusions (optional — adapters
958
1087
  * whose discovery walks sharedWalk pass this through to the counters). */
959
- onIgnored?: () => void;
1088
+ onIgnored?: (path: string) => void;
960
1089
  /** R4c Scope Integrity: counted files no adapter claims (optional). */
961
- onUnrecognized?: () => void;
1090
+ onUnrecognized?: (path: string) => void;
962
1091
  /**
963
1092
  * Called when a rule throws on a file (audit R-9): crash isolation
964
1093
  * stays silent by default, but the scan counts it and `--debug`
@@ -987,8 +1116,19 @@ interface LanguageAdapter {
987
1116
  * Contract: resolve to a ParsedAst on success, `undefined` when this
988
1117
  * adapter has no AST layer (or parsing failed — rules fall back to the
989
1118
  * regex path either way). Never throws.
1119
+ *
1120
+ * May return the result directly as well as a promise: the ts-morph path
1121
+ * (plan V5-021) is synchronous, and forcing it to allocate a promise per
1122
+ * file would be a cost the shared seam has no business imposing. The
1123
+ * pipeline awaits either form.
1124
+ *
1125
+ * Declared as a PROPERTY, not a method signature. A method signature
1126
+ * promises a `this` binding that no adapter uses, and it makes every
1127
+ * consumer that reads `adapter.parseAst` trip the unbound-method rule —
1128
+ * which is a lint error that gets suppressed rather than a design that
1129
+ * gets fixed, so the shape has to say what it means.
990
1130
  */
991
- parseAst?(file: ParsedFile): Promise<ParsedAst | undefined>;
1131
+ parseAst?: (file: ParsedFile) => ParsedAst | undefined | Promise<ParsedAst | undefined>;
992
1132
  /**
993
1133
  * Run all rules this adapter hosts against one file. `onCrash` is
994
1134
  * invoked when a rule throws (audit R-9) — the crash is still
@@ -1041,6 +1181,55 @@ interface UniversalRule {
1041
1181
  run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
1042
1182
  }
1043
1183
  //#endregion
1184
+ //#region src/engine/evidence-core.d.ts
1185
+ /**
1186
+ * The core's own version — stamped into every record's provenance so a
1187
+ * consumer can tell which normalization contract produced a record.
1188
+ * Bump ONLY on a semantic change to the normalization itself.
1189
+ */
1190
+ declare const EVIDENCE_CORE_VERSION = "evidence-core@1";
1191
+ /** Evidence source formats the core understands today (WI-17 adds trace.zip). */
1192
+ type EvidenceSource = ForensicsReport["source"];
1193
+ /** Normalized per-test runtime evidence (plan WI-2 field contract). */
1194
+ interface EvidenceRecord {
1195
+ /** Where this evidence came from (format identity). */
1196
+ source: EvidenceSource;
1197
+ /** Ingested artifact identity — the report path the records derive from. */
1198
+ artifact: string;
1199
+ /** Test identity: file + title (+ declaration line when known). */
1200
+ file: string;
1201
+ title: string;
1202
+ /** 1-based declaration line when the source carries one (JUnit: absent). */
1203
+ line?: number;
1204
+ /** Normalized execution status facts. */
1205
+ status: {
1206
+ /** Last attempt's outcome. */
1207
+ final: RunStatus;
1208
+ /** Failed at least once across attempts. */
1209
+ failed: boolean;
1210
+ /** Executed more than once. */
1211
+ retried: boolean;
1212
+ /** Passed only on attempt >= 2 (TRUE-FLAKE). */
1213
+ passedOnRetry: boolean;
1214
+ skipped: boolean;
1215
+ /** Final attempt timed out. */
1216
+ timedOut: boolean;
1217
+ };
1218
+ /** Number of attempts (retries = attempts − 1). */
1219
+ attempts: number;
1220
+ /** Total duration across attempts. */
1221
+ durationMs: number;
1222
+ /** Error messages, in report order. Empty: not captured by this source yet. */
1223
+ errors: string[];
1224
+ /** Attachment names/paths. Empty: not captured by this source yet (WI-17). */
1225
+ attachments: string[];
1226
+ /** Where the record came from and under which normalization contract. */
1227
+ provenance: {
1228
+ core: typeof EVIDENCE_CORE_VERSION;
1229
+ ingest: "mjolnir.forensics";
1230
+ };
1231
+ }
1232
+ //#endregion
1044
1233
  //#region src/engine/provenance.d.ts
1045
1234
  type FileProvenance = "unmarked" | "generated-marked" | "codegen-like";
1046
1235
  /** Classify one file's provenance. Pure. */
@@ -1068,7 +1257,7 @@ interface IgnoreEntry {
1068
1257
  ruleId: string;
1069
1258
  files?: string[];
1070
1259
  reason: string;
1071
- /** ISO date; defaults to 90 days from creation (S11). */
1260
+ /** ISO date; omitted means the suppression has no expiry. */
1072
1261
  expires?: string;
1073
1262
  }
1074
1263
  interface QADoctorConfig {
@@ -1291,7 +1480,9 @@ interface FileAnalysisResult {
1291
1480
  testDeclarationCount: number;
1292
1481
  rulesPartial: boolean;
1293
1482
  parseFailed: number;
1483
+ parseFallbacks: number;
1294
1484
  scanned: number;
1485
+ analyzed: number;
1295
1486
  }
1296
1487
  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<{
1297
1488
  path: string;
@@ -1306,6 +1497,13 @@ interface PostScanResult {
1306
1497
  suppressionCount: number;
1307
1498
  frameworks: ReturnType<typeof detectFrameworks>;
1308
1499
  runtimeReportPath: string | undefined;
1500
+ runtimeIncomplete: boolean;
1501
+ /**
1502
+ * The normalized evidence core (V5-011). Persisted rather than discarded,
1503
+ * so every post-ingest projection reads records instead of re-parsing a
1504
+ * report that may have changed underneath it.
1505
+ */
1506
+ evidenceRecords: EvidenceRecord[];
1309
1507
  /** Aggregate forensic classifications from the ingested runtime report. */
1310
1508
  forensicVerdicts: ForensicVerdictSummary | undefined;
1311
1509
  config: ReturnType<typeof loadConfig>["config"];
@@ -1331,7 +1529,9 @@ interface AssembleScanResultInput {
1331
1529
  scopeIgnored: number;
1332
1530
  scopeUnrecognized: number;
1333
1531
  parseFailed: number;
1532
+ parseFallbacks?: number;
1334
1533
  scanned: number;
1534
+ analyzed?: number;
1335
1535
  testFiles: string[];
1336
1536
  workspace: Workspace;
1337
1537
  scanRoot: Workspace;
@@ -1350,6 +1550,8 @@ interface AssembleScanResultInput {
1350
1550
  suppressionCount: number;
1351
1551
  frameworks: ReturnType<typeof detectFrameworks>;
1352
1552
  runtimeReportPath: string | undefined;
1553
+ runtimeIncomplete?: boolean;
1554
+ evidenceRecords?: EvidenceRecord[];
1353
1555
  forensicVerdicts: ForensicVerdictSummary | undefined;
1354
1556
  config: ReturnType<typeof loadConfig>["config"];
1355
1557
  fileProvenance: Array<{
@@ -1383,7 +1585,7 @@ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult
1383
1585
  * scripts/sync-sarif-version.cjs and guarded by the version-consistency
1384
1586
  * spec. cli.ts re-exports this as CLI_VERSION.
1385
1587
  */
1386
- declare const ENGINE_VERSION = "2.1.0";
1588
+ declare const ENGINE_VERSION = "4.0.0";
1387
1589
  //#endregion
1388
1590
  //#region src/cli-io.d.ts
1389
1591
  /**
@@ -1565,11 +1767,17 @@ export declare function runReleaseTrustCommand(argv: string[], io?: {
1565
1767
  * Entry point for `mjolnir business-case`.
1566
1768
  *
1567
1769
  * Flags:
1568
- * --industry <type> Industry cost profile (default/fintech/healthcare/...)
1569
- * --history <months> Use git history for the last N months (estimates from
1570
- * actual scan improvements, not projections)
1571
- * --projected <months> Show projected savings over N months
1770
+ * --incident-cost <n> The cost of ONE false-green incident in your
1771
+ * organisation. Until you supply it, no dollar
1772
+ * figure is printed at all.
1572
1773
  * --strict Include quarantine-tier findings
1774
+ *
1775
+ * `--history` and `--projected` are GONE. Both promised arithmetic this
1776
+ * tool cannot do: `--history` claimed to estimate from actual scan
1777
+ * improvements while reading no history at all, and `--projected` divided
1778
+ * the total by six and called the quotient a monthly rate. A flag that
1779
+ * does not do what its help says is worse than no flag, because the help
1780
+ * is the promise.
1573
1781
  */
1574
1782
  export declare function runBusinessCaseCommand(argv: string[], io?: {
1575
1783
  out: Output;
@@ -1613,7 +1821,7 @@ export declare function runPolicyCommand(argv: string[], io: {
1613
1821
  export declare function runQuarantineCommand(argv: string[], io: {
1614
1822
  out: Output;
1615
1823
  err: Output;
1616
- }): Promise<number>;
1824
+ }): number;
1617
1825
  //#endregion
1618
1826
  //#region src/commands/analyze.d.ts
1619
1827
  export declare function runAnalyzeCommand(argv: string[], io: {
@@ -1684,6 +1892,8 @@ export interface UsageErrorDetail {
1684
1892
  /** The flag whose value was rejected (`--tone` for `--tone loud`). */
1685
1893
  flag?: string | undefined;
1686
1894
  }
1895
+ export declare const DEFAULT_MAX_DURATION_MS = 600000;
1896
+ export declare const MAX_DURATION_MS = 3600000;
1687
1897
  export declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
1688
1898
  /** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
1689
1899
  export declare function levenshtein(a: string, b: string): number;
@@ -1712,9 +1922,14 @@ export declare function parseArgsOrUsage(argv: string[], io: {
1712
1922
  */
1713
1923
  export declare function validateScanTarget(target: string, err: Output): number | null;
1714
1924
  /**
1715
- * Exit-code decision for a finished scan under the given gate level
1716
- * (audit H-7): the previously-dead config.gate field now selects which
1717
- * severities block. Advisory (E0) findings never gate at any level.
1925
+ * Exit-code decision for findings only (audit H-7): the previously-dead
1926
+ * config.gate field selects which severities block. Advisory (E0) findings
1927
+ * never gate at any level.
1928
+ *
1929
+ * This is a findings-only view of the one exit matrix in `claim-evidence`.
1930
+ * Callers that have just finished an ANALYSIS must use `scanExitCode`
1931
+ * instead — this function cannot see `partial`, and a truncated scan with
1932
+ * zero findings is exactly the case that turns a bug into a green build.
1718
1933
  */
1719
1934
  export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1720
1935
  /** Testable `ci install` handler. Returns the process exit code. */
@@ -1744,4 +1959,5 @@ export declare function runHelpCommand(argv: string[], io?: {
1744
1959
  }): number;
1745
1960
  export declare function isEntryPoint(): boolean;
1746
1961
  //#endregion
1747
- export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };
1962
+ export { ENGINE_VERSION as CLI_VERSION, type CliArgs, type Output, type ScanHooks };
1963
+ //# sourceMappingURL=cli.d.mts.map