{"record":{"id":"4e623956d94995d2","repo":"CherryHQ/cherry-studio","slug":"theme-contract-theme-input-css-declares-unregist","errorCode":null,"errorMessage":"[theme-contract] theme-input.css declares unregistered runtime input ${declaration.name}","messagePattern":"\\[theme-contract\\] theme-input\\.css declares unregistered runtime input (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":260,"sourceCode":"        throw new Error(`[theme-contract] foundation cannot declare runtime input ${declaration.name}`)\n      }\n      for (const reference of extractReferences(declaration.value, declaration.source)) {\n        if (\n          reference.startsWith(runtimeVariablePrefix) ||\n          officialVariables.has(reference) ||\n          productVariables.has(reference) ||\n          reference.startsWith('--color-') ||\n          reference.startsWith('--app-')\n        ) {\n          throw new Error(`[theme-contract] foundation ${declaration.name} cannot depend on upper-layer ${reference}`)\n        }\n      }\n    }\n  }\n\n  for (const declaration of extractDeclarations(sources.themeInput, 'theme-input.css')) {\n    if (!runtimeVariables.has(declaration.name)) {\n      throw new Error(`[theme-contract] theme-input.css declares unregistered runtime input ${declaration.name}`)\n    }\n    for (const reference of extractReferences(declaration.value, declaration.source)) {\n      if (officialVariables.has(reference) || productVariables.has(reference) || reference.startsWith('--color-')) {\n        throw new Error(`[theme-contract] runtime input ${declaration.name} cannot depend on upper-layer ${reference}`)\n      }\n      if (reference.startsWith('--app-')) {\n        throw new Error(`[theme-contract] runtime input ${declaration.name} cannot depend on host-local ${reference}`)\n      }\n    }\n  }\n\n  for (const declaration of extractDeclarations(sources.shadcn, 'shadcn.css')) {\n    if (declaration.name.startsWith(runtimeVariablePrefix)) {\n      throw new Error(`[theme-contract] shadcn.css cannot own runtime input ${declaration.name}`)\n    }\n    for (const reference of extractReferences(declaration.value, declaration.source)) {\n      if (productVariables.has(reference) || reference.startsWith('--color-') || reference.startsWith('--app-')) {\n        throw new Error(`[theme-contract] Shadcn ${declaration.name} cannot depend on product/adapter ${reference}`)","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L242-L278","documentation":"Thrown by assertLayerDependencies when theme-input.css declares a --cs-theme-* variable whose unprefixed name is not in the RUNTIME_THEME_INPUT_TOKENS registry (currently ['primary', 'primary-foreground']). The runtime-input surface is a closed, explicitly-registered set so the host code that writes these variables at runtime stays synchronized with the contract.","triggerScenarios":"Adding `--cs-theme-speculative: hotpink;` to theme-input.css without adding 'speculative' to RUNTIME_THEME_INPUT_TOKENS in scripts/theme-contract.ts; declaring a runtime input for an unregistered role.","commonSituations":"Adding a new themable role and forgetting the registry half of the change; leftover experimental declarations.","solutions":["If the variable is a real new runtime input, register its unprefixed name (e.g. 'speculative') in RUNTIME_THEME_INPUT_TOKENS in scripts/theme-contract.ts and document it in variable-catalog.md.","If it is not a runtime input, remove the declaration from theme-input.css (and if it belongs elsewhere, move it to the correct layer with the correct prefix).","Re-run `pnpm --filter @cherrystudio/ui theme:check`."],"exampleFix":"// before — theme-input.css declares an unregistered input\n:root { --cs-theme-speculative: hotpink; }\n// after (option A — register it)\n// scripts/theme-contract.ts\nexport const RUNTIME_THEME_INPUT_TOKENS = ['primary', 'primary-foreground', 'speculative'] as const\n// after (option B — remove the declaration)","handlingStrategy":"validation","validationCode":"// Ensure every --cs-theme-* declared in theme-input.css is registered.\nconst registered = new Set(RUNTIME_THEME_INPUT_TOKENS.map((t) => `--cs-theme-${t}`))\nconst unregistered = [...sources.themeInput.matchAll(/(--cs-theme-[a-z0-9-]+)\\s*:/g)].map((m) => m[1]).filter((n) => !registered.has(n))\nif (unregistered.length) throw new Error(`unregistered runtime inputs: ${unregistered.join(', ')}`)","typeGuard":null,"tryCatchPattern":"try {\n  validateThemeContractSources(sources)\n} catch (error) {\n  if (error instanceof Error && /declares unregistered runtime input/.test(error.message)) {\n    console.error(error.message)\n    process.exitCode = 1\n    return\n  }\n  throw error\n}","preventionTips":["Adding a --cs-theme-* declaration requires a matching entry in RUNTIME_THEME_INPUT_TOKENS in the same change.","Keep the runtime-input surface intentionally tiny; prefer extending foundation --cs-* over new runtime inputs.","Run theme:check after editing theme-input.css or scripts/theme-contract.ts."],"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"}