mjolnir-qa 4.0.0 → 6.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 +3178 -16
- package/README.md +81 -760
- package/dist/cli.d.mts +552 -206
- package/dist/cli.mjs +31409 -13990
- package/dist/mcp/stdio.mjs +1290 -405
- package/package.json +76 -32
- package/README.ar.md +0 -647
- package/README.bn.md +0 -647
- package/README.br.md +0 -647
- package/README.bs.md +0 -647
- package/README.da.md +0 -647
- package/README.de.md +0 -647
- package/README.es.md +0 -647
- package/README.fr.md +0 -647
- package/README.gr.md +0 -647
- package/README.he.md +0 -647
- package/README.it.md +0 -647
- package/README.ja.md +0 -647
- package/README.ko.md +0 -647
- package/README.no.md +0 -647
- package/README.pl.md +0 -647
- package/README.ru.md +0 -647
- package/README.th.md +0 -647
- package/README.tr.md +0 -647
- package/README.uk.md +0 -647
- package/README.vi.md +0 -647
- package/README.zh.md +0 -647
- package/README.zht.md +0 -647
- package/dist/scan-pipeline-Bg-46cLS.mjs +0 -2
- package/dist/scan-pipeline-CCJdEKNa.mjs +0 -16336
package/dist/cli.d.mts
CHANGED
|
@@ -60,6 +60,23 @@ type RuleCategory = (typeof RULE_CATEGORIES)[number];
|
|
|
60
60
|
* retried, or timed out in the report).
|
|
61
61
|
* INVARIANT (structurally enforced): L3–L5 require runtime
|
|
62
62
|
* corroboration — a static-only finding can never claim L4/L5.
|
|
63
|
+
*
|
|
64
|
+
* THREE LADDER VOCABULARIES now share this shape, and the letters are the
|
|
65
|
+
* only thing distinguishing them:
|
|
66
|
+
*
|
|
67
|
+
* `L0`–`L5` RUNTIME TRUST — how much a consumer can believe one finding.
|
|
68
|
+
* Here.
|
|
69
|
+
* `F0`–`F5` FRAMEWORK SUPPORT — a hand-declared target per framework.
|
|
70
|
+
* `FrameworkSupportLevel`, src/frameworks/framework-inventory.ts
|
|
71
|
+
* `M0`–`M5` CAPABILITY MATURITY — DERIVED from evidence, and the only
|
|
72
|
+
* one of the three that is a measurement.
|
|
73
|
+
* `Maturity`, src/v6/maturity.ts
|
|
74
|
+
*
|
|
75
|
+
* `tests/v6/maturity-vocabulary.spec.ts` holds the two vocabulary sets apart
|
|
76
|
+
* and fails if they ever share a member. `M` is the only DERIVED of the three,
|
|
77
|
+
* and that is the distinction worth remembering when reading a level out of a
|
|
78
|
+
* report: `F` and `L` are declarations about the world, `M` is a function of
|
|
79
|
+
* what this repository can demonstrate.
|
|
63
80
|
*/
|
|
64
81
|
declare const TRUST_ORDER: readonly ["L0", "L1", "L2", "L3", "L4", "L5"];
|
|
65
82
|
type TrustLevel = (typeof TRUST_ORDER)[number];
|
|
@@ -80,7 +97,13 @@ interface RuntimeCorroboration {
|
|
|
80
97
|
* constrains TRUE-FLAKE derivation, which lives in the analysis, not
|
|
81
98
|
* in the provenance label.
|
|
82
99
|
*/
|
|
83
|
-
source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har"
|
|
100
|
+
source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har" |
|
|
101
|
+
/**
|
|
102
|
+
* Visual Studio TRX (`dotnet test`). Added so .NET runs can corroborate at
|
|
103
|
+
* all — previously the union had no member a TRX source could inhabit, so a
|
|
104
|
+
* C# run was unrepresentable rather than merely unrecognised.
|
|
105
|
+
*/
|
|
106
|
+
"dotnet-trx";
|
|
84
107
|
/** Number of tests executed in the finding's file (any level). */
|
|
85
108
|
testsExecuted: number;
|
|
86
109
|
/**
|
|
@@ -202,14 +225,16 @@ interface Finding {
|
|
|
202
225
|
/**
|
|
203
226
|
* Mutation evidence (master plan P5, plan 1788853205786 — flag 6,
|
|
204
227
|
* decision 8): provenance from a mutation-testing report
|
|
205
|
-
* (
|
|
228
|
+
* (a Stryker or Mutmut report — the `mutation` verb is retired in the
|
|
229
|
+
* v6 carve), stamped on matching findings.
|
|
206
230
|
*
|
|
207
231
|
* PROVENANCE IS NOT TRUTH: a survived mutant is code the suite would
|
|
208
232
|
* not notice changing — evidence FOR a nearby finding, never a claim
|
|
209
|
-
* the finding is "proven real". The E1→E2 consolidation
|
|
210
|
-
* DERIVATION and
|
|
211
|
-
* docs/RULE-LIFECYCLE.md
|
|
212
|
-
* rises from mutation evidence
|
|
233
|
+
* the finding is "proven real". The E1→E2 consolidation was BY
|
|
234
|
+
* DERIVATION, and lived with the `mutation` verb the v6 carve removed;
|
|
235
|
+
* the rule is documented in docs/RULE-LIFECYCLE.md and the
|
|
236
|
+
* machine-contract docs. trustLevel never rises from mutation evidence
|
|
237
|
+
* alone: nothing ran.
|
|
213
238
|
*/
|
|
214
239
|
interface MutationEvidence {
|
|
215
240
|
/** Which mutation tool produced the report. */
|
|
@@ -383,12 +408,42 @@ interface ScanResult {
|
|
|
383
408
|
durationMs: number;
|
|
384
409
|
/**
|
|
385
410
|
* Named reasons the scan stopped early (audit H-8): "deadline",
|
|
386
|
-
* "file-cap:<adapter>", "rule-loop-deadline".
|
|
387
|
-
* truncation actually happened — absence means the
|
|
411
|
+
* "file-cap:<adapter>", "rule-loop-deadline", "ast-budget-fallback".
|
|
412
|
+
* Present only when truncation actually happened — absence means the
|
|
413
|
+
* scan is whole.
|
|
414
|
+
*
|
|
415
|
+
* Open `string[]`, NOT a closed union: a producer may add a member
|
|
416
|
+
* without a schema bump. The cost of that choice is that a consumer
|
|
417
|
+
* asserting the EXACT set breaks on every new reason, so assertions
|
|
418
|
+
* here are containment assertions, never `toEqual([...])`.
|
|
419
|
+
*
|
|
420
|
+
* "ast-budget-fallback" specifically means the scan ran out of time
|
|
421
|
+
* before a file could be analyzed through its adapter's AST stage, and
|
|
422
|
+
* that file was analyzed by regex instead. It is a capability loss, not
|
|
423
|
+
* a speed trade, and the count of affected files is in `reasons` as
|
|
424
|
+
* "ast-budget-fallback-files:<n>".
|
|
388
425
|
*/
|
|
389
426
|
truncationReasons?: string[];
|
|
390
427
|
/** Canonical completion reasons, including scope and parser degradation. */
|
|
391
428
|
reasons?: string[];
|
|
429
|
+
/**
|
|
430
|
+
* Per-reason counts of capability lost inside a `catch` that returned a
|
|
431
|
+
* clean default — the degradation ledger (`src/engine/degradation-ledger.ts`).
|
|
432
|
+
*
|
|
433
|
+
* Distinct from `truncationReasons`, which means the scan STOPPED, and
|
|
434
|
+
* from `reasons`, which is a flat string set. This one is structured and
|
|
435
|
+
* reason-coded so "the AST mask was unavailable for 3 files" and "3 files
|
|
436
|
+
* were skipped" cannot be confused. Each entry is ALSO surfaced in
|
|
437
|
+
* `reasons` as `degraded:<reason>:<count>`.
|
|
438
|
+
*
|
|
439
|
+
* Present only when at least one degradation happened; absence means the
|
|
440
|
+
* scan lost no capability to a swallowed error, OR the producer predates
|
|
441
|
+
* the ledger. A non-empty value always forces `partial: true`.
|
|
442
|
+
*/
|
|
443
|
+
degradations?: Array<{
|
|
444
|
+
reason: string;
|
|
445
|
+
count: number;
|
|
446
|
+
}>;
|
|
392
447
|
/**
|
|
393
448
|
* Rule executions that threw and were swallowed by crash isolation
|
|
394
449
|
* (audit R-9). 0 means no rule silently failed; absence means the
|
|
@@ -396,6 +451,45 @@ interface ScanResult {
|
|
|
396
451
|
*/
|
|
397
452
|
rulesCrashed?: number;
|
|
398
453
|
parseFallbacks?: number;
|
|
454
|
+
/**
|
|
455
|
+
* How much of the rule registry this scan could see.
|
|
456
|
+
*
|
|
457
|
+
* ORTHOGONAL TO `partial`, and that is the contract. `partial` means
|
|
458
|
+
* "this run lost something while doing it" and is what `scanExitCode`,
|
|
459
|
+
* SARIF `executionSuccessful` and generated-CI gating read. A rule
|
|
460
|
+
* removed from the set before the walk started never ran, so nothing
|
|
461
|
+
* about it was lost mid-run — and folding it into `partial` would make
|
|
462
|
+
* every ordinary non-`--strict` scan exit 2, at which point nobody
|
|
463
|
+
* reads the field.
|
|
464
|
+
*
|
|
465
|
+
* `PARTIAL` means at least one quarantined rule was withheld. It is the
|
|
466
|
+
* disclosure that `analysisStatus.rules: "complete"` cannot make: a
|
|
467
|
+
* scan can read every file it was given, parse all of them, crash no
|
|
468
|
+
* rule, and still have run 45 of 79 detectors.
|
|
469
|
+
*/
|
|
470
|
+
coverageState?: "COMPLETE" | "PARTIAL";
|
|
471
|
+
/**
|
|
472
|
+
* Rules that ran, and rules withheld from the run. Their sum is the
|
|
473
|
+
* rule set this scan was offered; the registry total is in
|
|
474
|
+
* `coverageState`'s reason (`coverage:quarantine:<n>` in `reasons`).
|
|
475
|
+
*/
|
|
476
|
+
rulesApplied?: number;
|
|
477
|
+
rulesWithheld?: number;
|
|
478
|
+
/**
|
|
479
|
+
* WHICH rules were withheld, when the count is non-zero.
|
|
480
|
+
*
|
|
481
|
+
* The count answers "how much of the registry did this scan not see";
|
|
482
|
+
* this answers "which". It exists because a consumer that has to
|
|
483
|
+
* classify one finding — lifecycle resolution must, because a
|
|
484
|
+
* quarantined rule that never ran cannot have fixed anything — cannot
|
|
485
|
+
* answer from a number. The count alone forced those consumers to
|
|
486
|
+
* claim what they could not know.
|
|
487
|
+
*
|
|
488
|
+
* Absent means the producer did not report the set. It does NOT mean
|
|
489
|
+
* nothing was withheld: `rulesWithheld` is the authority on that, and
|
|
490
|
+
* this field is only the disambiguator.
|
|
491
|
+
*/
|
|
492
|
+
withheldRuleIds?: string[];
|
|
399
493
|
};
|
|
400
494
|
/**
|
|
401
495
|
* Scoring model version stamped into the result (ENGINE-001). Allows
|
|
@@ -404,6 +498,22 @@ interface ScanResult {
|
|
|
404
498
|
* this field.
|
|
405
499
|
*/
|
|
406
500
|
scoringModelVersion?: string;
|
|
501
|
+
/**
|
|
502
|
+
* Why `score` is 99 when the scorer computed 100 (contract v2).
|
|
503
|
+
*
|
|
504
|
+
* Two sites clamp: a degraded scope, and a partial scan. The clamp itself
|
|
505
|
+
* is right — 100 is the strongest claim the scorer makes and neither
|
|
506
|
+
* condition permits it — but the RESULT was byte-identical to a genuine 99,
|
|
507
|
+
* so a reader could not distinguish "withheld to 99" from "scored 99"
|
|
508
|
+
* without reading the pipeline. The presentation surfaces render the
|
|
509
|
+
* reason rather than the bare number.
|
|
510
|
+
*
|
|
511
|
+
* Absent means the score is exactly what the scorer computed. The field
|
|
512
|
+
* lives on `ScanResult`, NOT inside `runIdentity`: it describes the score,
|
|
513
|
+
* and `runIdentity` is the machine anchor whose digest must not change
|
|
514
|
+
* because a presentation reason was added.
|
|
515
|
+
*/
|
|
516
|
+
scoreClampReason?: "scope-degraded" | "partial-scan";
|
|
407
517
|
/**
|
|
408
518
|
* Scope Integrity block (product-gap master plan §7, R4c): the
|
|
409
519
|
* claimed-vs-analyzed accounting. `scopeVerdict` is PROVEN only when
|
|
@@ -638,7 +748,7 @@ type Tier = "core" | "extended" | "quarantine";
|
|
|
638
748
|
interface OverlapMeta {
|
|
639
749
|
/** Rule IDs this rule can double-report on the same root cause. */
|
|
640
750
|
overlapWith?: string[];
|
|
641
|
-
/** Tier rank for survivor resolution (missing =
|
|
751
|
+
/** Tier rank for survivor resolution (missing = extended, see below). */
|
|
642
752
|
tier?: Tier;
|
|
643
753
|
/** RULES registry order for final tie-breaking. */
|
|
644
754
|
order?: number;
|
|
@@ -709,7 +819,7 @@ interface ForensicsReport {
|
|
|
709
819
|
* `playwright-trace`: one record per trace artifact (the per-test
|
|
710
820
|
* trace convention), action-level facts only.
|
|
711
821
|
*/
|
|
712
|
-
source: "playwright-json" | "junit-xml" | "jest-json" | "vitest-json" | "playwright-trace" | "har";
|
|
822
|
+
source: "playwright-json" | "junit-xml" | "dotnet-trx" | "jest-json" | "vitest-json" | "playwright-trace" | "har";
|
|
713
823
|
totalTests: number;
|
|
714
824
|
failed: number;
|
|
715
825
|
skipped: number;
|
|
@@ -757,6 +867,53 @@ declare class DependencyGraph {
|
|
|
757
867
|
has(path: string): boolean;
|
|
758
868
|
}
|
|
759
869
|
//#endregion
|
|
870
|
+
//#region src/engine/degradation-ledger.d.ts
|
|
871
|
+
/**
|
|
872
|
+
* One ledger for every silent capability loss.
|
|
873
|
+
*
|
|
874
|
+
* The defect class this exists to kill is not a deprecation warning. It is a
|
|
875
|
+
* `catch` in a DETECTION path that swallows an error and returns a clean
|
|
876
|
+
* default, so the scan still reports `analysisComplete` while quietly running
|
|
877
|
+
* with less capability than the reader was promised. Fifteen such sites were
|
|
878
|
+
* verified in this tree; the AST-layer ones are the worst, because the layer
|
|
879
|
+
* they degrade is the one that keeps prose comments and sample strings from
|
|
880
|
+
* becoming false-positive findings.
|
|
881
|
+
*
|
|
882
|
+
* Two shapes, one rule. The precedent already in the tree is
|
|
883
|
+
* `parserRetryDegradationCount` (`src/engine/tree-sitter-ast.ts`): a counted,
|
|
884
|
+
* readable number rather than a swallowed exception. This generalizes it —
|
|
885
|
+
* every site records a REASON from the closed set below, and the scan result
|
|
886
|
+
* carries the per-reason counts, so "the parser failed" becomes
|
|
887
|
+
* "the AST mask was unavailable for 3 files".
|
|
888
|
+
*
|
|
889
|
+
* The ledger is APPEND-ONLY and process-scoped, which has one consequence
|
|
890
|
+
* worth stating because it is the security-relevant direction: `beginWindow`
|
|
891
|
+
* returns a MARKER, not an ownership claim. Two concurrent `runScan` calls in
|
|
892
|
+
* one process (there is a test that does exactly this,
|
|
893
|
+
* `tests/stress/concurrent-same-process.spec.ts`) each read the records
|
|
894
|
+
* appended after their own marker, so a degradation one scan records may be
|
|
895
|
+
* counted by the other. That can only ever OVER-count — a scan reporting a
|
|
896
|
+
* degradation that happened to a sibling is more cautious than the truth, and
|
|
897
|
+
* never less — so the failure direction is the honest one. The alternative,
|
|
898
|
+
* a sink threaded through every rule context, is a diff across every call site
|
|
899
|
+
* in the tree to fix a problem that cannot make a scan look better than it is.
|
|
900
|
+
*/
|
|
901
|
+
/**
|
|
902
|
+
* Every reason a detection path may lose capability.
|
|
903
|
+
*
|
|
904
|
+
* Closed on purpose: a `catch` site that wants a new reason has to add it
|
|
905
|
+
* here, which is the moment a reviewer sees the claim. `tests/contract/
|
|
906
|
+
* no-uncounted-degradation.spec.ts` reads this list and fails on any `catch`
|
|
907
|
+
* in a detection path that neither rethrows, records one of these, nor
|
|
908
|
+
* appears in its allowlist with a stated reason.
|
|
909
|
+
*/
|
|
910
|
+
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", "python-manifest-unreadable", "ast-grammar-unavailable", "ast-typescript-parse-failed", "workflow-parse-failed"];
|
|
911
|
+
type DegradationReason = (typeof DEGRADATION_REASONS)[number];
|
|
912
|
+
interface DegradationCount {
|
|
913
|
+
readonly reason: DegradationReason;
|
|
914
|
+
readonly count: number;
|
|
915
|
+
}
|
|
916
|
+
//#endregion
|
|
760
917
|
//#region src/discovery/workspace.d.ts
|
|
761
918
|
/**
|
|
762
919
|
* Repository discovery (Sprint-Plan W1-03).
|
|
@@ -851,6 +1008,58 @@ interface StrategyJustification {
|
|
|
851
1008
|
*/
|
|
852
1009
|
detail: string;
|
|
853
1010
|
}
|
|
1011
|
+
/**
|
|
1012
|
+
* A declared, owned, expiring grant of core status.
|
|
1013
|
+
*
|
|
1014
|
+
* The 6.0 demotion emptied the core tier: no rule's 95% Wilson interval
|
|
1015
|
+
* clears the 10% ceiling, the smallest observed `ciHigh` across 73 measured
|
|
1016
|
+
* rules being 0.138. That is the corpus being too small, not the ceiling being
|
|
1017
|
+
* wrong — moving 0.1 to 0.15 would admit a rule on 24 samples, which is tuning
|
|
1018
|
+
* a threshold to fit data.
|
|
1019
|
+
*
|
|
1020
|
+
* So core is, for now, reachable only by a maintainer saying so in the open.
|
|
1021
|
+
* This is that record. It is deliberately not a boolean: `corePromotion: true`
|
|
1022
|
+
* would be indistinguishable from the silent override it replaces, and would
|
|
1023
|
+
* carry no date on which the claim lapses.
|
|
1024
|
+
*/
|
|
1025
|
+
interface CorePromotion {
|
|
1026
|
+
/**
|
|
1027
|
+
* Why this rule is trusted before the corpus can support it. A structural
|
|
1028
|
+
* argument ("a shared mutable across tests is a defect regardless of
|
|
1029
|
+
* frequency") is legitimate; "seemed fine" is not, and the registry
|
|
1030
|
+
* check rejects a rationale shorter than a sentence.
|
|
1031
|
+
*/
|
|
1032
|
+
rationale: string;
|
|
1033
|
+
/**
|
|
1034
|
+
* A named human who owns this claim. Per P9: an unowned claim is not a
|
|
1035
|
+
* claim, it is a default nobody chose.
|
|
1036
|
+
*/
|
|
1037
|
+
owner: string;
|
|
1038
|
+
/** ISO-8601 date the grant was made. */
|
|
1039
|
+
grantedAt: string;
|
|
1040
|
+
/**
|
|
1041
|
+
* ISO-8601 date the grant lapses. On this date the rule resolves to
|
|
1042
|
+
* `extended` unless the grant is re-justified — the record has to be
|
|
1043
|
+
* renewed by a person, not held in place by not being looked at.
|
|
1044
|
+
*/
|
|
1045
|
+
expiresOn: string;
|
|
1046
|
+
/**
|
|
1047
|
+
* What backs the rationale, beyond the rationale: corpus verdict ids, a
|
|
1048
|
+
* contract path, an ADR.
|
|
1049
|
+
*
|
|
1050
|
+
* An empty list is ALLOWED, and the registry check does not demand one —
|
|
1051
|
+
* because a genuine structural premise ("a mutable shared across tests is a
|
|
1052
|
+
* defect regardless of frequency") has no corpus verdict behind it, and
|
|
1053
|
+
* inventing a citation for one would make the field decorative.
|
|
1054
|
+
*
|
|
1055
|
+
* What an empty list is NOT is invisible: `docs/CORE-READINESS.md` reports a
|
|
1056
|
+
* promotion with no evidence reference as `NEEDS-SAMPLES` in its note, so a
|
|
1057
|
+
* human sees an uncited grant next to a rule that still owes the corpus
|
|
1058
|
+
* samples. That is the honest handling — a judgement the tree cannot verify
|
|
1059
|
+
* is labelled as one — rather than a field the check pretends to verify.
|
|
1060
|
+
*/
|
|
1061
|
+
evidenceRefs: string[];
|
|
1062
|
+
}
|
|
854
1063
|
interface RuleMeta {
|
|
855
1064
|
/** Frozen public API — never reused (§18.4). */
|
|
856
1065
|
id: string;
|
|
@@ -885,7 +1094,7 @@ interface RuleMeta {
|
|
|
885
1094
|
/**
|
|
886
1095
|
* How detection works, as an enforced enum (plan §09.6/§12.1 — D6
|
|
887
1096
|
* closed). Free-text declarations were migrated to the enum in
|
|
888
|
-
* Phase 2; the registry ratchet (tests/rules
|
|
1097
|
+
* Phase 2; the registry ratchet (tests/rules/registry.spec.ts) makes
|
|
889
1098
|
* omission or a bad value a CI failure, so new rules must declare it.
|
|
890
1099
|
*/
|
|
891
1100
|
detectionStrategy?: DetectionStrategy;
|
|
@@ -899,17 +1108,99 @@ interface RuleMeta {
|
|
|
899
1108
|
/** First released version (semver). Immutable once set. */
|
|
900
1109
|
introduced?: string;
|
|
901
1110
|
/**
|
|
902
|
-
* Tier assignment
|
|
903
|
-
*
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
1111
|
+
* Tier assignment — a declared floor that may only ever TIGHTEN.
|
|
1112
|
+
*
|
|
1113
|
+
* Three floors decide a rule's effective tier, and they are not
|
|
1114
|
+
* interchangeable. Measured over all 79 live rules (B0 dry-run, 2026-10-01):
|
|
1115
|
+
*
|
|
1116
|
+
* - POINT ESTIMATE — `fpRate <= 30%` -> extended, else quarantine.
|
|
1117
|
+
* The noise floor. Answers "does this ship by default?"
|
|
1118
|
+
* - INTERVAL — `ciHigh <= 10%` -> core, `ciLow >= 50%` -> quarantine,
|
|
1119
|
+
* else extended. The evidence floor. Answers "does the evidence place it?"
|
|
1120
|
+
* - DECLARED (this field) — overrides both.
|
|
1121
|
+
*
|
|
1122
|
+
* B0 measured what the first two do to this registry, and the result is the
|
|
1123
|
+
* reason this field exists rather than being deleted:
|
|
1124
|
+
*
|
|
1125
|
+
* - The INTERVAL floor quarantines **exactly one** of the 79 rules:
|
|
1126
|
+
* `QA-ENV-001`, which was wrong 20 times out of 20. `ciLow >= 50%`
|
|
1127
|
+
* needs 15 of 20 false positives to fire, so the worst of the rest
|
|
1128
|
+
* (`QA-TEST-002`, 62%) clears neither bound.
|
|
1129
|
+
* - No rule derives CORE. `ciHigh <= 10%` needs n >= 35 with zero false
|
|
1130
|
+
* positives and the sampler caps at 20, where even 0/20 reads 16.1%.
|
|
1131
|
+
* The arithmetic is in `tests/rules/core-tier-reachability.spec.ts`.
|
|
1132
|
+
* - **34 rules declare `quarantine`, and 33 of them would be PROMOTED into
|
|
1133
|
+
* the default scan if this field were deleted** — every one of the 33
|
|
1134
|
+
* moves in the promoting direction and none moves the other way.
|
|
1135
|
+
*
|
|
1136
|
+
* So this field is a THIRD floor, stricter than both derivations, and for 33
|
|
1137
|
+
* of the 34 quarantined rules it is the ONLY thing holding them out of a
|
|
1138
|
+
* default scan. That is why deleting the declared tiers is not a refactor: it
|
|
1139
|
+
* ships 33 quarantined detectors — including the six CI rules the README's
|
|
1140
|
+
* caught-by-default table marks advisory — as ordinary findings.
|
|
1141
|
+
*
|
|
1142
|
+
* THE INVARIANT, enforced by the tightening law in
|
|
1143
|
+
* `tests/rules/registry-ratchet.spec.ts` (not by
|
|
1144
|
+
* `checkDeclaredTierMayOnlyTighten`, which does not exist — the first
|
|
1145
|
+
* version of this comment named a function that was never written, which is
|
|
1146
|
+
* the same defect as a spec pointing at a deleted file): a declared
|
|
1147
|
+
* tier may only ever hold a rule DOWN. Quarantining a well-measured rule is
|
|
1148
|
+
* permitted (conservative, costs a reader a `--strict` flag); shipping a
|
|
1149
|
+
* badly-measured rule is not (it hands a user a finding the corpus says is
|
|
1150
|
+
* wrong). B0 found zero rules loosening, but nothing made that structural —
|
|
1151
|
+
* one hand-edit could have released `QA-TEST-002` at 62% observed FP, and
|
|
1152
|
+
* every derived number in the tree would have agreed with it.
|
|
1153
|
+
*
|
|
907
1154
|
* When omitted, the tier resolves measurement-dependently via
|
|
908
|
-
* `effectiveTier` (src/rules/measurement.ts)
|
|
909
|
-
* valid
|
|
910
|
-
* otherwise — an unmeasured rule can never default into core.
|
|
1155
|
+
* `effectiveTier` (src/rules/measurement.ts) — extended when there is no
|
|
1156
|
+
* valid measurement, so an unmeasured rule can never default into core.
|
|
911
1157
|
*/
|
|
912
1158
|
tier?: "core" | "extended" | "quarantine";
|
|
1159
|
+
/**
|
|
1160
|
+
* A declared, OWNED, EXPIRING grant of core status for a rule the corpus
|
|
1161
|
+
* cannot yet support.
|
|
1162
|
+
*
|
|
1163
|
+
* Required by `checkRegistry` for any rule declaring `tier: "core"` without
|
|
1164
|
+
* a valid measurement — the anti-creep law's counterpart to the measurement
|
|
1165
|
+
* criterion. Without it, a core claim is a human assertion rendered with a
|
|
1166
|
+
* measurement's appearance, which is precisely the defect the 6.0 demotion
|
|
1167
|
+
* removed: nineteen rules showed `tier: "core"`, `measured: true`,
|
|
1168
|
+
* `fpRate: 0` while their 95% Wilson upper bounds ran 13.8%–40.4%.
|
|
1169
|
+
*
|
|
1170
|
+
* The fields are load-bearing:
|
|
1171
|
+
*
|
|
1172
|
+
* - `owner` — a named human. Per P9 an unowned claim is not a claim.
|
|
1173
|
+
* - `expiresOn` — the claim decays to `extended` when it lapses. An
|
|
1174
|
+
* assertion that never expires is a tier, which is what this exists to
|
|
1175
|
+
* avoid.
|
|
1176
|
+
* - `evidenceRefs` — corpus IDs, contract paths, or an ADR. A rationale
|
|
1177
|
+
* with nothing behind it is a preference.
|
|
1178
|
+
*
|
|
1179
|
+
* A rule that HAS earned core by measurement does not need one: the
|
|
1180
|
+
* measurement is the record, and a hand-written rationale beside it is a
|
|
1181
|
+
* second source of truth for the same fact.
|
|
1182
|
+
*/
|
|
1183
|
+
corePromotion?: CorePromotion;
|
|
1184
|
+
/**
|
|
1185
|
+
* A declared, OWNED, EXPIRING commitment about a QUARANTINED rule.
|
|
1186
|
+
*
|
|
1187
|
+
* Same shape as `corePromotion`, and for the same reason: 34 quarantined
|
|
1188
|
+
* rules carry no `owner`, no `rationale` and no `expiresOn`, so nothing in
|
|
1189
|
+
* the tree says who will re-measure them or when the quarantine lapses.
|
|
1190
|
+
* The expiry date IS the exit condition — `CorePromotion` has no separate
|
|
1191
|
+
* exit-condition field, and a message naming one would send the next
|
|
1192
|
+
* person looking for a field the contract does not have.
|
|
1193
|
+
* `src/rules/tier-evidence.ts` is the worked example of the pattern for the
|
|
1194
|
+
* nineteen rules demoted in 6.0, and `docs/QUARANTINE-REMEDIATION.md` is the
|
|
1195
|
+
* ledger for the rest — but a ledger nobody must update is a report.
|
|
1196
|
+
*
|
|
1197
|
+
* The difference from `corePromotion` is the direction. `corePromotion` says
|
|
1198
|
+
* "this rule is trusted before the corpus can show it"; this says "this rule
|
|
1199
|
+
* is NOT trusted yet, here is when that is re-decided". A rule in quarantine
|
|
1200
|
+
* WITHOUT one is a rule whose quarantine is permanent by default, which is
|
|
1201
|
+
* the outcome the tier exists to avoid.
|
|
1202
|
+
*/
|
|
1203
|
+
quarantinePromotion?: CorePromotion;
|
|
913
1204
|
/**
|
|
914
1205
|
* Detector implementation revision (Verification Trust Evolution Plan
|
|
915
1206
|
* §07). Increment on ANY detection-logic change — pattern, scoping,
|
|
@@ -1275,6 +1566,18 @@ declare function loadConfig(root: string, options?: {
|
|
|
1275
1566
|
/** Non-fatal notes (e.g. unknown rule IDs in severityOverrides). */
|
|
1276
1567
|
warnings: string[];
|
|
1277
1568
|
};
|
|
1569
|
+
//#endregion
|
|
1570
|
+
//#region src/lib/glob.d.ts
|
|
1571
|
+
/**
|
|
1572
|
+
* Whether `path` matches `glob`, in the dialect documented above.
|
|
1573
|
+
*
|
|
1574
|
+
* Both sides are normalized to forward slashes first. Finding paths are already
|
|
1575
|
+
* normalized by the walker, but a suppression `files` pattern written on
|
|
1576
|
+
* Windows ("e2e\\x.spec.ts") compiled to a literal-backslash regex that could
|
|
1577
|
+
* never match any finding — the suppression silently never applied, which is
|
|
1578
|
+
* the defect that made the whole dialect worth unifying.
|
|
1579
|
+
*/
|
|
1580
|
+
declare function pathMatchesGlob$1(path: string, glob: string): boolean;
|
|
1278
1581
|
declare namespace scan_pipeline_d_exports {
|
|
1279
1582
|
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, summarizeForensicVerdicts };
|
|
1280
1583
|
}
|
|
@@ -1300,6 +1603,12 @@ declare function buildUniversalRules$1(root: string, strict?: boolean, opts?: {
|
|
|
1300
1603
|
rules: number;
|
|
1301
1604
|
}>;
|
|
1302
1605
|
externalRules: QADoctorRule[];
|
|
1606
|
+
/** Rules that ran, after the quarantine filter. */
|
|
1607
|
+
rulesApplied: number;
|
|
1608
|
+
/** Rules removed by the quarantine filter because `--strict` was absent. */
|
|
1609
|
+
rulesWithheld: number;
|
|
1610
|
+
/** The same rules, named. The count is the disclosure; this is the evidence. */
|
|
1611
|
+
withheldRuleIds: string[];
|
|
1303
1612
|
}>;
|
|
1304
1613
|
/** Rule-declared evidence-level overrides (Honesty Core). */
|
|
1305
1614
|
declare const EVIDENCE_OVERRIDES$1: ReadonlyMap<string, string>;
|
|
@@ -1314,21 +1623,60 @@ interface CliArgs {
|
|
|
1314
1623
|
verbose: boolean;
|
|
1315
1624
|
maxDurationMs: number;
|
|
1316
1625
|
scopeChanged: boolean;
|
|
1317
|
-
|
|
1626
|
+
/**
|
|
1627
|
+
* The output shape.
|
|
1628
|
+
*
|
|
1629
|
+
* `trust-report` | `pr-comment` | `github-summary` are the v6 collapse's
|
|
1630
|
+
* REPLACE arm (plan §3): they were separate verbs whose entire body was
|
|
1631
|
+
* "scan, then render". They are render-only — the scan, the findings, the
|
|
1632
|
+
* score and the exit code are identical under all of them, and under
|
|
1633
|
+
* `terminal`, which is the property that lets a format be a flag.
|
|
1634
|
+
*/
|
|
1635
|
+
format: "terminal" | "json" | "sarif" | "mermaid" | "codequality" | "trust-report" | "pr-comment" | "github-summary";
|
|
1318
1636
|
/** --width override for terminal box/gauge wrapping (Sprint 5 Task 22). */
|
|
1319
1637
|
width?: number;
|
|
1320
1638
|
/** --ascii / --no-ascii override for shouldUseAscii()'s heuristic. */
|
|
1321
1639
|
ascii?: boolean;
|
|
1322
1640
|
/** --tone blunt: opt-in blunter messages (Sprint 9 Task 40). */
|
|
1323
1641
|
tone?: "blunt";
|
|
1324
|
-
/**
|
|
1642
|
+
/**
|
|
1643
|
+
* --include-warn (was --strict): include quarantine-tier rules in the scan
|
|
1644
|
+
* (Phase 4). The internal name predates the 6.0 flag rename and names a scan
|
|
1645
|
+
* option rather than a user-facing promise; renaming it would touch every
|
|
1646
|
+
* call site for a rename no user would ever see.
|
|
1647
|
+
*/
|
|
1325
1648
|
strict?: boolean;
|
|
1649
|
+
/**
|
|
1650
|
+
* Deprecated flag spellings used on this invocation, each with its
|
|
1651
|
+
* replacement. Populated by the parser so the CLI can print the whole
|
|
1652
|
+
* deprecation notice once, in one place, rather than each arm printing its
|
|
1653
|
+
* own warning where a `--json` consumer would have to read stderr to find it.
|
|
1654
|
+
*/
|
|
1655
|
+
deprecatedFlags?: Array<{
|
|
1656
|
+
flag: string;
|
|
1657
|
+
replacement: string;
|
|
1658
|
+
}>;
|
|
1326
1659
|
/** --base <ref>: base ref for --scope changed (audit H-10). */
|
|
1327
1660
|
base?: string;
|
|
1328
1661
|
/** --debug: print errors swallowed by crash isolation (audit R-9). */
|
|
1329
1662
|
debug?: boolean;
|
|
1330
1663
|
/** --record-milestones: let a scan write .mjolnir/stats.json (audit R-1). */
|
|
1331
1664
|
recordMilestones?: boolean;
|
|
1665
|
+
/**
|
|
1666
|
+
* --save-baseline: write this scan's findings to `.mjolnir/baseline.json`
|
|
1667
|
+
* so a LATER scan can say RESOLVED rather than reporting the same debt
|
|
1668
|
+
* forever.
|
|
1669
|
+
*
|
|
1670
|
+
* This flag exists because the `baseline` verb was removed in the v6 carve
|
|
1671
|
+
* and nothing replaced it. That left `ci verify` unable to leave its
|
|
1672
|
+
* no-baseline arm for any user, and left three shipped messages — the
|
|
1673
|
+
* verify digest, the MCP `verify` tool, and the agent instruction brief —
|
|
1674
|
+
* naming a command that could not perform the action they described. A
|
|
1675
|
+
* recovery instruction that does not recover is worse than no instruction:
|
|
1676
|
+
* it costs the reader a round trip and teaches them that the tool's own
|
|
1677
|
+
* guidance is unreliable.
|
|
1678
|
+
*/
|
|
1679
|
+
saveBaseline?: boolean;
|
|
1332
1680
|
/**
|
|
1333
1681
|
* --cache: reuse per-file rule verdicts from the local content-addressed
|
|
1334
1682
|
* cache (M5.2). Post-loop processing always re-runs; the cache only
|
|
@@ -1369,6 +1717,22 @@ interface CliArgs {
|
|
|
1369
1717
|
* scan semantics and exit codes are unchanged (plan §5.6).
|
|
1370
1718
|
*/
|
|
1371
1719
|
scoreOnly?: boolean;
|
|
1720
|
+
/**
|
|
1721
|
+
* `--require-full-coverage`: exit `EXIT_PARTIAL` when the quarantine
|
|
1722
|
+
* filter withheld any rule, even though the scan itself was whole.
|
|
1723
|
+
*
|
|
1724
|
+
* Off by default, and the default is the point. Coverage-PARTIAL is the
|
|
1725
|
+
* state of every ordinary scan — the quarantine tier is non-empty — so
|
|
1726
|
+
* making it gate by default would exit 2 on nearly every run. The fix a
|
|
1727
|
+
* user reaches for is `--strict`, which switches the quarantine ON, and
|
|
1728
|
+
* then the flag has nothing left to check. A flag that inverts its own
|
|
1729
|
+
* remedy is worse than no flag.
|
|
1730
|
+
*
|
|
1731
|
+
* The opt-in is for a project that has decided quarantined detectors must
|
|
1732
|
+
* be live: then a withheld rule is a real loss of coverage and the run is
|
|
1733
|
+
* inconclusive, exactly as a truncated walk is.
|
|
1734
|
+
*/
|
|
1735
|
+
requireFullCoverage?: boolean;
|
|
1372
1736
|
/**
|
|
1373
1737
|
* Audit C2: --enable-plugins opens the plugin trust gate for THIS
|
|
1374
1738
|
* invocation — npm-plugin and JS-module rule sources may load (and
|
|
@@ -1390,6 +1754,30 @@ interface CliArgs {
|
|
|
1390
1754
|
* mode" (no monorepo analysis).
|
|
1391
1755
|
*/
|
|
1392
1756
|
monorepo?: boolean;
|
|
1757
|
+
/**
|
|
1758
|
+
* --suppressions (v6 collapse, plan §3): print the suppression LEDGER and
|
|
1759
|
+
* stop. A mode, not a verb.
|
|
1760
|
+
*
|
|
1761
|
+
* It is a mode because the ledger is a view of `mjolnir.config.json` and
|
|
1762
|
+
* nothing else — a verb for it would be a command whose whole body is
|
|
1763
|
+
* "read a file and print it", which is what a flag on the command that
|
|
1764
|
+
* enforces the file is for. It reads the CWD, exactly as the retired verb
|
|
1765
|
+
* did, so the one observable behaviour a script can depend on is unchanged.
|
|
1766
|
+
*/
|
|
1767
|
+
suppressions?: boolean;
|
|
1768
|
+
/**
|
|
1769
|
+
* --suppression-gate (v6 collapse, plan §3): fail when a suppression is
|
|
1770
|
+
* ungoverned — expired, reasonless, or covering a rule that no longer
|
|
1771
|
+
* exists. Runs the scan first, because a governance judgement about a
|
|
1772
|
+
* suppression needs the findings the suppression would have hidden.
|
|
1773
|
+
*/
|
|
1774
|
+
suppressionGate?: boolean;
|
|
1775
|
+
/**
|
|
1776
|
+
* --policy (v6 collapse, plan §3): print the scoring policy in force and
|
|
1777
|
+
* stop. A policy nobody can read is a policy nobody can check, and the
|
|
1778
|
+
* scorer publishes one on purpose.
|
|
1779
|
+
*/
|
|
1780
|
+
policy?: boolean;
|
|
1393
1781
|
}
|
|
1394
1782
|
interface ScanHooks {
|
|
1395
1783
|
/** Invoked when a rule throws on a file (audit R-9). */
|
|
@@ -1425,20 +1813,6 @@ interface ScanHooks {
|
|
|
1425
1813
|
* scanning a filesystem root.
|
|
1426
1814
|
*/
|
|
1427
1815
|
declare function fallbackWorkspace$1(targetAbs: string): Workspace;
|
|
1428
|
-
/**
|
|
1429
|
-
* Minimal glob match for suppression `files` patterns, with gitignore
|
|
1430
|
-
* `**` semantics (bug-audit M5). Supports:
|
|
1431
|
-
* "tests/**" — everything inside tests/
|
|
1432
|
-
* "tests" + "/**\/*.spec.ts" — any depth UNDER tests/ (including none) ending in .spec.ts
|
|
1433
|
-
* "**" + "/*.spec.ts" — any depth including root-level files
|
|
1434
|
-
* "tests/foo.spec.ts" — exact path
|
|
1435
|
-
* "*" within a segment never crosses "/".
|
|
1436
|
-
*
|
|
1437
|
-
* Forward slashes only (findings always use normalized paths). `?`,
|
|
1438
|
-
* character classes and `!` negation are not metacharacters here — same
|
|
1439
|
-
* as before this rewrite.
|
|
1440
|
-
*/
|
|
1441
|
-
declare function pathMatchesGlob$1(path: string, glob: string): boolean;
|
|
1442
1816
|
/**
|
|
1443
1817
|
* Plan §16 + WI-11: locate a runtime run report next to the scan
|
|
1444
1818
|
* target, using the exact conventions the forensics ingestion already
|
|
@@ -1483,6 +1857,12 @@ interface FileAnalysisResult {
|
|
|
1483
1857
|
parseFallbacks: number;
|
|
1484
1858
|
scanned: number;
|
|
1485
1859
|
analyzed: number;
|
|
1860
|
+
/**
|
|
1861
|
+
* Files that HAD an AST stage and lost it to the scan deadline, so they
|
|
1862
|
+
* were analyzed by regex alone. Zero for every adapter that declares no
|
|
1863
|
+
* `parseAst` — that is a capability envelope, not a downgrade.
|
|
1864
|
+
*/
|
|
1865
|
+
astFallbackFiles: number;
|
|
1486
1866
|
}
|
|
1487
1867
|
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
1868
|
path: string;
|
|
@@ -1530,6 +1910,24 @@ interface AssembleScanResultInput {
|
|
|
1530
1910
|
scopeUnrecognized: number;
|
|
1531
1911
|
parseFailed: number;
|
|
1532
1912
|
parseFallbacks?: number;
|
|
1913
|
+
/** Files that lost their AST stage to the scan deadline. See completion.ts. */
|
|
1914
|
+
astFallbackFiles?: number;
|
|
1915
|
+
/**
|
|
1916
|
+
* Reason-coded capability losses recorded during this scan's window. See
|
|
1917
|
+
* `src/engine/degradation-ledger.ts` for why the window is an index rather
|
|
1918
|
+
* than an ownership claim.
|
|
1919
|
+
*/
|
|
1920
|
+
degradations?: readonly DegradationCount[];
|
|
1921
|
+
/**
|
|
1922
|
+
* Rules removed from the run by the quarantine filter because `--strict`
|
|
1923
|
+
* was absent. Threaded from the rule loader — the only site that knows
|
|
1924
|
+
* the withheld set — into `coverageState`. Never an input to `partial`.
|
|
1925
|
+
*/
|
|
1926
|
+
rulesWithheld?: number;
|
|
1927
|
+
/** Rules withheld, named. See `buildUniversalRules`. */
|
|
1928
|
+
withheldRuleIds?: string[];
|
|
1929
|
+
/** Rules that actually ran. The denominator `rulesWithheld` is measured against. */
|
|
1930
|
+
rulesApplied?: number;
|
|
1533
1931
|
scanned: number;
|
|
1534
1932
|
analyzed?: number;
|
|
1535
1933
|
testFiles: string[];
|
|
@@ -1574,18 +1972,23 @@ declare function assembleScanResult(o: AssembleScanResultInput): ScanResult;
|
|
|
1574
1972
|
* inherently async); `runRules` and every rule stay synchronous and
|
|
1575
1973
|
* consume `ParsedFile.ast`. Callers await the returned promise.
|
|
1576
1974
|
*/
|
|
1975
|
+
/**
|
|
1976
|
+
* Run one scan inside its own ts-morph Project scope.
|
|
1977
|
+
*
|
|
1978
|
+
* The wrapper is the whole fix. ts-morph's Project caches SourceFiles by path
|
|
1979
|
+
* and `parseTsFile` calls `replaceWithText` on them, so a Project shared between
|
|
1980
|
+
* two concurrent scans lets one rewrite the file the other is mid-rule on —
|
|
1981
|
+
* which surfaced as three of four concurrent scans reporting
|
|
1982
|
+
* `rulesCrashed: 4` and `rules: "partial"`, and therefore as two different
|
|
1983
|
+
* machine-contract digests from one tree. See `src/engine/ts-ast.ts`.
|
|
1984
|
+
*
|
|
1985
|
+
* Async context propagates across `await`, so the scope covers the whole scan
|
|
1986
|
+
* without threading a Project through every adapter.
|
|
1987
|
+
*/
|
|
1577
1988
|
declare function runScan$1(args: CliArgs, hooks?: ScanHooks): Promise<ScanResult>;
|
|
1578
1989
|
//#endregion
|
|
1579
1990
|
//#region src/engine/version.d.ts
|
|
1580
|
-
|
|
1581
|
-
* Engine version, as a leaf module: `runIdentity` (R4c) needs the engine
|
|
1582
|
-
* version inside scan-pipeline WITHOUT importing cli.ts (a cycle — cli
|
|
1583
|
-
* imports the pipeline). The literal follows the same discipline as
|
|
1584
|
-
* CLI_VERSION and SARIF's driver.version: kept in sync on release by
|
|
1585
|
-
* scripts/sync-sarif-version.cjs and guarded by the version-consistency
|
|
1586
|
-
* spec. cli.ts re-exports this as CLI_VERSION.
|
|
1587
|
-
*/
|
|
1588
|
-
declare const ENGINE_VERSION = "4.0.0";
|
|
1991
|
+
declare const ENGINE_VERSION = "6.0.0";
|
|
1589
1992
|
//#endregion
|
|
1590
1993
|
//#region src/cli-io.d.ts
|
|
1591
1994
|
/**
|
|
@@ -1601,6 +2004,23 @@ export declare const out: Output;
|
|
|
1601
2004
|
export declare const err: Output;
|
|
1602
2005
|
export declare function internalErrorMessage(err: unknown, emit: (s: string) => void, debug: boolean): void;
|
|
1603
2006
|
//#endregion
|
|
2007
|
+
//#region src/claim-evidence.d.ts
|
|
2008
|
+
/**
|
|
2009
|
+
* The gate level a command applies to its findings. `advisory` is the
|
|
2010
|
+
* `--blocking none` posture: findings are reported and never gate.
|
|
2011
|
+
*/
|
|
2012
|
+
type GateLevel = "advisory" | "error" | "warning";
|
|
2013
|
+
//#endregion
|
|
2014
|
+
//#region src/commands/why.d.ts
|
|
2015
|
+
/**
|
|
2016
|
+
* Testable why command core. Returns the process exit code.
|
|
2017
|
+
* Live mode awaits the real scan; saved-report mode is synchronous.
|
|
2018
|
+
*/
|
|
2019
|
+
export declare function runWhyCommand(argv: string[], io?: {
|
|
2020
|
+
out: Output;
|
|
2021
|
+
err: Output;
|
|
2022
|
+
}): Promise<number>;
|
|
2023
|
+
//#endregion
|
|
1604
2024
|
//#region src/cli-handlers.d.ts
|
|
1605
2025
|
/**
|
|
1606
2026
|
* Render scan output in the requested format.
|
|
@@ -1608,90 +2028,134 @@ export declare function internalErrorMessage(err: unknown, emit: (s: string) =>
|
|
|
1608
2028
|
* Extracted from runScanCommand (Task 6) to reduce cyclomatic complexity.
|
|
1609
2029
|
* Handles sarif, mermaid, codequality, json, and terminal (trust-report)
|
|
1610
2030
|
* rendering. The caller handles first-run hint + milestones separately.
|
|
2031
|
+
*
|
|
2032
|
+
* The v6 collapse's REPLACE arm (plan §3) lands here: `trust-report`,
|
|
2033
|
+
* `pr-comment` and `summary` were three verbs whose whole body was this
|
|
2034
|
+
* function with one branch each. The renderers already existed; what was
|
|
2035
|
+
* missing was a way to name them from the one verb a reader already knows.
|
|
2036
|
+
*
|
|
2037
|
+
* `--format trust-report` is the terminal render, which is why the `else`
|
|
2038
|
+
* below is spelled `terminal` and not `trust-report`: naming a default after
|
|
2039
|
+
* a flag that is also its value is a way to make the default untypeable.
|
|
1611
2040
|
*/
|
|
1612
2041
|
export declare function renderScanOutput(result: Awaited<ReturnType<typeof runScan$1>>, args: CliArgs, target: string, io: {
|
|
1613
2042
|
out: Output;
|
|
1614
2043
|
err: Output;
|
|
1615
|
-
}): void;
|
|
2044
|
+
}, gate: GateLevel): void;
|
|
1616
2045
|
export declare function runScanCommand(argv: string[], io?: {
|
|
1617
2046
|
out: Output;
|
|
1618
2047
|
err: Output;
|
|
1619
2048
|
}): Promise<number>;
|
|
1620
|
-
export declare function
|
|
1621
|
-
out: Output;
|
|
1622
|
-
err: Output;
|
|
1623
|
-
}): number;
|
|
1624
|
-
export declare function runMutationCommand(argv: string[], io?: {
|
|
1625
|
-
out: Output;
|
|
1626
|
-
err: Output;
|
|
1627
|
-
}): Promise<number>;
|
|
1628
|
-
export declare function runBadgeCommand(argv: string[], io?: {
|
|
2049
|
+
export declare function runFixCommand(argv: string[], io?: {
|
|
1629
2050
|
out: Output;
|
|
1630
2051
|
err: Output;
|
|
1631
2052
|
}): Promise<number>;
|
|
1632
|
-
export declare function
|
|
2053
|
+
export declare function runVerifyCommand(argv: string[], io?: {
|
|
1633
2054
|
out: Output;
|
|
1634
2055
|
err: Output;
|
|
1635
2056
|
}): Promise<number>;
|
|
1636
|
-
export declare function
|
|
2057
|
+
export declare function runPrCommentCommand(argv: string[], io?: {
|
|
1637
2058
|
out: Output;
|
|
1638
2059
|
err: Output;
|
|
1639
2060
|
}): Promise<number>;
|
|
1640
|
-
export declare function
|
|
2061
|
+
export declare function runStatsCommand(argv: string[], io?: {
|
|
1641
2062
|
out: Output;
|
|
1642
2063
|
err: Output;
|
|
1643
2064
|
}): number;
|
|
1644
|
-
export declare function
|
|
1645
|
-
out: Output;
|
|
1646
|
-
err: Output;
|
|
1647
|
-
}): Promise<number>;
|
|
1648
|
-
export declare function runBaselineCommand(argv: string[], io?: {
|
|
1649
|
-
out: Output;
|
|
1650
|
-
err: Output;
|
|
1651
|
-
}): Promise<number>;
|
|
1652
|
-
export declare function runVerifyCommand(argv: string[], io?: {
|
|
2065
|
+
export declare function runHandoverCommand(argv: string[], io?: {
|
|
1653
2066
|
out: Output;
|
|
1654
2067
|
err: Output;
|
|
1655
2068
|
}): Promise<number>;
|
|
1656
|
-
export declare function
|
|
2069
|
+
export declare function runPwReportCommand(argv: string[], io?: {
|
|
1657
2070
|
out: Output;
|
|
1658
2071
|
err: Output;
|
|
1659
|
-
}):
|
|
1660
|
-
export declare function
|
|
2072
|
+
}): number;
|
|
2073
|
+
export declare function runRulesCommand(argv: string[], io?: {
|
|
1661
2074
|
out: Output;
|
|
1662
2075
|
err: Output;
|
|
1663
2076
|
}): Promise<number>;
|
|
1664
|
-
|
|
2077
|
+
/**
|
|
2078
|
+
* `explain` ARMS — the v6 collapse's MOVE arm, group 2 (plan §3).
|
|
2079
|
+
*
|
|
2080
|
+
* `explain` is the question verb: what does this finding mean, and what else
|
|
2081
|
+
* does it touch. Five capabilities were top-level verbs that all answer a
|
|
2082
|
+
* variation of that question, and each is a flag here:
|
|
2083
|
+
*
|
|
2084
|
+
* --list the rule catalogue (was `mjolnir explain --list`)
|
|
2085
|
+
* <file:line> why this finding (was `mjolnir explain <file:line>` — already
|
|
2086
|
+
* delegated by the subject form)
|
|
2087
|
+
* --callers who calls this symbol (was `mjolnir explain --callers`)
|
|
2088
|
+
* --plan what to do next (was `mjolnir explain --plan`)
|
|
2089
|
+
* --evidence what really ran (was the `triage` verb)
|
|
2090
|
+
* --playwright a Playwright run summary (was `mjolnir explain --playwright`)
|
|
2091
|
+
*
|
|
2092
|
+
* The table is the same shape as `CI_SUBCOMMANDS`, and for the same reason: it
|
|
2093
|
+
* is the single source the dispatcher reads AND the help renders from, so an
|
|
2094
|
+
* arm that exists is documented and one that is documented exists.
|
|
2095
|
+
*
|
|
2096
|
+
* `--playwright` is INTERNAL in the plan and marked here as such. `pw-report`
|
|
2097
|
+
* summarised a Playwright JSON run for a person reading CI output; the same
|
|
2098
|
+
* information arrives through `--evidence` once it reads a report rather than
|
|
2099
|
+
* a specific runner's format, and one flag that means "the run summary"
|
|
2100
|
+
* whichever runner produced it beats two that mean it for one of them.
|
|
2101
|
+
*/
|
|
2102
|
+
/**
|
|
2103
|
+
* `explain --evidence` — the runtime-evidence arm.
|
|
2104
|
+
*
|
|
2105
|
+
* The `triage` verb's body, verbatim in behaviour: read a real run, classify
|
|
2106
|
+
* every test's verdict, and render the table plus the meeting artifact. The
|
|
2107
|
+
* arm does not add anything, which is the point — the capability is the same
|
|
2108
|
+
* one `src/forensics/triage.ts` and the MCP server already expose, and the
|
|
2109
|
+
* reason the verb is retired is the reason the flag exists.
|
|
2110
|
+
*
|
|
2111
|
+
* `--json` and `--md` are the verb's own output selectors, preserved so a
|
|
2112
|
+
* script written against them keeps working with one word changed.
|
|
2113
|
+
*
|
|
2114
|
+
* The one behaviour note: the report is written to `--flaky-md`'s path, which
|
|
2115
|
+
* the verb also did, and the arm does not silently write a file into a
|
|
2116
|
+
* directory a reader did not name. `--no-md` is still how you decline it.
|
|
2117
|
+
*/
|
|
2118
|
+
export declare function runEvidenceArm(argv: string[], io: {
|
|
1665
2119
|
out: Output;
|
|
1666
2120
|
err: Output;
|
|
1667
2121
|
}): number;
|
|
1668
|
-
export declare
|
|
2122
|
+
export declare const EXPLAIN_ARMS: Record<string, {
|
|
2123
|
+
summary: string;
|
|
2124
|
+
status: "public" | "internal";
|
|
2125
|
+
run: (argv: string[], io: {
|
|
2126
|
+
out: Output;
|
|
2127
|
+
err: Output;
|
|
2128
|
+
}) => Promise<number> | number;
|
|
2129
|
+
}>;
|
|
2130
|
+
export declare function runExplainCommand(argv: string[], io?: {
|
|
1669
2131
|
out: Output;
|
|
1670
2132
|
err: Output;
|
|
1671
2133
|
}): Promise<number>;
|
|
1672
|
-
export declare function
|
|
2134
|
+
export declare function runDoctorPlaywright(argv: string[], io?: {
|
|
1673
2135
|
out: Output;
|
|
1674
|
-
err
|
|
1675
|
-
}): number
|
|
1676
|
-
|
|
2136
|
+
err?: Output;
|
|
2137
|
+
}): Promise<number>;
|
|
2138
|
+
//#endregion
|
|
2139
|
+
//#region src/commands/milestone.d.ts
|
|
2140
|
+
export declare function runFrameworkMaturityCommand(argv: string[], io?: {
|
|
1677
2141
|
out: Output;
|
|
1678
2142
|
err: Output;
|
|
1679
2143
|
}): number;
|
|
1680
|
-
export declare function
|
|
2144
|
+
export declare function runSuppressionGateCommand(argv: string[], io?: {
|
|
1681
2145
|
out: Output;
|
|
1682
2146
|
err: Output;
|
|
1683
|
-
}): number
|
|
1684
|
-
export declare function
|
|
2147
|
+
}): Promise<number>;
|
|
2148
|
+
export declare function runCrossFileCommand(argv: string[], io?: {
|
|
1685
2149
|
out: Output;
|
|
1686
2150
|
err: Output;
|
|
1687
2151
|
}): Promise<number>;
|
|
1688
|
-
export declare function
|
|
2152
|
+
export declare function runEvidenceGraphCommand(argv: string[], io?: {
|
|
1689
2153
|
out: Output;
|
|
1690
2154
|
err: Output;
|
|
1691
2155
|
}): Promise<number>;
|
|
1692
|
-
export declare function
|
|
2156
|
+
export declare function runCIIntegrityCommand(argv: string[], io?: {
|
|
1693
2157
|
out: Output;
|
|
1694
|
-
err
|
|
2158
|
+
err: Output;
|
|
1695
2159
|
}): Promise<number>;
|
|
1696
2160
|
//#endregion
|
|
1697
2161
|
//#region src/commands/doctor-run.d.ts
|
|
@@ -1700,29 +2164,6 @@ export declare function runDoctorCommand(argv: string[], io?: {
|
|
|
1700
2164
|
err: Output;
|
|
1701
2165
|
}): number;
|
|
1702
2166
|
//#endregion
|
|
1703
|
-
//#region src/commands/summary.d.ts
|
|
1704
|
-
/**
|
|
1705
|
-
* Testable summary command core. Returns the process exit code.
|
|
1706
|
-
* Streams: annotations → stdout (always; GitHub greps them), summary →
|
|
1707
|
-
* step-summary file when set unless --stdout. Unknown flags are a
|
|
1708
|
-
* usage error (exit 10) — a typo'd --stdout must not silently route
|
|
1709
|
-
* the summary to $GITHUB_STEP_SUMMARY.
|
|
1710
|
-
*/
|
|
1711
|
-
export declare function runSummaryCommand(argv: string[], io?: {
|
|
1712
|
-
out: Output;
|
|
1713
|
-
err: Output;
|
|
1714
|
-
}): number;
|
|
1715
|
-
//#endregion
|
|
1716
|
-
//#region src/commands/why.d.ts
|
|
1717
|
-
/**
|
|
1718
|
-
* Testable why command core. Returns the process exit code.
|
|
1719
|
-
* Live mode awaits the real scan; saved-report mode is synchronous.
|
|
1720
|
-
*/
|
|
1721
|
-
export declare function runWhyCommand(argv: string[], io?: {
|
|
1722
|
-
out: Output;
|
|
1723
|
-
err: Output;
|
|
1724
|
-
}): Promise<number>;
|
|
1725
|
-
//#endregion
|
|
1726
2167
|
//#region src/commands/handoff.d.ts
|
|
1727
2168
|
/**
|
|
1728
2169
|
* Testable handoff command core. Returns the process exit code.
|
|
@@ -1744,15 +2185,9 @@ export declare function runInstallCommand(argv: string[], io?: {
|
|
|
1744
2185
|
err: Output;
|
|
1745
2186
|
}, cwd?: string): number;
|
|
1746
2187
|
//#endregion
|
|
1747
|
-
//#region src/commands/trust-report.d.ts
|
|
1748
|
-
export declare function runTrustReportCommand(argv: string[], io: {
|
|
1749
|
-
out: Output;
|
|
1750
|
-
err: Output;
|
|
1751
|
-
}): Promise<number>;
|
|
1752
|
-
//#endregion
|
|
1753
2188
|
//#region src/commands/release-trust.d.ts
|
|
1754
2189
|
/**
|
|
1755
|
-
* CLI verb: `mjolnir release-trust [--json] [repo-root]`.
|
|
2190
|
+
* CLI verb: `mjolnir ci release-trust [--json] [repo-root]`.
|
|
1756
2191
|
* Exit contract (frozen set): 0 verdict PASS · 1 verdict non-PASS ·
|
|
1757
2192
|
* 2 no fixtures root (not an mjolnir checkout — BLOCKED context) ·
|
|
1758
2193
|
* 10 usage error · 20 internal error.
|
|
@@ -1762,67 +2197,12 @@ export declare function runReleaseTrustCommand(argv: string[], io?: {
|
|
|
1762
2197
|
err: Output;
|
|
1763
2198
|
}): number;
|
|
1764
2199
|
//#endregion
|
|
1765
|
-
//#region src/commands/business-case.d.ts
|
|
1766
|
-
/**
|
|
1767
|
-
* Entry point for `mjolnir business-case`.
|
|
1768
|
-
*
|
|
1769
|
-
* Flags:
|
|
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.
|
|
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.
|
|
1781
|
-
*/
|
|
1782
|
-
export declare function runBusinessCaseCommand(argv: string[], io?: {
|
|
1783
|
-
out: Output;
|
|
1784
|
-
err?: Output;
|
|
1785
|
-
}): Promise<number>;
|
|
1786
|
-
//#endregion
|
|
1787
|
-
//#region src/commands/release-report.d.ts
|
|
1788
|
-
/**
|
|
1789
|
-
* CLI verb: `mjolnir release-report [--since <ref>] [--history <json>] [target]`.
|
|
1790
|
-
*/
|
|
1791
|
-
export declare function runReleaseReportCommand(argv: string[], io: {
|
|
1792
|
-
out: Output;
|
|
1793
|
-
err: Output;
|
|
1794
|
-
}): Promise<number>;
|
|
1795
|
-
//#endregion
|
|
1796
|
-
//#region src/commands/report-playwright.d.ts
|
|
1797
|
-
export declare function runReportPlaywrightCommand(argv: string[], io: {
|
|
1798
|
-
out: Output;
|
|
1799
|
-
err: Output;
|
|
1800
|
-
}): Promise<number>;
|
|
1801
|
-
//#endregion
|
|
1802
|
-
//#region src/commands/trend.d.ts
|
|
1803
|
-
export declare function runTrendCommand(argv: string[], io: {
|
|
1804
|
-
out: Output;
|
|
1805
|
-
err: Output;
|
|
1806
|
-
}): Promise<number>;
|
|
1807
|
-
//#endregion
|
|
1808
|
-
//#region src/commands/exec-report.d.ts
|
|
1809
|
-
export declare function runExecReportCommand(argv: string[], io: {
|
|
1810
|
-
out: Output;
|
|
1811
|
-
err: Output;
|
|
1812
|
-
}): Promise<number>;
|
|
1813
|
-
//#endregion
|
|
1814
2200
|
//#region src/commands/policy.d.ts
|
|
1815
2201
|
export declare function runPolicyCommand(argv: string[], io: {
|
|
1816
2202
|
out: Output;
|
|
1817
2203
|
err: Output;
|
|
1818
2204
|
}): Promise<number>;
|
|
1819
2205
|
//#endregion
|
|
1820
|
-
//#region src/commands/quarantine.d.ts
|
|
1821
|
-
export declare function runQuarantineCommand(argv: string[], io: {
|
|
1822
|
-
out: Output;
|
|
1823
|
-
err: Output;
|
|
1824
|
-
}): number;
|
|
1825
|
-
//#endregion
|
|
1826
2206
|
//#region src/commands/analyze.d.ts
|
|
1827
2207
|
export declare function runAnalyzeCommand(argv: string[], io: {
|
|
1828
2208
|
out: Output;
|
|
@@ -1835,53 +2215,17 @@ export declare function runCiAdapterCommand(argv: string[], io: {
|
|
|
1835
2215
|
err: Output;
|
|
1836
2216
|
}): number;
|
|
1837
2217
|
//#endregion
|
|
1838
|
-
//#region src/commands/
|
|
1839
|
-
export declare function runDashboardCommand(argv: string[], io: {
|
|
1840
|
-
out: Output;
|
|
1841
|
-
err: Output;
|
|
1842
|
-
}): Promise<number>;
|
|
1843
|
-
//#endregion
|
|
1844
|
-
//#region src/commands/enterprise.d.ts
|
|
1845
|
-
export declare function runEnterpriseCommand(argv: string[], io: {
|
|
1846
|
-
out: Output;
|
|
1847
|
-
err: Output;
|
|
1848
|
-
}): number;
|
|
1849
|
-
//#endregion
|
|
1850
|
-
//#region src/commands/maturity.d.ts
|
|
1851
|
-
export declare function runMaturityCommand(argv: string[], io: {
|
|
1852
|
-
out: Output;
|
|
1853
|
-
err: Output;
|
|
1854
|
-
}): number;
|
|
1855
|
-
//#endregion
|
|
1856
|
-
//#region src/commands/milestone.d.ts
|
|
1857
|
-
export declare function runFrameworkMaturityCommand(argv: string[], io?: {
|
|
1858
|
-
out: Output;
|
|
1859
|
-
err: Output;
|
|
1860
|
-
}): number;
|
|
1861
|
-
export declare function runSuppressionGateCommand(argv: string[], io?: {
|
|
1862
|
-
out: Output;
|
|
1863
|
-
err: Output;
|
|
1864
|
-
}): Promise<number>;
|
|
1865
|
-
export declare function runCrossFileCommand(argv: string[], io?: {
|
|
1866
|
-
out: Output;
|
|
1867
|
-
err: Output;
|
|
1868
|
-
}): Promise<number>;
|
|
2218
|
+
//#region src/commands/contract-verify.d.ts
|
|
1869
2219
|
export declare function runContractVerifyCommand(argv: string[], io?: {
|
|
1870
2220
|
out: Output;
|
|
1871
2221
|
err: Output;
|
|
1872
2222
|
}): Promise<number>;
|
|
2223
|
+
//#endregion
|
|
2224
|
+
//#region src/commands/release-trend.d.ts
|
|
1873
2225
|
export declare function runTrustTrendCommand(argv: string[], io?: {
|
|
1874
2226
|
out: Output;
|
|
1875
2227
|
err: Output;
|
|
1876
2228
|
}): Promise<number>;
|
|
1877
|
-
export declare function runEvidenceGraphCommand(argv: string[], io?: {
|
|
1878
|
-
out: Output;
|
|
1879
|
-
err: Output;
|
|
1880
|
-
}): Promise<number>;
|
|
1881
|
-
export declare function runCIIntegrityCommand(argv: string[], io?: {
|
|
1882
|
-
out: Output;
|
|
1883
|
-
err: Output;
|
|
1884
|
-
}): Promise<number>;
|
|
1885
2229
|
//#endregion
|
|
1886
2230
|
//#region src/cli.d.ts
|
|
1887
2231
|
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>;
|
|
@@ -1897,7 +2241,7 @@ export declare const MAX_DURATION_MS = 3600000;
|
|
|
1897
2241
|
export declare function parseArgs(argv: string[], onError?: (detail: UsageErrorDetail) => void): CliArgs | null;
|
|
1898
2242
|
/** Hand-rolled Levenshtein distance (plan M2: no new dependencies). */
|
|
1899
2243
|
export declare function levenshtein(a: string, b: string): number;
|
|
1900
|
-
/** Nearest known flags within distance
|
|
2244
|
+
/** Nearest known flags within distance ≤ 2, nearest first. */
|
|
1901
2245
|
export declare function nearestFlags(flag: string, max?: number): string[];
|
|
1902
2246
|
/**
|
|
1903
2247
|
* Friendly usage error (plan M2, exit 10 preserved): nearest-flag
|
|
@@ -1932,6 +2276,8 @@ export declare function validateScanTarget(target: string, err: Output): number
|
|
|
1932
2276
|
* zero findings is exactly the case that turns a bug into a green build.
|
|
1933
2277
|
*/
|
|
1934
2278
|
export declare function exitForFindings(findings: readonly Finding[], gate: "advisory" | "error" | "warning"): number;
|
|
2279
|
+
/** `mjolnir ci --help`, rendered from the table so it cannot drift. */
|
|
2280
|
+
export declare function renderCiSubcommandHelp(): string;
|
|
1935
2281
|
/** Testable `ci install` handler. Returns the process exit code. */
|
|
1936
2282
|
export declare function runCiInstall(argv: string[], io?: {
|
|
1937
2283
|
out: Output;
|