mjolnir-qa 4.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/CHANGELOG.md +269 -1
- package/README.br.md +10 -10
- package/README.md +12 -12
- package/dist/cli.d.mts +181 -4
- package/dist/cli.mjs +1481 -120
- package/dist/mcp/stdio.mjs +390 -193
- package/dist/scan-pipeline-DNYbnWjE.mjs +2 -0
- package/dist/{scan-pipeline-CCJdEKNa.mjs → scan-pipeline-DT8DWAMl.mjs} +344 -179
- package/package.json +25 -8
- package/dist/scan-pipeline-Bg-46cLS.mjs +0 -2
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).
|
|
@@ -383,12 +457,42 @@ interface ScanResult {
|
|
|
383
457
|
durationMs: number;
|
|
384
458
|
/**
|
|
385
459
|
* Named reasons the scan stopped early (audit H-8): "deadline",
|
|
386
|
-
* "file-cap:<adapter>", "rule-loop-deadline".
|
|
387
|
-
* truncation actually happened — absence means the
|
|
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>".
|
|
388
474
|
*/
|
|
389
475
|
truncationReasons?: string[];
|
|
390
476
|
/** Canonical completion reasons, including scope and parser degradation. */
|
|
391
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
|
+
}>;
|
|
392
496
|
/**
|
|
393
497
|
* Rule executions that threw and were swallowed by crash isolation
|
|
394
498
|
* (audit R-9). 0 means no rule silently failed; absence means the
|
|
@@ -429,6 +533,18 @@ interface ScanResult {
|
|
|
429
533
|
/** The named scope reasons, present only when PARTIAL. */
|
|
430
534
|
reasons?: string[];
|
|
431
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;
|
|
432
548
|
/**
|
|
433
549
|
* Run Identity (R4c): the deterministic anchor binding verdict ←
|
|
434
550
|
* evidence ← execution ← scope ← source ← rule(rev). Present when the
|
|
@@ -757,6 +873,53 @@ declare class DependencyGraph {
|
|
|
757
873
|
has(path: string): boolean;
|
|
758
874
|
}
|
|
759
875
|
//#endregion
|
|
876
|
+
//#region src/engine/degradation-ledger.d.ts
|
|
877
|
+
/**
|
|
878
|
+
* One ledger for every silent capability loss.
|
|
879
|
+
*
|
|
880
|
+
* The defect class this exists to kill is not a deprecation warning. It is a
|
|
881
|
+
* `catch` in a DETECTION path that swallows an error and returns a clean
|
|
882
|
+
* default, so the scan still reports `analysisComplete` while quietly running
|
|
883
|
+
* with less capability than the reader was promised. Fifteen such sites were
|
|
884
|
+
* verified in this tree; the AST-layer ones are the worst, because the layer
|
|
885
|
+
* they degrade is the one that keeps prose comments and sample strings from
|
|
886
|
+
* becoming false-positive findings.
|
|
887
|
+
*
|
|
888
|
+
* Two shapes, one rule. The precedent already in the tree is
|
|
889
|
+
* `parserRetryDegradationCount` (`src/engine/tree-sitter-ast.ts`): a counted,
|
|
890
|
+
* readable number rather than a swallowed exception. This generalizes it —
|
|
891
|
+
* every site records a REASON from the closed set below, and the scan result
|
|
892
|
+
* carries the per-reason counts, so "the parser failed" becomes
|
|
893
|
+
* "the AST mask was unavailable for 3 files".
|
|
894
|
+
*
|
|
895
|
+
* The ledger is APPEND-ONLY and process-scoped, which has one consequence
|
|
896
|
+
* worth stating because it is the security-relevant direction: `beginWindow`
|
|
897
|
+
* returns a MARKER, not an ownership claim. Two concurrent `runScan` calls in
|
|
898
|
+
* one process (there is a test that does exactly this,
|
|
899
|
+
* `tests/stress/concurrent-same-process.spec.ts`) each read the records
|
|
900
|
+
* appended after their own marker, so a degradation one scan records may be
|
|
901
|
+
* counted by the other. That can only ever OVER-count — a scan reporting a
|
|
902
|
+
* degradation that happened to a sibling is more cautious than the truth, and
|
|
903
|
+
* never less — so the failure direction is the honest one. The alternative,
|
|
904
|
+
* a sink threaded through every rule context, is a diff across every call site
|
|
905
|
+
* in the tree to fix a problem that cannot make a scan look better than it is.
|
|
906
|
+
*/
|
|
907
|
+
/**
|
|
908
|
+
* Every reason a detection path may lose capability.
|
|
909
|
+
*
|
|
910
|
+
* Closed on purpose: a `catch` site that wants a new reason has to add it
|
|
911
|
+
* here, which is the moment a reviewer sees the claim. `tests/contract/
|
|
912
|
+
* no-uncounted-degradation.spec.ts` reads this list and fails on any `catch`
|
|
913
|
+
* in a detection path that neither rethrows, records one of these, nor
|
|
914
|
+
* appears in its allowlist with a stated reason.
|
|
915
|
+
*/
|
|
916
|
+
declare const DEGRADATION_REASONS: readonly ["ast-parse-failed", "ast-mask-unavailable", "ast-range-scan-failed", "ci-workflow-listing-unreadable", "workspace-manifest-unreadable", "java-build-listing-unreadable", "rules-tree-listing-unreadable", "trend-record-unparseable", "explain-fixture-unreadable", "explain-workflow-parse-failed", "explain-rule-crash", "rule-doc-rule-crash", "rule-doc-fixture-unreadable", "rule-doc-workflow-parse-failed"];
|
|
917
|
+
type DegradationReason = (typeof DEGRADATION_REASONS)[number];
|
|
918
|
+
interface DegradationCount {
|
|
919
|
+
readonly reason: DegradationReason;
|
|
920
|
+
readonly count: number;
|
|
921
|
+
}
|
|
922
|
+
//#endregion
|
|
760
923
|
//#region src/discovery/workspace.d.ts
|
|
761
924
|
/**
|
|
762
925
|
* Repository discovery (Sprint-Plan W1-03).
|
|
@@ -1483,6 +1646,12 @@ interface FileAnalysisResult {
|
|
|
1483
1646
|
parseFallbacks: number;
|
|
1484
1647
|
scanned: number;
|
|
1485
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;
|
|
1486
1655
|
}
|
|
1487
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<{
|
|
1488
1657
|
path: string;
|
|
@@ -1530,6 +1699,14 @@ interface AssembleScanResultInput {
|
|
|
1530
1699
|
scopeUnrecognized: number;
|
|
1531
1700
|
parseFailed: number;
|
|
1532
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[];
|
|
1533
1710
|
scanned: number;
|
|
1534
1711
|
analyzed?: number;
|
|
1535
1712
|
testFiles: string[];
|
|
@@ -1585,7 +1762,7 @@ declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult
|
|
|
1585
1762
|
* scripts/sync-sarif-version.cjs and guarded by the version-consistency
|
|
1586
1763
|
* spec. cli.ts re-exports this as CLI_VERSION.
|
|
1587
1764
|
*/
|
|
1588
|
-
declare const ENGINE_VERSION = "
|
|
1765
|
+
declare const ENGINE_VERSION = "5.0.0";
|
|
1589
1766
|
//#endregion
|
|
1590
1767
|
//#region src/cli-io.d.ts
|
|
1591
1768
|
/**
|
|
@@ -1897,7 +2074,7 @@ export declare const MAX_DURATION_MS = 3600000;
|
|
|
1897
2074
|
export declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
|
|
1898
2075
|
/** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
|
|
1899
2076
|
export declare function levenshtein(a: string, b: string): number;
|
|
1900
|
-
/** Nearest known flags within distance
|
|
2077
|
+
/** Nearest known flags within distance ≤ 2, nearest first. */
|
|
1901
2078
|
export declare function nearestFlags(flag: string, max?: number): string[];
|
|
1902
2079
|
/**
|
|
1903
2080
|
* Friendly usage error (plan M2, exit 10 preserved): nearest-flag
|