Skip to content

Read a comment in a tenant theme as a comment, not as something the theme defines - #473

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:fix/theme-css-comment
Sep 10, 2026
Merged

Read a comment in a tenant theme as a comment, not as something the theme defines#473
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:fix/theme-css-comment

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

Put the line a hand-written stylesheet opens with at the top of a tenant package's theme.css:

/* Acme brand colours. Regenerate from the design tokens, do not hand-edit. */
:root { --primary: oklch(0.32 0.09 250); }

and the deployment does not start. loadTenantPackage is a top-level await in
server/src/index.ts, so the throw comes out before anything is listening. What it says is:

comment above the blocks:            Tenant theme may only define :root and .dark blocks
comment inside a block:              Tenant theme variable /* the one colour */
                                       --primary is not an approved theme variable
trailing comment on a declaration:   Tenant theme variable /* flat * is not an approved theme variable

(verbatim, from calling validateThemeCss on origin/main.)

None of those sentences mentions a comment, so the thing to remove is not in the message. The middle
one quotes the comment back as the name of a variable.

Why

docs/configuration.md states the rule as: "Theme CSS may define only :root and .dark blocks,
approved theme variables, and no @import or url()." Every clause of that is about what a theme
defines. A comment defines nothing — but validateThemeCss applies all three checks to the raw
file, so a comment is read as a definition:

  • Above or between the blocks, it survives css.replace(/(:root|\.dark)\s*\{[^{}]*\}/g, "") and lands
    in remaining, which is the test for a second selector.
  • Inside a block, body.split(";") hands it to the variable loop as a declaration, and it is
    refused under whatever the comment happens to say.

The fix

One line, before the three checks: take the comments out, then read what is left as definitions.

Removing them first is stricter than leaving them, not weaker, which is the part worth checking:

  • A comment wedged into the middle of url makes something a browser does not tokenise as a URL, and
    the url\s*\( test did not read it as one either. With the comment gone, both do, and it is
    refused.
  • A comment that is never closed does not match, so it is not removed, stays as the nonsense it is,
    and is still refused.
  • What a comment cannot do here is hide anything: what is left once they are gone is exactly what a
    browser would act on.

No doc change: the sentence in docs/configuration.md was already true, and is still true.

Measured

bun test server/tests/tenant-package.test.ts

  • Against origin/main with only the new tests applied: 30 pass, 19 fail.
    • The three new cases fail: accepts a comment above the blocks, accepts a comment inside a block, accepts a comment between the blocks.
    • The other 16 are pre-existing on this machine — this file's describes for package
      synchronisation talk to Postgres, and there is none here. They fail identically on unmodified
      main (29 pass, 16 fail before any of my tests were added).
  • With the change: 33 pass, 16 fail — the same 16 Postgres failures, and nothing else.

The fourth new test, still refuses what a comment is wrapped around, is the guard against
over-correcting and passes both before and after: a url() in a value is still refused, a comment
followed by body { } is still refused as a selector, and an unterminated comment is still refused.
So do the two theme tests that were already there.

Also run, all green:

  • bun run --filter server typecheck — exit 0
  • bunx biome check on both changed files — clean

Note on the CHANGELOG

A deployment that would not start now starts, so there is an entry. It goes at the top of
## Unreleased, the one line every entry goes at, so it will conflict with any other PR open against
that anchor. Happy to rebase whenever it suits you.

…heme defines

Every rule `validateThemeCss` applies is about what a theme may define, and a CSS
comment defines nothing. They were applied to the raw file anyway, so a comment
above the blocks read as a second selector and one inside a block read as a
variable name. A tenant package is loaded at start-up, so the deployment did not
come up, over a comment, saying nothing about comments.

Comments are removed before the text is read as definitions. That is stricter
than leaving them in: a comment wedged into the middle of `url(` now reaches the
rule that refuses it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@davidmckayv
davidmckayv requested a review from mxmzb as a code owner September 10, 2026 16:37

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep-reviewed against live code (correctness, governance, no vendor/secret/scale issues). Composed build+tests green. CHANGELOG/format rebase on CI-validated substance.

@davidmckayv
davidmckayv merged commit 460d2ac into CopilotKit:main Sep 10, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants