Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
8.4 KiB
This file is a ready-to-use prompt. Open this repo and say "do dopamine-ux-02-structural-check". It is self-contained -- it needs no context from any other session.
Canonical copy:
~/src/utils/dopamine-ux/02-structural-check.md(source of truth; this is a distributed copy). Running order and cross-prompt rules:~/src/utils/dopamine-ux/00-INDEX.md. Generated 2026-08-16 from a survey of this repo -- line numbers are accurate as of that date; anchor on the symbol names, not the line numbers.
⛔ WHEN YOU FINISH THIS PROMPT
Do both, in the same session that completes the work:
-
Delete this file and commit that deletion together with the implementation:
git rm prompts/dopamine-ux-02-structural-check.mdDo not leave a finished prompt lying in the repo -- a stale prompt is indistinguishable from a pending one, and the next session will re-run it.
-
Log completion in the canonical index, in
~/src/utils(a different repo, so it needs its own commit):cd ~/src/utils # append to the "Completion log" table in dopamine-ux/00-INDEX.md: # | 02-structural-check.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> | git add dopamine-ux/00-INDEX.md git commit -m "dopamine-ux: mark 02-structural-check.md done" git push
If you finish only part of the prompt, do neither -- instead edit this file to strike out what is done and state what remains, so the next session starts from the true state. Partial completion must never be recorded as DONE.
Add a structural-token drift checker to CI
findings (2, ranked by value over effort)
- Spacing, radius and type have never been drift-checked —
palette_check.pyparses hex only, so three whole token categories can diverge across stacks while CI stays green. Motion inherits the same hole. palette_map.pypoints at a "scale check" that does not exist — a comment describing a guarantee nobody wrote.
what
palette_check.py guarantees the colour palette agrees across all stack copies.
It does not check anything else — verified 2026-08-16 by reading its four
parsers, every one of which requires a #RRGGBB value:
parse_css : r"(--[a-z0-9-]+)\s*:\s*(#[0-9A-Fa-f]{6})\s*;"
parse_dart: r"static const Color (\w+) = Color\(0x[fF]{2}([0-9A-Fa-f]{6})\)"
parse_tk : r'^\s{4}(\w+):\s*str\s*=\s*"(#[0-9A-Fa-f]{6})"'
parse_md : r"^\|\s*`([a-z0-9-]+)`\s*\|\s*`(#[0-9A-Fa-f]{6})`"
So spacing, radius and type have been silently unprotected all along, and the
motion tokens added by prompt 01 inherit the same hole. A --duration-fast: 120ms
is invisible to every parser: CI passes while the stacks drift apart.
Worse, scripts/palette_map.py:159 claims structural values are "checked by the
scale check" — no such script exists anywhere in ~/src/utils. That comment
describes a check that was never written. This prompt writes it.
where
Repo: ~/src/utils.
Create ~/src/utils/unified-design-system/scripts/structural_check.py.
Edit ~/src/utils/.github/workflows/palette-drift.yml — add a step invoking it.
The workflow deliberately has no paths: filter; keep it that way.
Fix the stale comment at ~/src/utils/unified-design-system/scripts/palette_map.py
(~:159) to name the real script.
Sources to compare (three code stacks + the prose freeze):
| Role | Path |
|---|---|
| prose freeze | ~/src/utils/unified-design-system/tokens.md (spacing/radius/type) |
| motion freeze | ~/src/utils/unified-design-system/motion.md (created by prompt 01 — run it first) |
| web/React | ~/src/utils/web_ui/src/tokens.css |
| Flutter/Dart | ~/src/utils/design_system/lib/src/tokens.dart |
| Python/Tkinter | ~/src/utils/gatelock/gatelock/_window.py (class LockConfig) |
must
-
Cover motion (durations, easing) and the already-unprotected spacing, radius and type scales, in one checker.
-
Mirror
palette_check.py's two-halves design, because it is the part that actually works:- Agreement — for each mapped token, resolve it in every non-
n/astack and fail if values disagree. - Completeness, bidirectional — every structural value parsed out of any source must be mapped, or explicitly exempted. This is what catches "added it to one stack, forgot the others".
- Agreement — for each mapped token, resolve it in every non-
-
Support an explicit
n/aper stack, and require a reason string. Tkinter has no animation framework; a duration legitimately does not exist there. An honestn/awith a reason is correct — a fabricated constant is not. -
Adjudicate with exit codes. Exit non-zero on drift with a message naming the token and the disagreeing files. No warnings-only mode.
-
Keep every file ≤ 250 lines (
~/src/utils/file_length/config.py). Notepalette_check.pyis 231 andpalette_map.pyis 162 — that pair was split precisely to stay under the cap. Split yours the same way from the start (structural_check.py+structural_map.py) rather than discovering the problem at 251 lines. -
Ruff-clean under the repo's config. No
# noqa, notype: ignore. -
Expect it to fail on first run. Spacing/radius/type have never been compared; there may be real pre-existing drift. That is the point of writing it. Fix whatever it surfaces, in the stack copy that is wrong, and say in the session summary what drifted.
-
must not: weaken the checker, add an exemption, or narrow its scope to make it green. If a token genuinely differs per stack for a good reason, encode it as an
n/a/variant with the reason recorded, not as a silent skip. -
must not: modify
palette_check.py. It works; it is simply colour-only. The two scripts run side by side. -
must not: change any token value to resolve a disagreement without saying so explicitly — that is a design change, and
tokens.mdis a freeze. Surface it and let kuhy decide which value is canonical. -
optional: teach it the shadow tier too, if that falls out cheaply.
done
python3 ~/src/utils/unified-design-system/scripts/structural_check.pyexits 0, and exits non-zero when you deliberately break one stack copy (test both directions — a checker that never fails is not a checker).- It is wired into
~/src/utils/.github/workflows/palette-drift.ymland the workflow passes. palette_map.py's "scale check" comment names the real script.~/src/utils/scripts/check_file_length.sh --allreports no new violations.- The session summary states what drift, if any, the first run found.
verify
Desktop. Run the script, paste the output. Then prove it fails: change one spacing or duration value in one stack copy, re-run, paste the non-zero exit and the message, and revert. Both halves of that demonstration are required — the passing run alone proves nothing.
read first
~/src/utils/unified-design-system/scripts/palette_check.py— the design to mirror. ReadNON_COLOUR_CSS: it is the regex that currently excludes spacing/radius/type/shadow from the colour check, and therefore the precise list of what you are now covering.~/src/utils/unified-design-system/scripts/palette_map.py— thePALETTEtuple ofToken(canonical, md, css, dart, tk, why)rows. Your map wants the same shape, including thewhyfield.~/src/utils/.github/workflows/palette-drift.yml— howpalette_check.pyandramp_check.pyare invoked; add your step alongside.~/src/utils/unified-design-system/motion.md— what prompt 01 created.~/src/utils/unified-design-system/tokens.md— the spacing/radius/type tables. Do not edit it; it is 260 lines and already over the cap.
context you would otherwise rediscover
- The four "stack copies" are really three code stacks plus a prose freeze. There is no fourth code stack; do not hunt for one.
~/src/dufs-cloud/app/lib/ui/theme.dartis a fifth, uncompared copy of the palette (the Flutter app has nodesign_systemdependency and hand-restates every hex). It is out of scope here — prompt 07 wires it up properly. Do not add it as a source to this checker; wait until 07 has removed it.- Three Flutter apps (diet-guard, workout_app, wake_alarm) similarly
hand-transcribe tokens into local
lib/ui/theme.dartfiles. Those are deliberately outside the checker's scope for now — bringing them in is a much larger job than this prompt. ~/src/utils/gatelock/is a package inside this monorepo, not a separate repo.
REMOVE ME AFTER FINISH