mjolnir-qa 6.0.0 → 6.0.1

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.
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { createInterface } from "node:readline";
3
3
  import { Buffer as Buffer$1 } from "node:buffer";
4
4
  import { createHash, randomBytes } from "node:crypto";
5
- import { closeSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, statSync, unlinkSync, writeSync } from "node:fs";
5
+ import { closeSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeSync } from "node:fs";
6
6
  import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
7
7
  import { execFileSync } from "node:child_process";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -12,7 +12,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import { Language, Parser } from "web-tree-sitter";
13
13
  import { parse } from "yaml";
14
14
  import { inflateRawSync } from "node:zlib";
15
- //#region src/types.ts
15
+ // src/types.ts
16
16
  /** Severity ladder. Order matters for sorting and gating. */
17
17
  const SEVERITY_ORDER = [
18
18
  "error",
@@ -99,8 +99,7 @@ const DEDUCTIONS = {
99
99
  warning: 3,
100
100
  info: 1
101
101
  };
102
- //#endregion
103
- //#region src/engine/completion.ts
102
+ // src/engine/completion.ts
104
103
  function deriveCompletion(input) {
105
104
  const truncationReasons = [...new Set(input.truncationReasons)].sort();
106
105
  const degradations = [...input.degradations ?? []].sort((a, b) => a.reason < b.reason ? -1 : a.reason > b.reason ? 1 : 0);
@@ -142,8 +141,7 @@ function deriveCompletion(input) {
142
141
  }
143
142
  };
144
143
  }
145
- //#endregion
146
- //#region src/engine/degradation-ledger.ts
144
+ // src/engine/degradation-ledger.ts
147
145
  /**
148
146
  * Append-only. Records are never edited and never removed by a recording
149
147
  * site; a reader takes a window, and the window is defined by index, so
@@ -179,8 +177,7 @@ function summarizeDegradations(window) {
179
177
  count
180
178
  })).sort((a, b) => a.reason < b.reason ? -1 : a.reason > b.reason ? 1 : 0);
181
179
  }
182
- //#endregion
183
- //#region src/engine/runtime-corroboration.ts
180
+ // src/engine/runtime-corroboration.ts
184
181
  /**
185
182
  * Stamp runtime corroboration + trust levels onto findings (mutates in
186
183
  * place, the same contract as stampEvidenceLevels). Returns the number
@@ -262,8 +259,7 @@ function deriveTrustLevel(finding, corroboration) {
262
259
  if (corroboration.level === "test") return "L4";
263
260
  return "L3";
264
261
  }
265
- //#endregion
266
- //#region src/rules/measured-fp.generated.ts
262
+ // src/rules/measured-fp.generated.ts
267
263
  const MEASURED_FP = {
268
264
  "QA-CI-001": {
269
265
  fpRate: .105,
@@ -777,8 +773,7 @@ const MEASURED_FP = {
777
773
  ciHigh: .4343
778
774
  }
779
775
  };
780
- //#endregion
781
- //#region src/engine/trust-summary.ts
776
+ // src/engine/trust-summary.ts
782
777
  /**
783
778
  * Trust level of the summary — the BEST trust level any finding in the
784
779
  * scan reached. Static-only findings settle at L2 (deriveTrustLevel's
@@ -902,8 +897,7 @@ function buildTrustSummary(result, declarationsByFile) {
902
897
  ceilingReasons: reasons
903
898
  };
904
899
  }
905
- //#endregion
906
- //#region src/governance/run-mode.ts
900
+ // src/governance/run-mode.ts
907
901
  /**
908
902
  * Which mode a run is in, and how the other one is reached.
909
903
  *
@@ -954,8 +948,7 @@ function resolveRunMode(input) {
954
948
  if (declared === "local-only" || declared === "hosted") return declared;
955
949
  throw new Error(`${HOSTED_MODE_FLAG}=${String(declared)} is not a run mode. Expected "hosted" or "local-only". Falling back to a default here would let a mistyped flag look like a deliberate choice.`);
956
950
  }
957
- //#endregion
958
- //#region src/engine/run-identity.ts
951
+ // src/engine/run-identity.ts
959
952
  /**
960
953
  * Run Identity (product-gap master plan §7, plan 1789009691197 R4c) —
961
954
  * the deterministic binding that anchors every trust claim to its
@@ -1084,8 +1077,7 @@ function buildEvidenceGraph(parts) {
1084
1077
  } } : {}
1085
1078
  };
1086
1079
  }
1087
- //#endregion
1088
- //#region src/engine/file-executor.ts
1080
+ // src/engine/file-executor.ts
1089
1081
  function cacheHitOutcome(job) {
1090
1082
  return {
1091
1083
  path: job.path,
@@ -1176,8 +1168,7 @@ function createExecutor(name, concurrency, runOne) {
1176
1168
  execute: runOne
1177
1169
  };
1178
1170
  }
1179
- //#endregion
1180
- //#region src/scope/git-resolve.ts
1171
+ // src/scope/git-resolve.ts
1181
1172
  /**
1182
1173
  * Absolute-path git resolution (audit S1).
1183
1174
  *
@@ -1252,8 +1243,7 @@ function runGit(root, args) {
1252
1243
  return null;
1253
1244
  }
1254
1245
  }
1255
- //#endregion
1256
- //#region src/lib/safe-json.ts
1246
+ // src/lib/safe-json.ts
1257
1247
  /**
1258
1248
  * Reusable safe-JSON parse-and-validate helper.
1259
1249
  *
@@ -1290,8 +1280,7 @@ function isRecord(v) {
1290
1280
  return false;
1291
1281
  }
1292
1282
  }
1293
- //#endregion
1294
- //#region src/engine/candidate-binding.ts
1283
+ // src/engine/candidate-binding.ts
1295
1284
  /**
1296
1285
  * Candidate binding (plan V5-010).
1297
1286
  *
@@ -1424,18 +1413,17 @@ function readCandidateBinding(root, manifestPath = CANDIDATE_MANIFEST_PATH) {
1424
1413
  return null;
1425
1414
  }
1426
1415
  }
1427
- //#endregion
1428
- //#region src/engine/version.ts
1416
+ // src/engine/version.ts
1429
1417
  /**
1430
1418
  * Engine version, as a leaf module: `runIdentity` (R4c) needs the engine
1431
1419
  * version inside scan-pipeline WITHOUT importing cli.ts (a cycle — cli
1432
1420
  * imports the pipeline). The literal follows the same discipline as
1433
1421
  * CLI_VERSION and SARIF's driver.version: kept in sync on release by
1434
- * scripts/archive/sync-sarif-version.cjs (archived; superseded by the release
1435
- * workflow's own version stamping) and guarded by the version-consistency
1422
+ * `npm run version:surface:sync` (this file is an identity surface) and
1423
+ * guarded by the version-consistency
1436
1424
  * spec. cli.ts re-exports this as CLI_VERSION.
1437
1425
  */
1438
- const ENGINE_VERSION = "6.0.0";
1426
+ const ENGINE_VERSION = "6.0.1";
1439
1427
  resolveBuildId();
1440
1428
  /**
1441
1429
  * The decision, with the process boundary already behind `runGit`.
@@ -1475,16 +1463,7 @@ function resolveBuildId() {
1475
1463
  };
1476
1464
  return buildIdFrom(runGit);
1477
1465
  }
1478
- //#endregion
1479
- //#region src/engine/contract-versions.ts
1480
- /**
1481
- * Versioned contract registry (VERSION-001).
1482
- *
1483
- * Single source of truth for every version constant the engine exposes
1484
- * to consumers (run identity, evidence graph, machine contract).
1485
- * Constants are `as const` — literal types flow into the identity
1486
- * surface without widening.
1487
- */
1466
+ // src/engine/contract-versions.ts
1488
1467
  const TRUST_MODEL_VERSION = "1.0.0";
1489
1468
  /**
1490
1469
  * 2.0.0 (ADR 0014): the core tier carries an evidence FLOOR. A core rule's
@@ -1496,8 +1475,7 @@ const TRUST_MODEL_VERSION = "1.0.0";
1496
1475
  */
1497
1476
  const SCORING_MODEL_VERSION = "2.0.0";
1498
1477
  const FRAMEWORK_SUPPORT_MATRIX_VERSION = "1.0.0";
