@decantr/verifier 2.2.0 → 2.3.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/dist/index.js CHANGED
@@ -747,6 +747,7 @@ var VERIFICATION_SCHEMA_URLS = {
747
747
  common: "https://decantr.ai/schemas/verification-report.common.v1.json",
748
748
  projectAudit: "https://decantr.ai/schemas/project-audit-report.v1.json",
749
749
  projectHealth: "https://decantr.ai/schemas/project-health-report.v1.json",
750
+ decantrCi: "https://decantr.ai/schemas/decantr-ci-report.v1.json",
750
751
  evidenceBundle: "https://decantr.ai/schemas/evidence-bundle.v1.json",
751
752
  workspaceHealth: "https://decantr.ai/schemas/workspace-health-report.v1.json",
752
753
  fileCritique: "https://decantr.ai/schemas/file-critique-report.v1.json",
@@ -895,7 +896,7 @@ function createContractAssertions(projectRoot, audit) {
895
896
  status: packManifest ? "passed" : "failed",
896
897
  message: packManifest ? "Compiled execution pack manifest is present." : "Compiled execution pack manifest is missing.",
897
898
  evidence: [redactEvidenceText(projectRoot, join2(contextDir, "pack-manifest.json"))],
898
- suggestedFix: packManifest ? void 0 : "Run `decantr refresh` to regenerate context packs."
899
+ suggestedFix: packManifest ? void 0 : "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate the full context pack bundle."
899
900
  })
900
901
  );
901
902
  assertions.push(
@@ -907,7 +908,7 @@ function createContractAssertions(projectRoot, audit) {
907
908
  status: audit?.reviewPack ? "passed" : "failed",
908
909
  message: audit?.reviewPack ? "Compiled review pack is present." : "Compiled review pack is missing.",
909
910
  evidence: [redactEvidenceText(projectRoot, join2(contextDir, "review-pack.json"))],
910
- suggestedFix: audit?.reviewPack ? void 0 : "Run `decantr refresh` or hydrate the review pack from the registry."
911
+ suggestedFix: audit?.reviewPack ? void 0 : "Run `decantr registry compile-packs decantr.essence.json --write-context` so critique has the compiled review contract."
911
912
  })
912
913
  );
913
914
  const tokensPath = join2(projectRoot, "src", "styles", "tokens.css");
@@ -1042,6 +1043,71 @@ function loadPackManifest(projectRoot) {
1042
1043
  join2(projectRoot, ".decantr", "context", "pack-manifest.json")
1043
1044
  );
1044
1045
  }
1046
+ function collectPackManifestReferences(packManifest) {
1047
+ const references = [];
1048
+ if (packManifest.scaffold) {
1049
+ references.push({
1050
+ entryId: packManifest.scaffold.id || "scaffold",
1051
+ kind: "scaffold",
1052
+ markdown: packManifest.scaffold.markdown,
1053
+ json: packManifest.scaffold.json
1054
+ });
1055
+ }
1056
+ if (packManifest.review) {
1057
+ references.push({
1058
+ entryId: packManifest.review.id || "review",
1059
+ kind: "review",
1060
+ markdown: packManifest.review.markdown,
1061
+ json: packManifest.review.json
1062
+ });
1063
+ }
1064
+ for (const section of packManifest.sections ?? []) {
1065
+ references.push({
1066
+ entryId: section.id,
1067
+ kind: "section",
1068
+ markdown: section.markdown,
1069
+ json: section.json
1070
+ });
1071
+ }
1072
+ for (const page of packManifest.pages ?? []) {
1073
+ references.push({
1074
+ entryId: page.id,
1075
+ kind: "page",
1076
+ markdown: page.markdown,
1077
+ json: page.json
1078
+ });
1079
+ }
1080
+ for (const mutation of packManifest.mutations ?? []) {
1081
+ references.push({
1082
+ entryId: mutation.id,
1083
+ kind: "mutation",
1084
+ markdown: mutation.markdown,
1085
+ json: mutation.json
1086
+ });
1087
+ }
1088
+ return references;
1089
+ }
1090
+ function collectMissingPackManifestFiles(projectRoot, packManifest = loadPackManifest(projectRoot)) {
1091
+ if (!packManifest) return [];
1092
+ const contextDir = join2(projectRoot, ".decantr", "context");
1093
+ const missing = [];
1094
+ for (const reference of collectPackManifestReferences(packManifest)) {
1095
+ for (const field of ["markdown", "json"]) {
1096
+ const fileName = reference[field];
1097
+ if (!fileName) continue;
1098
+ const absolutePath = join2(contextDir, fileName);
1099
+ if (existsSync2(absolutePath)) continue;
1100
+ missing.push({
1101
+ entryId: reference.entryId,
1102
+ kind: reference.kind,
1103
+ field,
1104
+ relativePath: `.decantr/context/${fileName}`,
1105
+ absolutePath
1106
+ });
1107
+ }
1108
+ }
1109
+ return missing;
1110
+ }
1045
1111
  function readTextIfExists(path) {
1046
1112
  try {
1047
1113
  return existsSync2(path) ? readFileSync2(path, "utf-8") : "";
@@ -1049,6 +1115,11 @@ function readTextIfExists(path) {
1049
1115
  return "";
1050
1116
  }
1051
1117
  }
1118
+ function readProjectAdoptionMode(projectRoot) {
1119
+ const projectJson = readJsonIfExists(join2(projectRoot, ".decantr", "project.json"));
1120
+ const adoptionMode = projectJson?.initialized?.adoptionMode;
1121
+ return typeof adoptionMode === "string" ? adoptionMode : null;
1122
+ }
1052
1123
  function createSourceAuditBucket() {
1053
1124
  return { count: 0, files: [] };
1054
1125
  }
@@ -2555,23 +2626,24 @@ function appendRuntimeAuditFindings(findings, runtimeAudit, projectRoot, topolog
2555
2626
  );
2556
2627
  }
2557
2628
  }
2558
- function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
2629
+ function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode) {
2559
2630
  if (sourceAudit.filesChecked === 0) {
2560
2631
  return;
2561
2632
  }
2633
+ const isContractOnly = adoptionMode === "contract-only";
2562
2634
  if (sourceAudit.inlineStyles.count > 0) {
2563
2635
  findings.push(
2564
2636
  makeFinding({
2565
2637
  id: "source-inline-styles-present",
2566
2638
  category: "Source Audit",
2567
2639
  severity: "warn",
2568
- message: "Source files still contain disallowed inline style attributes, which undermines the compiled treatment contract.",
2640
+ message: isContractOnly ? "Source files contain inline style attributes; contract-only projects should route static visual decisions through project-owned styling law." : "Source files still contain disallowed inline style attributes, which undermines the compiled treatment contract.",
2569
2641
  evidence: buildSourceAuditEvidence(
2570
2642
  sourceAudit,
2571
2643
  sourceAudit.inlineStyles,
2572
2644
  "Disallowed inline style attributes"
2573
2645
  ),
2574
- suggestedFix: "Move static visual styling into treatments, atoms, or design-token-backed classes. Inline style remains acceptable for Decantr CSS-variable writes and truly dynamic geometry."
2646
+ suggestedFix: isContractOnly ? "Move static visual styling into the app design system, Tailwind/theme tokens, component variants, or accepted local rules. Keep inline style only for truly dynamic geometry." : "Move static visual styling into treatments, atoms, or design-token-backed classes. Inline style remains acceptable for Decantr CSS-variable writes and truly dynamic geometry."
2575
2647
  })
2576
2648
  );
2577
2649
  }
@@ -2581,17 +2653,17 @@ function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
2581
2653
  id: "source-component-style-tags-present",
2582
2654
  category: "Source Audit",
2583
2655
  severity: "warn",
2584
- message: "Source files inject component-scoped style tags or dynamic style elements, which bypass the compiled Decantr layer contract.",
2656
+ message: isContractOnly ? "Source files inject component-scoped style tags or dynamic style elements, which makes project-owned styling rules harder to enforce." : "Source files inject component-scoped style tags or dynamic style elements, which bypass the compiled Decantr layer contract.",
2585
2657
  evidence: buildSourceAuditEvidence(
2586
2658
  sourceAudit,
2587
2659
  sourceAudit.componentStyleTags,
2588
2660
  "Component-level style tag signals"
2589
2661
  ),
2590
- suggestedFix: "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so styling stays inside the reviewed Decantr layer stack."
2662
+ suggestedFix: isContractOnly ? "Move shared keyframes, media queries, and visual rules into the project stylesheet, component library, or accepted local pattern/rule manifest." : "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so styling stays inside the reviewed Decantr layer stack."
2591
2663
  })
