Skip to content

Commit 5f7b706

Browse files
nigelfentonclaudeten9876
authored
ci(theme): pin the compiled theme seed against the bundled JSON (#4570)
Groundwork for #3184 item 2 ("generate the seed table from the resource at build time — the high-leverage one; kills the drift class permanently"). **This PR changes no colours.** It proves the drift class is real, pins it, and stops it widening, so the generator lands against a floor and gets a regression test for free. ## The drift already happened `ThemeManager::seedBuiltinDefaults()` compiles a copy of the default theme into the binary so the UI works with zero theme files on disk. It is kept in sync with `resources/themes/default-dark.json` **by hand**, and the source says so twice — including this warning: > `KEEP IN SYNC: ... If those primitives shift, update both sites or the seeded look will drift` > `from the JSON-defined look on bundled themes (silently — both layers resolve, the JSON wins,` > `but the visible vs. seeded values diverge for pre-PR user themes).` Nine tokens on `main` @ `d50377f6` disagree today: | token | compiled seed | `default-dark.json` | |---|---|---| | `color.accent.dim` | `#0090e0` | `#0070c0` | | `color.slice.a` | `#ff4040` | `#00d4ff` | | `color.slice.b` | `#ff8c00` | `#ff40ff` | | `color.slice.c` | `#ffd040` | `#40ff40` | | `color.slice.d` | `#40c060` | `#ffff00` | | `color.slice.e` | `#00b4d8` | `#ffa000` | | `color.slice.f` | `#4080ff` | `#00e0c0` | | `color.slice.g` | `#c060ff` | `#ff6080` | | `color.slice.h` | `#ff60a0` | `#b080ff` | The slice colours explain themselves — the seed's own comment reads *"Preliminary values — a dedicated slice-colour audit may tune these in a follow-up."* The JSON was later tuned; the seed never was. Both bundled themes agree slice A is cyan/blue; only the compiled seed still says red. **Why it stayed hidden:** on a normal run the JSON wins, so a drifted seed is invisible. It only surfaces for a user whose theme predates a token — the seed then supplies the value the JSON would have overridden — or when no theme file loads at all. Both are exactly the situations nobody is watching, which is what makes this worth a gate rather than a one-off correction. ## What this adds - **`tools/check_theme_seed.py`** — parses both sides (resolving `{color.red.500}` aliases against the primitives palette) and diffs them. The nine existing divergences are frozen in `KNOWN_SEED_DRIFT`; `--strict` fails on anything else. The list may only shrink: a token that stops drifting is reported as **stale** so it gets removed rather than quietly padding the baseline. - **`.github/workflows/theme-seed-check.yml`** — runs it on PRs touching `ThemeManager.cpp`, `resources/themes/**`, or the checker. Modelled directly on `engine-boundary.yml`: same concurrency block, same pinned action SHAs, same known-warn / new-fail shape. The checker **refuses to report a clean run if it parses zero shared tokens**, so a future refactor of `seedBuiltinDefaults()` that breaks the regex fails loudly instead of silently passing. That failure mode is the whole risk of a parser-based gate, so it is guarded explicitly. ## Verification All three paths exercised, not just the happy one: | case | result | |---|---| | current `main` | 9 known, 0 new → exit 0 | | inject a new divergence (`color.spectrum.grid` → `#deadbe`) | reported as **NEW**, separate from the nine → **exit 1** | | repair a known one (`slice.a` → JSON value) | reported as **stale**, prompting its removal | Same results on Windows/Python 3.14 and on Linux/Python 3.12 — the version CI pins. ## Deliberately not in this PR **Fixing the nine.** Which side wins is a design call, not a mechanical one: the JSON is almost certainly right (it is what every user actually sees, and both bundled themes agree), but the seed is what pre-v2 user themes fall back to, so changing it is a visible change for those users. That belongs with whoever owns the slice-colour audit the comment refers to — happy to do it in a follow-up once someone says which way. **The generator itself.** Once the seed is generated from the resource, `KNOWN_SEED_DRIFT` empties and this checker becomes the test that the generator stays correct. Refs #3184. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com>
1 parent b82f613 commit 5f7b706

2 files changed

Lines changed: 488 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Theme Seed Check
2+
3+
# ThemeManager::seedBuiltinDefaults() compiles a copy of the default theme into
4+
# the binary, kept in sync with resources/themes/default-dark.json BY HAND — the
5+
# source comments say so, and warn that a drifted seed diverges "silently".
6+
# Nine tokens have already drifted that way (8 slice colours + color.accent.dim)
7+
# and are frozen in KNOWN_SEED_DRIFT; any NEW divergence fails the job
8+
# (--strict). The baseline only shrinks.
9+
#
10+
# Cheap to run (stdlib-only, no build), so it gates on the two files that can
11+
# cause the drift plus the checker itself.
12+
13+
on:
14+
pull_request:
15+
branches: ["**"]
16+
paths:
17+
- "src/core/ThemeManager.cpp"
18+
- "resources/themes/**"
19+
- "tools/check_theme_seed.py"
20+
- ".github/workflows/theme-seed-check.yml"
21+
22+
# Auto-cancel superseded runs when a newer commit lands on the same PR. Matches
23+
# the shape used by ci.yml / codeql.yml / engine-boundary.yml.
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}${{ github.ref == 'refs/heads/main' && format('-{0}', github.run_id) || '' }}
26+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
27+
28+
permissions:
29+
contents: read
30+
31+
jobs:
32+
theme-seed:
33+
name: Theme seed vs bundled JSON
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Checkout PR branch
37+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
38+
39+
- name: Set up Python
40+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
41+
with:
42+
python-version: "3.12"
43+
44+
- name: Check the compiled seed against default-dark.json
45+
run: python tools/check_theme_seed.py --strict

0 commit comments

Comments
 (0)