1499
- //#endregion
1500
- //#region src/discovery/evidence-discovery.ts
1478
+ // src/discovery/evidence-discovery.ts
1501
1479
  /**
1502
1480
  * Zero-config evidence discovery (Mega MVP Master Plan v3.1 §26 WI-11).
1503
1481
  *
@@ -1589,8 +1567,7 @@ function discoverEvidenceCandidates(scanRoot) {
1589
1567
  }
1590
1568
  return out.sort((a, b) => a.depth - b.depth || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
1591
1569
  }
1592
- //#endregion
1593
- //#region src/discovery/workspace.ts
1570
+ // src/discovery/workspace.ts
1594
1571
  /**
1595
1572
  * Repository discovery (Sprint-Plan W1-03).
1596
1573
  * Finds project root, parses package.json, detects npm/yarn/pnpm workspaces.
@@ -1637,8 +1614,7 @@ function basename$1(p) {
1637
1614
  const parts = p.split(/[\\/]/);
1638
1615
  return parts[parts.length - 1];
1639
1616
  }
1640
- //#endregion
1641
- //#region src/lib/fs-bounded.ts
1617
+ // src/lib/fs-bounded.ts
1642
1618
  function closeQuiet(fd) {
1643
1619
  try {
1644
1620
  closeSync(fd);
@@ -1690,8 +1666,7 @@ function readFileBounded(path, maxBytes) {
1690
1666
  if (fd !== void 0) closeQuiet(fd);
1691
1667
  }
1692
1668
  }
1693
- //#endregion
1694
- //#region src/config/config-schema.ts
1669
+ // src/config/config-schema.ts
1695
1670
  const VALID_GATES = /* @__PURE__ */ new Set([
1696
1671
  "advisory",
1697
1672
  "error",
@@ -1761,8 +1736,7 @@ function validateConfigSchema(config) {
1761
1736
  errors
1762
1737
  };
1763
1738
  }
1764
- //#endregion
1765
- //#region src/config/config.ts
1739
+ // src/config/config.ts
1766
1740
  /**
1767
1741
  * Config file support (Sprint-Plan W7, Product-MVP §27 + GAP-F).
1768
1742
  * Zero-config preserved: config presence never changes detection
@@ -1896,8 +1870,7 @@ function applySeverityOverrides(findings, cfg) {
1896
1870
  if (override && SEVERITY_ORDER.includes(override)) f.severity = override;
1897
1871
  }
1898
1872
  }
1899
- //#endregion
1900
- //#region src/lib/compare.ts
1873
+ // src/lib/compare.ts
1901
1874
  /**
1902
1875
  * Order that does not depend on who is asking.
1903
1876
  *
@@ -1943,8 +1916,7 @@ function compareCodePoints(a, b) {
1943
1916
  function compareLocalized(locale) {
1944
1917
  return (a, b) => a.localeCompare(b, locale);
1945
1918
  }
1946
- //#endregion
1947
- //#region src/lib/glob.ts
1919
+ // src/lib/glob.ts
1948
1920
  /**
1949
1921
  * The shared segment compiler: a glob to a regex BODY, with no anchors.
1950
1922
  *
@@ -1998,8 +1970,7 @@ function anyDepthGlobRegExp(glob) {
1998
1970
  function pathMatchesGlob(path, glob) {
1999
1971
  return anchoredGlobRegExp(glob).test(path.replaceAll("\\", "/"));
2000
1972
  }
2001
- //#endregion
2002
- //#region src/discovery/ignores.ts
1973
+ // src/discovery/ignores.ts
2003
1974
  /**
2004
1975
  * Ignore resolution (Product-MVP.txt R1 — Tier-1 launch blocker).
2005
1976
  *
@@ -2155,8 +2126,7 @@ function isLintFixtureDir(absoluteDir) {
2155
2126
  return false;
2156
2127
  }
2157
2128
  }
2158
- //#endregion
2159
- //#region src/discovery/shared-walk.ts
2129
+ // src/discovery/shared-walk.ts
2160
2130
  /**
2161
2131
  * Shared directory walker (audit P-2).
2162
2132
  *
@@ -2219,8 +2189,7 @@ function sharedWalk(options) {
2219
2189
  };
2220
2190
  walk(options.root);
2221
2191
  }
2222
- //#endregion
2223
- //#region src/discovery/frameworks.ts
2192
+ // src/discovery/frameworks.ts
2224
2193
  /**
2225
2194
  * Framework detection via config resolution (Sprint-Plan W3-01, R8).
2226
2195
  *
@@ -2303,8 +2272,7 @@ function detectFrameworks(ws) {
2303
2272
  unknown: false
2304
2273
  };
2305
2274
  }
2306
- //#endregion
2307
- //#region src/engine/ts-ast.ts
2275
+ // src/engine/ts-ast.ts
2308
2276
  /**
2309
2277
  * AST precision layer (Upgrade-Plan-v3 Phase 3).
2310
2278
  *
@@ -2503,8 +2471,7 @@ function getCodeOnlyText(file) {
2503
2471
  function resetTsMorphProject() {
2504
2472
  looseProject = null;
2505
2473
  }
2506
- //#endregion
2507
- //#region src/engine/code-text.ts
2474
+ // src/engine/code-text.ts
2508
2475
  /**
2509
2476
  * Code-only text masking (Phase 1 — Tempering Plan).
2510
2477
  *
@@ -2864,8 +2831,7 @@ function getCodeTextForCounting(text, language) {
2864
2831
  function blankRange(chars, start, end) {
2865
2832
  for (let i = start; i < end && i < chars.length; i++) if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
2866
2833
  }
2867
- //#endregion
2868
- //#region src/engine/adapter.ts
2834
+ // src/engine/adapter.ts
2869
2835
  /**
2870
2836
  * The §15.1 framework filter, shared by every language adapter: a rule
2871
2837
  * that declares `frameworks` runs on a file only when the file carries
@@ -2879,8 +2845,7 @@ function frameworkFilterApplies(rule, file) {
2879
2845
  if (tags === void 0 || tags.length === 0) return true;
2880
2846
  return rule.frameworks.some((f) => tags.includes(f));
2881
2847
  }
2882
- //#endregion
2883
- //#region src/engine/shared-run-rules.ts
2848
+ // src/engine/shared-run-rules.ts
2884
2849
  /**
2885
2850
  * Run ONE rule with crash isolation, and return what it produced.
2886
2851
  *
@@ -2917,8 +2882,7 @@ function runRuleIsolated(rule, file, onCrash) {
2917
2882
  return null;
2918
2883
  }
2919
2884
  }
2920
- //#endregion
2921
- //#region src/adapters/typescript.ts
2885
+ // src/adapters/typescript.ts
2922
2886
  /**
2923
2887
  * TypeScript/JavaScript adapter (R1).
2924
2888
  * Wraps the proven regex-based engine behind the LanguageAdapter
@@ -3171,8 +3135,7 @@ function loadWorkspaceShim(root) {
3171
3135
  return null;
3172
3136
  }
3173
3137
  }
3174
- //#endregion
3175
- //#region src/engine/tree-sitter-ast.ts
3138
+ // src/engine/tree-sitter-ast.ts
3176
3139
  /**
3177
3140
  * Tree-sitter WASM AST layer for Java and C# — Sprint 8 Task 36
3178
3141
  * (Master-Stabilization-Plan.md).
@@ -3391,8 +3354,7 @@ async function releaseTreeSitterResources() {
3391
3354
  } catch {}
3392
3355
  }
3393
3356
  }
3394
- //#endregion
3395
- //#region src/adapters/python.ts
3357
+ // src/adapters/python.ts
3396
3358
  /**
3397
3359
  * Python/pytest adapter (Upgrade-Plan-v2 R2).
3398
3360
  *
@@ -3550,8 +3512,7 @@ const pythonAdapter = {
3550
3512
  function readText$2(p) {
3551
3513
  return readFileSync(p, "utf8");
3552
3514
  }
3553
- //#endregion
3554
- //#region src/adapters/java.ts
3515
+ // src/adapters/java.ts
3555
3516
  /**
3556
3517
  * Java/JUnit adapter (Upgrade-Plan-v3 Phase 4).
3557
3518
  *
@@ -3717,8 +3678,7 @@ function javaFileTags(file) {
3717
3678
  function readText$1(path) {
3718
3679
  return readFileSync(path, "utf8");
3719
3680
  }
3720
- //#endregion
3721
- //#region src/adapters/csharp.ts
3681
+ // src/adapters/csharp.ts
3722
3682
  /**
3723
3683
  * C#/.NET adapter (Upgrade-Plan-v3 Phase 5).
3724
3684
  *
@@ -3874,8 +3834,7 @@ function csharpFileTags(file) {
3874
3834
  function readText(path) {
3875
3835
  return readFileSync(path, "utf8");
3876
3836
  }
3877
- //#endregion
3878
- //#region src/discovery/yaml-guards.ts
3837
+ // src/discovery/yaml-guards.ts
3879
3838
  /**
3880
3839
  * Shared hostile-YAML guards (product-gap master plan P3 common law —
3881
3840
  * `azure-pipelines.yml` detection rides the same YAML machinery as the
@@ -3944,8 +3903,7 @@ function enforceDepthCap(value, max, depthMessage = GENERIC_LABELS.depthMessage)
3944
3903
  });
3945
3904
  }
3946
3905
  }
3947
- //#endregion
3948
- //#region src/discovery/workflow-parser.ts
3906
+ // src/discovery/workflow-parser.ts
3949
3907
  /**
3950
3908
  * Safe GitHub Actions workflow parser (Phase 0.3, W4-03 debt).
3951
3909
  *
@@ -3991,8 +3949,7 @@ function parseWorkflow(text) {
3991
3949
  }
3992
3950
  return { jobs };
3993
3951
  }
3994
- //#endregion
3995
- //#region src/adapters/github-actions.ts
3952
+ // src/adapters/github-actions.ts
3996
3953
  /**
3997
3954
  * GitHub Actions adapter (R1).
3998
3955
  * Owns workflow discovery and safe-YAML parsing; CI rules consume the
@@ -4077,8 +4034,7 @@ const githubActionsAdapter = {
4077
4034
  };
4078
4035
  var WorkflowParseSkipped = class extends Error {};
4079
4036
  const LIMITS_MAX_BYTES$2 = 1048576;
4080
- //#endregion
4081
- //#region src/discovery/azure-pipeline-parser.ts
4037
+ // src/discovery/azure-pipeline-parser.ts
4082
4038
  /**
4083
4039
  * Safe Azure DevOps pipeline parser (product-gap master plan P3b).
4084
4040
  *
@@ -4312,8 +4268,7 @@ function isAzurePipelineFixture(normalizedPath) {
4312
4268
  const base = normalizedPath.replaceAll("\\", "/").split("/").pop() ?? "";
4313
4269
  return AZURE_PIPELINE_FILENAMES_SET.has(base);
4314
4270
  }
4315
- //#endregion
4316
- //#region src/adapters/azure-pipelines.ts
4271
+ // src/adapters/azure-pipelines.ts
4317
4272
  /**
4318
4273
  * Azure DevOps pipeline adapter (product-gap master plan P3b).
4319
4274
  * Owns `azure-pipelines.yml` discovery (repo root, the platform's
@@ -4393,8 +4348,7 @@ const azurePipelinesAdapter = {
4393
4348
  };
4394
4349
  var AzureParseSkipped = class extends Error {};
4395
4350
  const LIMITS_MAX_BYTES$1 = 1048576;
4396
- //#endregion
4397
- //#region src/adapters/jenkins.ts
4351
+ // src/adapters/jenkins.ts
4398
4352
  /**
4399
4353
  * Jenkins adapter (product-gap master plan P3c).
4400
4354
  * The root `Jenkinsfile` is a TEXT-target file kind: declarative and
@@ -4468,8 +4422,7 @@ const jenkinsAdapter = {
4468
4422
  }
4469
4423
  };
4470
4424
  const LIMITS_MAX_BYTES = 1048576;
4471
- //#endregion
4472
- //#region src/discovery/scan-adapters.ts
4425
+ // src/discovery/scan-adapters.ts
4473
4426
  /**
4474
4427
  * The canonical adapter registry (audits H-6/H-9).
4475
4428
  *
@@ -4606,8 +4559,7 @@ function isInsideSkippedDir(root, absPath, skipNames) {
4606
4559
  return relative(root, absPath).replaceAll("\\", "/").split("/").slice(0, -1).some((seg) => skipNames.includes(seg));
4607
4560
  }
4608
4561
  typescriptAdapter.testFileGlobs, pythonAdapter.testFileGlobs, javaAdapter.testFileGlobs, csharpAdapter.testFileGlobs, githubActionsAdapter.testFileGlobs, azurePipelinesAdapter.testFileGlobs, jenkinsAdapter.testFileGlobs;
4609
- //#endregion
4610
- //#region src/scope/changed.ts
4562
+ // src/scope/changed.ts
4611
4563
  /**
4612
4564
  * Changed-scope engine (Sprint-Plan W6, Product-MVP §9 `--scope changed`).
4613
4565
  *
@@ -4850,8 +4802,7 @@ function computeStagedFiles(root) {
4850
4802
  if (raw === null) return null;
4851
4803
  return raw.split("\0").filter((s) => s.length > 0);
4852
4804
  }
4853
- //#endregion
4854
- //#region src/rules/rule.ts
4805
+ // src/rules/rule.ts
4855
4806
  /**
4856
4807
  * Invoke an `astQuery` hook with the mandatory-fallback contract: no
4857
4808
  * hook or no AST on the context resolves to `undefined` (→ regex
@@ -4864,8 +4815,7 @@ function tryAstQuery(hook, ctx) {
4864
4815
  function defineRule(rule) {
4865
4816
  return rule;
4866
4817
  }
4867
- //#endregion
4868
- //#region src/rules/shared/positions.ts
4818
+ // src/rules/shared/positions.ts
4869
4819
  /**
4870
4820
  * Shared position helpers (Phase 6 — Tempering Plan).
4871
4821
  * One lineAt, one colAt, one matchBrace. The other 79 copies are dead.
@@ -4910,8 +4860,7 @@ function lineAt(text, index) {
4910
4860
  function colAt(text, index) {
4911
4861
  return index - (index <= 0 ? -1 : text.lastIndexOf("\n", index - 1));
4912
4862
  }
4913
- //#endregion
4914
- //#region src/rules/test/qa-test-001-focused-test.ts
4863
+ // src/rules/test/qa-test-001-focused-test.ts
4915
4864
  /**
4916
4865
  * QA-TEST-001 — Focused test committed (`fit`/`fdescribe`/`.only`).
4917
4866
  * Severity: error · Confidence: high · deterministic-defect
@@ -4977,8 +4926,7 @@ const focusedTestCommitted = defineRule({
4977
4926
  return findings;
4978
4927
  }
4979
4928
  });
4980
- //#endregion
4981
- //#region src/rules/test/qa-test-002-skipped-test.ts
4929
+ // src/rules/test/qa-test-002-skipped-test.ts
4982
4930
  /**
4983
4931
  * QA-TEST-002 — Skipped test (`it.skip`, `xit`, `test.skip`, `describe.skip`).
4984
4932
  * Severity: warning · Confidence: high · deterministic-defect
@@ -5034,8 +4982,7 @@ const skippedTest = defineRule({
5034
4982
  return findings;
5035
4983
  }
5036
4984
  });
5037
- //#endregion
5038
- //#region src/rules/shared/masking.ts
4985
+ // src/rules/shared/masking.ts
5039
4986
  /** True when the offset was blanked in codeText (inside a string or comment). */
5040
4987
  function isMasked(ctx, index) {
5041
4988
  const mask = ctx.codeText;
@@ -5096,8 +5043,7 @@ function isInsideEmbeddedCode(ctx, index) {
5096
5043
  const hasCallSyntax = /\w\s*\(/.test(inner);
5097
5044
  return hasNestedQuote && hasCallSyntax;
5098
5045
  }
5099
- //#endregion
5100
- //#region src/rules/test/qa-test-003-no-assertions.ts
5046
+ // src/rules/test/qa-test-003-no-assertions.ts
5101
5047
  /**
5102
5048
  * QA-TEST-003 — Test with no assertions.
5103
5049
  * Severity: error · Confidence: high · deterministic-defect
@@ -5175,8 +5121,7 @@ function matchBrace$2(text, open) {
5175
5121
  }
5176
5122
  return -1;
5177
5123
  }
5178
- //#endregion
5179
- //#region src/rules/test/qa-test-004-hard-sleep.ts
5124
+ // src/rules/test/qa-test-004-hard-sleep.ts
5180
5125
  /**
5181
5126
  * QA-TEST-004 — Hard sleep (`sleep()`, `waitForTimeout()`, `setTimeout` as wait).
5182
5127
  * Severity: warning · Confidence: high · deterministic-defect
@@ -5241,8 +5186,7 @@ const hardSleep = defineRule({
5241
5186
  return findings;
5242
5187
  }
5243
5188
  });
5244
- //#endregion
5245
- //#region src/rules/test/qa-test-006-retry-abuse.ts
5189
+ // src/rules/test/qa-test-006-retry-abuse.ts
5246
5190
  /**
5247
5191
  * QA-TEST-006 — Retry abuse in test config.
5248
5192
  * Severity: warning · Confidence: high · deterministic-defect
@@ -5308,8 +5252,7 @@ const retryAbuse = defineRule({
5308
5252
  return findings;
5309
5253
  }
5310
5254
  });
5311
- //#endregion
5312
- //#region src/rules/test/qa-test-010-empty-body.ts
5255
+ // src/rules/test/qa-test-010-empty-body.ts
5313
5256
  /**
5314
5257
  * QA-TEST-010 — Empty test body.
5315
5258
  * Severity: error · Confidence: high · deterministic-defect
@@ -5364,8 +5307,7 @@ const emptyTestBody = defineRule({
5364
5307
  return findings;
5365
5308
  }
5366
5309
  });
5367
- //#endregion
5368
- //#region src/rules/quality/qa-tqual-002-tautological.ts
5310
+ // src/rules/quality/qa-tqual-002-tautological.ts
5369
5311
  /**
5370
5312
  * QA-TQUAL-002 — Tautological assertion.
5371
5313
  * Severity: error · Confidence: high · deterministic-defect
@@ -5442,8 +5384,7 @@ const tautologicalAssertion = defineRule({
5442
5384
  return findings;
5443
5385
  }
5444
5386
  });
5445
- //#endregion
5446
- //#region src/rules/quality/qa-tqual-009-promise-assertion.ts
5387
+ // src/rules/quality/qa-tqual-009-promise-assertion.ts
5447
5388
  /**
5448
5389
  * QA-TQUAL-009 — Unawaited assertion inside a promise chain.
5449
5390
  * Severity: error · Confidence: high · deterministic-defect
@@ -5613,8 +5554,7 @@ function matchParen$2(text, open) {
5613
5554
  }
5614
5555
  return -1;
5615
5556
  }
5616
- //#endregion
5617
- //#region src/rules/quality/qa-tqual-011-commented-out.ts
5557
+ // src/rules/quality/qa-tqual-011-commented-out.ts
5618
5558
  /**
5619
5559
  * QA-TQUAL-011 — Commented-out tests.
5620
5560
  * Severity: warning · Confidence: high · deterministic-defect
@@ -5727,8 +5667,7 @@ function commentRanges(ctx) {
5727
5667
  }
5728
5668
  return ranges;
5729
5669
  }
5730
- //#endregion
5731
- //#region src/rules/playwright/qa-pw-002-unawaited-assertion.ts
5670
+ // src/rules/playwright/qa-pw-002-unawaited-assertion.ts
5732
5671
  /**
5733
5672
  * QA-PW-002 — Missing `await` on a Playwright locator assertion.
5734
5673
  * Severity: error · Confidence: high · deterministic-defect
@@ -5873,8 +5812,7 @@ function runRegexFallback(text, path) {
5873
5812
  });
5874
5813
  return findings;
5875
5814
  }
5876
- //#endregion
5877
- //#region src/rules/playwright/qa-pw-003-debug-artifacts.ts
5815
+ // src/rules/playwright/qa-pw-003-debug-artifacts.ts
5878
5816
  /**
5879
5817
  * QA-PW-003 — Committed `page.pause()` or `test.only()` in e2e specs.
5880
5818
  * Severity: error · Confidence: high · deterministic-defect
@@ -5926,8 +5864,7 @@ const committedDebugArtifacts = defineRule({
5926
5864
  return findings;
5927
5865
  }
5928
5866
  });
5929
- //#endregion
5930
- //#region src/rules/playwright/qa-pw-004-brittle-selectors.ts
5867
+ // src/rules/playwright/qa-pw-004-brittle-selectors.ts
5931
5868
  /**
5932
5869
  * QA-PW-004 — Brittle CSS/XPath selectors vs role-based locators.
5933
5870
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -6019,8 +5956,7 @@ function stripComments(ctx) {
6019
5956
  } catch {}
6020
5957
  return ctx.text.replace(/\/\/[^\n]*/g, (cm) => " ".repeat(cm.length));
6021
5958
  }
6022
- //#endregion
6023
- //#region src/rules/ci/verification-gate.ts
5959
+ // src/rules/ci/verification-gate.ts
6024
5960
  /**
6025
5961
  * Shared "is this shell command a verification gate?" matcher for the QA-CI
6026
5962
  * rules. A gate is a command whose failure MUST fail the job — test runners,
@@ -6060,8 +5996,7 @@ const VERIFICATION_GATE_RE = new RegExp([
6060
5996
  function looksLikeVerificationGate(cmd) {
6061
5997
  return VERIFICATION_GATE_RE.test(cmd);
6062
5998
  }
6063
- //#endregion
6064
- //#region src/rules/ci/azure-gates.ts
5999
+ // src/rules/ci/azure-gates.ts
6065
6000
  /** Discriminator: the Azure parser is the only one emitting `platform`. */
6066
6001
  function isAzurePipelineDoc(ast) {
6067
6002
  return typeof ast === "object" && ast !== null && ast.platform === "azure-pipelines";
@@ -6145,8 +6080,7 @@ function locateAzureJobKey(text, job, keyName) {
6145
6080
  if (m) return lineAt(text, m.index);
6146
6081
  return 1;
6147
6082
  }
6148
- //#endregion
6149
- //#region src/rules/ci/qa-ci-001-continue-on-error.ts
6083
+ // src/rules/ci/qa-ci-001-continue-on-error.ts
6150
6084
  /**
6151
6085
  * QA-CI-001 — `continue-on-error: true` masks a failing job.
6152
6086
  * Severity: error · Confidence: high · deterministic-defect
@@ -6374,8 +6308,7 @@ function describeAzureStep(step) {
6374
6308
  if (step.task) return step.task;
6375
6309
  return "step";
6376
6310
  }
6377
- //#endregion
6378
- //#region src/rules/ci/qa-ci-002-swallowed-exit.ts
6311
+ // src/rules/ci/qa-ci-002-swallowed-exit.ts
6379
6312
  /**
6380
6313
  * QA-CI-002 — `|| true` swallows a command's exit code.
6381
6314
  * Severity: error · Confidence: high · deterministic-defect
@@ -6430,8 +6363,7 @@ const swallowedExitCode = defineRule({
6430
6363
  return findings;
6431
6364
  }
6432
6365
  });
6433
- //#endregion
6434
- //#region src/rules/ci/qa-ci-007-retry-masking.ts
6366
+ // src/rules/ci/qa-ci-007-retry-masking.ts
6435
6367
  /**
6436
6368
  * QA-CI-007 — Retry masking test failures.
6437
6369
  * Severity: warning · Confidence: high · deterministic-defect
@@ -6582,8 +6514,7 @@ function azureArm$1(ctx, doc, findings) {
6582
6514
  });
6583
6515
  }
6584
6516
  }
6585
- //#endregion
6586
- //#region src/rules/ci/qa-ci-005-report-never-generated.ts
6517
+ // src/rules/ci/qa-ci-005-report-never-generated.ts
6587
6518
  /**
6588
6519
  * QA-CI-005 — Report consumed but never generated.
6589
6520
  * Severity: error · Confidence: high · deterministic-defect
@@ -6741,8 +6672,7 @@ function findJobConsumerLine(text, jobName, consumerRe) {
6741
6672
  const anywhere = new RegExp(consumerRe.source, "i").exec(text);
6742
6673
  return anywhere ? lineAt(text, anywhere.index) : 1;
6743
6674
  }
6744
- //#endregion
6745
- //#region src/rules/ci/jenkins-gates.ts
6675
+ // src/rules/ci/jenkins-gates.ts
6746
6676
  /**
6747
6677
  * Jenkinsfile block scanner for the QA-CI Jenkins arms (product-gap
6748
6678
  * master plan P3c). Jenkinsfiles are Groovy text — "no new language
@@ -6988,8 +6918,7 @@ function textIsVerificationGate(text) {
6988
6918
  function lineOfOffset(text, offset) {
6989
6919
  return lineAt(text, offset);
6990
6920
  }
6991
- //#endregion
6992
- //#region src/rules/ci/qa-ci-008-always-success.ts
6921
+ // src/rules/ci/qa-ci-008-always-success.ts
6993
6922
  /**
6994
6923
  * QA-CI-008 — Always-success step at the end of a job.
6995
6924
  * Severity: error · Confidence: high · deterministic-defect
@@ -7163,8 +7092,7 @@ function jenkinsArm$1(ctx, findings) {
7163
7092
  });
7164
7093
  }
7165
7094
  }
7166
- //#endregion
7167
- //#region src/rules/ci/qa-ci-009-exit-code.ts
7095
+ // src/rules/ci/qa-ci-009-exit-code.ts
7168
7096
  /**
7169
7097
  * QA-CI-009 — Test command does not propagate exit code.
7170
7098
  * Severity: error · Confidence: high · deterministic-defect
@@ -7299,8 +7227,7 @@ function jenkinsArm(ctx, findings) {
7299
7227
  });
7300
7228
  }
7301
7229
  }
7302
- //#endregion
7303
- //#region src/rules/ci/qa-ci-010-non-blocking.ts
7230
+ // src/rules/ci/qa-ci-010-non-blocking.ts
7304
7231
  /**
7305
7232
  * QA-CI-010 — Required test job is non-blocking for branch protection.
7306
7233
  * Severity: error · Confidence: medium · deterministic-defect
@@ -7383,8 +7310,7 @@ function findLine(text, re) {
7383
7310
  function escapeRe(s) {
7384
7311
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7385
7312
  }
7386
- //#endregion
7387
- //#region src/rules/ci/qa-ci-013-can-never-fail.ts
7313
+ // src/rules/ci/qa-ci-013-can-never-fail.ts
7388
7314
  /**
7389
7315
  * QA-CI-013 — Verification stage/job conditioned so it can never fail the
7390
7316
  * pipeline (product-gap master plan P3b).
@@ -7488,8 +7414,7 @@ const canNeverFailGate = defineRule({
7488
7414
  function jobHasGate(job) {
7489
7415
  return (job.steps ?? []).some((s) => s !== void 0 && stepIsVerificationGate$1(s));
7490
7416
  }
7491
- //#endregion
7492
- //#region src/rules/ci/qa-ci-014-swallowed-verification.ts
7417
+ // src/rules/ci/qa-ci-014-swallowed-verification.ts
7493
7418
  /**
7494
7419
  * QA-CI-014 — try/catch swallowing a verification-stage failure without
7495
7420
  * rethrow or failure-marking (product-gap master plan P3c).
@@ -7546,8 +7471,7 @@ const swallowedVerificationFailure = defineRule({
7546
7471
  return findings;
7547
7472
  }
7548
7473
  });
7549
- //#endregion
7550
- //#region src/rules/python/qa-py-003-no-assertions.ts
7474
+ // src/rules/python/qa-py-003-no-assertions.ts
7551
7475
  /**
7552
7476
  * QA-PY-003 — Test function with no assertions.
7553
7477
  * Severity: error · Confidence: high · deterministic-defect
@@ -7668,8 +7592,7 @@ function extractBlock$1(text, afterColon) {
7668
7592
  const lineEnd = rest.indexOf("\n", firstIdx);
7669
7593
  return lineEnd === -1 ? rest : rest.slice(0, lineEnd);
7670
7594
  }
7671
- //#endregion
7672
- //#region src/rules/python/qa-py-005-hard-sleep.ts
7595
+ // src/rules/python/qa-py-005-hard-sleep.ts
7673
7596
  /**
7674
7597
  * QA-PY-005 — time.sleep() in tests.
7675
7598
  * Severity: warning · Confidence: high · deterministic-defect
@@ -7716,8 +7639,7 @@ const pyHardSleep = defineRule({
7716
7639
  return findings;
7717
7640
  }
7718
7641
  });
7719
- //#endregion
7720
- //#region src/rules/python/qa-py-002-skipped-test.ts
7642
+ // src/rules/python/qa-py-002-skipped-test.ts
7721
7643
  /**
7722
7644
  * QA-PY-002 — Skipped test (`@pytest.mark.skip`, `xfail` non-strict).
7723
7645
  * Severity: warning · Confidence: high · deterministic-defect
@@ -7777,8 +7699,7 @@ const pySkippedTest = defineRule({
7777
7699
  return findings;
7778
7700
  }
7779
7701
  });
7780
- //#endregion
7781
- //#region src/rules/python/qa-py-012-tautological.ts
7702
+ // src/rules/python/qa-py-012-tautological.ts
7782
7703
  /**
7783
7704
  * QA-PY-012 — Tautological assertion (`assert True`, `assert x == x`).
7784
7705
  * Severity: error · Confidence: high · deterministic-defect
@@ -7833,8 +7754,7 @@ const pyTautological = defineRule({
7833
7754
  return findings;
7834
7755
  }
7835
7756
  });
7836
- //#endregion
7837
- //#region src/rules/python/qa-py-001-focused-test.ts
7757
+ // src/rules/python/qa-py-001-focused-test.ts
7838
7758
  /**
7839
7759
  * QA-PY-001 — Focused test committed.
7840
7760
  * Severity: error · Confidence: high · deterministic-defect
@@ -7888,8 +7808,7 @@ const pyFocusedTest = defineRule({
7888
7808
  return findings;
7889
7809
  }
7890
7810
  });
7891
- //#endregion
7892
- //#region src/engine/jv-cs-ast.ts
7811
+ // src/engine/jv-cs-ast.ts
7893
7812
  /** Narrow the loose `ast` seam back to a tree-sitter Tree. */
7894
7813
  function getTreeSitterTree(ast) {
7895
7814
  if (!(ast instanceof Object) || !("rootNode" in ast)) return void 0;
@@ -8093,8 +8012,7 @@ function isHelperIdiom(name) {
8093
8012
  }
8094
8013
  return false;
8095
8014
  }
8096
- //#endregion
8097
- //#region src/engine/python-ast.ts
8015
+ // src/engine/python-ast.ts
8098
8016
  function getPythonTree(ast) {
8099
8017
  return getTreeSitterTree(ast);
8100
8018
  }
@@ -8179,8 +8097,7 @@ function pythonWithRaisesBlocks(tree) {
8179
8097
  });
8180
8098
  return out;
8181
8099
  }