2592
2664
  );
2593
2665
  }
2594
- if (sourceAudit.localCssRuntimeSignals.count > 0) {
2666
+ if (!isContractOnly && sourceAudit.localCssRuntimeSignals.count > 0) {
2595
2667
  findings.push(
2596
2668
  makeFinding({
2597
2669
  id: "source-local-css-runtime-stub-present",
@@ -3694,6 +3766,7 @@ async function auditProject(projectRoot) {
3694
3766
  const findings = [];
3695
3767
  const reviewPack = loadReviewPack(projectRoot);
3696
3768
  const packManifest = loadPackManifest(projectRoot);
3769
+ const adoptionMode = readProjectAdoptionMode(projectRoot);
3697
3770
  const runtimeAudit = emptyRuntimeAudit();
3698
3771
  if (!existsSync2(essencePath)) {
3699
3772
  findings.push(
@@ -3774,10 +3847,25 @@ async function auditProject(projectRoot) {
3774
3847
  severity: "warn",
3775
3848
  message: "Compiled execution pack manifest is missing.",
3776
3849
  evidence: [join2(projectRoot, ".decantr", "context", "pack-manifest.json")],
3777
- suggestedFix: "Run `decantr refresh` to regenerate scaffold, review, mutation, section, and page packs."
3850
+ suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate scaffold, review, mutation, section, and page packs."
3778
3851
  })
3779
3852
  );
3780
3853
  } else {
3854
+ const missingPackFiles = collectMissingPackManifestFiles(projectRoot, packManifest);
3855
+ if (missingPackFiles.length > 0) {
3856
+ findings.push(
3857
+ makeFinding({
3858
+ id: "pack-manifest-referenced-files-missing",
3859
+ category: "Execution Packs",
3860
+ severity: "warn",
3861
+ message: "The compiled execution pack manifest references files that are missing.",
3862
+ evidence: missingPackFiles.slice(0, 12).map(
3863
+ (missing) => `${missing.kind}:${missing.entryId}:${missing.field} -> ${missing.relativePath}`
3864
+ ),
3865
+ suggestedFix: "Regenerate or hydrate the full execution pack bundle so every file named by pack-manifest.json exists beside it."
3866
+ })
3867
+ );
3868
+ }
3781
3869
  if (!packManifest.scaffold) {
3782
3870
  findings.push(
3783
3871
  makeFinding({
@@ -3810,7 +3898,7 @@ async function auditProject(projectRoot) {
3810
3898
  severity: "info",
3811
3899
  message: "No mutation packs were found in the manifest.",
3812
3900
  evidence: ["pack-manifest.json"],
3813
- suggestedFix: "Run `decantr refresh` to regenerate mutation task packs."
3901
+ suggestedFix: "Run `decantr registry compile-packs decantr.essence.json --write-context` to hydrate mutation task packs."
3814
3902
  })
3815
3903
  );
3816
3904
  }
@@ -3823,7 +3911,7 @@ async function auditProject(projectRoot) {
3823
3911
  severity: "warn",
3824
3912
  message: "The compiled review pack file is missing.",
3825
3913
  evidence: [join2(projectRoot, ".decantr", "context", "review-pack.json")],
3826
- suggestedFix: "Regenerate context with `decantr refresh`, or hydrate the hosted review contract with `decantr registry get-pack review --write-context`, so critique consumers can anchor findings to the compiled review contract."
3914
+ suggestedFix: "Hydrate the full hosted context bundle with `decantr registry compile-packs decantr.essence.json --write-context` so critique consumers can anchor findings to the compiled review contract."
3827
3915
  })
3828
3916
  );
3829
3917
  }
@@ -3837,7 +3925,7 @@ async function auditProject(projectRoot) {
3837
3925
  summarizeTopology(essence, reviewPack),
3838
3926
  sourceAudit
3839
3927
  );
3840
- appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack);
3928
+ appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack, adoptionMode);
3841
3929
  appendStyleContractFindings(findings, styleAudit, essence);
3842
3930
  const summary = {
3843
3931
  errorCount: findings.filter((finding) => finding.severity === "error").length,
@@ -4554,14 +4642,17 @@ function getObjectLiteralPropertyExpression(objectLiteral, propertyName, namedEx
4554
4642
  }
4555
4643
  return null;
4556
4644
  }
4557
- function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, propertyPath, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions) {
4645
+ var MAX_OPEN_REDIRECT_RESOLUTION_DEPTH = 40;
4646
+ function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, propertyPath, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
4647
+ if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
4558
4648
  let resolvedObjectLiteral = resolveObjectLiteralExpression(
4559
4649
  expression,
4560
4650
  sourceFile,
4561
4651
  namedExpressions,
4562
4652
  namedPropertyAliases,
4563
4653
  seenIdentifiers,
4564
- seenFunctions
4654
+ seenFunctions,
4655
+ depth + 1
4565
4656
  );
4566
4657
  if (!resolvedObjectLiteral) return null;
4567
4658
  for (const propertyName of propertyPath) {
@@ -4577,13 +4668,15 @@ function resolveObjectLiteralExpressionAtPropertyPath(expression, sourceFile, pr
4577
4668
  resolvedObjectLiteral.namedExpressions,
4578
4669
  namedPropertyAliases,
4579
4670
  seenIdentifiers,
4580
- seenFunctions
4671
+ seenFunctions,
4672
+ depth + 1
4581
4673
  );
4582
4674
  if (!resolvedObjectLiteral) return null;
4583
4675
  }
4584
4676
  return resolvedObjectLiteral;
4585
4677
  }
4586
- function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions) {
4678
+ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
4679
+ if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
4587
4680
  const functionKey = getFunctionLikeCacheKey(functionLike);
4588
4681
  if (seenFunctions.has(functionKey)) return null;
4589
4682
  seenFunctions.add(functionKey);
@@ -4595,7 +4688,8 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
4595
4688
  boundExpressions,
4596
4689
  namedPropertyAliases,
4597
4690
  new Set(seenIdentifiers),
4598
- seenFunctions
4691
+ seenFunctions,
4692
+ depth + 1
4599
4693
  );
