{"record":{"id":"2183ba7959f9383b","repo":"CherryHQ/cherry-studio","slug":"theme-contract-label-references-missing-found","errorCode":null,"errorMessage":"[theme-contract] ${label} references missing foundation variables: ${missing.join(', ')}","messagePattern":"\\[theme-contract\\] (.+?) references missing foundation variables: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":179,"sourceCode":"): void {\n  const missing = variableNames.map((name) => `${prefix}${name}`).filter((name) => !declarations.has(name))\n\n  if (missing.length > 0) {\n    throw new Error(`[theme-contract] ${label} is missing root declarations: ${missing.join(', ')}`)\n  }\n}\n\nfunction assertCompatibilityTokensDeclared(\n  label: string,\n  tokenNames: readonly string[],\n  source: string,\n  sourceName: string\n): void {\n  const declarations = new Set(extractDeclarations(source, sourceName).map((declaration) => declaration.name))\n  const missing = tokenNames.map((token) => `--cs-${token}`).filter((name) => !declarations.has(name))\n\n  if (missing.length > 0) {\n    throw new Error(`[theme-contract] ${label} references missing foundation variables: ${missing.join(', ')}`)\n  }\n}\n\nfunction assertReferencesResolve(mode: string, declarations: Map<string, Declaration>): void {\n  for (const declaration of declarations.values()) {\n    for (const reference of extractReferences(declaration.value, declaration.source)) {\n      if (!declarations.has(reference)) {\n        throw new Error(\n          `[theme-contract] ${mode} ${declaration.name} in ${declaration.source} references undefined ${reference}`\n        )\n      }\n    }\n  }\n}\n\nfunction assertNoCycles(mode: string, declarations: Map<string, Declaration>): void {\n  const visited = new Set<string>()\n  const visiting = new Set<string>()","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L161-L197","documentation":"Thrown by assertCompatibilityTokensDeclared when a compatibility token list (COMPATIBILITY_SEMANTIC_COLOR_TOKENS or COMPATIBILITY_STATUS_COLOR_TOKENS) contains tokens whose backing `--cs-<token>` foundation variable is not declared in the expected source (providers.css or status-legacy.css). Compatibility tokens are a frozen shrink-only Tailwind surface, but each still requires a real foundation declaration to resolve.","triggerScenarios":"Adding 'destructive-hover' to COMPATIBILITY_SEMANTIC_COLOR_TOKENS in theme-contract.ts without declaring `--cs-destructive-hover` in tokens/colors/providers.css; declaring the backing var in the wrong file (e.g. primitive.css instead of providers.css).","commonSituations":"Reviving a legacy Tailwind utility class by adding it to the compatibility list but forgetting the CSS half; moving provider colors between token files during a refactor.","solutions":["Declare each missing `--cs-<token>` variable in the source named in the label (providers.css for semantic colors, status-legacy.css for status colors).","If the token should not be exposed, remove it from the compatibility list in scripts/theme-contract.ts instead of adding CSS.","Re-run `pnpm --filter @cherrystudio/ui theme:check`."],"exampleFix":"// before — theme-contract.ts\nexport const COMPATIBILITY_SEMANTIC_COLOR_TOKENS = ['destructive-hover'] as const\n// providers.css has no --cs-destructive-hover\n// after — add to tokens/colors/providers.css\n:root { --cs-destructive-hover: oklch(...); }","handlingStrategy":"validation","validationCode":"// Pre-check that compatibility tokens resolve to --cs-<token> in the expected source.\nconst declared = new Set([...sources.providerColors.matchAll(/(--cs-[a-z0-9-]+)\\s*:/g)].map((m) => m[1]))\nconst missing = COMPATIBILITY_SEMANTIC_COLOR_TOKENS.map((t) => `--cs-${t}`).filter((n) => !declared.has(n))\nif (missing.length) throw new Error(`compatibility tokens missing foundation: ${missing.join(', ')}`)","typeGuard":null,"tryCatchPattern":"try {\n  validateThemeContractSources(sources)\n} catch (error) {\n  if (error instanceof Error && /references missing foundation variables/.test(error.message)) {\n    console.error(error.message)\n    process.exitCode = 1\n    return\n  }\n  throw error\n}","preventionTips":["When extending a COMPATIBILITY_*_COLOR_TOKENS list, add the backing --cs-<token> to the matching token CSS file in the same change.","Remember compatibility lists are shrink-only: prefer removing a legacy token over adding one.","Run theme:check after any edit to scripts/theme-contract.ts."],"tags":["theme-contract","css","build","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}