8182
- //#endregion
8183
- //#region src/rules/python/qa-py-007-raises-without-match.ts
8100
+ // src/rules/python/qa-py-007-raises-without-match.ts
8184
8101
  /**
8185
8102
  * QA-PY-007 — pytest.raises without match.
8186
8103
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8307,8 +8224,7 @@ function astArm(ctx, tree) {
8307
8224
  }
8308
8225
  return findings;
8309
8226
  }
8310
- //#endregion
8311
- //#region src/rules/python/qa-py-009-commented-out.ts
8227
+ // src/rules/python/qa-py-009-commented-out.ts
8312
8228
  /**
8313
8229
  * QA-PY-009 — Commented-out tests.
8314
8230
  * Severity: warning · Confidence: high · deterministic-defect
@@ -8355,8 +8271,7 @@ const pyCommentedOutTest = defineRule({
8355
8271
  return findings;
8356
8272
  }
8357
8273
  });
8358
- //#endregion
8359
- //#region src/rules/python/qa-py-004-bare-truthiness.ts
8274
+ // src/rules/python/qa-py-004-bare-truthiness.ts
8360
8275
  /**
8361
8276
  * QA-PY-004 — Bare truthiness assert on complex object.
8362
8277
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8435,8 +8350,7 @@ const pyBareTruthinessAssert = defineRule({
8435
8350
  return findings;
8436
8351
  }
8437
8352
  });
8438
- //#endregion
8439
- //#region src/rules/python/qa-py-011-mutable-fixture.ts
8353
+ // src/rules/python/qa-py-011-mutable-fixture.ts
8440
8354
  /**
8441
8355
  * QA-PY-011 — Mutable fixture shared across tests.
8442
8356
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8487,8 +8401,7 @@ const pyMutableFixture = defineRule({
8487
8401
  return findings;
8488
8402
  }
8489
8403
  });
8490
- //#endregion
8491
- //#region src/rules/playwright/qa-pw-101-wait-for-timeout.ts
8404
+ // src/rules/playwright/qa-pw-101-wait-for-timeout.ts
8492
8405
  /**
8493
8406
  * QA-PW-101 — waitForTimeout() anywhere.
8494
8407
  * Severity: error · Confidence: high · deterministic-defect
@@ -8535,8 +8448,7 @@ const pwWaitForTimeout = defineRule({
8535
8448
  return findings;
8536
8449
  }
8537
8450
  });
8538
- //#endregion
8539
- //#region src/rules/playwright/qa-pw-102-wait-load.ts
8451
+ // src/rules/playwright/qa-pw-102-wait-load.ts
8540
8452
  /**
8541
8453
  * QA-PW-102 — waitForEvent('load') / waitForLoadState('load') instead of
8542
8454
  * web-first assertions.
@@ -8590,8 +8502,7 @@ const pwWaitForLoadEvent = defineRule({
8590
8502
  return findings;
8591
8503
  }
8592
8504
  });
8593
- //#endregion
8594
- //#region src/rules/playwright/qa-pw-113-deep-frames.ts
8505
+ // src/rules/playwright/qa-pw-113-deep-frames.ts
8595
8506
  /**
8596
8507
  * QA-PW-113 — frameLocator chains > 2 deep.
8597
8508
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8641,8 +8552,7 @@ const pwDeepFrameLocator = defineRule({
8641
8552
  function count(fragment) {
8642
8553
  return fragment.match(/frameLocator\s*\(/g).length;
8643
8554
  }
8644
- //#endregion
8645
- //#region src/rules/playwright/qa-pw-117-serial.ts
8555
+ // src/rules/playwright/qa-pw-117-serial.ts
8646
8556
  /**
8647
8557
  * QA-PW-117 — test.describe.serial without justification comment.
8648
8558
  * Severity: warning · Confidence: high · deterministic-defect
@@ -8696,8 +8606,7 @@ const pwSerialNoJustification = defineRule({
8696
8606
  return findings;
8697
8607
  }
8698
8608
  });
8699
- //#endregion
8700
- //#region src/rules/playwright/qa-pw-121-config-retries.ts
8609
+ // src/rules/playwright/qa-pw-121-config-retries.ts
8701
8610
  /**
8702
8611
  * QA-PW-121 — playwright.config: retries >= 3 or workers = 0.
8703
8612
  * Severity: warning · Confidence: high · deterministic-defect
@@ -8762,8 +8671,7 @@ const pwConfigRetryAbuse = defineRule({
8762
8671
  return findings;
8763
8672
  }
8764
8673
  });
8765
- //#endregion
8766
- //#region src/rules/playwright/qa-pw-122-no-trace.ts
8674
+ // src/rules/playwright/qa-pw-122-no-trace.ts
8767
8675
  /**
8768
8676
  * QA-PW-122 — No trace capture on retry in playwright.config.
8769
8677
  * Severity: warning · Confidence: high · deterministic-defect
@@ -8812,8 +8720,7 @@ const pwNoTraceOnRetry = defineRule({
8812
8720
  return findings;
8813
8721
  }
8814
8722
  });
8815
- //#endregion
8816
- //#region src/rules/playwright/qa-pw-124-project-split.ts
8723
+ // src/rules/playwright/qa-pw-124-project-split.ts
8817
8724
  /**
8818
8725
  * QA-PW-124 — Projects defined but no smoke/regression split.
8819
8726
  * Severity: info · Confidence: high · deterministic-defect
@@ -8875,8 +8782,7 @@ const pwNoProjectSplit = defineRule({
8875
8782
  return findings;
8876
8783
  }
8877
8784
  });
8878
- //#endregion
8879
- //#region src/rules/playwright/qa-pw-104-trial-click.ts
8785
+ // src/rules/playwright/qa-pw-104-trial-click.ts
8880
8786
  /**
8881
8787
  * QA-PW-104 — `{ trial: true }` misuse — check-then-act race.
8882
8788
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8922,8 +8828,7 @@ const pwTrialMisuse = defineRule({
8922
8828
  return findings;
8923
8829
  }
8924
8830
  });
8925
- //#endregion
8926
- //#region src/rules/playwright/qa-pw-116-storage-state.ts
8831
+ // src/rules/playwright/qa-pw-116-storage-state.ts
8927
8832
  /**
8928
8833
  * QA-PW-116 — storageState reuse without expiry strategy.
8929
8834
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -8969,8 +8874,7 @@ const pwStorageStateNoExpiry = defineRule({
8969
8874
  return findings;
8970
8875
  }
8971
8876
  });
8972
- //#endregion
8973
- //#region src/rules/playwright/qa-pw-125-global-setup.ts
8877
+ // src/rules/playwright/qa-pw-125-global-setup.ts
8974
8878
  /**
8975
8879
  * QA-PW-125 — globalSetup mutating shared external state.
8976
8880
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -9028,8 +8932,7 @@ const pwGlobalSetupSharedState = defineRule({
9028
8932
  return findings;
9029
8933
  }
9030
8934
  });
9031
- //#endregion
9032
- //#region src/rules/playwright/qa-pw-115-shared-page.ts
8935
+ // src/rules/playwright/qa-pw-115-shared-page.ts
9033
8936
  /**
9034
8937
  * QA-PW-115 — Shared `page` across parallel tests without isolation.
9035
8938
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -9077,8 +8980,7 @@ const pwSharedPage = defineRule({
9077
8980
  return findings;
9078
8981
  }
9079
8982
  });
9080
- //#endregion
9081
- //#region src/rules/playwright/qa-pw-140.ts
8983
+ // src/rules/playwright/qa-pw-140.ts
9082
8984
  /**
9083
8985
  * QA-PW-140 — Screenshot without maxDiffPixelRatio.
9084
8986
  *
@@ -9152,8 +9054,7 @@ function matchParen(text, open) {
9152
9054
  }
9153
9055
  return -1;
9154
9056
  }
9155
- //#endregion
9156
- //#region src/rules/playwright/qa-pw-123-hardcoded-url.ts
9057
+ // src/rules/playwright/qa-pw-123-hardcoded-url.ts
9157
9058
  /**
9158
9059
  * QA-PW-123 — Hardcoded baseURL in specs.
9159
9060
  * Severity: warning · Confidence: high · deterministic-defect
@@ -9201,8 +9102,7 @@ const hardcodedBaseUrl = defineRule({
9201
9102
  return findings;
9202
9103
  }
9203
9104
  });
9204
- //#endregion
9205
- //#region src/rules/quality/qa-env-001-env-coupling.ts
9105
+ // src/rules/quality/qa-env-001-env-coupling.ts
9206
9106
  /**
9207
9107
  * QA-ENV-001 — "Works On My Machine" detector core (Tier 5 #24).
9208
9108
  * Environment coupling in test files: fixed ports, OS paths,
@@ -9269,8 +9169,7 @@ const envCoupling = defineRule({
9269
9169
  return findings;
9270
9170
  }
9271
9171
  });
9272
- //#endregion
9273
- //#region src/rules/playwright/qa-pw-141-retry-no-triage.ts
9172
+ // src/rules/playwright/qa-pw-141-retry-no-triage.ts
9274
9173
  /**
9275
9174
  * QA-PW-141 — playwright.config retries set high enough to mask flakes.
9276
9175
  * Severity: warning · Confidence: high · deterministic-defect
@@ -9327,8 +9226,7 @@ const pwRetryMaskingNoForensics = defineRule({
9327
9226
  return findings;
9328
9227
  }
9329
9228
  });
9330
- //#endregion
9331
- //#region src/rules/playwright/qa-pw-142-blanket-route.ts
9229
+ // src/rules/playwright/qa-pw-142-blanket-route.ts
9332
9230
  /**
9333
9231
  * QA-PW-142 — page.route() blanket-mocking instead of scoped intercepts.
9334
9232
  * Severity: warning · Confidence: medium · heuristic-risk
@@ -9383,8 +9281,7 @@ const pwBlanketRouteMock = defineRule({
9383
9281
  return findings;
9384
9282
  }
9385
9283
  });
9386
- //#endregion
9387
- //#region src/rules/playwright/qa-pw-143-no-artifacts.ts
9284
+ // src/rules/playwright/qa-pw-143-no-artifacts.ts
9388
9285
  /**
9389
9286
  * QA-PW-143 — No screenshot/video artifact capture on failure.
9390
9287
  * Severity: info · Confidence: high · deterministic-defect
@@ -9437,8 +9334,7 @@ const pwNoFailureArtifacts = defineRule({
9437
9334
  return findings;
9438
9335
  }
9439
9336
  });
9440
- //#endregion
9441
- //#region src/rules/playwright/qa-pw-144-single-browser.ts
9337
+ // src/rules/playwright/qa-pw-144-single-browser.ts
9442
9338
  /**
9443
9339
  * QA-PW-144 — Single-browser project matrix (no cross-browser coverage).
9444
9340
  * Severity: info · Confidence: high · deterministic-defect
@@ -9500,8 +9396,7 @@ const pwSingleBrowserMatrix = defineRule({
9500
9396
  return findings;
9501
9397
  }
9502
9398
  });
9503
- //#endregion
9504
- //#region src/rules/playwright/qa-pw-146-css-locator.ts
9399
+ // src/rules/playwright/qa-pw-146-css-locator.ts
9505
9400
  /**
9506
9401
  * QA-PW-146 — CSS/XPath string selector where Playwright's locator
9507
9402
  * standard expects a normalized locator (plan §17.3).
@@ -9580,8 +9475,7 @@ const pwLocatorNormalize = defineRule({
9580
9475
  return findings;
9581
9476
  }
9582
9477
  });
9583
- //#endregion
9584
- //#region src/rules/playwright/qa-pw-147-codegen-artifact.ts
9478
+ // src/rules/playwright/qa-pw-147-codegen-artifact.ts
9585
9479
  /**
9586
9480
  * QA-PW-147 — Playwright codegen artifact left unedited (plan §17.1).
9587
9481
  * Severity: info · Confidence: medium · observation
@@ -9640,8 +9534,7 @@ const pwCodegenArtifact = defineRule({
9640
9534
  return findings;
9641
9535
  }
9642
9536
  });
9643
- //#endregion
9644
- //#region src/rules/python/qa-py-101-sync-async-mix.ts
9537
+ // src/rules/python/qa-py-101-sync-async-mix.ts
9645
9538
  /**
9646
9539
  * QA-PY-101 — Playwright-Python: sync API in async test (or vice versa).
9647
9540
  * Severity: warning · Confidence: high · deterministic-defect
@@ -9690,8 +9583,7 @@ const pyPwSyncAsyncMix = defineRule({
9690
9583
  return findings;
9691
9584
  }
9692
9585
  });
9693
- //#endregion
9694
- //#region src/rules/python/qa-py-103-wait-for-timeout.ts
9586
+ // src/rules/python/qa-py-103-wait-for-timeout.ts
9695
9587
  /**
9696
9588
  * QA-PY-103 — Playwright-Python: wait_for_timeout() as synchronization.
9697
9589
  * Severity: warning · Confidence: high · deterministic-defect
@@ -9738,8 +9630,7 @@ const pyPwWaitForTimeout = defineRule({
9738
9630
  return findings;
9739
9631
  }
9740
9632
  });
9741
- //#endregion
9742
- //#region src/rules/python/qa-py-105-pw-no-assertions.ts
9633
+ // src/rules/python/qa-py-105-pw-no-assertions.ts
9743
9634
  /**
9744
9635
  * QA-PY-105 — Playwright-Python: no assertion in Playwright test.
9745
9636
  * Severity: error · Confidence: high · deterministic-defect
@@ -9819,8 +9710,7 @@ function extractBlock(text, afterColon) {
9819
9710
  const lineEnd = rest.indexOf("\n", firstContent.index);
9820
9711
  return lineEnd === -1 ? rest : rest.slice(0, lineEnd);
9821
9712
  }
9822
- //#endregion
9823
- //#region src/rules/java/qa-jv-101-disabled-test.ts
9713
+ // src/rules/java/qa-jv-101-disabled-test.ts
9824
9714
  /**
9825
9715
  * QA-JV-101 — @Disabled test without justification.
9826
9716
  * Severity: warning · Confidence: high · deterministic-defect
@@ -9874,8 +9764,7 @@ const jvDisabledTest = defineRule({
9874
9764
  return findings;
9875
9765
  }
9876
9766
  });
9877
- //#endregion
9878
- //#region src/rules/java/qa-jv-103-no-assertions.ts
9767
+ // src/rules/java/qa-jv-103-no-assertions.ts
9879
9768
  /**
9880
9769
  * QA-JV-103 — Test method without assertions.
9881
9770
  * Severity: error · Confidence: high · deterministic-defect
@@ -10032,8 +9921,7 @@ function matchBrace$1(text, open) {
10032
9921
  }
10033
9922
  return -1;
10034
9923
  }
10035
- //#endregion
10036
- //#region src/engine/qa-model.ts
9924
+ // src/engine/qa-model.ts
10037
9925
  /**
10038
9926
  * Extract the semantic model for one parsed file. Resolves undefined
10039
9927
  * when the language has no extractor or the parse stage produced no
@@ -10576,8 +10464,7 @@ function extractPythonModel(file) {
10576
10464
  nodes
10577
10465
  };
10578
10466
  }
10579
- //#endregion
10580
- //#region src/rules/java/qa-jv-105-wait-for-timeout.ts
10467
+ // src/rules/java/qa-jv-105-wait-for-timeout.ts
10581
10468
  /**
10582
10469
  * QA-JV-105 — waitForTimeout / hardcoded navigation waits.
10583
10470
  * Severity: warning · Confidence: high · deterministic-defect
@@ -10662,8 +10549,7 @@ const jvWaitForTimeout = defineRule({
10662
10549
  return findings;
10663
10550
  }
10664
10551
  });
10665
- //#endregion
10666
- //#region src/rules/csharp/qa-cs-101-skipped-test.ts
10552
+ // src/rules/csharp/qa-cs-101-skipped-test.ts
10667
10553
  /**
10668
10554
  * QA-CS-101 — [Ignore]/[Skip] test without justification.
10669
10555
  * Severity: warning · Confidence: high · deterministic-defect
@@ -10727,8 +10613,7 @@ const csSkippedTest = defineRule({
10727
10613
  return findings;
10728
10614
  }
10729
10615
  });
10730
- //#endregion
10731
- //#region src/rules/csharp/qa-cs-103-no-assertions.ts
10616
+ // src/rules/csharp/qa-cs-103-no-assertions.ts
10732
10617
  /**
10733
10618
  * QA-CS-103 — Test method without assertions.
10734
10619
  * Severity: error · Confidence: high · deterministic-defect
@@ -10762,7 +10647,6 @@ const csSkippedTest = defineRule({
10762
10647
  * Deep-diffing those requires symbol semantics Mjölnir deliberately does
10763
10648
  * not promise for C# (plan §13.4).
10764
10649
  */