4600
4694
  if (objectLiteral) {
4601
4695
  seenFunctions.delete(functionKey);
@@ -4605,8 +4699,9 @@ function resolveReturnedObjectLiteralExpression(functionLike, args, sourceFile,
4605
4699
  seenFunctions.delete(functionKey);
4606
4700
  return null;
4607
4701
  }
4608
- function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set()) {
4702
+ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set(), depth = 0) {
4609
4703
  if (!expression) return null;
4704
+ if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
4610
4705
  if (ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isTypeAssertionExpression(expression) || ts.isNonNullExpression(expression)) {
4611
4706
  return resolveObjectLiteralExpression(
4612
4707
  expression.expression,
@@ -4614,7 +4709,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4614
4709
  namedExpressions,
4615
4710
  namedPropertyAliases,
4616
4711
  seenIdentifiers,
4617
- seenFunctions
4712
+ seenFunctions,
4713
+ depth + 1
4618
4714
  );
4619
4715
  }
4620
4716
  if (ts.isObjectLiteralExpression(expression)) {
@@ -4631,7 +4727,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4631
4727
  namedExpressions,
4632
4728
  namedPropertyAliases,
4633
4729
  seenIdentifiers,
4634
- seenFunctions
4730
+ seenFunctions,
4731
+ depth + 1
4635
4732
  );
4636
4733
  seenIdentifiers.delete(expression.text);
4637
4734
  return result2;
@@ -4646,7 +4743,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4646
4743
  namedExpressions,
4647
4744
  namedPropertyAliases,
4648
4745
  seenIdentifiers,
4649
- seenFunctions
4746
+ seenFunctions,
4747
+ depth + 1
4650
4748
  );
4651
4749
  seenIdentifiers.delete(expression.text);
4652
4750
  return result;
@@ -4657,7 +4755,9 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4657
4755
  sourceFile,
4658
4756
  namedExpressions,
4659
4757
  namedPropertyAliases,
4660
- /* @__PURE__ */ new Set()
4758
+ new Set(seenIdentifiers),
4759
+ seenFunctions,
4760
+ depth + 1
4661
4761
  );
4662
4762
  if (!functionResolution) return null;
4663
4763
  return resolveReturnedObjectLiteralExpression(
@@ -4667,7 +4767,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4667
4767
  functionResolution.namedExpressions,
4668
4768
  namedPropertyAliases,
4669
4769
  seenIdentifiers,
4670
- seenFunctions
4770
+ seenFunctions,
4771
+ depth + 1
4671
4772
  );
4672
4773
  }
