@cejel/cejel 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,10 @@ certificate + badge over all of them. See "Aggregate your scanners" below.
18
18
 
19
19
  > Dogfooded in production. Cejel is run continuously on Barg Labs' own multi-product
20
20
  > monorepo — the ten-product studio it was built inside — which it currently scores
21
- > 3.5/4.0 ("Verified"). We score ourselves before asking you to score yourself.
21
+ > 3.3/4.0 on its rubric-native certificate. That transparency snapshot opts into prospective
22
+ > v18; the public CLI default remains calibrated v17. On the comparative board, our code sits
23
+ > outside the ranked population and receives no verdict band. We score ourselves before
24
+ > asking you to score yourself.
22
25
 
23
26
  ## Install
24
27
 
@@ -99,7 +102,8 @@ gh attestation verify ./cejel-Darwin-arm64 -R BargLabs/cejel
99
102
  This is cryptographically signed provenance. It is distinct from Apple Developer ID or
100
103
  Microsoft Authenticode code-signing.
101
104
 
102
- **Docker / OCI.** The same MCP server is published as a non-root, multi-platform image:
105
+ **Docker / OCI.** The current container release is `0.2.0` (container releases are
106
+ versioned independently from npm-only patches):
103
107
 
104
108
  ```bash
105
109
  docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.2.0
@@ -4368,7 +4368,7 @@ var WitanAttestationStatementSchema = external_exports.object({
4368
4368
  var WITAN_NO_MEASUREMENT_REASON = "No free-core rubric criterion produced a measurable signal. Cejel abstains rather than publish a numeric zero for an entirely unmeasured repository.";
4369
4369
  var WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY = "Reviewable source is present, but the complete tracked inventory contains no concrete test file, configured test runner, CI test command, or coverage configuration.";
4370
4370
  function isWitanNoMeasurementAbstention(report) {
4371
- return report.verdict === "insufficient_source" && report.insufficientSourceReason === WITAN_NO_MEASUREMENT_REASON && report.criteria.every(
4371
+ return report.verdict === "insufficient_source" && report.insufficientSourceReason.startsWith(WITAN_NO_MEASUREMENT_REASON) && report.criteria.every(
4372
4372
  ({ status }) => status === "not_applicable" || status === "insufficient_data"
4373
4373
  );
4374
4374
  }
@@ -5086,8 +5086,12 @@ var WITAN_RUBRIC_VERSION_V14 = "witan-rubric-v14-2026-07-23";
5086
5086
  var WITAN_RUBRIC_VERSION_V15 = "witan-rubric-v15-2026-07-24";
5087
5087
  var WITAN_RUBRIC_VERSION_V16 = "witan-rubric-v16-2026-07-24";
5088
5088
  var WITAN_RUBRIC_VERSION_V17 = "witan-rubric-v17-2026-07-24";
5089
+ var WITAN_RUBRIC_VERSION_V18 = "witan-rubric-v18-prospective-2026-07-25";
5089
5090
 
5090
5091
  // src/witan/repo-signals.ts
5092
+ function usesV17DetectorClosure(rubricVersion) {
5093
+ return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18;
5094
+ }
5091
5095
  function buildWitanInputFromRepo(options) {
5092
5096
  assertRepoPathExists(options.repoPath);
5093
5097
  const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
@@ -5096,26 +5100,27 @@ function buildWitanInputFromRepo(options) {
5096
5100
  const repoFiles = listRepoFiles(options.repoPath);
5097
5101
  const inventoryFiles = listRepoInventory(options.repoPath, repoFiles);
5098
5102
  const ignoredTargetReason = repoFiles.length === 0 ? explainIgnoredScanTarget(options.repoPath) : void 0;
5099
- const usesV17Detectors = rubricVersion === WITAN_RUBRIC_VERSION_V17;
5103
+ const usesV17Detectors = usesV17DetectorClosure(rubricVersion);
5104
+ const usesV18NativeRls = rubricVersion === WITAN_RUBRIC_VERSION_V18;
5100
5105
  const structuralArchetype = classifyRepoArchetype(inventoryFiles, rubricVersion);
5101
- const readableArchetype = 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 ? applyReadableSourceRepresentationGate(
5106
+ 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(
5102
5107
  options.repoPath,
5103
5108
  inventoryFiles,
5104
5109
  structuralArchetype,
5105
5110
  headSha,
5106
5111
  rubricVersion
5107
5112
  ) : structuralArchetype;
5108
- const gatedArchetype = rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || rubricVersion === WITAN_RUBRIC_VERSION_V17 ? applySemanticSourceRepresentationGate(
5113
+ const gatedArchetype = rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion) ? applySemanticSourceRepresentationGate(
5109
5114
  options.repoPath,
5110
5115
  inventoryFiles,
5111
5116
  readableArchetype,
5112
5117
  rubricVersion
5113
5118
  ) : readableArchetype;
5114
- const usesV8Detectors = 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;
5115
- const usesV33Detectors = 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;
5116
- const usesV36Detectors = 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;
5117
- const usesV39Detectors = 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;
5118
- const usesV47Detectors = rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || rubricVersion === WITAN_RUBRIC_VERSION_V17;
5119
+ const usesV8Detectors = 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 || usesV17DetectorClosure(rubricVersion);
5120
+ const usesV33Detectors = 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 || usesV17DetectorClosure(rubricVersion);
5121
+ const usesV36Detectors = 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 || usesV17DetectorClosure(rubricVersion);
5122
+ const usesV39Detectors = 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 || usesV17DetectorClosure(rubricVersion);
5123
+ const usesV47Detectors = rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion);
5119
5124
  const reviewableSourceProof = usesV17Detectors ? buildReviewableSourceProof(options.repoPath, inventoryFiles, headSha, rubricVersion) : void 0;
5120
5125
  const coreSignals = collectRepoSignals(
5121
5126
  options.repoPath,
@@ -5126,6 +5131,7 @@ function buildWitanInputFromRepo(options) {
5126
5131
  usesV36Detectors,
5127
5132
  usesV39Detectors,
5128
5133
  usesV47Detectors,
5134
+ usesV18NativeRls,
5129
5135
  reviewableSourceProof
5130
5136
  );
5131
5137
  const archetype = usesV17Detectors && reviewableSourceProof?.passes === true && isV17RescuableAbstention(gatedArchetype.abstentionKind) && coreSignals.some(isMeasuredCriterionSignal) ? {
@@ -5233,7 +5239,7 @@ var SOURCE_EXTENSION_PATTERN_V10 = new RegExp(
5233
5239
  "i"
5234
5240
  );
5235
5241
  function isRecognizedSourcePath(path, rubricVersion = WITAN_RUBRIC_VERSION) {
5236
- return (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 ? SOURCE_EXTENSION_PATTERN_V10 : SOURCE_EXTENSION_PATTERN).test(path);
5242
+ return (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 || usesV17DetectorClosure(rubricVersion) ? SOURCE_EXTENSION_PATTERN_V10 : SOURCE_EXTENSION_PATTERN).test(path);
5237
5243
  }
5238
5244
  var BUNDLED_BINARY_EXTENSION_PATTERN = /\.(vsix|zip|tgz|whl|exe|dmg|pkg|msi|jar|war|apk|ipa|dll|dylib|ttf|otf|woff2?|eot|eps)$/i;
5239
5245
  var DOCS_OR_MEDIA_EXTENSION_PATTERN = /\.(md|mdx|rst|png|jpe?g|gif|svg|webp|ico|pdf)$/i;
@@ -5249,6 +5255,7 @@ var SOURCE_DOMINANCE_RATIO_THRESHOLD_V8 = 0.2;
5249
5255
  var SOURCE_DOMINANCE_RATIO_THRESHOLD_V9 = 0.8;
5250
5256
  var SOURCE_DOMINANCE_RATIO_THRESHOLD_V10 = 0.5;
5251
5257
  var SOURCE_DOMINANCE_RATIO_THRESHOLD_V11 = 0.2;
5258
+ var V17_REVIEWABLE_SOURCE_RATABLE_SHARE_THRESHOLD = 0.2;
5252
5259
  var GENERATED_SOURCE_MINIMUM = 8;
5253
5260
  var GENERATED_SOURCE_RATIO_THRESHOLD = 0.8;
5254
5261
  var GENERATED_AUTHORED_MAXIMUM = 3;
@@ -5292,7 +5299,7 @@ function classifyRepoArchetype(repoFiles, rubricVersion = WITAN_RUBRIC_VERSION)
5292
5299
  insufficientSourceReason: `${catalogSourceFiles.length} of ${sourceFileCount} source-shaped file(s) are under independent solution, exercise, challenge, or algorithm catalog paths; only ${sourceFileCount - catalogSourceFiles.length} source-shaped file(s) remain outside that catalog. Cejel abstains because the repository does not expose one cohesive product implementation to score. ${INGEST_POINTER}`
5293
5300
  };
5294
5301
  }
5295
- if (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) {
5302
+ if (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 || usesV17DetectorClosure(rubricVersion)) {
5296
5303
  const authoredSourceFiles = sourceFiles.filter(isAuthoredProductionPath);
5297
5304
  const documentationFiles = repoFiles.filter(
5298
5305
  (file) => DOCS_OR_MEDIA_EXTENSION_PATTERN.test(file)
@@ -5310,7 +5317,7 @@ function classifyRepoArchetype(repoFiles, rubricVersion = WITAN_RUBRIC_VERSION)
5310
5317
  (file) => ARTIFACT_DOMINANCE_EXTENSION_PATTERN.test(file)
5311
5318
  );
5312
5319
  const materialFiles = authoredSourceFiles.length + artifactFiles.length;
5313
- if (artifactFiles.length >= 10 && (authoredSourceFiles.length <= 10 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || rubricVersion === WITAN_RUBRIC_VERSION_V17) && materialFiles > 0 && authoredSourceFiles.length / materialFiles <= 0.1) {
5320
+ if (artifactFiles.length >= 10 && (authoredSourceFiles.length <= 10 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion)) && materialFiles > 0 && authoredSourceFiles.length / materialFiles <= 0.1) {
5314
5321
  return {
5315
5322
  archetype: "binary_only",
5316
5323
  sourceFileCount,
@@ -5337,7 +5344,7 @@ function classifyRepoArchetype(repoFiles, rubricVersion = WITAN_RUBRIC_VERSION)
5337
5344
  const docsDistributionFiles = repoFiles.filter(
5338
5345
  (file) => DOCS_OR_MEDIA_EXTENSION_PATTERN.test(file) || DOC_DISTRIBUTION_COMPANION_PATTERN.test(file)
5339
5346
  );
5340
- if (docsFiles.length > 0 && (docsDistributionFiles.length === totalFileCount || (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) && unrecognisedSourceFiles.length === 0)) {
5347
+ if (docsFiles.length > 0 && (docsDistributionFiles.length === totalFileCount || (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 || usesV17DetectorClosure(rubricVersion)) && unrecognisedSourceFiles.length === 0)) {
5341
5348
  return {
5342
5349
  archetype: "docs_only",
5343
5350
  sourceFileCount,
@@ -5456,6 +5463,23 @@ function isRepresentativeSourceText(contents) {
5456
5463
  function representativeSourceRank(rankKey, file) {
5457
5464
  return createHash2("sha256").update(`${rankKey}:${file}`).digest("hex");
5458
5465
  }
5466
+ function explainNoMeasurementSourceCoverage(repoPath, rubricVersion) {
5467
+ if (!usesV17DetectorClosure(rubricVersion)) return void 0;
5468
+ const repoFiles = listRepoFiles(repoPath);
5469
+ const inventoryFiles = listRepoInventory(repoPath, repoFiles);
5470
+ const proof = buildReviewableSourceProof(
5471
+ repoPath,
5472
+ inventoryFiles,
5473
+ readGitHead(repoPath),
5474
+ rubricVersion
5475
+ );
5476
+ if (proof.sourceShapedFileCount === 0) return void 0;
5477
+ const ratableShare = proof.eligibleSourceFileCount / proof.sourceShapedFileCount;
5478
+ if (ratableShare >= V17_REVIEWABLE_SOURCE_RATABLE_SHARE_THRESHOLD) return void 0;
5479
+ const ratablePct = (ratableShare * 100).toFixed(1);
5480
+ const thresholdPct = (V17_REVIEWABLE_SOURCE_RATABLE_SHARE_THRESHOLD * 100).toFixed(0);
5481
+ return `${WITAN_NO_MEASUREMENT_REASON} Source coverage: ${proof.eligibleSourceFileCount} of ${proof.sourceShapedFileCount} source-shaped files (${ratablePct}%) are criterion-ratable, below the ${thresholdPct}% reviewable-source threshold (${inventoryFiles.length} tracked files in total).`;
5482
+ }
5459
5483
  function buildReviewableSourceProof(repoPath, repoFiles, headSha, rubricVersion) {
5460
5484
  const sourceShapedFiles = repoFiles.filter(
5461
5485
  (path) => isRecognizedSourcePath(path, rubricVersion) || isUnrecognisedSourcePath(path, rubricVersion) || /\.ipynb$/i.test(path)
@@ -5480,7 +5504,7 @@ function buildReviewableSourceProof(repoPath, repoFiles, headSha, rubricVersion)
5480
5504
  sampledFileCount: sample.length,
5481
5505
  readableSampledFileCount: readablePaths.length,
5482
5506
  ...readablePaths[0] ? { firstReadablePath: readablePaths[0] } : {},
5483
- passes: eligibleSourceFiles.length > 0 && ratableShare >= 0.2 && readablePaths.length > 0 && (readableShare >= 0.75 || readablePaths.length >= 8)
5507
+ passes: eligibleSourceFiles.length > 0 && ratableShare >= V17_REVIEWABLE_SOURCE_RATABLE_SHARE_THRESHOLD && readablePaths.length > 0 && (readableShare >= 0.75 || readablePaths.length >= 8)
5484
5508
  };
5485
5509
  }
5486
5510
  function applyReadableSourceRepresentationGate(repoPath, repoFiles, classification, headSha, rubricVersion) {
@@ -5622,7 +5646,7 @@ function readRepresentativeSourceText(repoPath, file) {
5622
5646
  }
5623
5647
  }
5624
5648
  function sourceDominanceThreshold(rubricVersion) {
5625
- if (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) {
5649
+ if (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 || usesV17DetectorClosure(rubricVersion)) {
5626
5650
  return SOURCE_DOMINANCE_RATIO_THRESHOLD_V11;
5627
5651
  }
5628
5652
  if (rubricVersion === WITAN_RUBRIC_VERSION_V10) return SOURCE_DOMINANCE_RATIO_THRESHOLD_V10;
@@ -5636,7 +5660,7 @@ function isUnrecognisedSourcePath(file, rubricVersion = WITAN_RUBRIC_VERSION) {
5636
5660
  const base = basename(file);
5637
5661
  if (!/\.([a-zA-Z0-9_]+)$/.test(base)) return false;
5638
5662
  if (KNOWN_NON_SOURCE_EXTENSION_PATTERN.test(base)) return false;
5639
- if ((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) && V13_NON_SOURCE_EXTENSION_PATTERN.test(base)) {
5663
+ if ((rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion)) && V13_NON_SOURCE_EXTENSION_PATTERN.test(base)) {
5640
5664
  return false;
5641
5665
  }
5642
5666
  if (DOCS_OR_MEDIA_EXTENSION_PATTERN.test(base)) return false;
@@ -5722,7 +5746,7 @@ function buildNotApplicableSignal(criterionId, reason) {
5722
5746
  notApplicable: true
5723
5747
  };
5724
5748
  }
5725
- function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, reviewableSourceProof) {
5749
+ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, reviewableSourceProof) {
5726
5750
  const signals = [];
5727
5751
  const mono = resolveMonorepoContext(repoPath);
5728
5752
  const a1Signal = collectA1TestIntegrityEvidence(
@@ -5740,7 +5764,8 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
5740
5764
  useV33Detectors,
5741
5765
  useV36Detectors,
5742
5766
  useV39Detectors,
5743
- useV47Detectors
5767
+ useV47Detectors,
5768
+ useV18NativeRls
5744
5769
  );
5745
5770
  const a3Signal = collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors);
5746
5771
  const a4Signal = collectA4DependencyEvidence(
@@ -6032,7 +6057,7 @@ function buildA1AuthenticatedAbsenceSignal(repoPath, proof) {
6032
6057
  notes: `V17 authenticated absence: ${proof.eligibleSourceFileCount} criterion-ratable of ${proof.sourceShapedFileCount} source-shaped files; sampled ${proof.sampledFileCount}; ${proof.readableSampledFileCount} representative-text files.`
6033
6058
  };
6034
6059
  }
6035
- function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors) {
6060
+ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls) {
6036
6061
  const evidence = [];
6037
6062
  const findings = [];
6038
6063
  const gitignore = repoFiles.find((file) => basename(file) === ".gitignore");
@@ -6128,19 +6153,21 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
6128
6153
  evidence.push(evidenceForRelative(repoPath, dbLayerImportFile, "artifact", "DB client import"));
6129
6154
  }
6130
6155
  const v47RlsPolicyFiles = useV47Detectors ? findRlsPolicyFiles(repoPath, repoFiles) : [];
6131
- const v47TenantStorageFiles = useV47Detectors ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
6156
+ const nativeRlsInventory = useV18NativeRls ? deriveRlsPolicyScopeInventory(repoPath, repoFiles) : null;
6157
+ const tenantWithoutRlsPremiseFiles = useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
6158
+ const v47TenantStorageFiles = useV47Detectors && !useV18NativeRls ? findTenantStoragePremiseFiles(repoPath, repoFiles) : [];
6132
6159
  const rlsMigration = useV47Detectors ? v47RlsPolicyFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, RLS_PATTERN));
6133
6160
  const tenantScopePattern = useV39Detectors ? TENANT_SCOPE_PATTERN_V11 : useV36Detectors ? TENANT_SCOPE_PATTERN_V10 : TENANT_SCOPE_PATTERN;
6134
- const tenantScopedFile = useV47Detectors ? v47TenantStorageFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, tenantScopePattern));
6135
- const rlsPolicyCount = useV47Detectors ? v47RlsPolicyFiles.length : countPatternMatches(
6161
+ const tenantScopedFile = useV18NativeRls ? nativeRlsInventory?.storageFiles[0] : useV47Detectors ? v47TenantStorageFiles[0] : migrationFiles.find((file) => fileContains(repoPath, file, tenantScopePattern));
6162
+ const rlsPolicyCount = useV18NativeRls ? nativeRlsInventory?.policyCount ?? 0 : useV47Detectors ? v47RlsPolicyFiles.length : countPatternMatches(
6136
6163
  repoPath,
6137
6164
  migrationFiles,
6138
6165
  /create policy|enable row level security|force row level security/gi
6139
6166
  );
6140
- const tenantScopedMigrationFileCount = useV47Detectors ? v47TenantStorageFiles.length : countFilesContaining(repoPath, migrationFiles, tenantScopePattern);
6167
+ const tenantScopedMigrationFileCount = useV18NativeRls ? nativeRlsInventory?.storageFiles.length ?? 0 : useV47Detectors ? v47TenantStorageFiles.length : countFilesContaining(repoPath, migrationFiles, tenantScopePattern);
6141
6168
  const gitignoreEnvFile = gitignore && fileContains(repoPath, gitignore, /^\.env(\*|\b)|\.env\./m) ? gitignore : null;
6142
6169
  const envHandlingDepth = (envExamples.length > 0 ? 1 : 0) + (gitignoreEnvFile ? 1 : 0) + (countFilesContaining(repoPath, repoFiles.filter(isImplementationFile), ENV_READ_PATTERN) > 0 ? 1 : 0);
6143
- const isMultiTenant = tenantScopedMigrationFileCount > 0;
6170
+ const isMultiTenant = useV18NativeRls ? (nativeRlsInventory?.policyCount ?? 0) > 0 && (nativeRlsInventory?.scopeIdentifiers.length ?? 0) > 0 : tenantScopedMigrationFileCount > 0;
6144
6171
  if (gitignoreEnvFile) {
6145
6172
  evidence.push(
6146
6173
  evidenceForRelative(repoPath, gitignoreEnvFile, "secret_scan", ".env files are gitignored")
@@ -6156,7 +6183,12 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
6156
6183
  }
6157
6184
  if (tenantScopedFile) {
6158
6185
  evidence.push(
6159
- evidenceForRelative(repoPath, tenantScopedFile, "artifact", "Tenant scoping signal")
6186
+ evidenceForRelative(
6187
+ repoPath,
6188
+ tenantScopedFile,
6189
+ "artifact",
6190
+ useV18NativeRls ? `Repository-native RLS scope (${nativeRlsInventory?.scopeIdentifiers.join(", ")})` : "Tenant scoping signal"
6191
+ )
6160
6192
  );
6161
6193
  }
6162
6194
  if (committedSecret) {
@@ -6214,8 +6246,9 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
6214
6246
  evidence: historySecretScan.envPathEvidence
6215
6247
  });
6216
6248
  }
6217
- if (isMultiTenant && rlsPolicyCount === 0) {
6218
- const gapEvidence = rlsMigration ?? tenantScopedFile;
6249
+ const hasTenantWithoutRlsGap = rlsPolicyCount === 0 && (useV18NativeRls ? tenantWithoutRlsPremiseFiles.length > 0 : isMultiTenant);
6250
+ if (hasTenantWithoutRlsGap) {
6251
+ const gapEvidence = rlsMigration ?? tenantScopedFile ?? tenantWithoutRlsPremiseFiles[0];
6219
6252
  if (gapEvidence) {
6220
6253
  findings.push({
6221
6254
  severity: "warning",
@@ -6258,20 +6291,20 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
6258
6291
  "rls_policy_count",
6259
6292
  "RLS policy count",
6260
6293
  rlsPolicyCount,
6261
- Math.max(tenantScopedMigrationFileCount * 3, 3),
6294
+ useV18NativeRls ? Math.max(tenantScopedMigrationFileCount, 1) : Math.max(tenantScopedMigrationFileCount * 3, 3),
6262
6295
  0.4,
6263
6296
  "policies",
6264
- "Counts static row-level-security enables, forced-RLS statements, and policy definitions.",
6297
+ useV18NativeRls ? "Counts CREATE POLICY definitions with repository-native USING/WITH CHECK clauses against the repository-native scoped data-layer surface." : "Counts static row-level-security enables, forced-RLS statements, and policy definitions.",
6265
6298
  "saturating_count"
6266
6299
  ),
6267
6300
  metric(
6268
6301
  "tenant_scope_ratio",
6269
6302
  "Tenant-scoped schema ratio",
6270
- countFilesContaining(repoPath, migrationFiles, tenantScopePattern),
6303
+ useV18NativeRls ? tenantScopedMigrationFileCount : countFilesContaining(repoPath, migrationFiles, tenantScopePattern),
6271
6304
  Math.max(migrationFiles.length, 1),
6272
6305
  0.25,
6273
6306
  "ratio",
6274
- "Measures tenant/studio/workspace scoping evidence across data-layer files."
6307
+ useV18NativeRls ? `Measures repository-native RLS scope identifiers (${nativeRlsInventory?.scopeIdentifiers.join(", ")}) across data-layer files.` : "Measures tenant/studio/workspace scoping evidence across data-layer files."
6275
6308
  )
6276
6309
  ] : [];
6277
6310
  const cryptoMetrics = cryptoHygiene.hasSurface ? [
@@ -6290,7 +6323,7 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
6290
6323
  positiveEvidence: evidence,
6291
6324
  findings,
6292
6325
  metrics: [...baseMetrics, ...isolationMetrics, ...cryptoMetrics],
6293
- notes: "History secret scanning covers all reachable git history for credential-pattern paths unless the explicit credential-blob safety valve is reported."
6326
+ notes: useV18NativeRls ? "History secret scanning covers all reachable git history for credential-pattern paths unless the explicit credential-blob safety valve is reported. Positive multi-tenancy credit is derived only from this repository\u2019s own CREATE POLICY USING/WITH CHECK clauses; no fixed tenant-token vocabulary can create isolation credit. A conservative schema premise is retained only to fail closed when a tenant-shaped schema has zero RLS policies." : "History secret scanning covers all reachable git history for credential-pattern paths unless the explicit credential-blob safety valve is reported."
6294
6327
  };
6295
6328
  }
6296
6329
  function collectCryptoHygieneEvidence(repoPath, implFiles, useV39Detectors = false) {
@@ -7186,7 +7219,7 @@ var TEST_DIRECTORY_SOURCE_PATTERN_V17 = new RegExp(
7186
7219
  "i"
7187
7220
  );
7188
7221
  function isTestFile(file, rubricVersion = WITAN_RUBRIC_VERSION) {
7189
- return rubricVersion === WITAN_RUBRIC_VERSION_V17 && (TEST_DIRECTORY_SOURCE_PATTERN_V17.test(file) || /\.tftest\.hcl$/i.test(file) || /\.bats$/i.test(file) || /(^|\/)test[^/]*\.m$/i.test(file) || /_test\.m$/i.test(file)) || TEST_DIRECTORY_SOURCE_PATTERN.test(file) || // AVA's default discovery includes test.js and test-*.js at the scanned package root.
7222
+ return usesV17DetectorClosure(rubricVersion) && (TEST_DIRECTORY_SOURCE_PATTERN_V17.test(file) || /\.tftest\.hcl$/i.test(file) || /\.bats$/i.test(file) || /(^|\/)test[^/]*\.m$/i.test(file) || /_test\.m$/i.test(file)) || TEST_DIRECTORY_SOURCE_PATTERN.test(file) || // AVA's default discovery includes test.js and test-*.js at the scanned package root.
7190
7223
  // Keep this root-anchored: nested files need an existing test-directory/suffix convention.
7191
7224
  /^test(?:-[^/]+)?\.[cm]?[jt]sx?$/.test(file) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(file) || /(^|\/)test_.*\.py$/.test(file) || /_test\.go$/.test(file) || /(^|\/)test_.*\.(cpp|cc|cxx)$/.test(file) || /_(test|tests)\.(cpp|cc|cxx)$/.test(file) || /Tests?\.(java|kt)$/.test(file) || /(_test|_spec)\.(rs|rb|php|swift|kt)$/.test(file);
7192
7225
  }
@@ -7857,6 +7890,138 @@ function findRlsPolicyFiles(repoPath, repoFiles) {
7857
7890
  )
7858
7891
  );
7859
7892
  }
7893
+ var RLS_CLAUSE_SQL_WORDS = /* @__PURE__ */ new Set([
7894
+ "all",
7895
+ "and",
7896
+ "any",
7897
+ "as",
7898
+ "asc",
7899
+ "between",
7900
+ "by",
7901
+ "case",
7902
+ "cast",
7903
+ "current",
7904
+ "desc",
7905
+ "distinct",
7906
+ "else",
7907
+ "end",
7908
+ "exists",
7909
+ "false",
7910
+ "from",
7911
+ "in",
7912
+ "is",
7913
+ "join",
7914
+ "like",
7915
+ "limit",
7916
+ "not",
7917
+ "null",
7918
+ "on",
7919
+ "or",
7920
+ "order",
7921
+ "select",
7922
+ "then",
7923
+ "text",
7924
+ "true",
7925
+ "uuid",
7926
+ "varchar",
7927
+ "when",
7928
+ "where"
7929
+ ]);
7930
+ function extractBalancedSqlClause(statement, keyword) {
7931
+ const clauses = [];
7932
+ const keywordPattern = keyword === "using" ? /\busing\s*\(/gi : /\bwith\s+check\s*\(/gi;
7933
+ for (const match of statement.matchAll(keywordPattern)) {
7934
+ const openingParen = (match.index ?? 0) + match[0].lastIndexOf("(");
7935
+ let depth = 0;
7936
+ let quote = null;
7937
+ for (let index = openingParen; index < statement.length; index += 1) {
7938
+ const character = statement[index];
7939
+ if (quote) {
7940
+ if (character === quote && statement[index - 1] !== "\\") quote = null;
7941
+ continue;
7942
+ }
7943
+ if (character === "'" || character === '"') {
7944
+ quote = character;
7945
+ } else if (character === "(") {
7946
+ depth += 1;
7947
+ } else if (character === ")") {
7948
+ depth -= 1;
7949
+ if (depth === 0) {
7950
+ clauses.push(statement.slice(openingParen + 1, index));
7951
+ break;
7952
+ }
7953
+ }
7954
+ }
7955
+ }
7956
+ return clauses;
7957
+ }
7958
+ function extractRlsScopeIdentifiers(clause) {
7959
+ const scrubbed = clause.replace(/'(?:''|[^'])*'/g, " ").replace(/"(?:""|[^"])*"/g, " ");
7960
+ const identifiers = /* @__PURE__ */ new Set();
7961
+ const identifierPattern = /\b[A-Za-z_][A-Za-z0-9_$]*(?:\.[A-Za-z_][A-Za-z0-9_$]*)*\b/g;
7962
+ for (const match of scrubbed.matchAll(identifierPattern)) {
7963
+ const token = match[0];
7964
+ const segments = token.split(".").map((segment) => segment.toLowerCase());
7965
+ const lastSegment = segments.at(-1);
7966
+ if (!lastSegment || lastSegment.length < 2 || RLS_CLAUSE_SQL_WORDS.has(lastSegment)) continue;
7967
+ if (["app", "auth", "jwt", "request", "session"].includes(segments[0] ?? "")) continue;
7968
+ const remainder = scrubbed.slice((match.index ?? 0) + token.length);
7969
+ if (/^\s*\(/.test(remainder)) continue;
7970
+ if (["auth", "current_setting", "jwt", "pg_catalog", "public", "uid"].includes(lastSegment)) {
7971
+ continue;
7972
+ }
7973
+ identifiers.add(lastSegment);
7974
+ }
7975
+ return [...identifiers].sort();
7976
+ }
7977
+ function deriveRlsPolicyScopeInventory(repoPath, repoFiles) {
7978
+ const candidatePolicyFiles = findRlsPolicyFiles(repoPath, repoFiles);
7979
+ const policyFiles = /* @__PURE__ */ new Set();
7980
+ const scopeIdentifiers = /* @__PURE__ */ new Set();
7981
+ let policyCount = 0;
7982
+ for (const file of candidatePolicyFiles) {
7983
+ let content;
7984
+ try {
7985
+ content = readFileSync(join(repoPath, file), "utf8");
7986
+ } catch {
7987
+ continue;
7988
+ }
7989
+ for (const match of content.matchAll(/\bcreate\s+policy\b[\s\S]*?(?:;|$)/gi)) {
7990
+ const statement = match[0];
7991
+ const clauses = [
7992
+ ...extractBalancedSqlClause(statement, "using"),
7993
+ ...extractBalancedSqlClause(statement, "with check")
7994
+ ];
7995
+ if (clauses.length === 0) continue;
7996
+ policyCount += 1;
7997
+ policyFiles.add(file);
7998
+ for (const clause of clauses) {
7999
+ for (const identifier of extractRlsScopeIdentifiers(clause)) {
8000
+ scopeIdentifiers.add(identifier);
8001
+ }
8002
+ }
8003
+ }
8004
+ }
8005
+ const identifiers = [...scopeIdentifiers].sort();
8006
+ const dataLayerFiles = repoFiles.filter(
8007
+ (file) => /(^|\/)(?:migrations?|drizzle|prisma)\/|\.(?:sql|prisma)$/i.test(file)
8008
+ );
8009
+ const storageFiles = identifiers.length === 0 ? [] : dataLayerFiles.filter(
8010
+ (file) => identifiers.some(
8011
+ (identifier) => fileContains(
8012
+ repoPath,
8013
+ file,
8014
+ new RegExp(`\\b${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i")
8015
+ )
8016
+ )
8017
+ );
8018
+ return {
8019
+ policyFiles: [...policyFiles].sort(),
8020
+ policyCount,
8021
+ scopeIdentifiers: identifiers,
8022
+ storageFiles
8023
+ };
8024
+ }
7860
8025
  function findDeployableServicePremiseFiles(repoPath, repoFiles, strictOperationalBoundary = false) {
7861
8026
  const explicitTargets = repoFiles.filter(
7862
8027
  (file) => isAuthoredProductionPath(file) && isExplicitDeployTarget(file) && (!strictOperationalBoundary || isV47OperationalApplicationPath(file))
@@ -9605,6 +9770,9 @@ var WITAN_RUBRIC = [
9605
9770
  ];
9606
9771
 
9607
9772
  // src/witan/scoring.ts
9773
+ function usesV17DetectorClosure2(rubricVersion) {
9774
+ return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18;
9775
+ }
9608
9776
  var MAX_SIGNAL_ADJUSTMENT = 0.8;
9609
9777
  function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
9610
9778
  const parsedInput = WitanReportInputSchema.parse(input);
@@ -9688,7 +9856,7 @@ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
9688
9856
  const isFreeCoreRubric = rubric.length === WITAN_RUBRIC.length && rubric.every(
9689
9857
  ({ id, category }, index) => id === WITAN_RUBRIC[index]?.id && category === WITAN_RUBRIC[index]?.category
9690
9858
  );
9691
- const noMeasurementAbstention = (parsedInput.rubricVersion === WITAN_RUBRIC_VERSION_V16 || parsedInput.rubricVersion === WITAN_RUBRIC_VERSION_V17) && isFreeCoreRubric && !hasMeasuredCriterion;
9859
+ const noMeasurementAbstention = (parsedInput.rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure2(parsedInput.rubricVersion)) && isFreeCoreRubric && !hasMeasuredCriterion;
9692
9860
  const insufficientSourceReason = parsedInput.insufficientSourceReason ?? (noMeasurementAbstention ? WITAN_NO_MEASUREMENT_REASON : void 0);
9693
9861
  const abstained = insufficientSourceReason != null;
9694
9862
  return WitanReportSchema.parse({
@@ -9756,7 +9924,7 @@ function statusForScore(score) {
9756
9924
  }
9757
9925
  function statusAfterInputAdjustment(nativeStatus, nativeScore, adjustment, rubricVersion) {
9758
9926
  if (adjustment === 0) return nativeStatus;
9759
- 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) {
9927
+ 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) {
9760
9928
  return statusForScore(roundScore(Math.max(0, nativeScore - adjustment)));
9761
9929
  }
9762
9930
  const statusFloor = {
@@ -9800,7 +9968,7 @@ function mergeSignalsByCriterion(signals) {
9800
9968
  return merged;
9801
9969
  }
9802
9970
  function usesMetricScoring(rubricVersion) {
9803
- 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_TRADING_RUBRIC_VERSION_V0;
9971
+ 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_TRADING_RUBRIC_VERSION_V0;
9804
9972
  }
9805
9973
  function unmeasuredStatus(rubricVersion) {
9806
9974
  return rubricVersion === WITAN_TRADING_RUBRIC_VERSION_V0 ? "unverified" : "insufficient_data";
@@ -9931,7 +10099,7 @@ function scoreMetrics(metrics) {
9931
10099
  function capScoreForFindings(score, findings, rubricVersion, criterionId) {
9932
10100
  if (findings.some((finding) => finding.severity === "critical")) {
9933
10101
  const criticalFindings = findings.filter(({ severity }) => severity === "critical");
9934
- const authenticatedA1Absence = rubricVersion === WITAN_RUBRIC_VERSION_V17 && criterionId === "A1" && criticalFindings.length === 1 && criticalFindings[0]?.summary === WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY;
10102
+ const authenticatedA1Absence = usesV17DetectorClosure2(rubricVersion) && criterionId === "A1" && criticalFindings.length === 1 && criticalFindings[0]?.summary === WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY;
9935
10103
  return authenticatedA1Absence ? Math.min(score, 1.4) : 1.4;
9936
10104
  }
9937
10105
  if (findings.some((finding) => finding.severity === "warning")) return Math.min(score, 2.4);
@@ -9940,8 +10108,8 @@ function capScoreForFindings(score, findings, rubricVersion, criterionId) {
9940
10108
  }
9941
10109
  function statusForScoreAndFindings(criterionId, score, findings, metrics, rubricVersion) {
9942
10110
  if (findings.some((finding) => finding.severity === "critical")) return "critical";
9943
- 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) {
9944
- const calibrated = 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 ? calibratedCriterionStateV11(criterionId, metrics) : rubricVersion === WITAN_RUBRIC_VERSION_V9 || rubricVersion === WITAN_RUBRIC_VERSION_V10 ? calibratedCriterionStateV9(criterionId, metrics) : calibratedCriterionStateV8(criterionId, metrics);
10111
+ 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 || usesV17DetectorClosure2(rubricVersion)) {
10112
+ const calibrated = 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 || usesV17DetectorClosure2(rubricVersion) ? calibratedCriterionStateV11(criterionId, metrics) : rubricVersion === WITAN_RUBRIC_VERSION_V9 || rubricVersion === WITAN_RUBRIC_VERSION_V10 ? calibratedCriterionStateV9(criterionId, metrics) : calibratedCriterionStateV8(criterionId, metrics);
9945
10113
  if (calibrated && calibrated !== "verified") return calibrated;
9946
10114
  if (calibrated === "verified" && !findings.some((finding) => finding.severity === "warning")) {
9947
10115
  return "verified";
@@ -10167,7 +10335,7 @@ function calibratedCriterionStateV11(criterionId, metrics) {
10167
10335
  return void 0;
10168
10336
  }
10169
10337
  function ensureFindingsExplainStatus(criterionId, score, status, findings, evidence, rubricVersion) {
10170
- 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 && findings.length > 0) {
10338
+ 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 && findings.length > 0) {
10171
10339
  return [...findings];
10172
10340
  }
10173
10341
  if (status !== "critical" && status !== "warning") return [...findings];
@@ -10212,7 +10380,18 @@ function scoreRepoWithPublicCejel(options) {
10212
10380
  ...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {}
10213
10381
  });
10214
10382
  const inputSignals = resolvePublicIngestSignals(options);
10215
- return createWitanReport(input, inputSignals.length > 0 ? inputSignals : void 0);
10383
+ const report = createWitanReport(input, inputSignals.length > 0 ? inputSignals : void 0);
10384
+ if (report.verdict !== "insufficient_source" || !isWitanNoMeasurementAbstention(report)) {
10385
+ return report;
10386
+ }
10387
+ const contextualReason = explainNoMeasurementSourceCoverage(
10388
+ options.repoPath,
10389
+ report.rubricVersion
10390
+ );
10391
+ return contextualReason ? {
10392
+ ...report,
10393
+ insufficientSourceReason: contextualReason
10394
+ } : report;
10216
10395
  }
10217
10396
  function resolvePublicIngestSignals(options) {
10218
10397
  const seen = /* @__PURE__ */ new Set();
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  runCejelScan,
10
10
  serializeWitanReport,
11
11
  verifyWitanAttestationBinding
12
- } from "./chunk-PYI3OIBW.js";
12
+ } from "./chunk-BR2DO4QJ.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { createHash } from "crypto";
package/dist/mcp/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  renderWitanBadgeSvg,
11
11
  renderWitanHtmlReport,
12
12
  runCejelScan
13
- } from "../chunk-PYI3OIBW.js";
13
+ } from "../chunk-BR2DO4QJ.js";
14
14
 
15
15
  // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
16
16
  var require_code = __commonJS({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cejel/cejel",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "mcpName": "io.github.BargLabs/cejel",
5
5
  "private": false,
6
6
  "publishConfig": {