From b9e54380dbda0dbb90a176ff59111afbef41642a Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 14 Aug 2026 23:38:17 +0200 Subject: [PATCH] Add copy-paste session prompts for Phase 2 and Phase 3 Each is written to be pasted into a fresh session with nothing added. That means carrying the answers the spec gate would otherwise demand -- goal, the decisions already made, boundaries, and a measurable done condition -- and opening with an explicit override so the gate does not fire a question round on a spec that is already complete. Both point at nielsen-audit.md for context but do not depend on it to be actionable, since a fresh session will not have read it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XS1m7b1piBtAL22USLW6jL --- unified-design-system/PHASE2-PROMPT.md | 28 ++++++++++++++++++++++++++ unified-design-system/PHASE3-PROMPT.md | 28 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 unified-design-system/PHASE2-PROMPT.md create mode 100644 unified-design-system/PHASE3-PROMPT.md diff --git a/unified-design-system/PHASE2-PROMPT.md b/unified-design-system/PHASE2-PROMPT.md new file mode 100644 index 0000000..6d2a07b --- /dev/null +++ b/unified-design-system/PHASE2-PROMPT.md @@ -0,0 +1,28 @@ +Implement Phase 2 of the shared component work: extend `~/utils/gatelock` (Python/Tk) with the composite widgets that four guard apps each reimplemented. Treat this as `override` on the spec gate — the spec is below, it is complete, and I do not want a round of questions before you start. + +**Goal.** gatelock already owns the design tokens (`LockConfig`) and the Tk plumbing, and has four consumers. Its scope stops short of composite widgets, so each consumer grew its own. Move those into gatelock and retire the copies in two proof consumers. + +**This is NOT a new package.** Add to `~/utils/gatelock`, which already exists and is consumed as `gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.4.1#subdirectory=gatelock`. + +**Build these four, in order:** + +1. `gatelock/widgets.py` — `make_button(parent, *, text, variant, command)`. Canonical implementation is diet-guard's `_gatelock_buttons.py::make_button`: its `variant` API picks the text colour *from* the fill, which structurally prevents the `fg`-vs-`on_fill` contrast bug the README documents. leetcode-guard's `_button` (no `` binding) and screen-locker's inline `tk.Button` are the regressions being retired. Two non-obvious traps both copies had to solve independently and which the shared version must keep: `` is unbound on `tk.Button` under X11, and the default focus ring is 1px black, invisible on `#211D1B`. +2. `gatelock/widgets.py` — `heading()` and `row()`, from `leetcode_guard/_status_sections.py`. +3. **Export the existing `ScrollableSurface`** and delete `leetcode_guard/status_view.py::_scrollable`, a hand-rolled reimplementation that lost `takefocus` and the focus ring the shared version already fixes. This one is a straight accessibility regression repair, not a refactor. +4. `gatelock/widget_group.py` — `WidgetGroup`, the per-output fan-out reimplemented four times (~916 lines total: screen-locker 173, leetcode-guard 105, diet-guard 309, wake-alarm 329). Highest line-count win on this stack. + +**Proof consumers: `diet-guard` and `leetcode-guard`** — the donor and the worst offender, so the API is validated against both ends. Bump **only these two**. Leave screen-locker and wake-alarm on their current pin: Phase 2 is additive, untouched consumers keep working, and restarting more live services than necessary is blast radius for no gain. Ask me before restarting anything live. + +**Extraction criterion.** Build a component only where two or more repos already contain a structurally similar implementation. Shipping a widget nobody imports is the failure mode to avoid. Corollary I learned the hard way in Phase 1: when you extract something, **retire the donor copies in the same pass**, or you have shipped a duplicate rather than removed one. + +**Verification gate — non-negotiable.** gatelock backs live systemd services; a bad import takes 3–4 of them down at once. + +- `/usr/bin/python3 -c "import gatelock"` against the **real** interpreter, not a dev venv. The library is tag-pinned in site-packages while the apps are editable, so a cwd-relative check will lie to you. +- Then diet-guard's and leetcode-guard's test suites. +- Then launch each gate under `xvfb-run -s "-screen 0 1366x768x24"` and confirm buttons render **and `` activates them** — that is the specific regression this phase repairs, so it is the specific thing to observe. +- Do **not** touch the gate window's X-grab path. Those fullscreen surfaces carry input-hijack invariants; a deadlock there has frozen the PC before. +- `pre-commit run --files ` in every touched repo, respect the 250-line file cap, and keep each repo's coverage bar. + +**Done means:** the four items exist in gatelock with tests, the two proof consumers import them with their local copies deleted, `` verifiably activates a button in each gate under xvfb, and everything is committed and pushed. + +Context, if you want it: `~/utils/unified-design-system/nielsen-audit.md` has the full grid and the Phase 1 record. Phase 1 (`~/utils/design_system`, Dart) is done — 7 exports, 100% coverage, six consumers — and is the pattern to imitate: `publish_to: none`, own analysis config, own tests, own coverage gate, tag-pinned git dep. diff --git a/unified-design-system/PHASE3-PROMPT.md b/unified-design-system/PHASE3-PROMPT.md new file mode 100644 index 0000000..fce0717 --- /dev/null +++ b/unified-design-system/PHASE3-PROMPT.md @@ -0,0 +1,28 @@ +Implement Phase 3 of the shared component work: create `~/utils/web_ui/` (TypeScript) and migrate two proof consumers onto it. Treat this as `override` on the spec gate — the spec is below, it is complete, and I do not want a round of questions before you start. + +**Goal.** Seven web repos define seven different vocabularies for the same palette: `--surface-1` vs `--panel` vs `--card`; `--text` vs `--ink` vs `--fg` vs `--bone`; spacing as `--space-N` vs `--sp-N` vs `--sp-xs`. Two of them additionally carry the same two components, drifted. Give the web stack the token + component layer that Dart already has. + +**Build these three:** + +1. `tokens.css` — one `:root` block using the canonical variable names from `~/utils/unified-design-system/README.md` and `tokens.md`. The palette is frozen: ink `#211D1B`, ink-raised-1 `#2B2624`, ink-raised-2 `#38312E`, line-dark `#463E3A`, text-on-dark `#ECEAE9`, muted-on-dark `#AAA09A`, paper `#F6F4F3`, paper-raised `#FCFBFB`, line-light `#E0DAD7`, muted-on-light `#70625B`, accent `#B8862E`, success `#8A9A3C`, warning `#E0A63C`, danger `#E2585F`, on-fill `#211D1B`. Spacing 4/8/16/24/32/48, radius 8/12/16, type 12/14/16/20/24/32. Do not invent new values; these are the same numbers `~/utils/design_system` ships for Dart. +2. `RangeSlider` — canonical is **dufs-cloud's**, whose pure `fractionFromPointer(rect, clientX)` is testable without layout (jsdom has none). awesome-mcp-explorer's copy has drifted 196 lines. Keep the pure function pure; that is the whole reason this one is the donor. +3. `FilterBar` + `filter-sort.ts` — reconcile dufs-cloud (174 lines) vs awesome-mcp-explorer (225). + +**Consumption mechanism — already decided, do not re-litigate.** A git dep `"@kuhyx/web-ui": "github:kuhyx/utils#web_ui-v0.1.0"`, matching the Dart/Python tag convention rather than introducing npm workspaces. **Ship a prebuilt `dist/` (tsc → ESM + `.d.ts`) committed in the tag.** Raw `.tsx` in `node_modules` would need per-repo bundler config, because Vite does not transpile dependencies by default; a committed `dist/` keeps consumers at zero config. This is the only stack with no existing precedent — verified: no `workspaces` key and no `file:`/`link:`/`github:kuhyx` dep anywhere — so the mechanism is the risky part, not the components. + +**Proof consumers: `dufs-cloud` and `awesome-mcp-explorer`** — the only two with confirmed shared components. Migrate both; do not stop at one. + +**Extraction criterion.** Build a component only where two or more repos already contain a structurally similar implementation. Nielsen's ten elements are the audit grid, not a build list — shipping a widget nobody imports is the failure mode to avoid. Two traps from Phase 1: a **name collision is not a duplicate** (judge structurally — the three `FilterSheet`s share a name but one uses no chips at all), and when you extract something, **retire the donor copies in the same pass**, or you have shipped a duplicate rather than removed one. + +**An open question you will hit.** The design system has one accent and therefore no answer for "N mutually distinguishable hues" — a categorical ramp for charts, tags and category dots. billsplit's four category colours were deliberately left unmapped for this reason. If `FilterBar` or anything else needs categorical colour, **do not invent a ramp inline**; raise it with me as its own decision. + +**Verification.** + +- `npm test` in both proof consumers. +- Verify at `1366x768` and `1024x600` with no horizontal scroll, and confirm `:focus-visible` is styled — an unstyled focus ring is the accessibility regression this layer is supposed to prevent. +- Confirm the migrated repos actually render from `tokens.css`: grep that their old bespoke custom properties are gone, not merely shadowed. +- `pre-commit run --files ` in every touched repo; respect the 250-line file cap. + +**Done means:** `~/utils/web_ui` exists with a committed `dist/`, tagged `web_ui-v0.1.0`; both proof consumers import it, with their local `RangeSlider`/`FilterBar`/token blocks deleted; tests pass in both; and everything is committed and pushed. + +Context, if you want it: `~/utils/unified-design-system/nielsen-audit.md` has the full grid, the deferred clusters, and the Phase 1 record. Phase 1 (`~/utils/design_system`, Dart) is done — 7 exports, 100% coverage, six consumers — and is the pattern to imitate: `publish_to: none`, own lint config, own tests, own coverage gate enforced in CI, tag-pinned dep.