10765
- /** Helper/verification idiom: verify/check/assert-prefixed names. */
10766
10650
  const WHY$5 = "Without an assertion the test can only fail by throwing — it cannot detect behavioral regressions.";
10767
10651
  const FIX$4 = "Add an assertion on the expected outcome, or remove the test.";
10768
10652
  function makeFinding(path, position, name) {
@@ -10926,8 +10810,7 @@ function matchBrace(text, open) {
10926
10810
  }
10927
10811
  return -1;
10928
10812
  }
10929
- //#endregion
10930
- //#region src/rules/csharp/qa-cs-105-wait-for-timeout.ts
10813
+ // src/rules/csharp/qa-cs-105-wait-for-timeout.ts
10931
10814
  /**
10932
10815
  * QA-CS-105 — Playwright-.NET: WaitForTimeoutAsync hard sleep.
10933
10816
  * Severity: warning · Confidence: high · deterministic-defect
@@ -11015,8 +10898,7 @@ const csWaitForTimeout = defineRule({
11015
10898
  return findings;
11016
10899
  }
11017
10900
  });
11018
- //#endregion
11019
- //#region src/rules/cypress/qa-cyp-001-cy-wait.ts
10901
+ // src/rules/cypress/qa-cyp-001-cy-wait.ts
11020
10902
  /**
11021
10903
  * QA-CYP-001 — Fixed `cy.wait(n)` (hard-coded wait).
11022
10904
  * Severity: warning · Confidence: high · deterministic-defect
@@ -11095,8 +10977,7 @@ function isCypressFile(ctx) {
11095
10977
  if (/\.cy\.[jt]sx?$/.test(ctx.path)) return true;
11096
10978
  return /\bcy\s*\./.test(ctx.codeText ?? ctx.text);
11097
10979
  }
11098
- //#endregion
11099
- //#region src/rules/cypress/qa-cyp-002-focused-test.ts
10980
+ // src/rules/cypress/qa-cyp-002-focused-test.ts
11100
10981
  /**
11101
10982
  * QA-CYP-002 — Focused test (`.only`).
11102
10983
  * Severity: error · Confidence: high · deterministic-defect
@@ -11159,8 +11040,7 @@ const cypFocusedTest = defineRule({
11159
11040
  return findings;
11160
11041
  }
11161
11042
  });
11162
- //#endregion
11163
- //#region src/rules/cypress/qa-cyp-003-config-security.ts
11043
+ // src/rules/cypress/qa-cyp-003-config-security.ts
11164
11044
  /**
11165
11045
  * QA-CYP-003 — Cypress config disables `chromeWebSecurity`.
11166
11046
  * Severity: error · Confidence: high · deterministic-defect
@@ -11230,8 +11110,7 @@ const cypConfigSecurity = defineRule({
11230
11110
  return findings;
11231
11111
  }
11232
11112
  });
11233
- //#endregion
11234
- //#region src/rules/selenium/qa-se-sleep-lookup.ts
11113
+ // src/rules/selenium/qa-se-sleep-lookup.ts
11235
11114
  /**
11236
11115
  * Selenium sleep-then-interact rules (Verification Trust Evolution Plan
11237
11116
  * §15.3 — "Selenium implicit-wait abuse / missing WebDriverWait",
@@ -11382,8 +11261,7 @@ const sePythonSleepLookup = defineRule({
11382
11261
  return sleepBeforeLookup(ctx, PY_SLEEP_RE, PY_LOOKUP_RE, "QA-SE-003");
11383
11262
  }
11384
11263
  });
11385
- //#endregion
11386
- //#region src/rules/shared/family.ts
11264
+ // src/rules/shared/family.ts
11387
11265
  /**
11388
11266
  * Cross-language pattern rule family generator (Phase 6 — Tempering Plan).
11389
11267
  *
@@ -11449,8 +11327,7 @@ function definePatternFamily(opts) {
11449
11327
  }
11450
11328
  }));
11451
11329
  }
11452
- //#endregion
11453
- //#region src/rules/families/hard-sleep.ts
11330
+ // src/rules/families/hard-sleep.ts
11454
11331
  /**
11455
11332
  * Hard-sleep family (Phase 6 — Tempering Plan).
11456
11333
  * Java: Thread.sleep(), C#: Thread.Sleep() / Task.Delay()
@@ -11601,8 +11478,7 @@ const hardSleepFamily = definePatternFamily({
11601
11478
  fix: "Use `await Assertions.Expect(locator).ToBeVisibleAsync()` or locator.WaitForAsync()."
11602
11479
  }]
11603
11480
  });
11604
- //#endregion
11605
- //#region src/rules/families/network-idle.ts
11481
+ // src/rules/families/network-idle.ts
11606
11482
  /**
11607
11483
  * Network-idle family (Phase 6 — Tempering Plan).
11608
11484
  * All three language variants of waitForLoadState(networkidle).
@@ -11663,8 +11539,7 @@ const networkIdleFamily = definePatternFamily({
11663
11539
  }
11664
11540
  ]
11665
11541
  });
11666
- //#endregion
11667
- //#region src/rules/families/hardcoded-url.ts
11542
+ // src/rules/families/hardcoded-url.ts
11668
11543
  /**
11669
11544
  * Hardcoded-URL family (Phase 6 — Tempering Plan).
11670
11545
  * Detects absolute URLs in navigation/request calls across languages.
@@ -11714,8 +11589,7 @@ const hardcodedUrlFamily = definePatternFamily({
11714
11589
  fix: "Use `--base-url` / `base_url` fixture, or `os.environ[...]`."
11715
11590
  }]
11716
11591
  });
11717
- //#endregion
11718
- //#region src/rules/families/shared-page.ts
11592
+ // src/rules/families/shared-page.ts
11719
11593
  /**
11720
11594
  * Shared-page family (Phase 6 — Tempering Plan).
11721
11595
  * Detects static/module-level Page/Browser instances shared across tests.
@@ -11776,8 +11650,7 @@ const sharedPageFamily = definePatternFamily({
11776
11650
  }
11777
11651
  ]
11778
11652
  });
11779
- //#endregion
11780
- //#region src/rules/families/brittle-selectors.ts
11653
+ // src/rules/families/brittle-selectors.ts
11781
11654
  /**
11782
11655
  * Brittle-selectors family (Phase 6 — Tempering Plan).
11783
11656
  * Detects XPath/structural CSS selectors across Java, C#, Python.
@@ -11889,8 +11762,7 @@ const brittleSelectorsFamily = [
11889
11762
  }
11890
11763
  ], "Prefer role-based locators (`get_by_role(...)`) or data-testid attributes.")
11891
11764
  ];
11892
- //#endregion
11893
- //#region src/rules/families/retry-masking.ts
11765
+ // src/rules/families/retry-masking.ts
11894
11766
  /**
11895
11767
  * Retry-masking family (Phase 6 — Tempering Plan).
11896
11768
  * Detects retry annotations/attributes that mask flaky tests.
@@ -12010,8 +11882,7 @@ const retryMaskingFamily = [defineRule({
12010
11882
  return findings;
12011
11883
  }
12012
11884
  })];
12013
- //#endregion
12014
- //#region src/rules/index.ts
11885
+ // src/rules/index.ts
12015
11886
  const RULES = [
12016
11887
  focusedTestCommitted,
12017
11888
  skippedTest,
@@ -12092,8 +11963,7 @@ const RULES = [
12092
11963
  function getRule(id) {
12093
11964
  return RULES.find((r) => r.id === id);
12094
11965
  }
12095
- //#endregion
12096
- //#region src/lib/wilson.ts
11966
+ // src/lib/wilson.ts
12097
11967
  /**
12098
11968
  * 95% Wilson score interval on a proportion (plan §20.2).
12099
11969
  *
@@ -12203,8 +12073,7 @@ function effectiveTier(rule) {
12203
12073
  if (rule.tier !== void 0) return rule.tier;
12204
12074
  return measurementTier(rule);
12205
12075
  }
12206
- //#endregion
12207
- //#region src/scorer/scorer.ts
12076
+ // src/scorer/scorer.ts
12208
12077
  /**
12209
12078
  * Transparent scorer (Product-MVP §8).
12210
12079
  * score = 100 − Σ(severity deductions), floor 0.
@@ -12356,9 +12225,9 @@ function countTestDeclarations(text, codeText) {
12356
12225
  const source = codeText ?? text;
12357
12226
  const patterns = [
12358
12227
  /\b(?:it|test)(?:\.\w+)*\s*(?:\(|`)/g,
12359
- /^\s*(?:async\s+)?def\s+test_\w*/gm,
12360
- /^\s*@Test\b/gm,
12361
- /^\s*\[(?:Test|Fact|Theory|TestMethod)\b/gm
12228
+ /^[ \t]*(?:async[ \t]+)?def[ \t]+test_\w*/gm,
12229
+ /^[ \t]*@Test\b/gm,
12230
+ /^[ \t]*\[(?:Test|Fact|Theory|TestMethod)\b/gm
12362
12231
  ];
12363
12232
  let count = 0;
12364
12233
  for (const re of patterns) {
@@ -12367,8 +12236,7 @@ function countTestDeclarations(text, codeText) {
12367
12236
  }
12368
12237
  return count;
12369
12238
  }
12370
- //#endregion
12371
- //#region src/engine/rule-runner.ts
12239
+ // src/engine/rule-runner.ts
12372
12240
  /**
12373
12241
  * Legacy appliesTo → adapter id mapping.
12374
12242
  *
@@ -12407,8 +12275,7 @@ function asUniversal(rule) {
12407
12275
  }
12408
12276
  };
12409
12277
  }
12410
- //#endregion
12411
- //#region src/engine/tier-policy.ts
12278
+ // src/engine/tier-policy.ts
12412
12279
  /**
12413
12280
  * Tier policy (audit H-1): the declared tier is authoritative over a
12414
12281
  * rule's severity and evidence claims.
@@ -12510,8 +12377,7 @@ function enforceTierPolicy(findings, tierByRuleId) {
12510
12377
  if (EVIDENCE_RANK$1[effective] < EVIDENCE_RANK$1[floor.evidenceLevel]) f.evidenceLevel = floor.evidenceLevel;
12511
12378
  }
12512
12379
  }
12513
- //#endregion
12514
- //#region src/engine/evidence-core.ts
12380
+ // src/engine/evidence-core.ts
12515
12381
  /**
12516
12382
  * The core's own version — stamped into every record's provenance so a
12517
12383
  * consumer can tell which normalization contract produced a record.
@@ -12578,8 +12444,7 @@ function countEvidence(records) {
12578
12444
  }
12579
12445
  return counts;
12580
12446
  }
12581
- //#endregion
12582
- //#region src/engine/provenance.ts
12447
+ // src/engine/provenance.ts
12583
12448
  const GENERATED_HEADER_RE = /^\s*(?:\/\/|#|\/\*)\s*(?:auto[- ]?generated|generated by|do not edit)/i;
12584
12449
  /**
12585
12450
  * Codegen fingerprint: the recorder names its first test exactly
@@ -12620,8 +12485,7 @@ function computeAgenticProfile(files, findings) {
12620
12485
  note: "Static provenance markers only (generated-file headers, Playwright codegen default titles). Absence of a marker does not prove human authorship; healed and MCP-edited files without markers are indistinguishable from hand-written ones. Provenance never changes scoring or evidence — authorship is metadata, not trust."
12621
12486
  };
12622
12487
  }
12623
- //#endregion
12624
- //#region src/engine/overlap-dedup.ts
12488
+ // src/engine/overlap-dedup.ts
12625
12489
  const TIER_RANK = {
12626
12490
  core: 0,
12627
12491
  extended: 1,
@@ -12716,8 +12580,7 @@ function dropOrderBefore(a, b, metaByRuleId) {
12716
12580
  if (ranksBefore(b, a, metaByRuleId)) return true;
12717
12581
  return (a.column ?? Number.MAX_SAFE_INTEGER) < (b.column ?? Number.MAX_SAFE_INTEGER);
12718
12582
  }
12719
- //#endregion
12720
- //#region src/engine/finding-identity.ts
12583
+ // src/engine/finding-identity.ts
12721
12584
  /**
12722
12585
  * Correlation identity for before/after comparison: ruleId + file +
12723
12586
  * message, deliberately EXCLUDING line — a source edit that shifts a
@@ -12726,8 +12589,7 @@ function dropOrderBefore(a, b, metaByRuleId) {
12726
12589
  function findingFingerprint(f) {
12727
12590
  return `${f.ruleId}\u0000${f.file}\u0000${f.message}`;
12728
12591
  }
12729
- //#endregion
12730
- //#region src/engine/correlation-engine.ts
12592
+ // src/engine/correlation-engine.ts
12731
12593
  const EVIDENCE_RANK = {
12732
12594
  E0: 0,
12733
12595
  E1: 1,
@@ -12857,8 +12719,7 @@ function correlateFindings(findings) {
12857
12719
  });
12858
12720
  return conclusions;
12859
12721
  }
12860
- //#endregion
12861
- //#region src/engine/dependency-graph.ts
12722
+ // src/engine/dependency-graph.ts
12862
12723
  /**
12863
12724
  * Dependency Graph (ECO-005).
12864
12725
  *
@@ -12995,8 +12856,7 @@ function parseManifest(filePath) {
12995
12856
  if (filePath.endsWith("pyproject.toml")) return parsePyprojectToml(filePath);
12996
12857
  if (filePath.endsWith("pom.xml")) return parsePomXml(filePath);
12997
12858
  }
12998
- //#endregion
12999
- //#region src/engine/incremental-analysis.ts
12859
+ // src/engine/incremental-analysis.ts
13000
12860
  /**
13001
12861
  * Semantic inputs that affect the full scan beyond individual file content.
13002
12862
  * If any of these changed, an incremental run is NOT safe — a full scan
@@ -13032,8 +12892,7 @@ function isIncrementalSafe(changedFiles) {
13032
12892
  reasons
13033
12893
  };
13034
12894
  }
13035
- //#endregion
13036
- //#region src/brand/tokens.ts
12895
+ // src/brand/tokens.ts
13037
12896
  /**
13038
12897
  * The single source of brand truth.
13039
12898
  *
@@ -13319,8 +13178,7 @@ function deriveScoreState(score) {
13319
13178
  rune: RUNES.critical
13320
13179
  };
13321
13180
  }
13322
- //#endregion
13323
- //#region src/engine/monorepo-analysis.ts
13181
+ // src/engine/monorepo-analysis.ts
13324
13182
  /**
13325
13183
  * Package verdict, derived from the one ScoreState band model (BW-104).
13326
13184
  * These two constants used to be a private copy of the band boundaries;
@@ -13427,8 +13285,7 @@ function configurableAggregation(results, config) {
13427
13285
  strategy: config.weightingStrategy
13428
13286
  };
13429
13287
  }
13430
- //#endregion
13431
- //#region src/lib/fs-atomic.ts
13288
+ // src/lib/fs-atomic.ts
13432
13289
  /**
13433
13290
  * Atomic file writes (audit S9).
13434
13291
  *
@@ -13511,8 +13368,7 @@ function renameWithWindowsRetry(from, to) {
13511
13368
  throw err;
13512
13369
  }
13513
13370
  }
13514
- //#endregion
13515
- //#region src/engine/scan-cache.ts
13371
+ // src/engine/scan-cache.ts
13516
13372
  /**
13517
13373
  * Local incremental scan cache (Beta-to-Stable 1.0 plan, M5.2 / A-2).
13518
13374
  *
@@ -13722,8 +13578,7 @@ function createScanCache(root) {
13722
13578
  }
13723
13579
  };
13724
13580
  }
13725
- //#endregion
13726
- //#region src/reporter/theme.ts
13581
+ // src/reporter/theme.ts
13727
13582
  /**
13728
13583
  * Norse-forge theme system. Pure string-in → string-out helpers.
13729
13584
  * Respects NO_COLOR and non-TTY via `palette(isTTY)` — every renderer
@@ -13823,8 +13678,7 @@ function rgb([r, g, b]) {
13823
13678
  function palette(enabled) {
13824
13679
  return enabled ? on : off;
13825
13680
  }
13826
- //#endregion
13827
- //#region src/reporter/ui.ts
13681
+ // src/reporter/ui.ts
13828
13682
  /**
13829
13683
  * Neutral context for direct calls (tests, library use) and as the
13830
13684
  * safe default when a command runner is invoked without wiring: no
@@ -13849,8 +13703,7 @@ function sectionHeader(title, ui) {
13849
13703
  const glyph = ui.ascii ? "=" : "▍";
13850
13704
  return ` ${ui.p.accent(`${glyph} ${title}`)}`;
13851
13705
  }
13852
- //#endregion
13853
- //#region src/forensics/classify.ts
13706
+ // src/forensics/classify.ts
13854
13707
  /**
13855
13708
  * Strong-signal marker tables. Kept deliberately narrow: a marker here
13856
13709
  * is a named family fact, not a vibe. Unknown text yields no signal —
@@ -13970,8 +13823,7 @@ function classifyForensicVerdict(signals) {
13970
13823
  signals: signalCounts
13971
13824
  };
13972
13825
  }
13973
- //#endregion
13974
- //#region src/forensics/analyze.ts
13826
+ // src/forensics/analyze.ts
13975
13827
  const ui$5 = plainContext();
13976
13828
  const MAX_RECORDS = 1e5;
13977
13829
  function analyze(records, source) {
@@ -14134,8 +13986,7 @@ function renderFlakyMd(report) {
14134
13986
  lines.push("");
14135
13987
  return lines.join("\n");
14136
13988
  }
14137
- //#endregion
14138
- //#region src/forensics/evidence-hygiene.ts
13989
+ // src/forensics/evidence-hygiene.ts
14139
13990
  /**
14140
13991
  * Evidence hygiene — secret redaction + control-char sanitization
14141
13992
  * (ENGINE-012).
@@ -14197,8 +14048,7 @@ function sanitizeErrorText(text, options) {
14197
14048
  if (result.length > maxLength) result = result.slice(0, maxLength) + `[truncated at ${maxLength} chars]`;
14198
14049
  return result;
14199
14050
  }
14200
- //#endregion
14201
- //#region src/forensics/trace.ts
14051
+ // src/forensics/trace.ts
14202
14052
  /**
14203
14053
  * Playwright trace ingester (product-gap master plan §9 R5 / WI-17).
14204
14054
  *
@@ -14391,8 +14241,7 @@ function parseTraceArtifact(bytes, artifactName) {
14391
14241
  const { actions } = parseTraceNdjson(text);
14392
14242
  return traceActionsToRecord(actions, artifactName);
14393
14243
  }
14394
- //#endregion
14395
- //#region src/forensics/parse-jest-json.ts
14244
+ // src/forensics/parse-jest-json.ts
14396
14245
  function toStatus$2(raw) {
14397
14246
  switch (raw) {
14398
14247
  case "passed": return "passed";
@@ -14452,8 +14301,7 @@ function parseJestJson(json) {
14452
14301
  }
14453
14302
  return out;
14454
14303
  }
14455
- //#endregion
14456
- //#region src/forensics/parse-junit.ts
14304
+ // src/forensics/parse-junit.ts
14457
14305
  const MAX_INPUT$1 = 20971520;
14458
14306
  function decodeEntities$1(s) {
14459
14307
  return s.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&#39;", "'").replaceAll("&amp;", "&");
@@ -14495,14 +14343,25 @@ function parseJunitXml(xml) {
14495
14343
  const skipped = /<skipped\b/i.test(inner);
14496
14344
  const errors = [];
14497
14345
  if (failed) {
14498
- const failRe = /<(?:failure|error)\b([^>]*)>([\s\S]*?)<\/(?:failure|error)\s*>/gi;
14499
- let failMatch;
14500
- while ((failMatch = failRe.exec(inner)) !== null) {
14501
- const attrsStr = failMatch[1] ?? "";
14346
+ const openRe = /<(?:failure|error)\b/gi;
14347
+ const endRe = /<\/(?:failure|error)\s*>/gi;
14348
+ let open;
14349
+ while ((open = openRe.exec(inner)) !== null) {
14350
+ const gt = inner.indexOf(">", open.index);
14351
+ if (gt === -1) break;
14352
+ const attrsStr = inner.slice(open.index, gt);
14353
+ if (attrsStr.endsWith("/")) {
14354
+ openRe.lastIndex = gt + 1;
14355
+ continue;
14356
+ }
14357
+ endRe.lastIndex = gt + 1;
14358
+ const end = endRe.exec(inner);
14359
+ if (end === null) break;
14502
14360
  const msgAttr = /\smessage\s*=\s*"([^"]*)"/i.exec(attrsStr)?.[1];
14503
14361
  if (msgAttr) errors.push(sanitizeErrorText(decodeEntities$1(msgAttr)));
14504
- const body = failMatch[2]?.trim();
14362
+ const body = inner.slice(gt + 1, end.index).trim();
14505
14363
  if (body && body !== msgAttr) errors.push(sanitizeErrorText(decodeEntities$1(body)));
14364
+ openRe.lastIndex = end.index + end[0].length;
14506
14365
  }
14507
14366
  const selfCloseRe = /<(?:failure|error)\b([^>]*)\/>/gi;
14508
14367
  let scMatch;
@@ -14526,8 +14385,7 @@ function parseJunitXml(xml) {
14526
14385
  }
14527
14386
  return out;
14528
14387
  }
14529
- //#endregion
14530
- //#region src/forensics/parse-trx.ts
14388
+ // src/forensics/parse-trx.ts
14531
14389
  const MAX_INPUT = 20971520;
14532
14390
  function decodeEntities(s) {
14533
14391
  return s.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&#39;", "'").replaceAll("&amp;", "&");
@@ -14638,11 +14496,24 @@ function parseTrxXml(xml) {
14638
14496
  if (id !== void 0 && name !== void 0) byTestId.set(id, name);
14639
14497
  }
14640
14498
  const records = [];
14641
- const resultRe = /<UnitTestResult\b([^>]*)>([\s\S]{0,4000}?)(?=<UnitTestResult\b|<\/Results>)/gi;
14642
- let m;
14643
- while ((m = resultRe.exec(xml)) !== null) {
14644
- const attrs = m[1] ?? "";
14645
- const inner = m[2] ?? "";
14499
+ const startRe = /<UnitTestResult\b/gi;
14500
+ const starts = [];
14501
+ let s;
14502
+ while ((s = startRe.exec(xml)) !== null) {
14503
+ starts.push(s.index);
14504
+ if (starts.length > 2e5) break;
14505
+ }
14506
+ const resultsEnd = xml.search(/<\/Results>/i);
14507
+ for (let i = 0; i < starts.length; i++) {
14508
+ const start = starts[i];
14509
+ const tagEnd = xml.indexOf(">", start);
14510
+ if (tagEnd === -1) break;
14511
+ const selfClosing = xml[tagEnd - 1] === "/";
14512
+ const attrs = xml.slice(start + 15, selfClosing ? tagEnd - 1 : tagEnd);
14513
+ const next = starts[i + 1] ?? -1;
14514
+ let bodyEnd = next === -1 ? xml.length : next;
14515
+ if (resultsEnd > tagEnd && resultsEnd < bodyEnd) bodyEnd = resultsEnd;
14516
+ const inner = selfClosing ? "" : xml.slice(tagEnd + 1, bodyEnd);
14646
14517
  const testName = attr(attrs, "testName") ?? attr(attrs, "testId");
14647
14518
  if (testName === void 0) continue;
14648
14519
  const attempt = {
@@ -14650,7 +14521,8 @@ function parseTrxXml(xml) {
14650
14521
  status: mapTrxOutcome(attr(attrs, "outcome")),
14651
14522
  durationMs: parseDuration(attr(attrs, "duration"))
14652
14523
  };
14653
- const error = messageOf(inner, 0);
14524
+ const messageAt = inner.search(/<Message\b/i);
14525
+ const error = messageAt === -1 ? void 0 : messageOf(inner, messageAt);
14654
14526
  records.push({
14655
14527
  file: "unknown",
14656
14528
  title: testName,
@@ -14662,8 +14534,7 @@ function parseTrxXml(xml) {
14662
14534
  }
14663
14535
  return records;
14664
14536
  }
14665
- //#endregion
14666
- //#region src/forensics/parse-playwright-json.ts
14537
+ // src/forensics/parse-playwright-json.ts
14667
14538
  const MAX_DEPTH = 32;
14668
14539
  function toStatus$1(raw) {
14669
14540
  switch (raw) {
@@ -14728,8 +14599,7 @@ function parsePlaywrightJson(json) {
14728
14599
  walk(root, 0);
14729
14600
  return out;
14730
14601
  }
14731
- //#endregion
14732
- //#region src/forensics/parse-vitest-json.ts
14602
+ // src/forensics/parse-vitest-json.ts
14733
14603
  function toStatus(raw) {
14734
14604
  switch (raw) {
14735
14605
  case "passed": return "passed";
@@ -14783,8 +14653,7 @@ function parseVitestJson(json) {
14783
14653
  }
14784
14654
  return out;
14785
14655
  }
14786
- //#endregion
14787
- //#region src/forensics/parse-har.ts
14656
+ // src/forensics/parse-har.ts
14788
14657
  const MAX_ENTRIES = 2e4;
14789
14658
  function sanitizeTitleUrl(url) {
14790
14659
  try {
@@ -14869,8 +14738,7 @@ function parseHarJsonDetailed(json) {
14869
14738
  truncated
14870
14739
  };
14871
14740
  }
14872
- //#endregion
14873
- //#region src/forensics/run.ts
14741
+ // src/forensics/run.ts
14874
14742
  /**
14875
14743
  * Runtime evidence entry point (R4) — the library behind `explain
14876
14744
  * --evidence`. The `forensics` verb is retired in the v6 carve; this module
@@ -15161,8 +15029,7 @@ function loadSuppressions(root, now = /* @__PURE__ */ new Date()) {
15161
15029
  entries
15162
15030
  };