4673
4774
  if (isMemberAccessExpression(expression)) {
@@ -4680,7 +4781,8 @@ function resolveObjectLiteralExpression(expression, sourceFile, namedExpressions
4680
4781
  namedExpressions,
4681
4782
  namedPropertyAliases,
4682
4783
  seenIdentifiers,
4683
- seenFunctions
4784
+ seenFunctions,
4785
+ depth + 1
4684
4786
  );
4685
4787
  }
4686
4788
  return null;
@@ -4699,7 +4801,8 @@ function findFunctionLikeOnObjectLiteral(objectLiteral, propertyName, namedFunct
4699
4801
  }
4700
4802
  return null;
4701
4803
  }
4702
- function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions) {
4804
+ function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions, depth = 0) {
4805
+ if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
4703
4806
  const functionKey = getFunctionLikeCacheKey(functionLike);
4704
4807
  if (seenFunctions.has(functionKey)) return null;
4705
4808
  const nextSeenFunctions = new Set(seenFunctions);
@@ -4712,14 +4815,16 @@ function resolveReturnedTrackedFunctionLike(functionLike, args, sourceFile, name
4712
4815
  boundExpressions,
4713
4816
  namedPropertyAliases,
4714
4817
  new Set(seenIdentifiers),
4715
- nextSeenFunctions
4818
+ nextSeenFunctions,
4819
+ depth + 1
4716
4820
  );
4717
4821
  if (result) return result;
4718
4822
  }
4719
4823
  return null;
4720
4824
  }
4721
- function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set()) {
4825
+ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExpressions, namedPropertyAliases, seenIdentifiers, seenFunctions = /* @__PURE__ */ new Set(), depth = 0) {
4722
4826
  if (!expression) return null;
4827
+ if (depth > MAX_OPEN_REDIRECT_RESOLUTION_DEPTH) return null;
4723
4828
  const namedFunctions = getCachedNamedFunctionLikeDeclarations(sourceFile);
4724
4829
  const directFunctionLike = resolveFunctionLikeHandler(expression, namedFunctions);
4725
4830
  if (directFunctionLike) {
@@ -4732,7 +4837,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4732
4837
  namedExpressions,
4733
4838
  namedPropertyAliases,
4734
4839
  seenIdentifiers,
4735
- seenFunctions
4840
+ seenFunctions,
4841
+ depth + 1
4736
4842
  );
4737
4843
  }
4738
4844
  if (isCallLikeExpression(expression)) {
@@ -4741,8 +4847,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4741
4847
  sourceFile,
4742
4848
  namedExpressions,
4743
4849
  namedPropertyAliases,
4744
- /* @__PURE__ */ new Set(),
4745
- seenFunctions
4850
+ new Set(seenIdentifiers),
4851
+ seenFunctions,
4852
+ depth + 1
4746
4853
  );
4747
4854
  if (!functionResolution) return null;
4748
4855
  return resolveReturnedTrackedFunctionLike(
@@ -4752,7 +4859,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4752
4859
  functionResolution.namedExpressions,
4753
4860
  namedPropertyAliases,
4754
4861
  seenIdentifiers,
4755
- seenFunctions
4862
+ seenFunctions,
4863
+ depth + 1
4756
4864
  );
4757
4865
  }
4758
4866
  if (ts.isIdentifier(expression)) {
@@ -4766,7 +4874,8 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4766
4874
  namedExpressions,
4767
4875
  namedPropertyAliases,
4768
4876
  seenIdentifiers,
4769
- seenFunctions
4877
+ seenFunctions,
4878
+ depth + 1
4770
4879
  );
4771
4880
  seenIdentifiers.delete(expression.text);
4772
4881
  if (result) return result;
@@ -4779,8 +4888,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4779
4888
  propertyAlias.propertyPath.slice(0, -1),
4780
4889
  namedExpressions,
4781
4890
  namedPropertyAliases,
4782
- /* @__PURE__ */ new Set(),
4783
- /* @__PURE__ */ new Set()
4891
+ new Set(seenIdentifiers),
4892
+ seenFunctions,
4893
+ depth + 1
4784
4894
  );
4785
4895
  if (!resolvedObjectLiteral2) return null;
