{"record":{"id":"fafbf1149720d6c7","repo":"CherryHQ/cherry-studio","slug":"theme-contract-label-is-missing-root-declarat","errorCode":null,"errorMessage":"[theme-contract] ${label} is missing root declarations: ${missing.join(', ')}","messagePattern":"\\[theme-contract\\] (.+?) is missing root declarations: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":165,"sourceCode":"        )\n      }\n      declarations.set(declaration.name, declaration)\n    }\n  }\n\n  return declarations\n}\n\nfunction assertRequiredDeclarations(\n  label: string,\n  declarations: Map<string, Declaration>,\n  variableNames: readonly string[],\n  prefix: string\n): 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 {","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L147-L183","documentation":"Thrown by assertRequiredDeclarations when a registered contract token list (runtime inputs, Shadcn variables, or product variables) has entries with no matching `--<prefix><token>` declaration in the :root block of the owning source. The contract is closed: every registered token MUST be declared in :root so consumers always resolve. The message lists every missing prefixed name.","triggerScenarios":"Deleting `--background: var(--cs-background);` from shadcn.css :root; deleting `--chat-user: ...;` from product.css :root; declaring a required variable only inside `.dark` and not in `:root`.","commonSituations":"Cleaning up 'unused' variables that are actually contract-mandated; moving a declaration into a nested selector; rebasing across a commit that added a new token to SHADCN_VARIABLE_TOKENS / CHERRY_PRODUCT_VARIABLE_TOKENS without authoring the CSS.","solutions":["Restore the missing declaration(s) into the :root block of the source named in the label (label maps to file: 'Shadcn contract in shadcn.css' -> shadcn.css, 'product contract in product.css' -> product.css, 'runtime theme inputs' -> the aggregated :root graph).","If a token was deliberately removed from the CSS, also remove it from the corresponding token registry in scripts/theme-contract.ts (SHADCN_VARIABLE_TOKENS / CHERRY_PRODUCT_VARIABLE_TOKENS / RUNTIME_THEME_INPUT_TOKENS).","Re-run `pnpm --filter @cherrystudio/ui theme:check`."],"exampleFix":"// before — :root block of shadcn.css with --background removed\n// after\n:root {\n  --background: var(--cs-background);\n}","handlingStrategy":"validation","validationCode":"// Pre-check that every registered token has a :root declaration in its owning source.\nconst required = new Set([...SHADCN_VARIABLE_TOKENS, ...CHERRY_PRODUCT_VARIABLE_TOKENS].map((t) => `--${t}`))\nconst declared = new Set([...sources.shadcn.matchAll(/^[\\s{;]*(--[a-z0-9-]+)\\s*:/gm), ...sources.product.matchAll(/^[\\s{;]*(--[a-z0-9-]+)\\s*:/gm)].map((m) => m[1]))\nconst missing = [...required].filter((n) => !declared.has(n))\nif (missing.length) throw new Error(`missing :root declarations: ${missing.join(', ')}`)","typeGuard":null,"tryCatchPattern":"try {\n  validateThemeContractSources(sources)\n} catch (error) {\n  if (error instanceof Error && /is missing root declarations/.test(error.message)) {\n    console.error(error.message) // lists the exact missing variables\n    process.exitCode = 1\n    return\n  }\n  throw error\n}","preventionTips":["When adding a token to a SHADCN_*/CHERRY_PRODUCT_*/RUNTIME_THEME_INPUT_TOKENS registry, author its :root declaration in the same commit.","Never move a required declaration out of :root into a nested selector.","Treat the lint/build:check gate as non-optional; it catches this before push."],"tags":["theme-contract","css","build","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}