15163
15031
  }
15164
- //#endregion
15165
- //#region src/plugins/reserved-prefixes.ts
15032
+ // src/plugins/reserved-prefixes.ts
15166
15033
  /**
15167
15034
  * Core-owned rule-ID prefixes that external rule sources (npm plugins,
15168
15035
  * JS modules, JSON manifests) may never claim — one shared law.
@@ -15192,8 +15059,7 @@ function isReservedPrefix(ruleId) {
15192
15059
  const upper = ruleId.toUpperCase();
15193
15060
  return RESERVED_PREFIXES.some((p) => upper.startsWith(p));
15194
15061
  }
15195
- //#endregion
15196
- //#region src/plugins/load.ts
15062
+ // src/plugins/load.ts
15197
15063
  /**
15198
15064
  * Plugin API (Upgrade-Plan-v3 Phase 6, tier-2 item #1).
15199
15065
  *
@@ -15299,8 +15165,7 @@ function loadPlugins(root, gateOpen = false) {
15299
15165
  }
15300
15166
  return result;
15301
15167
  }
15302
- //#endregion
15303
- //#region src/plugins/local-rules.ts
15168
+ // src/plugins/local-rules.ts
15304
15169
  /**
15305
15170
  * Local plugin/rule contract (Verification Trust Evolution Plan §18 —
15306
15171
  * Local Extensibility).
@@ -15612,8 +15477,7 @@ async function loadModuleRules(path, result) {
15612
15477
  result.rules.push(rule);
15613
15478
  }
15614
15479
  }
15615
- //#endregion
15616
- //#region src/plugins/trust-gate.ts
15480
+ // src/plugins/trust-gate.ts
15617
15481
  /**
15618
15482
  * Plugin trust gate (audit-remediation-master-plan.md, locked decision C2).
15619
15483
  *
@@ -15649,8 +15513,7 @@ function renderGateNotice(skipped) {
15649
15513
  lines.push("To run them, re-run with --enable-plugins (or set MJOLNIR_ENABLE_PLUGINS=1).", "Declarative JSON rule manifests (mjolnir-rules/*.json) are unaffected — they execute no code.");
15650
15514
  return lines.join("\n");
15651
15515
  }
15652
- //#endregion
15653
- //#region src/engine/scan-pipeline.ts
15516
+ // src/engine/scan-pipeline.ts
15654
15517
  /**
15655
15518
  * Canonical scan pipeline (M6 — blueprint §9.2).
15656
15519
  *
@@ -15710,9 +15573,10 @@ async function buildUniversalRules(root, strict, opts = {}) {
15710
15573
  for (const p of plugins) for (const r of p.rules) if (r.tier) tierByRuleId.set(r.id, r.tier);
15711
15574
  for (const r of local.rules) if (r.tier) tierByRuleId.set(r.id, r.tier);
15712
15575
  let rules = [...UNIVERSAL_RULES, ...externalRules];
15713
- const withheldRuleIds = rules.filter((r) => tierByRuleId.get(r.id) === "quarantine").map((r) => r.id).sort();
15576
+ const quarantined = rules.filter((r) => tierByRuleId.get(r.id) === "quarantine").map((r) => r.id).sort();
15577
+ const withheldRuleIds = strict ? [] : quarantined;
15714
15578
  if (!strict) rules = rules.filter((r) => tierByRuleId.get(r.id) !== "quarantine");
15715
- const rulesWithheld = strict ? 0 : withheldRuleIds.length;
15579
+ const rulesWithheld = withheldRuleIds.length;
15716
15580
  const rulesApplied = rules.length;
15717
15581
  const pluginMeta = [...plugins.map((p) => ({
15718
15582
  name: p.name,
@@ -16109,12 +15973,28 @@ function applyPostScanProcessing(findings, workspace, args, hooks, scanRoot, dec
16109
15973
  const runtimeIncomplete = discoveredReport !== void 0 && discoveredReport.report.analysisComplete !== true;
16110
15974
  let forensicVerdicts;
16111
15975
  let evidenceRecords = [];
16112
- if (discoveredReport && discoveredReport.report.analysisComplete === true) try {
16113
- evidenceRecords = buildEvidenceRecords(discoveredReport.report, discoveredReport.path);
16114
- stampRuntimeCorroboration(findings, discoveredReport.report, workspace.root);
16115
- forensicVerdicts = summarizeForensicVerdicts(discoveredReport.report);
16116
- } catch {
16117
- evidenceRecords = [];
15976
+ if (discoveredReport && discoveredReport.report.analysisComplete === true) {
15977
+ const before = findings.map((f) => ({
15978
+ runtimeCorroboration: f.runtimeCorroboration,
15979
+ trustLevel: f.trustLevel
15980
+ }));
15981
+ try {
15982
+ const records = buildEvidenceRecords(discoveredReport.report, discoveredReport.path);
15983
+ stampRuntimeCorroboration(findings, discoveredReport.report, workspace.root);
15984
+ forensicVerdicts = summarizeForensicVerdicts(discoveredReport.report);
15985
+ evidenceRecords = records;
15986
+ } catch {
15987
+ findings.forEach((f, i) => {
15988
+ const prev = before[i];
15989
+ if (prev?.runtimeCorroboration === void 0) delete f.runtimeCorroboration;
15990
+ else f.runtimeCorroboration = prev.runtimeCorroboration;
15991
+ if (prev?.trustLevel === void 0) delete f.trustLevel;
15992
+ else f.trustLevel = prev.trustLevel;
15993
+ });
15994
+ forensicVerdicts = void 0;
15995
+ evidenceRecords = [];
15996
+ recordDegradation("runtime-corroboration-failed");
15997
+ }
16118
15998
  }
16119
15999
  return {
16120
16000
  evidenceRecords,
@@ -16547,8 +16427,7 @@ async function runScanInProject(args, hooks = {}) {
16547
16427
  resetTsMorphProject();
16548
16428
  return result;
16549
16429
  }
16550
- //#endregion
16551
- //#region src/cli-io.ts
16430
+ // src/cli-io.ts
16552
16431
  /**
16553
16432
  * The ONE error-to-message derivation in `src/`.
16554
16433
  *
@@ -16707,8 +16586,7 @@ function buildMachineContract(result) {
16707
16586
  ...result.forensicVerdicts !== void 0 ? { forensicVerdicts: result.forensicVerdicts } : {}
16708
16587
  };
16709
16588
  }
16710
- //#endregion
16711
- //#region src/commands/fixture-example.ts
16589
+ // src/commands/fixture-example.ts
16712
16590
  /**
16713
16591
  * Shared example-fixture selection for `mjolnir explain` (explain.ts)
16714
16592
  * and the generated rule docs (rule-docs.ts) — one chooser so both
@@ -16809,8 +16687,7 @@ function explainRule(ruleId, fixturesRoot) {
16809
16687
  exampleFixtureRelPath: relative(fixturesRoot, fixturePath).replaceAll("\\", "/")
16810
16688
  };
16811
16689
  }
16812
- //#endregion
16813
- //#region src/engine/resolution.ts
16690
+ // src/engine/resolution.ts
16814
16691
  /** Correlation identity (v1-compatible): ruleId\0file\0message. */