4786
4896
  const functionLike2 = findFunctionLikeOnObjectLiteral(
@@ -4800,8 +4910,9 @@ function resolveTrackedOpenRedirectFunctionLike(expression, sourceFile, namedExp
4800
4910
  sourceFile,
4801
4911
  namedExpressions,
4802
4912
  namedPropertyAliases,
4803
- /* @__PURE__ */ new Set(),
4804
- /* @__PURE__ */ new Set()
4913
+ new Set(seenIdentifiers),
4914
+ seenFunctions,
4915
+ depth + 1
4805
4916
  );
4806
4917
  if (!resolvedObjectLiteral) return null;
4807
4918
  const functionLike = findFunctionLikeOnObjectLiteral(
@@ -10218,7 +10329,8 @@ function critiqueSource({
10218
10329
  code,
10219
10330
  reviewPack = null,
10220
10331
  packManifest = null,
10221
- treatmentsCss = ""
10332
+ treatmentsCss = "",
10333
+ adoptionMode = null
10222
10334
  }) {
10223
10335
  const codeLower = code.toLowerCase();
10224
10336
  const astSignals = analyzeAstSignals(filePath, code);
@@ -10226,67 +10338,92 @@ function critiqueSource({
10226
10338
  const findings = [];
10227
10339
  const scores = [];
10228
10340
  const antiPatternIds = new Set(reviewPack?.antiPatterns.map((entry) => entry.id) ?? []);
10341
+ const isContractOnly = adoptionMode === "contract-only";
10229
10342
  const usedTreatments = TREATMENT_CLASSES.filter((token) => code.includes(token));
10230
- const treatmentSuggestions = TREATMENT_CLASSES.filter((token) => !code.includes(token)).map(
10231
- (token) => `Consider using \`${token}\` where appropriate.`
10232
- );
10233
- scores.push({
10234
- category: "Treatment Usage",
10235
- focusArea: "treatment-usage",
10236
- score: scoreRatio(usedTreatments.length, TREATMENT_CLASSES.length),
10237
- details: `${usedTreatments.length}/${TREATMENT_CLASSES.length} base treatments used: ${usedTreatments.join(", ") || "none"}`,
10238
- suggestions: treatmentSuggestions
10239
- });
10240
- if (focusAreas.includes("treatment-usage") && usedTreatments.length === 0) {
10241
- findings.push(
10242
- makeFinding({
10243
- id: "treatment-usage-missing",
10244
- category: "Treatment Usage",
10245
- severity: resolveSeverityFromChecks(reviewPack, "warn", [
10246
- "page-pattern-contract",
10247
- "section-pattern-coverage"
10248
- ]),
10249
- message: "No Decantr treatment classes were detected in the reviewed file.",
10250
- evidence: [
10251
- filePath,
10252
- "Expected tokens include d-interactive, d-surface, d-data, d-control, d-section, d-annotation, d-label."
10253
- ],
10254
- file: filePath,
10255
- suggestedFix: "Apply the compiled treatment vocabulary instead of hand-rolled utility styling."
10256
- })
10257
- );
10343
+ if (isContractOnly) {
10344
+ scores.push({
10345
+ category: "Styling Authority",
10346
+ focusArea: "treatment-usage",
10347
+ score: 3,
10348
+ details: "Contract-only adoption does not require Decantr d-* treatment classes in source files.",
10349
+ suggestions: [
10350
+ "Codify project-owned component variants and local rules when button/card/surface drift needs mechanical enforcement."
10351
+ ]
10352
+ });
10353
+ } else {
10354
+ const treatmentSuggestions = TREATMENT_CLASSES.filter((token) => !code.includes(token)).map(
10355
+ (token) => `Consider using \`${token}\` where appropriate.`
10356
+ );
10357
+ scores.push({
10358
+ category: "Treatment Usage",
10359
+ focusArea: "treatment-usage",
10360
+ score: scoreRatio(usedTreatments.length, TREATMENT_CLASSES.length),
10361
+ details: `${usedTreatments.length}/${TREATMENT_CLASSES.length} base treatments used: ${usedTreatments.join(", ") || "none"}`,
10362
+ suggestions: treatmentSuggestions
10363
+ });
10364
+ if (focusAreas.includes("treatment-usage") && usedTreatments.length === 0) {
10365
+ findings.push(
10366
+ makeFinding({
10367
+ id: "treatment-usage-missing",
10368
+ category: "Treatment Usage",
10369
+ severity: resolveSeverityFromChecks(reviewPack, "warn", [
10370
+ "page-pattern-contract",
10371
+ "section-pattern-coverage"
10372
+ ]),
10373
+ message: "No Decantr treatment classes were detected in the reviewed file.",
10374
+ evidence: [
10375
+ filePath,
10376
+ "Expected tokens include d-interactive, d-surface, d-data, d-control, d-section, d-annotation, d-label."
10377
+ ],
10378
+ file: filePath,
10379
+ suggestedFix: "Apply the compiled treatment vocabulary instead of hand-rolled utility styling."
10380
+ })
10381
+ );
10382
+ }
10258
10383
  }
10259
10384
  const decoratorNames = buildDecoratorInventory(treatmentsCss);
10260
10385
  const usedDecorators = decoratorNames.filter((name) => code.includes(name));
10261
10386
  const usesCssVars = code.includes("var(--");
10262
- scores.push({
10263
- category: "Theme Consistency",
10264
- focusArea: "theme-consistency",
10265
- score: decoratorNames.length > 0 ? scoreRatio((usedDecorators.length > 0 ? 1 : 0) + (usesCssVars ? 1 : 0), 2) : usesCssVars ? 4 : 2,
10266
- details: `Decorators used: ${usedDecorators.join(", ") || "none"}; CSS vars: ${usesCssVars ? "yes" : "no"}`,
10267
- suggestions: [
10268
- ...!usesCssVars ? ["Prefer CSS variable references over hardcoded visual values."] : [],
10269
- ...decoratorNames.length > 0 && usedDecorators.length === 0 ? ["Use theme decorators from treatments.css when they fit the component intent."] : []
10270
- ]
10271
- });
10272
- if (focusAreas.includes("theme-consistency") && !usesCssVars && usedDecorators.length === 0) {
10273
- findings.push(
10274
- makeFinding({
10275
- id: "theme-consistency-weak",
10276
- category: "Theme Consistency",
10277
- severity: resolveSeverityFromChecks(reviewPack, "warn", [
10278
- "theme-consistency",
10279
- "mutation-theme-contract"
10280
- ]),
10281
- message: "The file does not appear to use theme decorators or CSS variables from the compiled contract.",
10282
- evidence: [
10283
- filePath,
10284
- `Decorators available: ${decoratorNames.slice(0, 5).join(", ") || "none"}`
10285
- ],
10286
- file: filePath,
10287
- suggestedFix: "Anchor styling to tokens.css and treatments.css instead of local hardcoded values."
10288
- })
10289
- );
10387
+ if (isContractOnly) {
10388
+ scores.push({
10389
+ category: "Theme Consistency",
10390
+ focusArea: "theme-consistency",
10391
+ score: usesCssVars ? 4 : 3,
10392
+ details: `Contract-only mode: Decantr decorators are not required; CSS vars: ${usesCssVars ? "yes" : "no"}.`,
10393
+ suggestions: [
10394
+ "Use the app design system, Tailwind theme, Sass variables, component variants, or accepted local rules as the styling authority."
10395
+ ]
10396
+ });
10397
+ } else {
10398
+ scores.push({
10399
+ category: "Theme Consistency",
10400
+ focusArea: "theme-consistency",
10401
+ score: decoratorNames.length > 0 ? scoreRatio((usedDecorators.length > 0 ? 1 : 0) + (usesCssVars ? 1 : 0), 2) : usesCssVars ? 4 : 2,
10402
+ details: `Decorators used: ${usedDecorators.join(", ") || "none"}; CSS vars: ${usesCssVars ? "yes" : "no"}`,
10403
+ suggestions: [
10404
+ ...!usesCssVars ? ["Prefer CSS variable references over hardcoded visual values."] : [],
10405
+ ...decoratorNames.length > 0 && usedDecorators.length === 0 ? ["Use theme decorators from treatments.css when they fit the component intent."] : []
10406
+ ]
10407
+ });
10408
+ if (focusAreas.includes("theme-consistency") && !usesCssVars && usedDecorators.length === 0) {
10409
+ findings.push(
10410
+ makeFinding({
10411
+ id: "theme-consistency-weak",
10412
+ category: "Theme Consistency",
10413
+ severity: resolveSeverityFromChecks(reviewPack, "warn", [
10414
+ "theme-consistency",
10415
+ "mutation-theme-contract"
10416
+ ]),
10417
+ message: "The file does not appear to use theme decorators or CSS variables from the compiled contract.",
10418
+ evidence: [
10419
+ filePath,
10420
+ `Decorators available: ${decoratorNames.slice(0, 5).join(", ") || "none"}`
10421
+ ],
10422
+ file: filePath,
10423
+ suggestedFix: "Anchor styling to tokens.css and treatments.css instead of local hardcoded values."
10424
+ })
10425
+ );
10426
+ }
10290
10427
  }
10291
10428
  const hasAria = codeLower.includes("aria-") || codeLower.includes("role=");
10292
10429
  const hasFocus = codeLower.includes("focus-visible") || codeLower.includes("focusvisible");
@@ -11216,7 +11353,9 @@ function critiqueSource({
11216
11353
  ),
11217
11354
  details: reviewPack ? `Compiled review contract covers ${knownRoutes} routes. Placeholder navigation targets: ${placeholderNavigationTargets}.` : packManifest ? `Pack manifest is available for ${knownRoutes} pages, but the review pack is missing. Placeholder navigation targets: ${placeholderNavigationTargets}.` : `No compiled route context was available during critique. Placeholder navigation targets: ${placeholderNavigationTargets}.`,
11218
11355
  suggestions: [
11219
- ...reviewPack ? [] : ["Run `decantr refresh` so critique starts from a compiled review contract."],
11356
+ ...reviewPack ? [] : [
11357
+ "Run `decantr registry compile-packs decantr.essence.json --write-context` so critique starts from a compiled review contract."
11358
+ ],
11220
11359
  ...placeholderNavigationTargets > 0 ? [
11221
11360
  "Replace placeholder href/to targets with real route destinations from the compiled contract."
11222
11361
  ] : []
@@ -11272,7 +11411,7 @@ function critiqueSource({
11272
11411
  `Disallowed inline style attributes: ${astSignals.inlineStyleAttributeCount}`
11273
11412
  ],
11274
11413
  file: filePath,
11275
- suggestedFix: "Replace static inline visual values with treatments, decorators, and CSS variables from the compiled contract. Keep inline style only for Decantr CSS-variable writes and truly dynamic geometry."
11414
+ suggestedFix: isContractOnly ? "Replace static inline visual values with the project design system, accepted component variants, or local style rules. Keep inline style only for truly dynamic geometry." : "Replace static inline visual values with treatments, decorators, and CSS variables from the compiled contract. Keep inline style only for Decantr CSS-variable writes and truly dynamic geometry."
11276
11415
  })
11277
11416
  );
11278
11417
  }
@@ -11289,7 +11428,7 @@ function critiqueSource({
11289
11428
  message: "Component-scoped style tags or dynamic style elements were detected in the reviewed file.",
11290
11429
  evidence: [filePath, `Component style tag signals: ${componentStyleTagSignals}`],
11291
11430
  file: filePath,
11292
- suggestedFix: "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so the file stays aligned with the Decantr layer contract."
11431
+ suggestedFix: isContractOnly ? "Move shared keyframes, media queries, and visual rules into the project stylesheet, component library, or accepted local pattern/rule manifest." : "Move shared keyframes, media queries, and visual rules into global.css or treatments.css so the file stays aligned with the Decantr layer contract."
11293
11432
  })
11294
11433
  );
11295
11434
  }
@@ -11942,12 +12081,14 @@ async function critiqueFile(filePath, projectRoot) {
11942
12081
  const treatmentsCss = readTextIfExists(join2(projectRoot, "src", "styles", "treatments.css"));
11943
12082
  const reviewPack = loadReviewPack(projectRoot);
11944
12083
  const packManifest = loadPackManifest(projectRoot);
12084
+ const adoptionMode = readProjectAdoptionMode(projectRoot);
11945
12085
  return critiqueSource({
11946
12086
  filePath: resolvedPath,
11947
12087
  code,
11948
12088
  reviewPack,
11949
12089
  packManifest,
11950
- treatmentsCss
12090
+ treatmentsCss,
12091
+ adoptionMode
11951
12092
  });
11952
12093
  }
11953
12094
  export {
@@ -11956,6 +12097,7 @@ export {
11956
12097
  VERIFICATION_SCHEMA_URLS,
11957
12098
  auditBuiltDist,
11958
12099
  auditProject,
12100
+ collectMissingPackManifestFiles,
11959
12101
  createContractAssertions,
11960
12102
  createEvidenceBundle,
11961
12103
  critiqueFile,