diff --git a/dopamine-ux/00-INDEX.md b/dopamine-ux/00-INDEX.md index c69da77..ef02add 100644 --- a/dopamine-ux/00-INDEX.md +++ b/dopamine-ux/00-INDEX.md @@ -69,6 +69,45 @@ Ranked by value-over-effort from the survey data: the `assets:` block and the VIBRATE permission are **already present**. Only the call is missing. +## Distributed copies, and the completion protocol + +Each prompt is **also copied into the repo it acts on**, at +`/prompts/dopamine-ux-*.md`, so you can open a repo and say +"do dopamine-ux-" without going hunting. **This directory stays the source +of truth** — if a prompt needs correcting, fix it here and re-distribute. + +| Prompt | Distributed to | +|---|---| +| 01 | `~/utils/prompts/dopamine-ux-01-motion-tokens.md` | +| 02 | `~/utils/prompts/dopamine-ux-02-structural-check.md` | +| 03 | `~/diet-guard/prompts/dopamine-ux-diet-guard.md` | +| 04 | `~/screen-locker/prompts/dopamine-ux-04-screen-locker.md` | +| 05 | `~/screen-locker/prompts/dopamine-ux-05-workout-app.md` | +| 06 | `~/todo/prompts/dopamine-ux-todo.md` | +| 07 | `~/dufs-cloud/prompts/dopamine-ux-07-theme.md` | +| 08 | `~/dufs-cloud/prompts/dopamine-ux-08-motion.md` | +| 09 | `~/wake-alarm/prompts/dopamine-ux-wake-alarm.md` | + +When a session finishes a prompt it **deletes its repo copy** (in the same +commit as the implementation) and **appends a row below**. A finished prompt left +in a repo is indistinguishable from a pending one, and the next session re-runs +it. Partial completion is never recorded as DONE — the file is edited to state +what remains instead. + +### Completion log + +| Prompt | Status | Impl commit | Note | +|---|---|---|---| +| 01-motion-tokens.md | not started | — | — | +| 02-structural-check.md | not started | — | — | +| 03-diet-guard.md | not started | — | — | +| 04-screen-locker.md | not started | — | — | +| 05-workout-app.md | not started | — | — | +| 06-todo.md | not started | — | — | +| 07-dufs-theme.md | not started | — | — | +| 08-dufs-motion.md | not started | — | — | +| 09-wake-alarm.md | not started | — | — | + ## Keeping these prompts honest Every path and symbol cited across these files is machine-checked: diff --git a/dopamine-ux/03-diet-guard.md b/dopamine-ux/03-diet-guard.md index 005f862..5f10e78 100644 --- a/dopamine-ux/03-diet-guard.md +++ b/dopamine-ux/03-diet-guard.md @@ -42,7 +42,7 @@ Primary: Theme (local hand-copy, **no `design_system` dependency**): - `app/lib/ui/theme.dart` — `buildAppTheme()` ~:12, `AppStatusColors` - ThemeExtension ~:89, re-declared `AppSpacing` ~:124, `AppRadius` ~:146, + ThemeExtension ~:89, redeclared `AppSpacing` ~:124, `AppRadius` ~:146, `AppTextSize`, plus an `AppWidth` class the shared package does not have. Settings (for the sound opt-out in step 2): diff --git a/prompts/dopamine-ux-01-motion-tokens.md b/prompts/dopamine-ux-01-motion-tokens.md new file mode 100644 index 0000000..bea4767 --- /dev/null +++ b/prompts/dopamine-ux-01-motion-tokens.md @@ -0,0 +1,176 @@ +> **This file is a ready-to-use prompt.** Open this repo and say +> "do dopamine-ux-01-motion-tokens". It is self-contained -- it needs no context from +> any other session. +> +> Canonical copy: `~/utils/dopamine-ux/01-motion-tokens.md` (source of truth; this is a +> distributed copy). Running order and cross-prompt rules: `~/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: + +1. **Delete this file** and commit that deletion together with the + implementation: + ```bash + git rm prompts/dopamine-ux-01-motion-tokens.md + ``` + Do 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. + +2. **Log completion in the canonical index**, in `~/utils` (a *different* repo, + so it needs its own commit): + ```bash + cd ~/utils + # append to the "Completion log" table in dopamine-ux/00-INDEX.md: + # | 01-motion-tokens.md | DONE | | | + git add dopamine-ux/00-INDEX.md + git commit -m "dopamine-ux: mark 01-motion-tokens.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 motion, haptic and sound tokens to the unified design system + +## findings (3, ranked by value over effort) + +1. **No motion token exists in any stack** — greenfield. Every app that wants an + animation currently invents its own number, and one already has. +2. **No haptic vocabulary exists** — the highest-value gap, because + `HapticFeedback` needs no dependency, so three of four apps can adopt it + the day this lands. +3. **`tokens.md` is 260 lines against a 250 cap** — it cannot absorb a new + section, so motion needs a sibling file. + +## what + +The shared design system has colour, spacing, radius, type and shadow tokens — +and **no motion tokens at all**. Verified 2026-08-16: no `Duration`, `Curve`, +`--duration-*`, `--ease-*`, or haptic constant exists in any stack copy. There is +not even a prose position that motion is out of scope. + +Consequence across kuhy's Flutter fleet: **exactly one animation exists in four +apps** — a lone `AnimatedContainer(duration: Duration(milliseconds: 200))` with an +inline magic number. Every other transition is a Flutter/browser default. + +Create the motion token layer so the per-app prompts (03–09) have something +canonical to consume instead of each inventing durations. + +## where + +Repo: `~/utils` (git repo, has CI + the file-length gate wired). + +**Create** `~/utils/unified-design-system/motion.md` — the prose freeze. + +**Do NOT append to `~/utils/unified-design-system/tokens.md`.** It is 260 lines +and the cap is 250, so it already fails the gate; adding to it makes that worse. +`screen-size.md` was split out of it for exactly this reason — follow that +precedent and add a pointer line in `tokens.md` only if you can do so without +growing it (i.e. by shortening another line). + +Then mirror the tokens into the three code stacks: + +| Stack | File | Form | +|---|---|---| +| Flutter/Dart | `~/utils/design_system/lib/src/tokens.dart` | `class AppDuration` / `class AppCurve` | +| web/React | `~/utils/web_ui/src/tokens.css` | `--duration-*`, `--ease-*` custom props | +| Python/Tkinter | `~/utils/gatelock/gatelock/_window.py` (`class LockConfig`) | int ms fields — **only if meaningful**; see `must not` | + +There are **three** code stacks plus the prose freeze. Do not go hunting for a +fourth — it does not exist. + +## must + +- Define a **small** scale. Recommended, but use judgement: + `instant 0ms · fast 120ms · base 200ms · slow 320ms`, and easing + `standard (0.2, 0, 0, 1)` · `decelerate (0, 0, 0, 1)` · `accelerate (0.3, 0, 1, 1)`. + Justify each value in `motion.md` in one line — the file is a freeze, so the + reasoning has to survive without you. +- Include a **haptic** vocabulary mapped to Flutter's `HapticFeedback` API: + which semantic event gets `selectionClick` vs `lightImpact` vs `mediumImpact`. + This is the highest-value part — `HapticFeedback` needs no dependency, so + three of four apps can adopt it immediately. +- Include a **sound** vocabulary: named semantic cues (`confirm`, `complete`, + `error`) with the rule that sound ships **on** (opt-out) per app, and that + each app owns its own asset files. +- State the reduced-motion contract in `motion.md`: web uses + `@media (prefers-reduced-motion: reduce)`, Flutter uses + `MediaQuery.of(context).disableAnimations`. Motion tokens collapse to `0ms` + under it; **haptics and sound are unaffected** (they are not motion). +- Keep every file at or under **250 lines** (`~/utils/file_length/config.py`, + `MAX_LINES = 250`). `.md` and `.py` are capped; `.css` is not. +- **Cut tags at the end** — this is the step that makes the work reach consumers: + - `design_system-v0.2.0` (consumed by `~/todo`, currently on `v0.1.0`) + - `web_ui-v0.3.2` (consumed by `~/dufs-cloud/web`, currently on `v0.3.1`) + Print both tag names at the end of the session; prompts 06, 07 and 08 need them. + +- must not: add motion values to the Tkinter `LockConfig` just for symmetry. + Tkinter has no animation framework. If a duration has no meaning there, mark + the row `n/a (tk)` in `motion.md` and leave the Python alone. An honest `n/a` + beats a dead constant. +- must not: express any motion token as a hex-looking value. The existing + `palette_check.py` parses `#RRGGBB` and would either ignore it or trip on it. +- must not: touch any colour token, or add a colour alongside the motion work. + That *would* break `palette_check.py`'s completeness half. +- must not: add `# noqa` or `type: ignore` anywhere. + +- optional: a `prefers-reduced-motion` helper in + `~/utils/design_system/lib/src/` and/or `web_ui/src/` so consumers get the + collapse behaviour without reimplementing it. Nice, not required. + +## done + +All four hold: + +1. `~/utils/unified-design-system/motion.md` exists, is ≤250 lines, and defines + duration, easing, haptic and sound vocabularies with a one-line rationale each. +2. `python3 ~/utils/file_length/check.py` (or + `~/utils/scripts/check_file_length.sh --all`) reports **no new** violations. + `tokens.md` (260), `nielsen-audit.md` (258) and `README.md` (257) are + pre-existing failures — do not fix them here, but do not add a fourth. +3. `python3 ~/utils/unified-design-system/scripts/palette_check.py` still exits 0. +4. Both tags exist: `git -C ~/utils tag | grep -E 'design_system-v0.2.0|web_ui-v0.3.2'` + prints two lines. + +## verify + +Desktop, any way you like — this prompt ships no UI. Run the two gate commands in +`done` and paste their output. Then confirm the token names you chose are what +prompts 03–09 will reference by printing the Dart class and the CSS custom-prop +block. + +## read first + +- `~/utils/unified-design-system/tokens.md` — the existing freeze; match its + table format and tone exactly. Note its length problem before editing. +- `~/utils/unified-design-system/screen-size.md` — the precedent for a sibling + file split out of `tokens.md` (32 lines; short and self-contained). +- `~/utils/design_system/lib/src/tokens.dart` — `AppPalette`, `AppSpacing`, + `AppRadius`, `AppTextSize`. Your `AppDuration`/`AppCurve` sit alongside these. +- `~/utils/web_ui/src/tokens.css` — the full custom-prop list. +- `~/utils/design_system/lib/src/feedback.dart` — `showToast`/`showError` and + `_show`. `confirm.dart` next to it **already imports + `package:flutter/services.dart`**, so `HapticFeedback` is one call away with no + new import. This is the natural home for a shared haptic helper. +- `~/utils/unified-design-system/scripts/palette_check.py` — read the four + `parse_*` regexes to see why motion tokens are invisible to it. Prompt 02 + builds the checker that covers them; **do not extend this script here.** + +## context you would otherwise rediscover + +- **Green CI does not mean no drift for these tokens.** `palette_check.py` only + matches `#RRGGBB`, so a `--duration-fast: 120ms` is invisible to every one of + its parsers. `palette_map.py:159` claims structural values are "checked by the + scale check" — **no such script exists**. Prompt 02 writes it. +- `~/utils/gatelock/` is a package inside the `~/utils` monorepo, not a + standalone repo. It is consumed as a pinned git dependency. +- Only `~/todo` depends on `design_system`; diet-guard, workout_app and + wake_alarm hand-transcribe the token values into local `lib/ui/theme.dart` + copies. So tokens added here reach exactly one app automatically — the others + are handled per-prompt. diff --git a/prompts/dopamine-ux-02-structural-check.md b/prompts/dopamine-ux-02-structural-check.md new file mode 100644 index 0000000..cf5591d --- /dev/null +++ b/prompts/dopamine-ux-02-structural-check.md @@ -0,0 +1,175 @@ +> **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: `~/utils/dopamine-ux/02-structural-check.md` (source of truth; this is a +> distributed copy). Running order and cross-prompt rules: `~/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: + +1. **Delete this file** and commit that deletion together with the + implementation: + ```bash + git rm prompts/dopamine-ux-02-structural-check.md + ``` + Do 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. + +2. **Log completion in the canonical index**, in `~/utils` (a *different* repo, + so it needs its own commit): + ```bash + cd ~/utils + # append to the "Completion log" table in dopamine-ux/00-INDEX.md: + # | 02-structural-check.md | DONE | | | + 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) + +1. **Spacing, radius and type have never been drift-checked** — `palette_check.py` + parses hex only, so three whole token categories can diverge across stacks + while CI stays green. Motion inherits the same hole. +2. **`palette_map.py` points 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: + +```python +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 `~/utils`**. That comment +describes a check that was never written. This prompt writes it. + +## where + +Repo: `~/utils`. + +**Create** `~/utils/unified-design-system/scripts/structural_check.py`. + +**Edit** `~/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 `~/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 | `~/utils/unified-design-system/tokens.md` (spacing/radius/type) | +| motion freeze | `~/utils/unified-design-system/motion.md` (**created by prompt 01** — run it first) | +| web/React | `~/utils/web_ui/src/tokens.css` | +| Flutter/Dart | `~/utils/design_system/lib/src/tokens.dart` | +| Python/Tkinter | `~/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: + 1. **Agreement** — for each mapped token, resolve it in every non-`n/a` stack + and fail if values disagree. + 2. **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". +- Support an explicit **`n/a` per stack**, and require a reason string. Tkinter + has no animation framework; a duration legitimately does not exist there. An + honest `n/a` with 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** (`~/utils/file_length/config.py`). Note + `palette_check.py` is 231 and `palette_map.py` is 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`, no `type: 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.md` is 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 + +1. `python3 ~/utils/unified-design-system/scripts/structural_check.py` exits 0, + and exits non-zero when you deliberately break one stack copy (**test both + directions** — a checker that never fails is not a checker). +2. It is wired into `~/utils/.github/workflows/palette-drift.yml` and the + workflow passes. +3. `palette_map.py`'s "scale check" comment names the real script. +4. `~/utils/scripts/check_file_length.sh --all` reports no new violations. +5. 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 + +- `~/utils/unified-design-system/scripts/palette_check.py` — the design to + mirror. Read `NON_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. +- `~/utils/unified-design-system/scripts/palette_map.py` — the `PALETTE` tuple of + `Token(canonical, md, css, dart, tk, why)` rows. Your map wants the same shape, + including the `why` field. +- `~/utils/.github/workflows/palette-drift.yml` — how `palette_check.py` and + `ramp_check.py` are invoked; add your step alongside. +- `~/utils/unified-design-system/motion.md` — what prompt 01 created. +- `~/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. +- `~/dufs-cloud/app/lib/ui/theme.dart` is a **fifth, uncompared copy** of the + palette (the Flutter app has no `design_system` dependency 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.dart` files. Those are + deliberately outside the checker's scope for now — bringing them in is a much + larger job than this prompt. +- `~/utils/gatelock/` is a package inside this monorepo, not a separate repo.