@cejel/cejel 0.4.6 → 0.4.7

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.
@@ -5,12 +5,13 @@ import {
5
5
  describeGitFailure,
6
6
  execGit,
7
7
  isExpectedGitAbsence
8
- } from "./chunk-BZXBLDCJ.js";
8
+ } from "./chunk-OIGCYULH.js";
9
9
  import {
10
10
  external_exports
11
- } from "./chunk-Y6NO5HAI.js";
11
+ } from "./chunk-UYJBSQCV.js";
12
12
  import {
13
13
  MAX_REPOSITORY_CONTENT_BYTES,
14
+ contentReadSignalKey,
14
15
  init_esm_shims,
15
16
  readRepoText,
16
17
  readRepoTextPrefix,
@@ -18,8 +19,10 @@ import {
18
19
  recordContentSkipForCriteria,
19
20
  recordFilesystemSkip,
20
21
  trackContentReads,
21
- withContentReadCriterion
22
- } from "./chunk-H55KECVT.js";
22
+ withContentReadCriterion,
23
+ withContentReadSignal,
24
+ withContentReadSignals
25
+ } from "./chunk-ZNVILRHK.js";
23
26
 
24
27
  // src/witan/schemas.ts
25
28
  init_esm_shims();
@@ -374,6 +377,11 @@ var WitanAttestationStatementSchema = external_exports.object({
374
377
  // explicit report contract version.
375
378
  reportFormatVersion: external_exports.string().regex(/^1\.\d+$/).optional(),
376
379
  rubricVersion: external_exports.string().min(1).max(120),
380
+ // Only present when the scan ran under the GitHub Action (GITHUB_RUN_ATTEMPT observed in
381
+ // the runner environment). Absent, never fabricated or defaulted to "1", for a local CLI
382
+ // or any other CI. Not part of report.json: it is per-invocation, not per-repository-
383
+ // state, and report.json's reproducibility guarantee excludes exactly that kind of field.
384
+ githubRunAttempt: external_exports.string().regex(/^[1-9][0-9]*$/).optional(),
377
385
  repository: external_exports.object({
378
386
  productSlug: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/),
379
387
  url: external_exports.string().url().optional(),
@@ -415,13 +423,15 @@ var WITAN_RUBRIC_VERSION_V19 = "witan-rubric-v19-prospective-2026-08-09";
415
423
  var WITAN_RUBRIC_VERSION_V20 = "witan-rubric-v20-prospective-2026-08-10";
416
424
  var WITAN_RUBRIC_VERSION_V21 = "witan-rubric-v21-prospective-2026-08-10";
417
425
  var WITAN_RUBRIC_VERSION_V22 = "witan-rubric-v22-prospective-2026-08-10";
426
+ var WITAN_RUBRIC_VERSION_V23 = "witan-rubric-v23-prospective-2026-09-06";
418
427
  var WITAN_LAST_CALIBRATED_RUBRIC_VERSION = WITAN_RUBRIC_VERSION_V17;
419
428
  var WITAN_PROSPECTIVE_RUBRIC_VERSIONS = Object.freeze([
420
429
  WITAN_RUBRIC_VERSION_V18,
421
430
  WITAN_RUBRIC_VERSION_V19,
422
431
  WITAN_RUBRIC_VERSION_V20,
423
432
  WITAN_RUBRIC_VERSION_V21,
424
- WITAN_RUBRIC_VERSION_V22
433
+ WITAN_RUBRIC_VERSION_V22,
434
+ WITAN_RUBRIC_VERSION_V23
425
435
  ]);
426
436
  var WITAN_SELECTABLE_RUBRIC_VERSIONS = Object.freeze([
427
437
  WITAN_LAST_CALIBRATED_RUBRIC_VERSION,
@@ -810,6 +820,32 @@ function glossaryEntryForMetric(metric2) {
810
820
  function formatCertificateMetricLabel(metric2) {
811
821
  return CERTIFICATE_METRIC_REGISTRY[metric2.name]?.displayLabel ?? metric2.label;
812
822
  }
823
+ function formatCertificateMetricAppliedWeightPercent(criterion, metric2) {
824
+ const metrics = criterion.metrics;
825
+ const index = metrics.indexOf(metric2);
826
+ if (index === -1) {
827
+ throw new Error(
828
+ "formatCertificateMetricAppliedWeightPercent: metric does not belong to criterion.metrics"
829
+ );
830
+ }
831
+ const totalWeight = metrics.reduce((sum, m) => sum + m.weight, 0);
832
+ if (totalWeight <= 0) return 0;
833
+ const shares = metrics.map((m, i) => {
834
+ const raw = m.weight / totalWeight * 100;
835
+ const floor = Math.floor(raw);
836
+ return { i, floor, fraction: raw - floor };
837
+ });
838
+ const flooredTotal = shares.reduce((sum, s) => sum + s.floor, 0);
839
+ const byLargestRemainder = [...shares].sort((a, b) => b.fraction - a.fraction);
840
+ const result = new Map(shares.map((s) => [s.i, s.floor]));
841
+ let remainder = Math.round(100 - flooredTotal);
842
+ for (const s of byLargestRemainder) {
843
+ if (remainder <= 0) break;
844
+ result.set(s.i, (result.get(s.i) ?? s.floor) + 1);
845
+ remainder -= 1;
846
+ }
847
+ return result.get(index) ?? 0;
848
+ }
813
849
  function glossaryEntriesForReport(report) {
814
850
  const entries = new Map(CERTIFICATE_GLOSSARY.map((entry) => [entry.key, entry]));
815
851
  for (const metric2 of report.criteria.flatMap((criterion) => criterion.metrics)) {
@@ -925,6 +961,74 @@ function formatCertificateMetricValue(criterion, metric2) {
925
961
  `${formatMetricNumber(metric2.value)}/${formatMetricNumber(metric2.max)}${unit}`
926
962
  );
927
963
  }
964
+ var NEXT_STEP_FINDING_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
965
+ var NEXT_STEP_FINDING_SEVERITY_RANK = {
966
+ critical: 0,
967
+ warning: 1,
968
+ info: 2
969
+ };
970
+ var MAX_NEXT_STEP_FINDINGS = 3;
971
+ function buildNextSteps(report, unmeasured, notApplicable, a1, coverageMetric) {
972
+ const steps = [];
973
+ if (unmeasured.length > 0) {
974
+ const ids = unmeasured.map((criterion) => criterion.id).join(", ");
975
+ steps.push(
976
+ `Obtain measurable evidence for ${ids} \u2014 ${unmeasured.length === 1 ? "this scored dimension has" : "these scored dimensions have"} no recorded result, and could change the verdict.`
977
+ );
978
+ }
979
+ const criterionFindings = report.criteria.flatMap(
980
+ (criterion) => criterion.findings.filter((finding) => NEXT_STEP_FINDING_SEVERITIES.has(finding.severity)).map((finding) => ({ criterionId: criterion.id, finding }))
981
+ );
982
+ const sortedFindings = [...criterionFindings].sort(
983
+ (a, b) => NEXT_STEP_FINDING_SEVERITY_RANK[a.finding.severity] - NEXT_STEP_FINDING_SEVERITY_RANK[b.finding.severity]
984
+ );
985
+ for (const { criterionId, finding } of sortedFindings.slice(0, MAX_NEXT_STEP_FINDINGS)) {
986
+ steps.push(
987
+ `Evidence is currently absent for the ${finding.severity} finding on ${criterionId}: obtain independent verification before relying on this criterion.`
988
+ );
989
+ }
990
+ if (sortedFindings.length > MAX_NEXT_STEP_FINDINGS) {
991
+ steps.push(
992
+ `${sortedFindings.length - MAX_NEXT_STEP_FINDINGS} more critical/warning finding(s) need the same independent verification; see the findings listed above for the complete set.`
993
+ );
994
+ }
995
+ if (a1 && coverageMetric && isCoverageNotMeasured(a1, coverageMetric)) {
996
+ steps.push(
997
+ "Obtain a coverage report or configured threshold for A1's static coverage metric; none was found, so it was not measured."
998
+ );
999
+ }
1000
+ if (!report.repo.headSha && report.criteria.some(
1001
+ (criterion) => criterion.metrics.some((metric2) => metric2.name === "pr_merge_ratio")
1002
+ )) {
1003
+ steps.push(
1004
+ "Restore Git history access so the recent-commit PR-reference proxy has commit subjects to inspect; its scoring zero here is a conservative default, not a detected outcome."
1005
+ );
1006
+ }
1007
+ if (notApplicable.length > 0) {
1008
+ const ids = notApplicable.map((criterion) => criterion.id).join(", ");
1009
+ steps.push(
1010
+ `Confirm ${ids} ${notApplicable.length === 1 ? "is" : "are"} correctly marked not applicable to this repository; ${notApplicable.length === 1 ? "it was" : "they were"} not examined.`
1011
+ );
1012
+ }
1013
+ const scanLimitationCount = report.scanLimitations?.length ?? 0;
1014
+ if (scanLimitationCount > 0) {
1015
+ steps.push(
1016
+ `Resolve the ${scanLimitationCount} scan limitation${scanLimitationCount === 1 ? "" : "s"} qualifying the readable evidence, then reproduce the scan.`
1017
+ );
1018
+ }
1019
+ const skippedCount = report.contentReadSummary?.skipped ?? 0;
1020
+ if (skippedCount > 0) {
1021
+ steps.push(
1022
+ `Make the ${skippedCount} skipped content entr${skippedCount === 1 ? "y" : "ies"} readable (see the itemized reasons in the certificate), then reproduce the scan.`
1023
+ );
1024
+ }
1025
+ if (steps.length === 0) {
1026
+ steps.push(
1027
+ "No unresolved evidence gaps were identified across the categories Cejel tracks; reproduce the scan at the reported revision when one is available before relying on this certificate."
1028
+ );
1029
+ }
1030
+ return steps;
1031
+ }
928
1032
  function buildRelyingPartySummary(report) {
929
1033
  const applicable = report.criteria.filter((criterion) => criterion.status !== "not_applicable");
930
1034
  const measured = applicable.filter((criterion) => criterion.status !== "insufficient_data");
@@ -972,7 +1076,7 @@ function buildRelyingPartySummary(report) {
972
1076
  examined: `Cejel examined the repository evidence recorded for ${report.productDisplayName}${revision} under ${report.rubricVersion}.`,
973
1077
  established: `The report established measured results for ${measured.length} of ${applicable.length} applicable rubric dimensions and recorded ${findings} evidence-backed finding${findings === 1 ? "" : "s"}.`,
974
1078
  notEstablished: gaps.join(" "),
975
- next: "Review the cited evidence and open or unverified items, reproduce the scan at the reported revision when one is available, and obtain missing measurements or resolve limitations before deciding whether to accept the code."
1079
+ next: buildNextSteps(report, unmeasured, notApplicable, a1, coverageMetric).join(" ")
976
1080
  };
977
1081
  }
978
1082
  function formatMetricNumber(value) {
@@ -1091,6 +1195,7 @@ function renderWitanHtmlReport(report, options = {}) {
1091
1195
  ${options.generatedAt ? `<div><dt>Date</dt><dd>${escapeHtml(formatDate(options.generatedAt))}</dd></div>` : ""}
1092
1196
  <div><dt>Run</dt><dd>${escapeHtml(renderRepo(report))}</dd></div>
1093
1197
  <div><dt>CLI</dt><dd>${escapeHtml(options.cliVersion ? `Cejel ${options.cliVersion}` : "Not recorded")}</dd></div>
1198
+ ${options.runAttempt ? `<div><dt>Run attempt</dt><dd>${escapeHtml(options.runAttempt)}</dd></div>` : ""}
1094
1199
  <div><dt>Rubric</dt><dd>${escapeHtml(report.rubricVersion)}</dd></div>
1095
1200
  ${// Gated on the rubric actually being prospective — a calibrated (v17) certificate's
1096
1201
  // HTML stays byte-identical to before --rubric-pin existed.
@@ -1113,6 +1218,8 @@ function renderWitanHtmlReport(report, options = {}) {
1113
1218
  </div>
1114
1219
  </header>
1115
1220
 
1221
+ ${renderFindingsFirstSummary(report)}
1222
+
1116
1223
  ${renderRelyingPartySummary(relyingPartySummary)}
1117
1224
 
1118
1225
  ${(report.scanLimitations?.length ?? 0) > 0 ? `<section class="scan-limitations-section evidence-section" aria-label="Scan limitations">
@@ -1154,6 +1261,26 @@ function renderWitanHtmlReport(report, options = {}) {
1154
1261
  </html>
1155
1262
  `;
1156
1263
  }
1264
+ var FINDINGS_FIRST_SEVERITY_ORDER = {
1265
+ critical: 0,
1266
+ warning: 1,
1267
+ info: 2
1268
+ };
1269
+ function collectFindingsFirstEntries(report) {
1270
+ return report.criteria.flatMap(
1271
+ (criterion) => criterion.findings.filter((finding) => finding.severity === "critical" || finding.severity === "warning").map((finding) => ({ criterion, finding }))
1272
+ ).sort(
1273
+ (a, b) => FINDINGS_FIRST_SEVERITY_ORDER[a.finding.severity] - FINDINGS_FIRST_SEVERITY_ORDER[b.finding.severity]
1274
+ );
1275
+ }
1276
+ function renderFindingsFirstSummary(report) {
1277
+ const entries = collectFindingsFirstEntries(report);
1278
+ const body = entries.length > 0 ? `<ul>${entries.map(({ criterion, finding }) => `<li>${renderFindingEvidence(criterion, finding)}</li>`).join("")}</ul>` : '<p class="findings-first-empty">No critical or warning findings were identified across any criterion in this scan.</p>';
1279
+ return `<section class="findings-first-summary" aria-labelledby="findings-first-heading">
1280
+ <h2 id="findings-first-heading">Critical and warning findings</h2>
1281
+ ${body}
1282
+ </section>`;
1283
+ }
1157
1284
  function renderRelyingPartySummary(summary) {
1158
1285
  return `<section class="relying-party-summary" aria-labelledby="relying-party-heading">
1159
1286
  <h2 id="relying-party-heading">How to read this certificate</h2>
@@ -1167,10 +1294,12 @@ function renderRelyingPartySummary(summary) {
1167
1294
  }
1168
1295
  function renderGlossary(entries) {
1169
1296
  return `<section class="glossary" aria-labelledby="glossary-heading">
1170
- <h2 id="glossary-heading">Plain-language glossary</h2>
1171
- <dl>${entries.map(
1297
+ <details>
1298
+ <summary><h2 id="glossary-heading">Plain-language glossary</h2></summary>
1299
+ <dl>${entries.map(
1172
1300
  (entry) => `<div><dt id="glossary-${escapeAttribute(entry.key)}">${escapeHtml(entry.term)}</dt><dd>${escapeHtml(entry.definition)}</dd></div>`
1173
1301
  ).join("")}</dl>
1302
+ </details>
1174
1303
  </section>`;
1175
1304
  }
1176
1305
  function renderContentReadSummary(report) {
@@ -1245,12 +1374,12 @@ function renderCriterionColumn(title, criteria, gitHistoryUnavailable) {
1245
1374
  </section>`;
1246
1375
  }
1247
1376
  function renderNotApplicableGroup(criteria) {
1248
- return `<div class="na-group">
1249
- <h3 class="na-heading">Not applicable to this repository</h3>
1377
+ return `<details class="na-group">
1378
+ <summary><h3 class="na-heading">Not applicable to this repository</h3></summary>
1250
1379
  <ul class="na-list">
1251
1380
  ${criteria.map(renderNotApplicableItem).join("")}
1252
1381
  </ul>
1253
- </div>`;
1382
+ </details>`;
1254
1383
  }
1255
1384
  function renderNotApplicableItem(criterion) {
1256
1385
  const reason = criterion.notes ?? "Not applicable to this repository.";
@@ -1310,15 +1439,21 @@ function scoreBandForPresentation(score) {
1310
1439
  if (score >= 1.5) return "warning";
1311
1440
  return "critical";
1312
1441
  }
1442
+ function renderMetricWeightBadge(criterion, metric2) {
1443
+ const appliedSharePercent = formatCertificateMetricAppliedWeightPercent(criterion, metric2);
1444
+ return `<span class="metric-weight">weight ${appliedSharePercent}% of ${escapeHtml(criterion.id)}</span>`;
1445
+ }
1313
1446
  function renderMetric(criterion, metric2) {
1314
1447
  const entry = glossaryEntryForMetric(metric2);
1315
1448
  const tooltipId = `tooltip-${criterion.id}-${entry.key}`;
1316
1449
  const formatted = formatCertificateMetricValue(criterion, metric2);
1450
+ const weightBadge = renderMetricWeightBadge(criterion, metric2);
1317
1451
  const cappedEntry = formatted.includes("capped") ? CERTIFICATE_GLOSSARY.find((candidate) => candidate.key === "capped") : void 0;
1318
1452
  const label = `<span class="term-help" tabindex="0" aria-describedby="${escapeAttribute(tooltipId)}"><strong>${escapeHtml(formatCertificateMetricLabel(metric2))}</strong><span class="term-tooltip" id="${escapeAttribute(tooltipId)}" role="tooltip">${escapeHtml(entry.definition)}</span></span>`;
1319
- if (!cappedEntry) return `${label}<span class="metric-value">${escapeHtml(formatted)}</span>`;
1453
+ if (!cappedEntry)
1454
+ return `${label}<span class="metric-value">${escapeHtml(formatted)} ${weightBadge}</span>`;
1320
1455
  const cappedTooltipId = `${tooltipId}-capped`;
1321
- return `${label}<span class="term-help metric-value" tabindex="0" aria-describedby="${escapeAttribute(cappedTooltipId)}">${escapeHtml(formatted)}<span class="term-tooltip" id="${escapeAttribute(cappedTooltipId)}" role="tooltip">${escapeHtml(cappedEntry.definition)}</span></span>`;
1456
+ return `${label}<span class="term-help metric-value" tabindex="0" aria-describedby="${escapeAttribute(cappedTooltipId)}">${escapeHtml(formatted)} ${weightBadge}<span class="term-tooltip" id="${escapeAttribute(cappedTooltipId)}" role="tooltip">${escapeHtml(cappedEntry.definition)}</span></span>`;
1322
1457
  }
1323
1458
  function renderCategoryScore(score, coverage, category) {
1324
1459
  const categoryCoverage = coverage.byCategory.find((entry) => entry.category === category);
@@ -1466,6 +1601,9 @@ dd { margin: 0; color: var(--muted); overflow-wrap: anywhere; }
1466
1601
  .subscores { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 18px; color: var(--muted); font-family: var(--mono); font-size: 12px; }
1467
1602
  .coverage-note { margin-top: 8px; color: var(--faint); font-family: var(--mono); font-size: 11px; }
1468
1603
  .source-counts { margin: 8px 0 0; padding-left: 18px; font-family: var(--mono); font-size: 12px; color: var(--muted); }
1604
+ .findings-first-summary { margin-top: 20px; border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface); padding: 22px; }
1605
+ .findings-first-summary ul { margin: 0; padding-left: 18px; display: grid; gap: 10px; }
1606
+ .findings-first-empty { margin: 0; color: var(--muted); }
1469
1607
  .relying-party-summary, .glossary { margin-top: 28px; border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface); padding: 22px; }
1470
1608
  .relying-party-summary dl { display: grid; gap: 14px; margin: 0; }
1471
1609
  .relying-party-summary dl div { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 18px; }
@@ -1473,6 +1611,14 @@ dd { margin: 0; color: var(--muted); overflow-wrap: anywhere; }
1473
1611
  .glossary dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 22px; margin: 0; }
1474
1612
  .glossary dl div { border-top: 1px solid var(--line); padding-top: 10px; }
1475
1613
  .glossary dd { margin-top: 4px; font-size: 13px; }
1614
+ /* Track A3: native <details>/<summary> disclosure \u2014 the summary is the clickable control, a
1615
+ heading nests inside it as its accessible label (still exposed as a heading), and the heading's
1616
+ own vertical margin is neutralized by making it inline, so the marker sits flush against it. */
1617
+ summary { cursor: pointer; }
1618
+ summary h2, summary h3 { display: inline; }
1619
+ summary::marker { color: var(--periwinkle); }
1620
+ summary:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--periwinkle); border-radius: 3px; }
1621
+ .glossary details[open] dl, .na-group[open] .na-list { margin-top: 16px; }
1476
1622
  .trust-grid, .evidence-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; margin-top: 28px; }
1477
1623
  .scan-limitations-section { margin-top: 28px; border-color: rgba(231, 191, 114, .44); }
1478
1624
  .scan-limitations-section .scan-warning { list-style: none; margin-left: -18px; }
@@ -1504,6 +1650,7 @@ h3 { font-size: 15px; line-height: 1.35; font-weight: 600; margin-bottom: 0; }
1504
1650
  .criterion-metrics li { display: flex; justify-content: space-between; gap: 12px; border-bottom: 1px solid rgba(238, 244, 247, .08); padding-bottom: 6px; }
1505
1651
  .criterion-metrics strong { color: var(--text); font-weight: 650; }
1506
1652
  .criterion-metrics span { color: var(--muted); }
1653
+ .criterion-metrics .metric-weight { font-size: 10px; color: var(--faint); }
1507
1654
  .criterion-metrics > li > .metric-value {
1508
1655
  min-width: 0; white-space: normal; overflow-wrap: anywhere; text-align: right;
1509
1656
  }
@@ -1667,6 +1814,7 @@ function createWitanAttestation(report, options) {
1667
1814
  tool: { name: "cejel", version: options.toolVersion },
1668
1815
  generatedAt: options.generatedAt,
1669
1816
  reportFormatVersion: WITAN_REPORT_FORMAT_VERSION,
1817
+ ...options.githubRunAttempt ? { githubRunAttempt: options.githubRunAttempt } : {},
1670
1818
  rubricVersion: report.rubricVersion,
1671
1819
  repository: {
1672
1820
  productSlug: report.productSlug,
@@ -1788,7 +1936,7 @@ function stripBom(text) {
1788
1936
 
1789
1937
  // src/witan/repo-signals.ts
1790
1938
  function usesV17DetectorClosure(rubricVersion) {
1791
- return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
1939
+ return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
1792
1940
  }
1793
1941
  var LEGACY_INVENTORY_ABSENCE_CONTEXT = {
1794
1942
  rubricGeneration: "legacy",
@@ -1803,10 +1951,14 @@ var INVENTORY_SCAN_PATTERN_SETS = {
1803
1951
  id: "cejel.core-a1.concrete-test-files.v1",
1804
1952
  count: 16
1805
1953
  },
1806
- a1CoverageConfiguration: {
1954
+ a1CoverageConfigurationV1: {
1807
1955
  id: "cejel.core-a1.coverage-configuration.v1",
1808
1956
  count: 10
1809
1957
  },
1958
+ a1CoverageConfigurationV2: {
1959
+ id: "cejel.core-a1.coverage-configuration.v2",
1960
+ count: 14
1961
+ },
1810
1962
  a2CurrentSecret: {
1811
1963
  id: "cejel.core-a2.current-secret-shape.v1",
1812
1964
  count: 3
@@ -1855,25 +2007,77 @@ function inventoryAbsenceFinding(options) {
1855
2007
  } : {}
1856
2008
  };
1857
2009
  }
2010
+ function wholesaleCriterionAbstention(signal) {
2011
+ return {
2012
+ criterionId: signal.criterionId,
2013
+ positiveEvidence: [],
2014
+ findings: [],
2015
+ metrics: [],
2016
+ insufficientData: true,
2017
+ notes: "Cejel abstained on this criterion because one or more relevant repository files could not be read. Resolve the content-read limitations and re-scan."
2018
+ };
2019
+ }
1858
2020
  function buildWitanInputFromRepo(options) {
1859
2021
  const tracked = trackContentReads(options.repoPath, () => buildWitanInputFromRepoUntracked(options));
1860
2022
  const affectedCriteria = tracked.affectedCriteria;
2023
+ const rubricVersion = options.rubricVersion ?? WITAN_RUBRIC_VERSION;
2024
+ if (rubricVersion !== WITAN_RUBRIC_VERSION_V23) {
2025
+ const signals2 = (tracked.value.signals ?? []).map(
2026
+ (signal) => affectedCriteria.has(signal.criterionId) ? wholesaleCriterionAbstention(signal) : signal
2027
+ );
2028
+ const affectedCount = affectedCriteria.size;
2029
+ const scanLimitations2 = [...tracked.value.scanLimitations ?? []];
2030
+ if (affectedCount > 0 && scanLimitations2.length < 16) {
2031
+ scanLimitations2.push(
2032
+ `${affectedCount} ${affectedCount === 1 ? "criterion" : "criteria"} abstained because relevant repository content could not be read. Counts and errno classes are recorded in contentReadSummary; file paths are intentionally omitted.`
2033
+ );
2034
+ }
2035
+ return {
2036
+ ...tracked.value,
2037
+ signals: signals2,
2038
+ ...scanLimitations2.length > 0 ? { scanLimitations: scanLimitations2 } : {},
2039
+ contentReadSummary: tracked.summary
2040
+ };
2041
+ }
2042
+ const wholesaleAbstainedCriteria = [];
2043
+ const partiallyAbstainedSignals = [];
1861
2044
  const signals = (tracked.value.signals ?? []).map((signal) => {
1862
2045
  if (!affectedCriteria.has(signal.criterionId)) return signal;
2046
+ if (tracked.unattributedCriteria.has(signal.criterionId)) {
2047
+ wholesaleAbstainedCriteria.push(signal.criterionId);
2048
+ return wholesaleCriterionAbstention(signal);
2049
+ }
2050
+ const prefix = contentReadSignalKey(signal.criterionId, "");
2051
+ const affectedNames = [...tracked.affectedSignals].filter((key) => key.startsWith(prefix)).map((key) => key.slice(prefix.length));
2052
+ if (affectedNames.length === 0) {
2053
+ wholesaleAbstainedCriteria.push(signal.criterionId);
2054
+ return wholesaleCriterionAbstention(signal);
2055
+ }
2056
+ const affectedNameSet = new Set(affectedNames);
2057
+ const metrics = (signal.metrics ?? []).filter((metric2) => !affectedNameSet.has(metric2.name));
2058
+ const anythingSurvives = metrics.length > 0 || (signal.positiveEvidence?.length ?? 0) > 0 || (signal.findings?.length ?? 0) > 0;
2059
+ if (!anythingSurvives) {
2060
+ wholesaleAbstainedCriteria.push(signal.criterionId);
2061
+ return wholesaleCriterionAbstention(signal);
2062
+ }
2063
+ for (const name of affectedNames) {
2064
+ partiallyAbstainedSignals.push(`${signal.criterionId}.${name}`);
2065
+ }
1863
2066
  return {
1864
- criterionId: signal.criterionId,
1865
- positiveEvidence: [],
1866
- findings: [],
1867
- metrics: [],
1868
- insufficientData: true,
1869
- notes: "Cejel abstained on this criterion because one or more relevant repository files could not be read. Resolve the content-read limitations and re-scan."
2067
+ ...signal,
2068
+ metrics,
2069
+ notes: `Cejel abstained on ${[...affectedNames].sort().join(", ")} in this criterion because that content could not be read; other signals in this criterion were computed from readable files and are unaffected.`
1870
2070
  };
1871
2071
  });
1872
- const affectedCount = affectedCriteria.size;
1873
2072
  const scanLimitations = [...tracked.value.scanLimitations ?? []];
1874
- if (affectedCount > 0 && scanLimitations.length < 16) {
2073
+ if (wholesaleAbstainedCriteria.length > 0 && scanLimitations.length < 16) {
1875
2074
  scanLimitations.push(
1876
- `${affectedCount} ${affectedCount === 1 ? "criterion" : "criteria"} abstained because relevant repository content could not be read. Counts and errno classes are recorded in contentReadSummary; file paths are intentionally omitted.`
2075
+ `${wholesaleAbstainedCriteria.length} ${wholesaleAbstainedCriteria.length === 1 ? "criterion" : "criteria"} abstained because relevant repository content could not be read and the affected signal could not be isolated. Counts and errno classes are recorded in contentReadSummary; file paths are intentionally omitted.`
2076
+ );
2077
+ }
2078
+ if (partiallyAbstainedSignals.length > 0 && scanLimitations.length < 16) {
2079
+ scanLimitations.push(
2080
+ `${partiallyAbstainedSignals.length} ${partiallyAbstainedSignals.length === 1 ? "signal" : "signals"} abstained because its own repository content could not be read, while other signals in the same criterion were unaffected: ${[...partiallyAbstainedSignals].sort().join(", ")}.`
1877
2081
  );
1878
2082
  }
1879
2083
  return {
@@ -1897,11 +2101,12 @@ function buildWitanInputFromRepoUntracked(options) {
1897
2101
  rubricGeneration: usesV17Detectors ? "current" : "legacy",
1898
2102
  inventoryProvenance: fileInventory.repoFileInventoryScope === "tracked-scan-eligible" ? "tracked-scan-eligible" : "directory-walk"
1899
2103
  };
1900
- const usesV18NativeRls = rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
1901
- const usesV19CommitYear = rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
1902
- const usesV20A3ExplicitGaps = rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
1903
- const usesV21ExecutedEscalations = rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
1904
- const usesV22PackageStartEntrypoint = rubricVersion === WITAN_RUBRIC_VERSION_V22;
2104
+ const usesV18NativeRls = rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
2105
+ const usesV19CommitYear = rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
2106
+ const usesV20A3ExplicitGaps = rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
2107
+ const usesV21ExecutedEscalations = rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
2108
+ const usesV22PackageStartEntrypoint = rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
2109
+ const usesV23CommandCoverage = rubricVersion === WITAN_RUBRIC_VERSION_V23;
1905
2110
  const structuralArchetype = classifyRepoArchetype(inventoryFiles, rubricVersion);
1906
2111
  const readableArchetype = rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion) ? applyReadableSourceRepresentationGate(
1907
2112
  options.repoPath,
@@ -1937,6 +2142,7 @@ function buildWitanInputFromRepoUntracked(options) {
1937
2142
  usesV20A3ExplicitGaps,
1938
2143
  usesV21ExecutedEscalations,
1939
2144
  usesV22PackageStartEntrypoint,
2145
+ usesV23CommandCoverage,
1940
2146
  reviewableSourceProof,
1941
2147
  inventoryAbsenceContext,
1942
2148
  scanLimitations
@@ -2548,7 +2754,7 @@ function buildNotApplicableSignal(criterionId, reason) {
2548
2754
  notApplicable: true
2549
2755
  };
2550
2756
  }
2551
- function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set()) {
2757
+ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, useV23CommandCoverage, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set()) {
2552
2758
  const signals = [];
2553
2759
  const mono = resolveMonorepoContext(repoPath, scanLimitations);
2554
2760
  const collectCriterion = (criterionId, collect) => withContentReadCriterion(criterionId, collect);
@@ -2560,6 +2766,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
2560
2766
  useV27Detectors,
2561
2767
  useV33Detectors,
2562
2768
  useV36Detectors,
2769
+ useV23CommandCoverage,
2563
2770
  reviewableSourceProof,
2564
2771
  inventoryAbsenceContext
2565
2772
  )
@@ -2659,7 +2866,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
2659
2866
  }
2660
2867
  return signals;
2661
2868
  }
2662
- function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
2869
+ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV23CommandCoverage, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
2663
2870
  const evidence = [];
2664
2871
  const findings = [];
2665
2872
  const allTestFiles = findConcreteTestFiles(
@@ -2670,17 +2877,33 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2670
2877
  const testFiles = allTestFiles.slice(0, 8);
2671
2878
  const sourceFiles = repoFiles.filter(isSourceFile);
2672
2879
  const runnerFiles = repoFiles.filter(
2673
- (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isTestRunnerConfig(repoPath, file)
2880
+ (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && (isTestRunnerConfig(repoPath, file) || useV23CommandCoverage && isRecipeTestEntry(repoPath, file))
2674
2881
  ).slice(0, 6);
2675
- const configuredRunnerFiles = useV33Detectors ? findConfiguredTestRunnerFiles(repoPath, repoFiles) : runnerFiles;
2676
- const allCoverageFiles = findCoverageConfigFiles(repoPath, repoFiles, useV27Detectors);
2677
- const coverageFiles = allCoverageFiles.slice(0, 4);
2882
+ const baseConfiguredRunnerFiles = useV33Detectors ? findConfiguredTestRunnerFiles(repoPath, repoFiles) : runnerFiles;
2883
+ const configuredRunnerFiles = useV23CommandCoverage ? [
2884
+ .../* @__PURE__ */ new Set([
2885
+ ...baseConfiguredRunnerFiles,
2886
+ ...repoFiles.filter((file) => isRecipeTestEntry(repoPath, file))
2887
+ ])
2888
+ ].sort() : baseConfiguredRunnerFiles;
2678
2889
  const packageJson = findRootPackageJson(repoFiles);
2679
2890
  const packageScripts = packageJson ? readPackageScripts(join(repoPath, packageJson)) : /* @__PURE__ */ new Map();
2891
+ const { coverageAnalysis, coveragePercent } = withContentReadSignal("A1", "coverage_percent", () => {
2892
+ const analysis = useV23CommandCoverage ? analyzeCoverageConfiguration(repoPath, repoFiles, useV27Detectors) : {
2893
+ coverageFiles: findCoverageConfigFiles(repoPath, repoFiles, useV27Detectors),
2894
+ unresolvedFiles: [],
2895
+ hasCoverageCommand: packageScripts.has("coverage")
2896
+ };
2897
+ return {
2898
+ coverageAnalysis: analysis,
2899
+ coveragePercent: readCoveragePercent(repoPath, analysis.coverageFiles.slice(0, 4))
2900
+ };
2901
+ });
2902
+ const allCoverageFiles = coverageAnalysis.coverageFiles;
2903
+ const coverageFiles = allCoverageFiles.slice(0, 4);
2680
2904
  const packageJsonFiles = useV27Detectors ? repoFiles.filter(
2681
2905
  (file) => /(^|\/)package\.json$/.test(file) && isAuthoredProductionPath(file)
2682
2906
  ) : packageJson ? [packageJson] : [];
2683
- const coveragePercent = readCoveragePercent(repoPath, coverageFiles);
2684
2907
  const ciWorkflows = repoFiles.filter(
2685
2908
  (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isCiWorkflow(file)
2686
2909
  );
@@ -2695,7 +2918,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2695
2918
  );
2696
2919
  const verificationScriptCount = [
2697
2920
  packageScripts.has("test") || Boolean(ciTestWorkflow),
2698
- packageScripts.has("coverage"),
2921
+ coverageAnalysis.hasCoverageCommand,
2699
2922
  packageScripts.has("lint") || ciHasLintCommand,
2700
2923
  packageScripts.has("typecheck") || ciHasTypecheckCommand
2701
2924
  ].filter(Boolean).length;
@@ -2715,9 +2938,11 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2715
2938
  if (packageJson && [...packageScripts.values()].some((script) => TEST_RUNNER_PATTERN.test(script))) {
2716
2939
  evidence.push(evidenceForRelative(repoPath, packageJson, "test_run", "Configured test runner"));
2717
2940
  }
2718
- for (const file of coverageFiles) {
2719
- evidence.push(evidenceForRelative(repoPath, file, "coverage", "Coverage configuration"));
2720
- }
2941
+ withContentReadSignal("A1", "coverage_percent", () => {
2942
+ for (const file of coverageFiles) {
2943
+ evidence.push(evidenceForRelative(repoPath, file, "coverage", "Coverage configuration"));
2944
+ }
2945
+ });
2721
2946
  if (ciTestWorkflow) {
2722
2947
  evidence.push(
2723
2948
  evidenceForRelative(repoPath, ciTestWorkflow, "test_run", "CI workflow runs the test suite")
@@ -2787,7 +3012,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2787
3012
  })
2788
3013
  );
2789
3014
  }
2790
- } else if (coverageFiles.length === 0 && (!useV33Detectors || configuredRunnerFiles.length > 0)) {
3015
+ } else if (coverageFiles.length === 0 && coverageAnalysis.unresolvedFiles.length === 0 && (!useV33Detectors || configuredRunnerFiles.length > 0)) {
2791
3016
  if (isLeanTestToolchain) {
2792
3017
  const leanEvidenceFile = packageJson ?? testFiles[0];
2793
3018
  if (leanEvidenceFile) {
@@ -2814,16 +3039,16 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2814
3039
  evidence: firstEvidence,
2815
3040
  context: inventoryAbsenceContext,
2816
3041
  inventoryCount: repoFiles.length,
2817
- patternSet: INVENTORY_SCAN_PATTERN_SETS.a1CoverageConfiguration,
3042
+ patternSet: useV23CommandCoverage ? INVENTORY_SCAN_PATTERN_SETS.a1CoverageConfigurationV2 : INVENTORY_SCAN_PATTERN_SETS.a1CoverageConfigurationV1,
2818
3043
  matchCount: 0
2819
3044
  })
2820
3045
  );
2821
3046
  }
2822
3047
  }
2823
- const nonHollowShare = measureNonHollowTestShare(
2824
- repoPath,
2825
- allTestFiles,
2826
- reviewableSourceProof !== void 0
3048
+ const nonHollowShare = withContentReadSignal(
3049
+ "A1",
3050
+ "non_hollow_test_share",
3051
+ () => measureNonHollowTestShare(repoPath, allTestFiles, reviewableSourceProof !== void 0)
2827
3052
  );
2828
3053
  return {
2829
3054
  criterionId: "A1",
@@ -2864,7 +3089,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2864
3089
  label: "test command",
2865
3090
  count: packageScripts.has("test") || Boolean(ciTestWorkflow) ? 1 : 0
2866
3091
  },
2867
- { label: "coverage command", count: packageScripts.has("coverage") ? 1 : 0 },
3092
+ { label: "coverage command", count: coverageAnalysis.hasCoverageCommand ? 1 : 0 },
2868
3093
  {
2869
3094
  label: "lint command",
2870
3095
  count: packageScripts.has("lint") || ciHasLintCommand ? 1 : 0
@@ -2887,7 +3112,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
2887
3112
  "Penalizes skipped or placeholder-only test files; test-directory support scaffolding (helpers/fixtures with no test in them) is excluded from the denominator."
2888
3113
  )
2889
3114
  ],
2890
- notes: "A1 is detected from real test files, test runner configuration, and optional coverage configuration."
3115
+ notes: coverageFiles.length === 0 && coverageAnalysis.unresolvedFiles.length > 0 ? `A1 is detected from real test files, test runner configuration, and optional coverage configuration. Coverage command reachability or flag ownership was unresolved in ${coverageAnalysis.unresolvedFiles.slice(0, 4).join(", ")}; the no-coverage absence claim was withheld.` : "A1 is detected from real test files, test runner configuration, and optional coverage configuration."
2891
3116
  };
2892
3117
  }
2893
3118
  function buildA1AuthenticatedAbsenceSignal(repoPath, proof, inventoryCount, inventoryAbsenceContext) {
@@ -2979,40 +3204,47 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
2979
3204
  const migrationFiles = repoFiles.filter(
2980
3205
  (file) => /(^|\/)(migrations?|drizzle|prisma)\//.test(file)
2981
3206
  );
2982
- let committedSecret;
2983
- for (const path of repoFiles) {
2984
- const authoredProductionPath = useV47Detectors ? isV47AuthoredProductionPath(path) : useV39Detectors ? isV39AuthoredProductionPath(path) : isAuthoredProductionPath(path);
2985
- if (isIgnoredScanFile(path, useV47Detectors) || useV33Detectors && !authoredProductionPath) {
2986
- continue;
2987
- }
2988
- const match = findCommittedSecretInFile(
2989
- repoPath,
2990
- path,
2991
- useV36Detectors,
2992
- useV39Detectors,
2993
- useV47Detectors
2994
- );
2995
- if (!match) continue;
2996
- committedSecret = { path, match };
2997
- break;
2998
- }
2999
- const currentSecretFingerprintsByPath = /* @__PURE__ */ new Map();
3000
- if (useV27Detectors) {
3001
- for (const path of repoFiles.filter((candidate) => isCredentialHistoryPath(candidate))) {
3002
- currentSecretFingerprintsByPath.set(
3003
- path,
3004
- new Set(
3005
- findSecretFingerprintsInFile(
3006
- repoPath,
3207
+ const { committedSecret, currentSecretFingerprintsByPath } = withContentReadSignal(
3208
+ "A2",
3209
+ "secret_cleanliness",
3210
+ () => {
3211
+ let match_;
3212
+ for (const path of repoFiles) {
3213
+ const authoredProductionPath = useV47Detectors ? isV47AuthoredProductionPath(path) : useV39Detectors ? isV39AuthoredProductionPath(path) : isAuthoredProductionPath(path);
3214
+ if (isIgnoredScanFile(path, useV47Detectors) || useV33Detectors && !authoredProductionPath) {
3215
+ continue;
3216
+ }
3217
+ const found = findCommittedSecretInFile(
3218
+ repoPath,
3219
+ path,
3220
+ useV36Detectors,
3221
+ useV39Detectors,
3222
+ useV47Detectors
3223
+ );
3224
+ if (!found) continue;
3225
+ match_ = { path, match: found };
3226
+ break;
3227
+ }
3228
+ const fingerprintsByPath = /* @__PURE__ */ new Map();
3229
+ if (useV27Detectors) {
3230
+ for (const path of repoFiles.filter((candidate) => isCredentialHistoryPath(candidate))) {
3231
+ fingerprintsByPath.set(
3007
3232
  path,
3008
- useV36Detectors,
3009
- useV39Detectors,
3010
- useV47Detectors
3011
- )
3012
- )
3013
- );
3233
+ new Set(
3234
+ findSecretFingerprintsInFile(
3235
+ repoPath,
3236
+ path,
3237
+ useV36Detectors,
3238
+ useV39Detectors,
3239
+ useV47Detectors
3240
+ )
3241
+ )
3242
+ );
3243
+ }
3244
+ }
3245
+ return { committedSecret: match_, currentSecretFingerprintsByPath: fingerprintsByPath };
3014
3246
  }
3015
- }
3247
+ );
3016
3248
  const historySecretScan = committedSecret && !useV27Detectors ? null : collectHistorySecretEvidence(
3017
3249
  repoPath,
3018
3250
  useV27Detectors,
@@ -3024,10 +3256,14 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
3024
3256
  useV47Detectors
3025
3257
  );
3026
3258
  const hasConfirmedSecretFinding = committedSecret != null || historySecretScan?.evidence != null;
3027
- const cryptoHygiene = collectCryptoHygieneEvidence(
3028
- repoPath,
3029
- repoFiles.filter(isImplementationFile).slice(0, 60),
3030
- useV39Detectors
3259
+ const cryptoHygiene = withContentReadSignal(
3260
+ "A2",
3261
+ "crypto_comparison_hygiene",
3262
+ () => collectCryptoHygieneEvidence(
3263
+ repoPath,
3264
+ repoFiles.filter(isImplementationFile).slice(0, 60),
3265
+ useV39Detectors
3266
+ )
3031
3267
  );
3032
3268
  const implFiles = repoFiles.filter(isImplementationFile);
3033
3269
  const gitignoreHasEnvRule = gitignore != null && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m);
@@ -3067,42 +3303,77 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
3067
3303
  } else if (dbLayerImportFile != null) {
3068
3304
  evidence.push(evidenceForRelative(repoPath, dbLayerImportFile, "artifact", "DB client import"));
3069
3305
  }
3070
- const v47RlsPolicyFiles = useV47Detectors ? findRlsPolicyFiles(repoPath, repoFiles) : [];
3071
- const nativeRlsInventory = useV18NativeRls ? deriveRlsPolicyScopeInventory(repoPath, repoFiles) : null;
3072
- const tenantWithoutRlsPremiseFiles = useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
3073
- const v47TenantStorageFiles = useV47Detectors && !useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
3074
- const rlsMigration = useV47Detectors ? v47RlsPolicyFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, RLS_PATTERN));
3075
3306
  const tenantScopePattern = useV39Detectors ? TENANT_SCOPE_PATTERN_V11 : useV36Detectors ? TENANT_SCOPE_PATTERN_V10 : TENANT_SCOPE_PATTERN;
3076
- const tenantScopedFile = useV18NativeRls ? nativeRlsInventory?.storageFiles[0] : useV47Detectors ? v47TenantStorageFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, tenantScopePattern));
3077
- const rlsPolicyCount = useV18NativeRls ? nativeRlsInventory?.policyCount ?? 0 : useV47Detectors ? v47RlsPolicyFiles.length : countPatternMatches(
3078
- repoPath,
3079
- migrationFiles,
3080
- /create policy|enable row level security|force row level security/gi
3307
+ const {
3308
+ rlsMigration,
3309
+ tenantScopedFile,
3310
+ rlsPolicyCount,
3311
+ tenantScopedMigrationFileCount,
3312
+ nativeRlsInventory,
3313
+ tenantWithoutRlsPremiseFiles
3314
+ } = withContentReadSignals("A2", ["rls_policy_count", "tenant_scope_ratio"], () => {
3315
+ const v47RlsPolicyFiles = useV47Detectors ? findRlsPolicyFiles(repoPath, repoFiles) : [];
3316
+ const rlsInventory = useV18NativeRls ? deriveRlsPolicyScopeInventory(repoPath, repoFiles) : null;
3317
+ const tenantWithoutRlsFiles = useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
3318
+ const v47TenantStorageFiles = useV47Detectors && !useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
3319
+ return {
3320
+ rlsMigration: useV47Detectors ? v47RlsPolicyFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, RLS_PATTERN)),
3321
+ tenantScopedFile: useV18NativeRls ? rlsInventory?.storageFiles[0] : useV47Detectors ? v47TenantStorageFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, tenantScopePattern)),
3322
+ rlsPolicyCount: useV18NativeRls ? rlsInventory?.policyCount ?? 0 : useV47Detectors ? v47RlsPolicyFiles.length : countPatternMatches(
3323
+ repoPath,
3324
+ migrationFiles,
3325
+ /create policy|enable row level security|force row level security/gi
3326
+ ),
3327
+ tenantScopedMigrationFileCount: useV18NativeRls ? rlsInventory?.storageFiles.length ?? 0 : useV47Detectors ? v47TenantStorageFiles.length : countFilesContaining(repoPath, migrationFiles, tenantScopePattern),
3328
+ nativeRlsInventory: rlsInventory,
3329
+ tenantWithoutRlsPremiseFiles: tenantWithoutRlsFiles
3330
+ };
3331
+ });
3332
+ const { gitignoreEnvFile, hasEnvReadsInImplementation, envHandlingDepth } = withContentReadSignal(
3333
+ "A2",
3334
+ "env_handling_depth",
3335
+ () => {
3336
+ const envFile = gitignore && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m) ? gitignore : null;
3337
+ const hasEnvReads = countFilesContaining(repoPath, repoFiles.filter(isImplementationFile), ENV_READ_PATTERN) > 0;
3338
+ const depth = (envExamples.length > 0 ? 1 : 0) + (envFile ? 1 : 0) + (hasEnvReads ? 1 : 0);
3339
+ return { gitignoreEnvFile: envFile, hasEnvReadsInImplementation: hasEnvReads, envHandlingDepth: depth };
3340
+ }
3081
3341
  );
3082
- const tenantScopedMigrationFileCount = useV18NativeRls ? nativeRlsInventory?.storageFiles.length ?? 0 : useV47Detectors ? v47TenantStorageFiles.length : countFilesContaining(repoPath, migrationFiles, tenantScopePattern);
3083
- const gitignoreEnvFile = gitignore && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m) ? gitignore : null;
3084
- const envHandlingDepth = (envExamples.length > 0 ? 1 : 0) + (gitignoreEnvFile ? 1 : 0) + (countFilesContaining(repoPath, repoFiles.filter(isImplementationFile), ENV_READ_PATTERN) > 0 ? 1 : 0);
3085
3342
  const isMultiTenant = useV18NativeRls ? (nativeRlsInventory?.policyCount ?? 0) > 0 && (nativeRlsInventory?.scopeIdentifiers.length ?? 0) > 0 : tenantScopedMigrationFileCount > 0;
3086
3343
  if (gitignoreEnvFile) {
3087
- evidence.push(
3088
- evidenceForRelative(repoPath, gitignoreEnvFile, "secret_scan", ".env files are gitignored")
3344
+ withContentReadSignal(
3345
+ "A2",
3346
+ "env_handling_depth",
3347
+ () => evidence.push(
3348
+ evidenceForRelative(repoPath, gitignoreEnvFile, "secret_scan", ".env files are gitignored")
3349
+ )
3089
3350
  );
3090
3351
  }
3091
- for (const file of envExamples.slice(0, 3)) {
3092
- evidence.push(evidenceForRelative(repoPath, file, "secret_scan", "Environment template"));
3093
- }
3352
+ withContentReadSignal("A2", "env_handling_depth", () => {
3353
+ for (const file of envExamples.slice(0, 3)) {
3354
+ evidence.push(evidenceForRelative(repoPath, file, "secret_scan", "Environment template"));
3355
+ }
3356
+ });
3094
3357
  if (rlsMigration) {
3095
- evidence.push(
3096
- evidenceForRelative(repoPath, rlsMigration, "artifact", "RLS or tenant migration")
3358
+ withContentReadSignals(
3359
+ "A2",
3360
+ ["rls_policy_count", "tenant_scope_ratio"],
3361
+ () => evidence.push(
3362
+ evidenceForRelative(repoPath, rlsMigration, "artifact", "RLS or tenant migration")
3363
+ )
3097
3364
  );
3098
3365
  }
3099
3366
  if (tenantScopedFile) {
3100
- evidence.push(
3101
- evidenceForRelative(
3102
- repoPath,
3103
- tenantScopedFile,
3104
- "artifact",
3105
- useV18NativeRls ? `Repository-native RLS scope (${nativeRlsInventory?.scopeIdentifiers.join(", ")})` : "Tenant scoping signal"
3367
+ withContentReadSignals(
3368
+ "A2",
3369
+ ["rls_policy_count", "tenant_scope_ratio"],
3370
+ () => evidence.push(
3371
+ evidenceForRelative(
3372
+ repoPath,
3373
+ tenantScopedFile,
3374
+ "artifact",
3375
+ useV18NativeRls ? `Repository-native RLS scope (${nativeRlsInventory?.scopeIdentifiers.join(", ")})` : "Tenant scoping signal"
3376
+ )
3106
3377
  )
3107
3378
  );
3108
3379
  }
@@ -3226,11 +3497,7 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
3226
3497
  { label: ".gitignore environment-file rule", count: gitignoreEnvFile ? 1 : 0 },
3227
3498
  {
3228
3499
  label: "environment reads in implementation code",
3229
- count: countFilesContaining(
3230
- repoPath,
3231
- repoFiles.filter(isImplementationFile),
3232
- ENV_READ_PATTERN
3233
- ) > 0 ? 1 : 0
3500
+ count: hasEnvReadsInImplementation ? 1 : 0
3234
3501
  }
3235
3502
  ]
3236
3503
  }
@@ -3409,37 +3676,51 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
3409
3676
  const envTemplate = repoFiles.find(
3410
3677
  (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isEnvTemplatePath(file)
3411
3678
  );
3412
- const healthChecks = repoFiles.filter(
3413
- (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isHealthCheckSignalFile(repoPath, file)
3679
+ const healthChecks = withContentReadSignal(
3680
+ "A3",
3681
+ "prod_readiness_primitives",
3682
+ () => repoFiles.filter(
3683
+ (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isHealthCheckSignalFile(repoPath, file)
3684
+ )
3414
3685
  );
3415
3686
  const healthCheck = healthChecks[0];
3416
- const hasV20HealthOrReadinessRoute = useV20ExplicitGaps && repoFiles.some(
3417
- (file) => isAuthoredProductionPath(file) && isImplementationFile(file) && fileContains(repoPath, file, V20_HEALTH_OR_READINESS_ROUTE_PATTERN)
3418
- );
3419
- const hasV22PackageStartHealthOrReadinessRoute = v22PackageStartHttpEntrypoint !== null && fileContains(
3420
- repoPath,
3421
- v22PackageStartHttpEntrypoint,
3422
- V20_HEALTH_OR_READINESS_ROUTE_PATTERN
3423
- );
3687
+ const { hasV20HealthOrReadinessRoute, hasV22PackageStartHealthOrReadinessRoute } = withContentReadSignal("A3", "health_readiness_route", () => ({
3688
+ hasV20HealthOrReadinessRoute: useV20ExplicitGaps && repoFiles.some(
3689
+ (file) => isAuthoredProductionPath(file) && isImplementationFile(file) && fileContains(repoPath, file, V20_HEALTH_OR_READINESS_ROUTE_PATTERN)
3690
+ ),
3691
+ hasV22PackageStartHealthOrReadinessRoute: v22PackageStartHttpEntrypoint !== null && fileContains(
3692
+ repoPath,
3693
+ v22PackageStartHttpEntrypoint,
3694
+ V20_HEALTH_OR_READINESS_ROUTE_PATTERN
3695
+ )
3696
+ }));
3424
3697
  const serverEntrypoint = v22PackageStartHttpEntrypoint ?? findServerEntrypointFile(repoPath, repoFiles, useV27Detectors) ?? v20DirectHttpEntrypoint ?? null;
3425
3698
  const runtimeContainer = useV27Detectors ? findRuntimeContainerEntrypointFile(repoPath, repoFiles, useV20ExplicitGaps) : null;
3426
3699
  const errorBoundaries = repoFiles.filter(
3427
3700
  (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && /error-boundary|error\.(tsx|jsx|ts|js)$/.test(file)
3428
3701
  );
3429
3702
  const errorBoundary = errorBoundaries[0];
3430
- const observabilityCount = countFilesContaining(
3431
- repoPath,
3432
- repoFiles.filter(
3433
- (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isImplementationFile(file)
3434
- ),
3435
- /sentry|otel|opentelemetry|datadog|prometheus|metrics|logger|logtail/i
3703
+ const observabilityCount = withContentReadSignal(
3704
+ "A3",
3705
+ "observability_depth",
3706
+ () => countFilesContaining(
3707
+ repoPath,
3708
+ repoFiles.filter(
3709
+ (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isImplementationFile(file)
3710
+ ),
3711
+ /sentry|otel|opentelemetry|datadog|prometheus|metrics|logger|logtail/i
3712
+ )
3436
3713
  );
3437
- const rollbackSafetyCount = countFilesContaining(
3438
- repoPath,
3439
- repoFiles.filter(
3440
- (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && /(^|\/)(docs|migrations?|drizzle|prisma|scripts)\//.test(file)
3441
- ),
3442
- /rollback|roll back|migration safety|down migration|reversible|undo migration/i
3714
+ const rollbackSafetyCount = withContentReadSignal(
3715
+ "A3",
3716
+ "rollback_safety_depth",
3717
+ () => countFilesContaining(
3718
+ repoPath,
3719
+ repoFiles.filter(
3720
+ (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && /(^|\/)(docs|migrations?|drizzle|prisma|scripts)\//.test(file)
3721
+ ),
3722
+ /rollback|roll back|migration safety|down migration|reversible|undo migration/i
3723
+ )
3443
3724
  );
3444
3725
  if (packageJson && (scripts.has("build") || scripts.has("typecheck"))) {
3445
3726
  evidence.push(
@@ -3467,10 +3748,15 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
3467
3748
  );
3468
3749
  if (envTemplate)
3469
3750
  evidence.push(evidenceForRelative(repoPath, envTemplate, "prod_check", "Environment template"));
3470
- if (healthCheck)
3471
- evidence.push(
3472
- evidenceForRelative(repoPath, healthCheck, "prod_check", "Health/readiness signal")
3751
+ if (healthCheck) {
3752
+ withContentReadSignal(
3753
+ "A3",
3754
+ "prod_readiness_primitives",
3755
+ () => evidence.push(
3756
+ evidenceForRelative(repoPath, healthCheck, "prod_check", "Health/readiness signal")
3757
+ )
3473
3758
  );
3759
+ }
3474
3760
  if (errorBoundary)
3475
3761
  evidence.push(evidenceForRelative(repoPath, errorBoundary, "prod_check", "Error boundary"));
3476
3762
  if (evidence.length === 0) {
@@ -3509,7 +3795,12 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
3509
3795
  )
3510
3796
  });
3511
3797
  } else if (runtimeContainer) {
3512
- if (!fileContains(repoPath, runtimeContainer, /^\s*HEALTHCHECK\s+(?!NONE\b)/im)) {
3798
+ const hasActiveHealthcheck = withContentReadSignal(
3799
+ "A3",
3800
+ "dockerfile_healthcheck",
3801
+ () => fileContains(repoPath, runtimeContainer, /^\s*HEALTHCHECK\s+(?!NONE\b)/im)
3802
+ );
3803
+ if (!hasActiveHealthcheck) {
3513
3804
  findings.push({
3514
3805
  severity: "info",
3515
3806
  summary: "A runtime Dockerfile declares no active HEALTHCHECK instruction.",
@@ -3521,10 +3812,14 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
3521
3812
  )
3522
3813
  });
3523
3814
  }
3524
- } else if (serverEntrypoint && fileContains(
3525
- repoPath,
3526
- serverEntrypoint,
3527
- /\b(?:request|req)\.(?:url|path|pathname|method)\b/i
3815
+ } else if (serverEntrypoint && withContentReadSignal(
3816
+ "A3",
3817
+ "health_readiness_route",
3818
+ () => fileContains(
3819
+ repoPath,
3820
+ serverEntrypoint,
3821
+ /\b(?:request|req)\.(?:url|path|pathname|method)\b/i
3822
+ )
3528
3823
  ) && healthChecks.length === 0 && !hasV20HealthOrReadinessRoute && !hasV22PackageStartHealthOrReadinessRoute) {
3529
3824
  findings.push({
3530
3825
  severity: "info",
@@ -3630,22 +3925,41 @@ function collectA4DependencyEvidence(repoPath, repoFiles, mono, useV27Detectors,
3630
3925
  const findings = [];
3631
3926
  const manifests = repoFiles.filter(isDependencyManifest);
3632
3927
  const lockfiles = repoFiles.filter(isLockfile);
3633
- const manifest = pickPrimaryDependencyManifest(repoPath, manifests);
3634
3928
  const lockfile = lockfiles[0];
3635
3929
  const rootLockfile = mono ? mono.sharedFiles.find(isLockfile) : void 0;
3636
3930
  const rootUpdateConfig = mono ? mono.sharedFiles.find(isDependencyUpdateConfig) : void 0;
3637
3931
  const hasLockfile = Boolean(lockfile) || Boolean(rootLockfile);
3638
3932
  const updateConfig = repoFiles.find(isDependencyUpdateConfig);
3639
3933
  const hasUpdateConfig = Boolean(updateConfig) || Boolean(rootUpdateConfig);
3640
- const auditConfig = manifests.some((file) => packageJsonHasAuditScript(repoPath, file));
3641
- const dependencySpecs = readDependencySpecs(repoPath, manifests);
3934
+ const auditConfig = withContentReadSignal(
3935
+ "A4",
3936
+ "dependency_automation_ratio",
3937
+ () => manifests.some((file) => packageJsonHasAuditScript(repoPath, file))
3938
+ );
3939
+ const { manifest, dependencySpecs } = withContentReadSignals(
3940
+ "A4",
3941
+ ["pinned_dependency_ratio", "declared_version_range_ratio", "dependency_count_sanity"],
3942
+ () => ({
3943
+ // Cite the manifest that actually carries dependencies for evidence/findings, not
3944
+ // whichever sorts first (e.g. a near-empty requirements-windows.txt that precedes the
3945
+ // real requirements.txt), so the evidence pointer reflects the file the A4 score is
3946
+ // derived from. The score itself is unaffected: it aggregates specs across ALL manifests.
3947
+ manifest: pickPrimaryDependencyManifest(repoPath, manifests),
3948
+ dependencySpecs: readDependencySpecs(repoPath, manifests)
3949
+ })
3950
+ );
3642
3951
  const pinnedDependencyCount = dependencySpecs.filter(
3643
3952
  (dependency) => isPinnedDependencyVersion(dependency.version)
3644
3953
  ).length;
3645
- if (manifest)
3646
- evidence.push(
3647
- evidenceForRelative(repoPath, manifest, "dependency_report", "Dependency manifest")
3954
+ if (manifest) {
3955
+ withContentReadSignals(
3956
+ "A4",
3957
+ ["pinned_dependency_ratio", "declared_version_range_ratio", "dependency_count_sanity"],
3958
+ () => evidence.push(
3959
+ evidenceForRelative(repoPath, manifest, "dependency_report", "Dependency manifest")
3960
+ )
3648
3961
  );
3962
+ }
3649
3963
  if (lockfile)
3650
3964
  evidence.push(
3651
3965
  evidenceForRelative(repoPath, lockfile, "dependency_report", "Dependency lockfile")
@@ -3678,28 +3992,36 @@ function collectA4DependencyEvidence(repoPath, repoFiles, mono, useV27Detectors,
3678
3992
  const deployableServiceFiles = useV47Detectors ? findDeployableServicePremiseFiles(repoPath, repoFiles, true) : [];
3679
3993
  const isAppOrService = (useV47Detectors ? deployableServiceFiles.length > 0 : isDeployableService(repoPath, repoFiles, useV27Detectors)) || packagedApplicationFiles.length > 0;
3680
3994
  if (isAppOrService && !hasLockfile && manifest) {
3681
- findings.push(
3682
- inventoryAbsenceFinding({
3683
- severity: "critical",
3684
- summaries: {
3685
- legacy: "Dependency manifest is present without a detected lockfile \u2014 non-reproducible installs.",
3686
- currentTracked: "Dependency manifest is present, but the tracked scan-eligible inventory matched no recognized lockfile.",
3687
- currentDirectoryWalk: "Dependency manifest is present, but the bounded directory walk matched no recognized lockfile."
3688
- },
3689
- evidence: evidenceForRelative(
3690
- repoPath,
3691
- manifest,
3692
- "dependency_report",
3693
- "Dependency manifest"
3694
- ),
3695
- context: inventoryAbsenceContext,
3696
- inventoryCount: repoFiles.length + (mono?.sharedFiles.length ?? 0),
3697
- patternSet: INVENTORY_SCAN_PATTERN_SETS.a4Lockfile,
3698
- matchCount: 0
3699
- })
3995
+ withContentReadSignals(
3996
+ "A4",
3997
+ ["pinned_dependency_ratio", "declared_version_range_ratio", "dependency_count_sanity"],
3998
+ () => findings.push(
3999
+ inventoryAbsenceFinding({
4000
+ severity: "critical",
4001
+ summaries: {
4002
+ legacy: "Dependency manifest is present without a detected lockfile \u2014 non-reproducible installs.",
4003
+ currentTracked: "Dependency manifest is present, but the tracked scan-eligible inventory matched no recognized lockfile.",
4004
+ currentDirectoryWalk: "Dependency manifest is present, but the bounded directory walk matched no recognized lockfile."
4005
+ },
4006
+ evidence: evidenceForRelative(
4007
+ repoPath,
4008
+ manifest,
4009
+ "dependency_report",
4010
+ "Dependency manifest"
4011
+ ),
4012
+ context: inventoryAbsenceContext,
4013
+ inventoryCount: repoFiles.length + (mono?.sharedFiles.length ?? 0),
4014
+ patternSet: INVENTORY_SCAN_PATTERN_SETS.a4Lockfile,
4015
+ matchCount: 0
4016
+ })
4017
+ )
3700
4018
  );
3701
4019
  }
3702
- if (manifest && hasSuspiciousDependencies(repoPath, manifest, useV47Detectors)) {
4020
+ if (manifest && withContentReadSignal(
4021
+ "A4",
4022
+ "suspicious_dependencies",
4023
+ () => hasSuspiciousDependencies(repoPath, manifest, useV47Detectors)
4024
+ )) {
3703
4025
  findings.push({
3704
4026
  severity: "critical",
3705
4027
  summary: "Dependency manifest contains obviously suspicious placeholder dependency names.",
@@ -3853,16 +4175,24 @@ function collectA5ClaimRealityEvidence(repoPath, repoFiles, useV27Detectors, inv
3853
4175
  ...allDocFiles,
3854
4176
  ...repoFiles.filter((file) => /(^|\/)(SECURITY|THREAT[_-]?MODEL)\.md$/i.test(file))
3855
4177
  ]);
3856
- const negativeSpaceDoc = [...negativeSpaceCandidates].find(
3857
- (file) => fileContains(repoPath, file, NEGATIVE_SPACE_SECTION_PATTERN) || fileContains(repoPath, file, NEGATIVE_SPACE_PHRASE_PATTERN)
4178
+ const negativeSpaceDoc = withContentReadSignal(
4179
+ "A5",
4180
+ "negative_space_documentation",
4181
+ () => [...negativeSpaceCandidates].find(
4182
+ (file) => fileContains(repoPath, file, NEGATIVE_SPACE_SECTION_PATTERN) || fileContains(repoPath, file, NEGATIVE_SPACE_PHRASE_PATTERN)
4183
+ )
3858
4184
  );
3859
4185
  if (negativeSpaceDoc) {
3860
- evidence.push(
3861
- evidenceForRelative(
3862
- repoPath,
3863
- negativeSpaceDoc,
3864
- "claim_reconciliation",
3865
- 'Documented limitations / threat model / "not covered" section'
4186
+ withContentReadSignal(
4187
+ "A5",
4188
+ "negative_space_documentation",
4189
+ () => evidence.push(
4190
+ evidenceForRelative(
4191
+ repoPath,
4192
+ negativeSpaceDoc,
4193
+ "claim_reconciliation",
4194
+ 'Documented limitations / threat model / "not covered" section'
4195
+ )
3866
4196
  )
3867
4197
  );
3868
4198
  }
@@ -3999,18 +4329,33 @@ function collectB3CiDisciplineEvidence(repoPath, repoFiles, mono) {
3999
4329
  const workflows = repoFiles.filter(isCiWorkflow);
4000
4330
  const workflow = workflows[0];
4001
4331
  const rootWorkflows = mono ? mono.sharedFiles.filter(isCiWorkflow) : [];
4002
- const defaultBranchCiCount = workflows.filter((file) => workflowTargetsDefaultBranch(repoPath, file)).length + (mono ? rootWorkflows.filter((file) => workflowTargetsDefaultBranch(mono.root, file)).length : 0);
4332
+ const defaultBranchCiCount = withContentReadSignal(
4333
+ "B3",
4334
+ "default_branch_ci_depth",
4335
+ () => workflows.filter((file) => workflowTargetsDefaultBranch(repoPath, file)).length + (mono ? rootWorkflows.filter((file) => workflowTargetsDefaultBranch(mono.root, file)).length : 0)
4336
+ );
4003
4337
  if (packageJson && scripts.has("test")) {
4004
4338
  evidence.push(evidenceForRelative(repoPath, packageJson, "test_run", "Test script"));
4005
4339
  }
4006
4340
  if (packageJson && scripts.has("lint")) {
4007
4341
  evidence.push(evidenceForRelative(repoPath, packageJson, "ci_run", "Lint script"));
4008
4342
  }
4009
- if (workflow) evidence.push(evidenceForRelative(repoPath, workflow, "ci_run", "CI workflow"));
4010
- else if (rootWorkflows[0] && mono)
4011
- evidence.push(
4012
- evidenceForRelative(mono.root, rootWorkflows[0], "ci_run", "CI workflow (monorepo root)")
4343
+ if (workflow) {
4344
+ withContentReadSignals(
4345
+ "B3",
4346
+ ["ci_script_depth", "default_branch_ci_depth"],
4347
+ () => evidence.push(evidenceForRelative(repoPath, workflow, "ci_run", "CI workflow"))
4348
+ );
4349
+ } else if (rootWorkflows[0] && mono) {
4350
+ const rootWorkflow = rootWorkflows[0];
4351
+ withContentReadSignals(
4352
+ "B3",
4353
+ ["ci_script_depth", "default_branch_ci_depth"],
4354
+ () => evidence.push(
4355
+ evidenceForRelative(mono.root, rootWorkflow, "ci_run", "CI workflow (monorepo root)")
4356
+ )
4013
4357
  );
4358
+ }
4014
4359
  if (evidence.length === 0) return null;
4015
4360
  const CI_BUILD_COMMAND_PATTERN = /\b(cargo\s+build|cmake|ninja|make\s+(build|all))\b/i;
4016
4361
  const CI_COMMAND_CATEGORIES = [
@@ -4020,10 +4365,19 @@ function collectB3CiDisciplineEvidence(repoPath, repoFiles, mono) {
4020
4365
  CI_BUILD_COMMAND_PATTERN
4021
4366
  ];
4022
4367
  const scriptDepth = ["test", "lint", "typecheck", "build"].filter((s) => scripts.has(s)).length;
4023
- const countCiCommandCategories = (root, files) => CI_COMMAND_CATEGORIES.filter(
4024
- (pattern) => files.some((file) => fileContains(root, file, pattern))
4025
- ).length;
4026
- const ciCommandDepth = countCiCommandCategories(repoPath, workflows) + (mono ? countCiCommandCategories(mono.root, rootWorkflows) : 0);
4368
+ const { repoCategoryHits, rootCategoryHits } = withContentReadSignal(
4369
+ "B3",
4370
+ "ci_script_depth",
4371
+ () => ({
4372
+ repoCategoryHits: CI_COMMAND_CATEGORIES.map(
4373
+ (pattern) => workflows.some((file) => fileContains(repoPath, file, pattern))
4374
+ ),
4375
+ rootCategoryHits: mono ? CI_COMMAND_CATEGORIES.map(
4376
+ (pattern) => rootWorkflows.some((file) => fileContains(mono.root, file, pattern))
4377
+ ) : []
4378
+ })
4379
+ );
4380
+ const ciCommandDepth = repoCategoryHits.filter(Boolean).length + rootCategoryHits.filter(Boolean).length;
4027
4381
  const combinedDepth = scriptDepth + ciCommandDepth;
4028
4382
  return {
4029
4383
  criterionId: "B3",
@@ -4045,13 +4399,13 @@ function collectB3CiDisciplineEvidence(repoPath, repoFiles, mono) {
4045
4399
  label: `package script: ${name === "typecheck" ? "type-check" : name}`,
4046
4400
  count: scripts.has(name) ? 1 : 0
4047
4401
  })),
4048
- ...CI_COMMAND_CATEGORIES.map((pattern, index) => ({
4402
+ ...CI_COMMAND_CATEGORIES.map((_pattern, index) => ({
4049
4403
  label: `repository CI command: ${["test", "lint", "type-check", "build"][index]}`,
4050
- count: workflows.some((file) => fileContains(repoPath, file, pattern)) ? 1 : 0
4404
+ count: repoCategoryHits[index] ? 1 : 0
4051
4405
  })),
4052
- ...mono ? CI_COMMAND_CATEGORIES.map((pattern, index) => ({
4406
+ ...mono ? CI_COMMAND_CATEGORIES.map((_pattern, index) => ({
4053
4407
  label: `monorepo-root CI command: ${["test", "lint", "type-check", "build"][index]}`,
4054
- count: rootWorkflows.some((file) => fileContains(mono.root, file, pattern)) ? 1 : 0
4408
+ count: rootCategoryHits[index] ? 1 : 0
4055
4409
  })) : []
4056
4410
  ]
4057
4411
  }
@@ -4096,9 +4450,11 @@ function readAuditFreshnessCommitterYear(repoPath, scanLimitations) {
4096
4450
  function collectB4AuditEvidence(repoPath, repoFiles, generatedAt, useV19CommitYear = false, scanLimitations = /* @__PURE__ */ new Set()) {
4097
4451
  const evidence = [];
4098
4452
  const auditFiles = repoFiles.filter(isAuditFile);
4099
- for (const file of auditFiles.slice(0, 5)) {
4100
- evidence.push(evidenceForRelative(repoPath, file, "audit_log", "Audit or changelog artifact"));
4101
- }
4453
+ withContentReadSignal("B4", "audit_freshness_depth", () => {
4454
+ for (const file of auditFiles.slice(0, 5)) {
4455
+ evidence.push(evidenceForRelative(repoPath, file, "audit_log", "Audit or changelog artifact"));
4456
+ }
4457
+ });
4102
4458
  if (evidence.length === 0) {
4103
4459
  return buildNotApplicableSignal(
4104
4460
  "B4",
@@ -4143,7 +4499,11 @@ function collectB4AuditEvidence(repoPath, repoFiles, generatedAt, useV19CommitYe
4143
4499
  metric(
4144
4500
  "audit_freshness_depth",
4145
4501
  "Audit freshness depth",
4146
- countFilesContaining(repoPath, auditFiles, freshnessPattern),
4502
+ withContentReadSignal(
4503
+ "B4",
4504
+ "audit_freshness_depth",
4505
+ () => countFilesContaining(repoPath, auditFiles, freshnessPattern)
4506
+ ),
4147
4507
  Math.max(auditFiles.length, 1),
4148
4508
  0.2,
4149
4509
  "ratio",
@@ -4156,93 +4516,128 @@ function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detecto
4156
4516
  const evidence = [];
4157
4517
  const findings = [];
4158
4518
  const docFiles = repoFiles.filter((file) => /\.(md|mdx)$/i.test(file));
4159
- const humanGateDoc = docFiles.find(
4160
- (file) => fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN)
4519
+ const humanGateDoc = withContentReadSignal(
4520
+ "B6",
4521
+ "human_gate_documented",
4522
+ () => docFiles.find((file) => fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN))
4161
4523
  );
4162
4524
  const implFiles = repoFiles.filter(isImplementationFile);
4163
- const gatedPrivilegeCheckFile = implFiles.find(
4164
- (file) => fileContains(repoPath, file, GATED_PRIVILEGE_CHECK_PATTERN) && fileContains(repoPath, file, SET_ROLE_PATTERN)
4525
+ const gatedPrivilegeCheckFile = withContentReadSignal(
4526
+ "B6",
4527
+ "fail_closed_privilege_check",
4528
+ () => implFiles.find(
4529
+ (file) => fileContains(repoPath, file, GATED_PRIVILEGE_CHECK_PATTERN) && fileContains(repoPath, file, SET_ROLE_PATTERN)
4530
+ )
4165
4531
  );
4166
- const killSwitchFile = implFiles.find(
4167
- (file) => fileContains(repoPath, file, KILL_SWITCH_NAME_PATTERN) && fileContains(repoPath, file, KILL_SWITCH_FAIL_CLOSED_PATTERN)
4532
+ const killSwitchFile = withContentReadSignal(
4533
+ "B6",
4534
+ "kill_switch_fail_safe_present",
4535
+ () => implFiles.find(
4536
+ (file) => fileContains(repoPath, file, KILL_SWITCH_NAME_PATTERN) && fileContains(repoPath, file, KILL_SWITCH_FAIL_CLOSED_PATTERN)
4537
+ )
4168
4538
  );
4169
4539
  const codeownersFile = repoFiles.find((file) => /(^|\/)CODEOWNERS$/.test(file));
4170
- const reviewGateDoc = docFiles.find(
4171
- (file) => fileContains(repoPath, file, REQUIRED_REVIEW_PATTERN)
4540
+ const reviewGateDoc = withContentReadSignal(
4541
+ "B6",
4542
+ "protected_path_review_gate",
4543
+ () => docFiles.find((file) => fileContains(repoPath, file, REQUIRED_REVIEW_PATTERN))
4172
4544
  );
4173
4545
  const protectedPathReviewGate = codeownersFile ?? reviewGateDoc;
4174
- const executableFiles = implFiles.filter(
4175
- (file) => fileContains(repoPath, file, SQL_EXEC_PATTERN)
4176
- );
4177
- const historicalUngatedEscalationFiles = executableFiles.filter((file) => {
4178
- if (useV39Detectors && !fileHasExecutedPrivilegeEscalation(repoPath, file)) return false;
4179
- const hasEscalation = fileContains(repoPath, file, ROLE_MEMBERSHIP_GRANT_PATTERN) || fileContains(repoPath, file, SUPERUSER_ESCALATION_PATTERN);
4180
- if (!hasEscalation) return false;
4181
- return !fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN);
4546
+ const { executableFiles, v21ExecutedEscalationFileSet, productionUngatedEscalationFiles } = withContentReadSignal("B6", "privilege_escalation_cleanliness", () => {
4547
+ const executable = implFiles.filter((file) => fileContains(repoPath, file, SQL_EXEC_PATTERN));
4548
+ const historicalUngated = executable.filter((file) => {
4549
+ if (useV39Detectors && !fileHasExecutedPrivilegeEscalation(repoPath, file)) return false;
4550
+ const hasEscalation = fileContains(repoPath, file, ROLE_MEMBERSHIP_GRANT_PATTERN) || fileContains(repoPath, file, SUPERUSER_ESCALATION_PATTERN);
4551
+ if (!hasEscalation) return false;
4552
+ return !fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN);
4553
+ });
4554
+ const v21Executed = useV21ExecutedEscalations ? repoFiles.filter(
4555
+ (file) => isV39AuthoredProductionPath(file) && (fileHasV21RawSqlEscalation(repoPath, file) || isImplementationFile(file) && fileHasV21DriverEscalation(repoPath, file))
4556
+ ) : [];
4557
+ const v21ExecutedSet = new Set(v21Executed);
4558
+ const ungated = [.../* @__PURE__ */ new Set([...historicalUngated, ...v21Executed])].filter(
4559
+ (file) => !fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN)
4560
+ );
4561
+ const productionUngated = ungated.filter(
4562
+ (file) => useV39Detectors ? isV39AuthoredProductionPath(file) : !isTestOrFixturePath(file)
4563
+ );
4564
+ return {
4565
+ executableFiles: executable,
4566
+ v21ExecutedEscalationFileSet: v21ExecutedSet,
4567
+ productionUngatedEscalationFiles: productionUngated
4568
+ };
4182
4569
  });
4183
- const v21ExecutedEscalationFiles = useV21ExecutedEscalations ? repoFiles.filter(
4184
- (file) => isV39AuthoredProductionPath(file) && (fileHasV21RawSqlEscalation(repoPath, file) || isImplementationFile(file) && fileHasV21DriverEscalation(repoPath, file))
4185
- ) : [];
4186
- const v21ExecutedEscalationFileSet = new Set(v21ExecutedEscalationFiles);
4187
- const ungatedEscalationFiles = [
4188
- .../* @__PURE__ */ new Set([...historicalUngatedEscalationFiles, ...v21ExecutedEscalationFiles])
4189
- ].filter((file) => !fileContains(repoPath, file, HUMAN_GATE_MARKER_PATTERN));
4190
- const productionUngatedEscalationFiles = ungatedEscalationFiles.filter(
4191
- (file) => useV39Detectors ? isV39AuthoredProductionPath(file) : !isTestOrFixturePath(file)
4192
- );
4193
4570
  if (humanGateDoc) {
4194
- evidence.push(
4195
- evidenceForRelative(
4196
- repoPath,
4197
- humanGateDoc,
4198
- "artifact",
4199
- "Documents privileged operations as human-executed/gated"
4571
+ withContentReadSignal(
4572
+ "B6",
4573
+ "human_gate_documented",
4574
+ () => evidence.push(
4575
+ evidenceForRelative(
4576
+ repoPath,
4577
+ humanGateDoc,
4578
+ "artifact",
4579
+ "Documents privileged operations as human-executed/gated"
4580
+ )
4200
4581
  )
4201
4582
  );
4202
4583
  }
4203
4584
  if (gatedPrivilegeCheckFile) {
4204
- evidence.push(
4205
- evidenceForRelative(
4206
- repoPath,
4207
- gatedPrivilegeCheckFile,
4208
- "artifact",
4209
- "Fail-closed privilege-membership check before role elevation"
4585
+ withContentReadSignal(
4586
+ "B6",
4587
+ "fail_closed_privilege_check",
4588
+ () => evidence.push(
4589
+ evidenceForRelative(
4590
+ repoPath,
4591
+ gatedPrivilegeCheckFile,
4592
+ "artifact",
4593
+ "Fail-closed privilege-membership check before role elevation"
4594
+ )
4210
4595
  )
4211
4596
  );
4212
4597
  }
4213
4598
  if (killSwitchFile) {
4214
- evidence.push(
4215
- evidenceForRelative(
4216
- repoPath,
4217
- killSwitchFile,
4218
- "artifact",
4219
- "Un-overridable kill-switch / fail-safe governance toggle"
4599
+ withContentReadSignal(
4600
+ "B6",
4601
+ "kill_switch_fail_safe_present",
4602
+ () => evidence.push(
4603
+ evidenceForRelative(
4604
+ repoPath,
4605
+ killSwitchFile,
4606
+ "artifact",
4607
+ "Un-overridable kill-switch / fail-safe governance toggle"
4608
+ )
4220
4609
  )
4221
4610
  );
4222
4611
  }
4223
4612
  if (protectedPathReviewGate) {
4224
- evidence.push(
4225
- evidenceForRelative(
4226
- repoPath,
4227
- protectedPathReviewGate,
4228
- "artifact",
4229
- "CODEOWNERS/required-review gate on protected paths"
4613
+ withContentReadSignal(
4614
+ "B6",
4615
+ "protected_path_review_gate",
4616
+ () => evidence.push(
4617
+ evidenceForRelative(
4618
+ repoPath,
4619
+ protectedPathReviewGate,
4620
+ "artifact",
4621
+ "CODEOWNERS/required-review gate on protected paths"
4622
+ )
4230
4623
  )
4231
4624
  );
4232
4625
  }
4233
- for (const file of productionUngatedEscalationFiles.slice(0, 5)) {
4234
- const isV21ExecutedShape = v21ExecutedEscalationFileSet.has(file);
4235
- findings.push({
4236
- severity: "critical",
4237
- summary: isV21ExecutedShape ? "This file contains an authored administrative SQL statement \u2014 role-membership grant, SUPERUSER escalation, or schema-wide table privilege grant \u2014 or executes one through a direct database-driver call, and does not itself contain text matching the human-gate marker pattern." : "This file executes a role-membership GRANT or SUPERUSER escalation and does not itself contain text matching the human-gate marker pattern.",
4238
- evidence: evidenceForRelative(
4239
- repoPath,
4240
- file,
4241
- "artifact",
4242
- isV21ExecutedShape ? "Ungated authored or directly executed administrative SQL statement" : "Ungated privilege-escalation statement"
4243
- )
4244
- });
4245
- }
4626
+ withContentReadSignal("B6", "privilege_escalation_cleanliness", () => {
4627
+ for (const file of productionUngatedEscalationFiles.slice(0, 5)) {
4628
+ const isV21ExecutedShape = v21ExecutedEscalationFileSet.has(file);
4629
+ findings.push({
4630
+ severity: "critical",
4631
+ summary: isV21ExecutedShape ? "This file contains an authored administrative SQL statement \u2014 role-membership grant, SUPERUSER escalation, or schema-wide table privilege grant \u2014 or executes one through a direct database-driver call, and does not itself contain text matching the human-gate marker pattern." : "This file executes a role-membership GRANT or SUPERUSER escalation and does not itself contain text matching the human-gate marker pattern.",
4632
+ evidence: evidenceForRelative(
4633
+ repoPath,
4634
+ file,
4635
+ "artifact",
4636
+ isV21ExecutedShape ? "Ungated authored or directly executed administrative SQL statement" : "Ungated privilege-escalation statement"
4637
+ )
4638
+ });
4639
+ }
4640
+ });
4246
4641
  if (evidence.length === 0 && findings.length === 0) {
4247
4642
  return buildNotApplicableSignal(
4248
4643
  "B6",
@@ -4264,7 +4659,7 @@ function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detecto
4264
4659
  }
4265
4660
  )
4266
4661
  ] : [];
4267
- const hasPrivilegedOpsSurface = humanGateDoc != null || gatedPrivilegeCheckFile != null || executableFiles.length > 0 || v21ExecutedEscalationFiles.length > 0;
4662
+ const hasPrivilegedOpsSurface = humanGateDoc != null || gatedPrivilegeCheckFile != null || executableFiles.length > 0 || v21ExecutedEscalationFileSet.size > 0;
4268
4663
  return {
4269
4664
  criterionId: "B6",
4270
4665
  positiveEvidence: evidence,
@@ -5187,6 +5582,9 @@ function isRecord(value) {
5187
5582
  function isTestRunnerConfig(repoPath, file) {
5188
5583
  return /(^|\/)(vitest|jest|pytest|playwright|cypress|mocha)\.config\.[cm]?[jt]s$/.test(file) || /(^|\/)(pytest\.ini|tox\.ini)$/.test(file) || /(^|\/)phpunit\.xml(\.dist)?$/.test(file) || /(^|\/)pyproject\.toml$/.test(file) && fileContains(repoPath, file, /^\[tool\.pytest(?:\.|\])/m) || /(^|\/)CMakeLists\.txt$/.test(file) && fileContains(repoPath, file, /\b(?:enable_testing|add_test|ctest_add_tests)\s*\(/i) || /(^|\/)Makefile$/.test(file) && fileContains(repoPath, file, /^\s*(?:test|tests|check)\s*:/m);
5189
5584
  }
5585
+ function isRecipeTestEntry(repoPath, file) {
5586
+ return isRecipeFile(file) && fileContains(repoPath, file, /^\s*(?:test|tests|check)\s*:/m);
5587
+ }
5190
5588
  function findCoverageConfigFiles(repoPath, repoFiles, authoredOnly = false) {
5191
5589
  return repoFiles.filter(
5192
5590
  (file) => (!authoredOnly || isAuthoredProductionPath(file)) && isCoverageConfig(repoPath, file)
@@ -5480,6 +5878,580 @@ function packageJsonHasCoverageTooling(repoPath, file) {
5480
5878
  }
5481
5879
  return false;
5482
5880
  }
5881
+ function isV23CoverageConfig(repoPath, file) {
5882
+ if (/(^|\/)vitest\.config\.[cm]?[jt]s$/.test(file)) {
5883
+ const contents = stripJavaScriptComments(readRepoText(join(repoPath, file), "utf8"));
5884
+ const property = /(?:^|[{,])\s*["']?coverage["']?\s*:\s*([^,\r\n}]*)/im.exec(contents)?.[1]?.trim();
5885
+ if (!property || /^(?:false\b|null\b|undefined\b)/i.test(property)) return false;
5886
+ if (/^\{/.test(property) && /\benabled\s*:\s*false\b/i.test(contents)) return false;
5887
+ return true;
5888
+ }
5889
+ if (/(^|\/)jest\.config\.[cm]?[jt]s$/.test(file)) {
5890
+ const contents = stripJavaScriptComments(readRepoText(join(repoPath, file), "utf8"));
5891
+ return /(?:^|[{,])\s*["']?collectCoverage["']?\s*:\s*true\b/m.test(contents) || /(?:^|[{,])\s*["']?(?:coverageThreshold|coverageProvider|coverageDirectory)["']?\s*:/m.test(
5892
+ contents
5893
+ );
5894
+ }
5895
+ if (/(^|\/)package\.json$/.test(file)) {
5896
+ return packageJsonHasStaticCoverageTooling(repoPath, file);
5897
+ }
5898
+ return isCoverageConfig(repoPath, file);
5899
+ }
5900
+ function coverageConfigurationDecision(repoPath, file) {
5901
+ const detectorState = isV23CoverageConfig(repoPath, file) ? "collects" : "none";
5902
+ if (/(^|\/)vitest\.config\.[cm]?[jt]s$/.test(file)) {
5903
+ const rawContents = readRepoText(join(repoPath, file), "utf8");
5904
+ const contents = stripJavaScriptComments(rawContents);
5905
+ const match = /(?:^|[{,])\s*["']?coverage["']?\s*:\s*([^,\r\n}]*)/im.exec(contents);
5906
+ if (!match) return null;
5907
+ return {
5908
+ path: file,
5909
+ source: "runner-configuration",
5910
+ locator: "coverage",
5911
+ evidence: rawContents.slice(match.index, match.index + match[0].length).trim(),
5912
+ detectorRecognized: true,
5913
+ detectorState
5914
+ };
5915
+ }
5916
+ if (/(^|\/)jest\.config\.[cm]?[jt]s$/.test(file)) {
5917
+ const rawContents = readRepoText(join(repoPath, file), "utf8");
5918
+ const contents = stripJavaScriptComments(rawContents);
5919
+ const match = /(?:^|[{,])\s*["']?(collectCoverage|coverageThreshold|coverageProvider|coverageDirectory)["']?\s*:\s*([^,\r\n}]*)/m.exec(
5920
+ contents
5921
+ );
5922
+ if (!match) return null;
5923
+ return {
5924
+ path: file,
5925
+ source: "runner-configuration",
5926
+ locator: match[1] ?? "coverage",
5927
+ evidence: rawContents.slice(match.index, match.index + match[0].length).trim(),
5928
+ detectorRecognized: true,
5929
+ detectorState
5930
+ };
5931
+ }
5932
+ if (detectorState === "none") return null;
5933
+ return {
5934
+ path: file,
5935
+ source: "runner-configuration",
5936
+ locator: "coverage",
5937
+ evidence: file,
5938
+ detectorRecognized: true,
5939
+ detectorState
5940
+ };
5941
+ }
5942
+ function packageJsonHasStaticCoverageTooling(repoPath, file) {
5943
+ const fullPath = join(repoPath, file);
5944
+ if (!isRegularFile(fullPath)) return false;
5945
+ const parsed = parseJsonObject(readRepoText(fullPath, "utf8"));
5946
+ if (!parsed) return false;
5947
+ return ["nyc", "c8", "istanbul"].some((key) => {
5948
+ const config = parsed[key];
5949
+ return typeof config === "object" && config !== null && !Array.isArray(config);
5950
+ });
5951
+ }
5952
+ function analyzeCoverageConfiguration(repoPath, repoFiles, authoredOnly = false) {
5953
+ const eligibleFiles = repoFiles.filter(
5954
+ (file) => !authoredOnly || isAuthoredProductionPath(file)
5955
+ );
5956
+ const eligibleSet = new Set(eligibleFiles);
5957
+ const coverageFiles = /* @__PURE__ */ new Set();
5958
+ const unresolvedFiles = /* @__PURE__ */ new Set();
5959
+ const ciCommandsByFile = /* @__PURE__ */ new Map();
5960
+ const detectorDecisions = [];
5961
+ let hasCoverageCommand = false;
5962
+ for (const file of eligibleFiles.filter(isCiWorkflow)) {
5963
+ const commands = extractCiRunCommands(readRepoText(join(repoPath, file), "utf8"));
5964
+ ciCommandsByFile.set(file, commands);
5965
+ const commandDecisions = commands.map(
5966
+ (command, index) => coverageDetectorDecision(repoPath, eligibleSet, {
5967
+ path: file,
5968
+ source: "ci-run",
5969
+ locator: String(index),
5970
+ command
5971
+ })
5972
+ );
5973
+ detectorDecisions.push(...commandDecisions);
5974
+ const state = combineCoverageCommandStates(
5975
+ commandDecisions.map((decision) => decision.detectorState)
5976
+ );
5977
+ if (state === "collects") {
5978
+ coverageFiles.add(file);
5979
+ hasCoverageCommand = true;
5980
+ }
5981
+ if (state === "unresolved") unresolvedFiles.add(file);
5982
+ }
5983
+ const ciCommands = [...ciCommandsByFile.values()].flat();
5984
+ for (const file of eligibleFiles) {
5985
+ if (isCiWorkflow(file) || isRecipeFile(file)) continue;
5986
+ if (/(^|\/)package\.json$/.test(file)) {
5987
+ if (packageJsonHasStaticCoverageTooling(repoPath, file)) coverageFiles.add(file);
5988
+ const packageState = packageScriptCoverageState(
5989
+ repoPath,
5990
+ eligibleSet,
5991
+ file,
5992
+ file === "package.json" ? ciCommands : [],
5993
+ detectorDecisions
5994
+ );
5995
+ if (packageState === "collects") {
5996
+ coverageFiles.add(file);
5997
+ hasCoverageCommand = true;
5998
+ }
5999
+ if (packageState === "unresolved") unresolvedFiles.add(file);
6000
+ continue;
6001
+ }
6002
+ const configurationDecision = coverageConfigurationDecision(repoPath, file);
6003
+ if (configurationDecision) detectorDecisions.push(configurationDecision);
6004
+ if (configurationDecision?.detectorState === "collects") coverageFiles.add(file);
6005
+ }
6006
+ for (const file of eligibleFiles.filter(isRecipeFile)) {
6007
+ const state = recipeCoverageState(
6008
+ repoPath,
6009
+ eligibleSet,
6010
+ file,
6011
+ ciCommands,
6012
+ detectorDecisions
6013
+ );
6014
+ if (state === "collects") {
6015
+ coverageFiles.add(file);
6016
+ hasCoverageCommand = true;
6017
+ }
6018
+ if (state === "unresolved") unresolvedFiles.add(file);
6019
+ }
6020
+ for (const file of coverageFiles) unresolvedFiles.delete(file);
6021
+ return {
6022
+ coverageFiles: [...coverageFiles].sort(),
6023
+ unresolvedFiles: [...unresolvedFiles].sort(),
6024
+ hasCoverageCommand,
6025
+ detectorDecisions
6026
+ };
6027
+ }
6028
+ function stripJavaScriptComments(contents) {
6029
+ let output = "";
6030
+ let quote = null;
6031
+ let escaped = false;
6032
+ let lineComment = false;
6033
+ let blockComment = false;
6034
+ for (let index = 0; index < contents.length; index += 1) {
6035
+ const character = contents[index] ?? "";
6036
+ const next = contents[index + 1] ?? "";
6037
+ if (lineComment) {
6038
+ if (character === "\n") {
6039
+ lineComment = false;
6040
+ output += "\n";
6041
+ } else {
6042
+ output += " ";
6043
+ }
6044
+ continue;
6045
+ }
6046
+ if (blockComment) {
6047
+ if (character === "*" && next === "/") {
6048
+ output += " ";
6049
+ index += 1;
6050
+ blockComment = false;
6051
+ } else {
6052
+ output += character === "\n" ? "\n" : " ";
6053
+ }
6054
+ continue;
6055
+ }
6056
+ if (escaped) {
6057
+ output += character;
6058
+ escaped = false;
6059
+ continue;
6060
+ }
6061
+ if (quote) {
6062
+ output += character;
6063
+ if (character === "\\") escaped = true;
6064
+ else if (character === quote) quote = null;
6065
+ continue;
6066
+ }
6067
+ if (character === "'" || character === '"' || character === "`") {
6068
+ quote = character;
6069
+ output += character;
6070
+ continue;
6071
+ }
6072
+ if (character === "/" && next === "/") {
6073
+ output += " ";
6074
+ index += 1;
6075
+ lineComment = true;
6076
+ continue;
6077
+ }
6078
+ if (character === "/" && next === "*") {
6079
+ output += " ";
6080
+ index += 1;
6081
+ blockComment = true;
6082
+ continue;
6083
+ }
6084
+ output += character;
6085
+ }
6086
+ return output;
6087
+ }
6088
+ function packageScriptCoverageState(repoPath, repoFiles, packageJson, ciCommands, detectorDecisions) {
6089
+ const scripts = readPackageScripts(join(repoPath, packageJson));
6090
+ if (scripts.size === 0) return "none";
6091
+ const roots = /* @__PURE__ */ new Set();
6092
+ let unresolved = false;
6093
+ if (scripts.has("test")) roots.add("test");
6094
+ for (const command of ciCommands) {
6095
+ const references = packageScriptReferences(command, scripts);
6096
+ for (const name of references.names) roots.add(name);
6097
+ if (references.dynamic && [...scripts.values()].some(
6098
+ (candidate) => commandInvokesCoverageTool(candidate) || containsPotentialCoverageFlag(candidate)
6099
+ )) {
6100
+ unresolved = true;
6101
+ }
6102
+ }
6103
+ const queue = [...roots];
6104
+ const visited = /* @__PURE__ */ new Set();
6105
+ while (queue.length > 0) {
6106
+ const name = queue.shift();
6107
+ if (!name || visited.has(name)) continue;
6108
+ const command = scripts.get(name);
6109
+ if (command === void 0) continue;
6110
+ visited.add(name);
6111
+ const decision = coverageDetectorDecision(repoPath, repoFiles, {
6112
+ path: packageJson,
6113
+ source: "package-script",
6114
+ locator: name,
6115
+ command
6116
+ });
6117
+ detectorDecisions.push(decision);
6118
+ const state = decision.detectorState;
6119
+ if (state === "collects") return "collects";
6120
+ if (state === "unresolved") unresolved = true;
6121
+ const references = packageScriptReferences(command, scripts);
6122
+ for (const referenced of references.names) {
6123
+ if (!visited.has(referenced)) queue.push(referenced);
6124
+ }
6125
+ for (const lifecycle of [`pre${name}`, `post${name}`]) {
6126
+ if (scripts.has(lifecycle) && !visited.has(lifecycle)) queue.push(lifecycle);
6127
+ }
6128
+ if (references.dynamic && [...scripts.values()].some(
6129
+ (candidate) => commandInvokesCoverageTool(candidate) || containsPotentialCoverageFlag(candidate)
6130
+ )) {
6131
+ unresolved = true;
6132
+ }
6133
+ }
6134
+ return unresolved ? "unresolved" : "none";
6135
+ }
6136
+ function packageScriptReferences(command, scripts) {
6137
+ const names = /* @__PURE__ */ new Set();
6138
+ let dynamic = false;
6139
+ for (const segment of splitShellCommandSegments(command)) {
6140
+ for (const match of segment.matchAll(
6141
+ /\bpnpm\s+run\s+\/((?:\\.|[^/])+)\/([dgimsuvy]*)/giu
6142
+ )) {
6143
+ const source = match[1];
6144
+ if (!source) continue;
6145
+ try {
6146
+ const flags = (match[2] ?? "").replace(/[dgy]/g, "");
6147
+ const selector = new RegExp(source, flags);
6148
+ for (const name of scripts.keys()) {
6149
+ if (selector.test(name)) names.add(name);
6150
+ selector.lastIndex = 0;
6151
+ }
6152
+ } catch {
6153
+ dynamic = true;
6154
+ }
6155
+ }
6156
+ for (const match of segment.matchAll(
6157
+ /\b(?:npm|pnpm|yarn)\s+(?:run\s+)?([A-Za-z0-9][A-Za-z0-9:._-]*)/giu
6158
+ )) {
6159
+ const name = match[1];
6160
+ if (name && scripts.has(name)) names.add(name);
6161
+ }
6162
+ if (/\b(?:npm|pnpm|yarn)\s+run\s+(?:\$|["']\$|`)/u.test(segment)) dynamic = true;
6163
+ }
6164
+ return { names: [...names], dynamic };
6165
+ }
6166
+ function extractCiRunCommands(contents) {
6167
+ const lines = contents.split(/\r?\n/);
6168
+ const commands = [];
6169
+ for (let index = 0; index < lines.length; index += 1) {
6170
+ const line = lines[index] ?? "";
6171
+ const match = /^(\s*)(?:-\s*)?run\s*:\s*(.*)$/u.exec(line);
6172
+ if (!match) continue;
6173
+ const indentation = match[1]?.length ?? 0;
6174
+ const value = match[2]?.trim() ?? "";
6175
+ if (value === "|" || value === ">" || value.startsWith("|-") || value.startsWith(">-")) {
6176
+ const block = [];
6177
+ for (index += 1; index < lines.length; index += 1) {
6178
+ const nested = lines[index] ?? "";
6179
+ if (nested.trim().length === 0) {
6180
+ block.push("");
6181
+ continue;
6182
+ }
6183
+ const nestedIndentation = /^\s*/u.exec(nested)?.[0].length ?? 0;
6184
+ if (nestedIndentation <= indentation) {
6185
+ index -= 1;
6186
+ break;
6187
+ }
6188
+ block.push(nested.slice(Math.min(nested.length, indentation + 2)));
6189
+ }
6190
+ commands.push(block.join(value.startsWith(">") ? " " : "\n"));
6191
+ continue;
6192
+ }
6193
+ if (value.length === 0) continue;
6194
+ const quoted = /^(?:"([\s\S]*)"|'([\s\S]*)')$/u.exec(value);
6195
+ commands.push(quoted ? quoted[1] ?? quoted[2] ?? "" : value);
6196
+ }
6197
+ return commands;
6198
+ }
6199
+ function isRecipeFile(file) {
6200
+ return /(^|\/)(?:Makefile|makefile|GNUmakefile|[Jj]ustfile)$/.test(file);
6201
+ }
6202
+ function recipeCoverageState(repoPath, repoFiles, file, ciCommands, detectorDecisions) {
6203
+ const targets = readRecipeTargets(readRepoText(join(repoPath, file), "utf8"));
6204
+ const roots = new Set(["test", "tests", "check"].filter((target) => targets.has(target)));
6205
+ const runner = /[Jj]ustfile$/.test(file) ? "just" : "make";
6206
+ for (const command of ciCommands) {
6207
+ for (const match of command.matchAll(new RegExp(`\\b${runner}\\s+([A-Za-z0-9._-]+)`, "gu"))) {
6208
+ const target = match[1];
6209
+ if (target && targets.has(target)) roots.add(target);
6210
+ }
6211
+ }
6212
+ const queue = [...roots];
6213
+ const visited = /* @__PURE__ */ new Set();
6214
+ let unresolved = false;
6215
+ while (queue.length > 0) {
6216
+ const name = queue.shift();
6217
+ if (!name || visited.has(name)) continue;
6218
+ const target = targets.get(name);
6219
+ if (!target) continue;
6220
+ visited.add(name);
6221
+ const commandDecisions = target.commands.map(
6222
+ (command, index) => coverageDetectorDecision(repoPath, repoFiles, {
6223
+ path: file,
6224
+ source: "recipe-command",
6225
+ locator: `${name}:${index}`,
6226
+ command
6227
+ })
6228
+ );
6229
+ detectorDecisions.push(...commandDecisions);
6230
+ const state = combineCoverageCommandStates(
6231
+ commandDecisions.map((decision) => decision.detectorState)
6232
+ );
6233
+ if (state === "collects") return "collects";
6234
+ if (state === "unresolved") unresolved = true;
6235
+ for (const dependency of target.dependencies) {
6236
+ if (targets.has(dependency) && !visited.has(dependency)) queue.push(dependency);
6237
+ }
6238
+ for (const command of target.commands) {
6239
+ const pattern = new RegExp(`(?:\\$\\(MAKE\\)|\\b${runner})\\s+([A-Za-z0-9._-]+)`, "gu");
6240
+ for (const match of command.matchAll(pattern)) {
6241
+ const referenced = match[1];
6242
+ if (referenced && targets.has(referenced) && !visited.has(referenced)) queue.push(referenced);
6243
+ }
6244
+ }
6245
+ }
6246
+ return unresolved ? "unresolved" : "none";
6247
+ }
6248
+ function readRecipeTargets(contents) {
6249
+ const targets = /* @__PURE__ */ new Map();
6250
+ let active = null;
6251
+ for (const line of contents.split(/\r?\n/)) {
6252
+ const header = /^([A-Za-z0-9][A-Za-z0-9._-]*)\s*:\s*([^#]*)/u.exec(line);
6253
+ if (header) {
6254
+ const name = header[1];
6255
+ if (!name) continue;
6256
+ active = {
6257
+ dependencies: (header[2] ?? "").trim().split(/\s+/u).filter(Boolean),
6258
+ commands: []
6259
+ };
6260
+ targets.set(name, active);
6261
+ continue;
6262
+ }
6263
+ if (active && /^\s+/u.test(line) && line.trim().length > 0) {
6264
+ active.commands.push(line.trim().replace(/^[@+-]+/u, ""));
6265
+ continue;
6266
+ }
6267
+ if (line.trim().length > 0 && !line.trimStart().startsWith("#")) active = null;
6268
+ }
6269
+ return targets;
6270
+ }
6271
+ function coverageCommandEvaluation(repoPath, repoFiles, command, depth = 0) {
6272
+ if (depth > 4) {
6273
+ const recognized = containsPotentialCoverageFlag(command);
6274
+ return { recognized, state: recognized ? "unresolved" : "none" };
6275
+ }
6276
+ const normalized = normalizeCoverageCommandPrefixes(command);
6277
+ const segments = normalized.splitShellOperators ? splitShellCommandSegments(normalized.segment) : [normalized.segment];
6278
+ const evaluations = segments.map((rawSegment) => {
6279
+ const candidate = normalizeCoverageCommandPrefixes(rawSegment);
6280
+ if (commandInvokesCoverageTool(candidate.segment)) {
6281
+ return { recognized: true, state: "collects" };
6282
+ }
6283
+ const words = shellWords(candidate.segment);
6284
+ if (words.length === 0) return { recognized: false, state: "none" };
6285
+ const direct = coverageFlagCommandState(words);
6286
+ if (direct !== "none") return { recognized: true, state: direct };
6287
+ const hasCoverageSyntax = containsPotentialCoverageFlag(candidate.segment);
6288
+ if (isRecognizedCoverageFlagRunner(words)) {
6289
+ return { recognized: hasCoverageSyntax, state: "none" };
6290
+ }
6291
+ const wrapper = reachableShellWrapper(words, repoFiles);
6292
+ if (wrapper) {
6293
+ return coverageCommandEvaluation(
6294
+ repoPath,
6295
+ repoFiles,
6296
+ readRepoText(join(repoPath, wrapper), "utf8"),
6297
+ depth + 1
6298
+ );
6299
+ }
6300
+ const executable = commandExecutable(words);
6301
+ const potentialFlagState = flagState(
6302
+ words.slice(1),
6303
+ ["--experimental-test-coverage", "--collectcoverage", "--collect-coverage", "--coverage", "--cov"],
6304
+ [
6305
+ "--no-experimental-test-coverage",
6306
+ "--no-collectcoverage",
6307
+ "--no-collect-coverage",
6308
+ "--no-coverage",
6309
+ "--no-cov"
6310
+ ]
6311
+ );
6312
+ if (hasCoverageSyntax && potentialFlagState !== "none" && executable && /(?:test|spec|runner)/i.test(executable) && !/(?:docs?|map|report|upload|codecov|coveralls)/i.test(executable)) {
6313
+ return { recognized: true, state: "unresolved" };
6314
+ }
6315
+ return { recognized: false, state: "none" };
6316
+ });
6317
+ return {
6318
+ recognized: evaluations.some((evaluation) => evaluation.recognized),
6319
+ state: combineCoverageCommandStates(
6320
+ evaluations.map((evaluation) => evaluation.state)
6321
+ )
6322
+ };
6323
+ }
6324
+ function coverageDetectorDecision(repoPath, repoFiles, input) {
6325
+ const evaluation = coverageCommandEvaluation(repoPath, repoFiles, input.command);
6326
+ return {
6327
+ path: input.path,
6328
+ source: input.source,
6329
+ locator: input.locator,
6330
+ evidence: input.command,
6331
+ detectorRecognized: evaluation.recognized,
6332
+ detectorState: evaluation.state
6333
+ };
6334
+ }
6335
+ function combineCoverageCommandStates(states) {
6336
+ if (states.includes("collects")) return "collects";
6337
+ return states.includes("unresolved") ? "unresolved" : "none";
6338
+ }
6339
+ function shellWords(segment) {
6340
+ const words = [];
6341
+ let offset = 0;
6342
+ while (offset < segment.length) {
6343
+ const scanned = scanShellWord(segment, offset);
6344
+ if (!scanned) break;
6345
+ words.push(scanned.word);
6346
+ if (scanned.end <= offset) break;
6347
+ offset = scanned.end;
6348
+ }
6349
+ return words;
6350
+ }
6351
+ function commandExecutable(words) {
6352
+ const normalized = unwrapPackageManagerExecutable(words);
6353
+ const executable = normalized[0];
6354
+ return executable ? basename(executable).replace(/\.cmd$/i, "").toLowerCase() : void 0;
6355
+ }
6356
+ function unwrapPackageManagerExecutable(words) {
6357
+ if (words.length === 0) return [];
6358
+ const first = basename(words[0] ?? "").toLowerCase();
6359
+ if (first === "pnpm" || first === "yarn") {
6360
+ const second = words[1]?.toLowerCase();
6361
+ return words.slice(second === "exec" || second === "dlx" ? 2 : 1);
6362
+ }
6363
+ if (first === "npx") return words.slice(1).filter((word) => !/^--(?:yes|no-install)$/u.test(word));
6364
+ if (first === "npm" && words[1]?.toLowerCase() === "exec") {
6365
+ let offset = 2;
6366
+ while (/^--(?:yes|no-install)$/u.test(words[offset] ?? "")) offset += 1;
6367
+ if (words[offset] === "--") offset += 1;
6368
+ return words.slice(offset);
6369
+ }
6370
+ return [...words];
6371
+ }
6372
+ function coverageFlagCommandState(words) {
6373
+ const command = unwrapPackageManagerExecutable(words);
6374
+ const executable = basename(command[0] ?? "").replace(/\.cmd$/i, "").toLowerCase();
6375
+ const args = command.slice(1);
6376
+ if (!executable) return "none";
6377
+ if (executable === "node") {
6378
+ if (!args.includes("--test")) return "none";
6379
+ return flagState(args, ["--experimental-test-coverage"], ["--no-experimental-test-coverage"]);
6380
+ }
6381
+ if (executable === "pytest" || executable === "py.test") {
6382
+ return flagState(args, ["--cov"], ["--no-cov"]);
6383
+ }
6384
+ if (executable === "bun" || executable === "deno") {
6385
+ if (args[0]?.toLowerCase() !== "test") return "none";
6386
+ return flagState(args.slice(1), ["--coverage"], ["--no-coverage"]);
6387
+ }
6388
+ if (executable === "jest") {
6389
+ return flagState(
6390
+ args,
6391
+ ["--coverage", "--collectcoverage", "--collect-coverage"],
6392
+ ["--no-coverage", "--no-collectcoverage", "--no-collect-coverage"]
6393
+ );
6394
+ }
6395
+ if (executable === "vitest" || executable === "bnt") {
6396
+ return flagState(args, ["--coverage"], ["--no-coverage"]);
6397
+ }
6398
+ return "none";
6399
+ }
6400
+ function isRecognizedCoverageFlagRunner(words) {
6401
+ const command = unwrapPackageManagerExecutable(words);
6402
+ const executable = basename(command[0] ?? "").replace(/\.cmd$/i, "").toLowerCase();
6403
+ if (["bnt", "vitest", "jest", "pytest", "py.test"].includes(executable)) return true;
6404
+ if (executable === "node") return command.slice(1).includes("--test");
6405
+ return (executable === "bun" || executable === "deno") && command[1]?.toLowerCase() === "test";
6406
+ }
6407
+ function flagState(args, enablingNames, disablingNames) {
6408
+ let enabled = false;
6409
+ let disabled = false;
6410
+ let unresolved = false;
6411
+ const enabling = new Set(enablingNames.map((name) => name.toLowerCase()));
6412
+ const disabling = new Set(disablingNames.map((name) => name.toLowerCase()));
6413
+ for (let index = 0; index < args.length; index += 1) {
6414
+ const raw = args[index] ?? "";
6415
+ const lower = raw.toLowerCase();
6416
+ if (disabling.has(lower)) {
6417
+ disabled = true;
6418
+ continue;
6419
+ }
6420
+ const [name, assigned] = lower.split("=", 2);
6421
+ if (!name || !enabling.has(name)) continue;
6422
+ if (assigned !== void 0) {
6423
+ if (assigned === "false" || assigned === "0" || assigned === "off") disabled = true;
6424
+ else if (/\$|\{|\}/u.test(assigned)) unresolved = true;
6425
+ else enabled = true;
6426
+ continue;
6427
+ }
6428
+ const next = args[index + 1]?.toLowerCase();
6429
+ if (next === "false" || next === "0" || next === "off") {
6430
+ disabled = true;
6431
+ index += 1;
6432
+ } else if (next && /\$|\{|\}/u.test(next)) {
6433
+ unresolved = true;
6434
+ index += 1;
6435
+ } else {
6436
+ enabled = true;
6437
+ }
6438
+ }
6439
+ if (unresolved || enabled && disabled) return "unresolved";
6440
+ return enabled && !disabled ? "collects" : "none";
6441
+ }
6442
+ function containsPotentialCoverageFlag(command) {
6443
+ return /(?:^|\s)--(?:experimental-test-coverage|collect-?coverage|no-coverage|coverage|no-cov|cov)(?:[=\s]|$)/iu.test(
6444
+ command
6445
+ );
6446
+ }
6447
+ function reachableShellWrapper(words, repoFiles) {
6448
+ const executable = basename(words[0] ?? "").toLowerCase();
6449
+ const candidate = /^(?:ba|da|k|z)?sh$/u.test(executable) ? words[1] : words[0];
6450
+ if (!candidate || !/(?:^|\/)\.?[^\s]*\.sh$/i.test(candidate)) return void 0;
6451
+ const normalized = candidate.replace(/^\.\//u, "");
6452
+ if (normalized.startsWith("/") || normalized.split("/").includes("..")) return void 0;
6453
+ return repoFiles.has(normalized) ? normalized : void 0;
6454
+ }
5483
6455
  function commandInvokesCoverageTool(command) {
5484
6456
  return normalizedCommandInvokesCoverageTool(normalizeCoverageCommandPrefixes(command), 0);
5485
6457
  }
@@ -7325,7 +8297,7 @@ var WITAN_RUBRIC = [
7325
8297
 
7326
8298
  // src/witan/scoring.ts
7327
8299
  function usesV17DetectorClosure2(rubricVersion) {
7328
- return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
8300
+ return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
7329
8301
  }
7330
8302
  var MAX_SIGNAL_ADJUSTMENT = 0.8;
7331
8303
  function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
@@ -7484,7 +8456,7 @@ function statusForScore(score) {
7484
8456
  }
7485
8457
  function statusAfterInputAdjustment(nativeStatus, nativeScore, adjustment, rubricVersion) {
7486
8458
  if (adjustment === 0) return nativeStatus;
7487
- if (rubricVersion !== WITAN_RUBRIC_VERSION_V8 && rubricVersion !== WITAN_RUBRIC_VERSION_V9 && rubricVersion !== WITAN_RUBRIC_VERSION_V10 && rubricVersion !== WITAN_RUBRIC_VERSION_V11 && rubricVersion !== WITAN_RUBRIC_VERSION_V12 && rubricVersion !== WITAN_RUBRIC_VERSION_V13 && rubricVersion !== WITAN_RUBRIC_VERSION_V14 && rubricVersion !== WITAN_RUBRIC_VERSION_V15 && rubricVersion !== WITAN_RUBRIC_VERSION_V16 && rubricVersion !== WITAN_RUBRIC_VERSION_V17 && rubricVersion !== WITAN_RUBRIC_VERSION_V18 && rubricVersion !== WITAN_RUBRIC_VERSION_V19 && rubricVersion !== WITAN_RUBRIC_VERSION_V20 && rubricVersion !== WITAN_RUBRIC_VERSION_V21 && rubricVersion !== WITAN_RUBRIC_VERSION_V22) {
8459
+ if (rubricVersion !== WITAN_RUBRIC_VERSION_V8 && rubricVersion !== WITAN_RUBRIC_VERSION_V9 && rubricVersion !== WITAN_RUBRIC_VERSION_V10 && rubricVersion !== WITAN_RUBRIC_VERSION_V11 && rubricVersion !== WITAN_RUBRIC_VERSION_V12 && rubricVersion !== WITAN_RUBRIC_VERSION_V13 && rubricVersion !== WITAN_RUBRIC_VERSION_V14 && rubricVersion !== WITAN_RUBRIC_VERSION_V15 && rubricVersion !== WITAN_RUBRIC_VERSION_V16 && rubricVersion !== WITAN_RUBRIC_VERSION_V17 && rubricVersion !== WITAN_RUBRIC_VERSION_V18 && rubricVersion !== WITAN_RUBRIC_VERSION_V19 && rubricVersion !== WITAN_RUBRIC_VERSION_V20 && rubricVersion !== WITAN_RUBRIC_VERSION_V21 && rubricVersion !== WITAN_RUBRIC_VERSION_V22 && rubricVersion !== WITAN_RUBRIC_VERSION_V23) {
7488
8460
  return statusForScore(roundScore(Math.max(0, nativeScore - adjustment)));
7489
8461
  }
7490
8462
  const statusFloor = {
@@ -7529,7 +8501,7 @@ function mergeSignalsByCriterion(signals) {
7529
8501
  return merged;
7530
8502
  }
7531
8503
  function usesMetricScoring(rubricVersion) {
7532
- return rubricVersion === WITAN_RUBRIC_VERSION_V1 || rubricVersion === WITAN_RUBRIC_VERSION_V2 || rubricVersion === WITAN_RUBRIC_VERSION_V3 || rubricVersion === WITAN_RUBRIC_VERSION_V4 || rubricVersion === WITAN_RUBRIC_VERSION_V5 || rubricVersion === WITAN_RUBRIC_VERSION_V6 || rubricVersion === WITAN_RUBRIC_VERSION_V7 || rubricVersion === WITAN_RUBRIC_VERSION_V8 || rubricVersion === WITAN_RUBRIC_VERSION_V9 || rubricVersion === WITAN_RUBRIC_VERSION_V10 || rubricVersion === WITAN_RUBRIC_VERSION_V11 || rubricVersion === WITAN_RUBRIC_VERSION_V12 || rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0;
8504
+ return rubricVersion === WITAN_RUBRIC_VERSION_V1 || rubricVersion === WITAN_RUBRIC_VERSION_V2 || rubricVersion === WITAN_RUBRIC_VERSION_V3 || rubricVersion === WITAN_RUBRIC_VERSION_V4 || rubricVersion === WITAN_RUBRIC_VERSION_V5 || rubricVersion === WITAN_RUBRIC_VERSION_V6 || rubricVersion === WITAN_RUBRIC_VERSION_V7 || rubricVersion === WITAN_RUBRIC_VERSION_V8 || rubricVersion === WITAN_RUBRIC_VERSION_V9 || rubricVersion === WITAN_RUBRIC_VERSION_V10 || rubricVersion === WITAN_RUBRIC_VERSION_V11 || rubricVersion === WITAN_RUBRIC_VERSION_V12 || rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23 || rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0;
7533
8505
  }
7534
8506
  function unmeasuredStatus(rubricVersion) {
7535
8507
  return rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0 ? "unverified" : "insufficient_data";
@@ -7909,7 +8881,7 @@ function calibratedCriterionStateV11(criterionId, metrics) {
7909
8881
  return void 0;
7910
8882
  }
7911
8883
  function ensureFindingsExplainStatus(criterionId, score, status, findings, evidence, rubricVersion) {
7912
- if (rubricVersion !== WITAN_RUBRIC_VERSION_V8 && rubricVersion !== WITAN_RUBRIC_VERSION_V9 && rubricVersion !== WITAN_RUBRIC_VERSION_V10 && rubricVersion !== WITAN_RUBRIC_VERSION_V11 && rubricVersion !== WITAN_RUBRIC_VERSION_V12 && rubricVersion !== WITAN_RUBRIC_VERSION_V13 && rubricVersion !== WITAN_RUBRIC_VERSION_V14 && rubricVersion !== WITAN_RUBRIC_VERSION_V15 && rubricVersion !== WITAN_RUBRIC_VERSION_V16 && rubricVersion !== WITAN_RUBRIC_VERSION_V17 && rubricVersion !== WITAN_RUBRIC_VERSION_V18 && rubricVersion !== WITAN_RUBRIC_VERSION_V19 && rubricVersion !== WITAN_RUBRIC_VERSION_V20 && rubricVersion !== WITAN_RUBRIC_VERSION_V21 && rubricVersion !== WITAN_RUBRIC_VERSION_V22 && findings.length > 0) {
8884
+ if (rubricVersion !== WITAN_RUBRIC_VERSION_V8 && rubricVersion !== WITAN_RUBRIC_VERSION_V9 && rubricVersion !== WITAN_RUBRIC_VERSION_V10 && rubricVersion !== WITAN_RUBRIC_VERSION_V11 && rubricVersion !== WITAN_RUBRIC_VERSION_V12 && rubricVersion !== WITAN_RUBRIC_VERSION_V13 && rubricVersion !== WITAN_RUBRIC_VERSION_V14 && rubricVersion !== WITAN_RUBRIC_VERSION_V15 && rubricVersion !== WITAN_RUBRIC_VERSION_V16 && rubricVersion !== WITAN_RUBRIC_VERSION_V17 && rubricVersion !== WITAN_RUBRIC_VERSION_V18 && rubricVersion !== WITAN_RUBRIC_VERSION_V19 && rubricVersion !== WITAN_RUBRIC_VERSION_V20 && rubricVersion !== WITAN_RUBRIC_VERSION_V21 && rubricVersion !== WITAN_RUBRIC_VERSION_V22 && rubricVersion !== WITAN_RUBRIC_VERSION_V23 && findings.length > 0) {
7913
8885
  return [...findings];
7914
8886
  }
7915
8887
  if (status !== "critical" && status !== "warning") return [...findings];
@@ -8229,6 +9201,7 @@ export {
8229
9201
  CALLER_CONTEXT_PRODUCT_IDENTITY_NOTICE,
8230
9202
  CERTIFICATE_GLOSSARY,
8231
9203
  formatCertificateMetricLabel,
9204
+ formatCertificateMetricAppliedWeightPercent,
8232
9205
  formatCertificateMetricValue,
8233
9206
  buildRelyingPartySummary,
8234
9207
  WITAN_RUBRIC_VERSION_V22,