16815
16692
  const fingerprint$1 = findingFingerprint;
16816
16693
  /**
@@ -16961,8 +16838,7 @@ function diffAgainstBaseline(result, baseline) {
16961
16838
  unchangedCount
16962
16839
  };
16963
16840
  }
16964
- //#endregion
16965
- //#region src/commands/verify.ts
16841
+ // src/commands/verify.ts
16966
16842
  /**
16967
16843
  * `mjolnir ci verify` — the agent-loop digest (product-gap-remediation
16968
16844
  * master plan P7, plan 1788853205786 — flag agent, decision 6).
@@ -17109,8 +16985,7 @@ function renderTriageWorkflowJson(report) {
17109
16985
  rows: workflowRows(report)
17110
16986
  }, null, 2) + "\n";
17111
16987
  }
17112
- //#endregion
17113
- //#region src/commands/pw-report.ts
16988
+ // src/commands/pw-report.ts
17114
16989
  const ui = plainContext();
17115
16990
  function summarizePwRun(report) {
17116
16991
  const slowest = [...report.verdicts].sort((a, b) => b.totalDurationMs - a.totalDurationMs).slice(0, 5).map((v) => ({
@@ -17144,8 +17019,7 @@ function renderPwRunSummary(s) {
17144
17019
  }
17145
17020
  return lines.join("\n");
17146
17021
  }
17147
- //#endregion
17148
- //#region src/mcp/server.ts
17022
+ // src/mcp/server.ts
17149
17023
  /**
17150
17024
  * MCP stdio transport (blueprint §21 — Phase 4, PURE TRANSPORT).
17151
17025
  *
@@ -17163,7 +17037,10 @@ function renderPwRunSummary(s) {
17163
17037
  * - parameter size caps; ONE scan in flight (serialized queue) reusing
17164
17038
  * the pipeline's own budgets; zero network (no fetches anywhere); no
17165
17039
  * shell execution beyond Mjölnir's own verbs.
17166
- * - filesystem boundary = scan target + `.mjolnir/` (same as CLI).
17040
+ * - filesystem boundary: the tools are read-only, and with no further
17041
+ * configuration they accept any path the parent process passes (the
17042
+ * parent is the trust boundary). `MJOLNIR_WORKSPACE_ROOT` confines every
17043
+ * path argument to that directory, symlinks resolved.
17167
17044
  * - the plugin trust gate applies unchanged (--enable-plugins is NOT
17168
17045
  * exposed over MCP: tools run with the gate CLOSED unless the server
17169
17046
  * was started with MJOLNIR_ENABLE_PLUGINS=1 in its own environment).
@@ -17173,12 +17050,27 @@ const MAX_PARAM_BYTES = 65536;
17173
17050
  const MAX_DURATION_MS = 36e5;
17174
17051
  const DEFAULT_MAX_DURATION_MS = 6e5;
17175
17052
  const PROTOCOL_VERSION = "2025-06-18";
17176
- const CONFIGURED_ROOT = process.env.MJOLNIR_WORKSPACE_ROOT ? resolve(process.env.MJOLNIR_WORKSPACE_ROOT) : null;
17053
+ /** The real path of `p`, or its lexical resolution when it does not exist. */
17054
+ function realOrResolved(p) {
17055
+ try {
17056
+ return realpathSync.native(resolve(p));
17057
+ } catch {
17058
+ return resolve(p);
17059
+ }
17060
+ }
17061
+ /**
17062
+ * The optional workspace root. When set, every path argument must resolve
17063
+ * INSIDE it after symlinks are followed.
17064
+ *
17065
+ * Unset is open by design and is documented that way in the header: the MCP
17066
+ * tools are read-only and the stdio parent is the trust boundary. Setting
17067
+ * `MJOLNIR_WORKSPACE_ROOT` is how a host confines them.
17068
+ */
17069
+ const CONFIGURED_ROOT = process.env.MJOLNIR_WORKSPACE_ROOT ? realOrResolved(process.env.MJOLNIR_WORKSPACE_ROOT) : null;
17177
17070
  function isPathAllowed(path) {
17178
17071
  if (!CONFIGURED_ROOT) return true;
17179
- const candidate = resolve(path);
17180
- const rel = relative(CONFIGURED_ROOT, candidate);
17181
- return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
17072
+ const rel = relative(CONFIGURED_ROOT, realOrResolved(path));
17073
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
17182
17074
  }
