mjolnir-qa 3.0.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). */
@@ -382,6 +444,29 @@ interface ScanResult {
382
444
  scoringModelVersion?: string;
383
445
  frameworkSupportMatrixVersion?: string;
384
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;
385
470
  };
386
471
  /**
387
472
  * Evidence Graph (R4c): the chain-law links (VERDICT ← EVIDENCE ←
@@ -401,6 +486,34 @@ interface ScanResult {
401
486
  configFingerprint: string;
402
487
  engineVersion: string;
403
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;
404
517
  };
405
518
  /**
406
519
  * Local incremental cache report (Beta-to-Stable plan, M5.2). Present
@@ -639,6 +752,9 @@ declare class DependencyGraph {
639
752
  getDependents(path: string): string[];
640
753
  get allPaths(): string[];
641
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;
642
758
  }
643
759
  //#endregion
644
760
  //#region src/discovery/workspace.d.ts
@@ -657,9 +773,18 @@ interface Workspace {
657
773
  }
658
774
  //#endregion
659
775
  //#region src/discovery/frameworks.d.ts
660
- 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];
661
785
  interface FrameworkInfo$1 {
662
- frameworks: TestFramework[];
786
+ /** Detected frameworks, as inventory ids. */
787
+ frameworks: DetectedFramework[];
663
788
  /** True when no config evidence was found at all. */
664
789
  unknown: boolean;
665
790
  }
@@ -991,8 +1116,19 @@ interface LanguageAdapter {
991
1116
  * Contract: resolve to a ParsedAst on success, `undefined` when this
992
1117
  * adapter has no AST layer (or parsing failed — rules fall back to the
993
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.
994
1130
  */
995
- parseAst?(file: ParsedFile): Promise<ParsedAst | undefined>;
1131
+ parseAst?: (file: ParsedFile) => ParsedAst | undefined | Promise<ParsedAst | undefined>;
996
1132
  /**
997
1133
  * Run all rules this adapter hosts against one file. `onCrash` is
998
1134
  * invoked when a rule throws (audit R-9) — the crash is still
@@ -1045,6 +1181,55 @@ interface UniversalRule {
1045
1181
  run(file: ParsedFile): Array<Omit<Finding, "ruleId" | "category">>;
1046
1182
  }
1047
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
1048
1233
  //#region src/engine/provenance.d.ts
1049
1234
  type FileProvenance = "unmarked" | "generated-marked" | "codegen-like";
1050
1235
  /** Classify one file's provenance. Pure. */
@@ -1072,7 +1257,7 @@ interface IgnoreEntry {
1072
1257
  ruleId: string;
1073
1258
  files?: string[];
1074
1259
  reason: string;
1075
- /** ISO date; defaults to 90 days from creation (S11). */
1260
+ /** ISO date; omitted means the suppression has no expiry. */
1076
1261
  expires?: string;
1077
1262
  }
1078
1263
  interface QADoctorConfig {
@@ -1313,6 +1498,12 @@ interface PostScanResult {
1313
1498
  frameworks: ReturnType<typeof detectFrameworks>;
1314
1499
  runtimeReportPath: string | undefined;
1315
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[];
1316
1507
  /** Aggregate forensic classifications from the ingested runtime report. */
1317
1508
  forensicVerdicts: ForensicVerdictSummary | undefined;
1318
1509
  config: ReturnType<typeof loadConfig>["config"];
@@ -1360,6 +1551,7 @@ interface AssembleScanResultInput {
1360
1551
  frameworks: ReturnType<typeof detectFrameworks>;
1361
1552
  runtimeReportPath: string | undefined;
1362
1553
  runtimeIncomplete?: boolean;
1554
+ evidenceRecords?: EvidenceRecord[];
1363
1555
  forensicVerdicts: ForensicVerdictSummary | undefined;
1364
1556
  config: ReturnType<typeof loadConfig>["config"];
1365
1557
  fileProvenance: Array<{
@@ -1393,7 +1585,7 @@ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult
1393
1585
  * scripts/sync-sarif-version.cjs and guarded by the version-consistency
1394
1586
  * spec. cli.ts re-exports this as CLI_VERSION.
1395
1587
  */
1396
- declare const ENGINE_VERSION = "3.0.0";
1588
+ declare const ENGINE_VERSION = "4.0.0";
1397
1589
  //#endregion
1398
1590
  //#region src/cli-io.d.ts
1399
1591
  /**
@@ -1575,11 +1767,17 @@ export declare function runReleaseTrustCommand(argv: string[], io?: {
1575
1767
  * Entry point for `mjolnir business-case`.
1576
1768
  *
1577
1769
  * 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
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.
1582
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.
1583
1781
  */
1584
1782
  export declare function runBusinessCaseCommand(argv: string[], io?: {
1585
1783
  out: Output;
@@ -1623,7 +1821,7 @@ export declare function runPolicyCommand(argv: string[], io: {
1623
1821
  export declare function runQuarantineCommand(argv: string[], io: {
1624
1822
  out: Output;
1625
1823
  err: Output;
1626
- }): Promise<number>;
1824
+ }): number;
1627
1825
  //#endregion
1628
1826
  //#region src/commands/analyze.d.ts
1629
1827
  export declare function runAnalyzeCommand(argv: string[], io: {
@@ -1724,9 +1922,14 @@ export declare function parseArgsOrUsage(argv: string[], io: {
1724
1922
  */
1725
1923
  export declare function validateScanTarget(target: string, err: Output): number | null;
1726
1924
  /**
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.
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.
1730
1933
  */
1731
1934
  export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
1732
1935
  /** Testable `ci install` handler. Returns the process exit code. */
@@ -1756,4 +1959,5 @@ export declare function runHelpCommand(argv: string[], io?: {
1756
1959
  }): number;
1757
1960
  export declare function isEntryPoint(): boolean;
1758
1961
  //#endregion
1759
- 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