{"record":{"id":"c500408780b62e20","repo":"CherryHQ/cherry-studio","slug":"theme-contract-product-variable-token-overlap","errorCode":null,"errorMessage":"[theme-contract] product variable ${token} overlaps the official Shadcn contract","messagePattern":"\\[theme-contract\\] product variable (.+?) overlaps the official Shadcn contract","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":324,"sourceCode":"\nexport function validateThemeContractSources(sources: ThemeContractSources): void {\n  assertUnique('runtime theme inputs', RUNTIME_THEME_INPUT_TOKENS)\n  assertUnique('Shadcn variables', SHADCN_VARIABLE_TOKENS)\n  assertUnique('product variables', CHERRY_PRODUCT_VARIABLE_TOKENS)\n  assertUnique('Tailwind product colors', CHERRY_PRODUCT_COLOR_TOKENS)\n  assertUnique('compatibility semantic colors', COMPATIBILITY_SEMANTIC_COLOR_TOKENS)\n  assertUnique('compatibility status colors', COMPATIBILITY_STATUS_COLOR_TOKENS)\n  assertUnique('compatibility colors', COMPATIBILITY_COLOR_TOKENS)\n\n  const productVariables = new Set<string>(CHERRY_PRODUCT_VARIABLE_TOKENS)\n  const shadcnVariables = new Set<string>(SHADCN_VARIABLE_TOKENS)\n  const shadcnVariableNames = new Set<string>(SHADCN_VARIABLE_TOKENS.map((token) => `--${token}`))\n  const productVariableNames = new Set<string>(CHERRY_PRODUCT_VARIABLE_TOKENS.map((token) => `--${token}`))\n  const canonicalColors = new Set<string>([...SHADCN_COLOR_TOKENS, ...CHERRY_PRODUCT_COLOR_TOKENS])\n\n  for (const token of CHERRY_PRODUCT_VARIABLE_TOKENS) {\n    if (shadcnVariables.has(token)) {\n      throw new Error(`[theme-contract] product variable ${token} overlaps the official Shadcn contract`)\n    }\n  }\n\n  for (const token of CHERRY_PRODUCT_COLOR_TOKENS) {\n    if (!productVariables.has(token)) {\n      throw new Error(`[theme-contract] Tailwind product color ${token} is missing from the product contract`)\n    }\n  }\n  for (const token of COMPATIBILITY_COLOR_TOKENS) {\n    if (canonicalColors.has(token)) {\n      throw new Error(`[theme-contract] compatibility color ${token} overlaps the canonical color contract`)\n    }\n  }\n  assertCompatibilityTokensDeclared(\n    'compatibility semantic colors',\n    COMPATIBILITY_SEMANTIC_COLOR_TOKENS,\n    sources.providerColors,\n    'tokens/colors/providers.css'","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L306-L342","documentation":"Thrown by validateThemeContractSources when a token in CHERRY_PRODUCT_VARIABLE_TOKENS also appears in SHADCN_VARIABLE_TOKENS. Product variables must not collide with the official Shadcn namespace, because a shared name would silently shadow an official role and break the separation between the upstream contract and Cherry-specific extensions.","triggerScenarios":"Adding 'background' (already a Shadcn token) to CHERRY_PRODUCT_VARIABLE_TOKENS in scripts/theme-contract.ts; adding a Shadcn color name like 'primary' as a product token.","commonSituations":"Adding a product role whose name happens to match an official Shadcn role; not checking the Shadcn list before naming a new product token.","solutions":["Rename the product token to a non-colliding, product-specific name (e.g. 'background-subtle' instead of 'background').","If the role genuinely belongs to the official Shadcn contract, remove it from CHERRY_PRODUCT_VARIABLE_TOKENS and add it to SHADCN_VARIABLE_TOKENS instead (then author it in shadcn.css).","Re-run `pnpm --filter @cherrystudio/ui theme:check`."],"exampleFix":"// before — theme-contract.ts\nexport const CHERRY_PRODUCT_VARIABLE_TOKENS = ['background', ...] as const\n// after — rename to a product-specific token\nexport const CHERRY_PRODUCT_VARIABLE_TOKENS = ['background-subtle', ...] as const","handlingStrategy":"validation","validationCode":"// Reject tokens present in both product and shadcn registries.\nconst shadcn = new Set(SHADCN_VARIABLE_TOKENS)\nconst overlap = CHERRY_PRODUCT_VARIABLE_TOKENS.filter((t) => shadcn.has(t))\nif (overlap.length) throw new Error(`product tokens overlap shadcn: ${overlap.join(', ')}`)","typeGuard":null,"tryCatchPattern":"try {\n  validateThemeContractSources(sources)\n} catch (error) {\n  if (error instanceof Error && /product variable .* overlaps the official Shadcn contract/.test(error.message)) {\n    console.error(error.message)\n    process.exitCode = 1\n    return\n  }\n  throw error\n}","preventionTips":["Before naming a new product token, check it is not already in SHADCN_VARIABLE_TOKENS.","Keep product names role-specific (e.g. -subtle, -strong suffixes) to avoid collisions with official Shadcn roles.","Run theme:check after editing either registry."],"tags":["theme-contract","css","build","validation","registry"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}