17183
17075
  /** JSON-RPC error codes (subset used here). */
17184
17076
  const MCP_ERRORS = {
@@ -17655,8 +17547,7 @@ async function handleMcpMessage(msg) {
17655
17547
  }
17656
17548
  };
17657
17549
  }
17658
- //#endregion
17659
- //#region src/mcp/transport.ts
17550
+ // src/mcp/transport.ts
17660
17551
  /**
17661
17552
  * MCP stdio transport loop (blueprint §21 — the framing around the
17662
17553
  * pure-transport server).
@@ -17753,8 +17644,7 @@ async function handleStdioLine(line, write) {
17753
17644
  }
17754
17645
  write(`${JSON.stringify(response)}\n`);
17755
17646
  }
17756
- //#endregion
17757
- //#region src/integrations/sentry.ts
17647
+ // src/integrations/sentry.ts
17758
17648
  /**
17759
17649
  * Sentry crash reporting — opt-in, and inert unless a DSN is configured.
17760
17650
  *
@@ -17880,8 +17770,7 @@ async function flushSentry(timeoutMs = FLUSH_TIMEOUT_MS) {
17880
17770
  return false;
17881
17771
  }
17882
17772
  }
17883
- //#endregion
17884
- //#region src/mcp/stdio.ts
17773
+ // src/mcp/stdio.ts
17885
17774
  /**
17886
17775
  * The standalone MCP stdio binary (blueprint §21), built to
17887
17776
  * `dist/mcp/stdio.mjs` and run by `npm run mcp`.
@@ -17906,7 +17795,6 @@ runStdioTransport(process.stdin, process.stdout).then(() => process.exit(0)).cat
17906
17795
  process.stderr.write(`mjolnir mcp fatal: ${err instanceof Error ? err.message : err}\n`);
17907
17796
  process.exitCode = 20;
17908
17797
  });
17909
- //#endregion
17910
17798
  export {};
17911
17799
 
17912
17800
  //# sourceMappingURL=stdio.mjs.map