unified-design-system: record the extraction audit, unstale the phase headings

Answers "did we extract every component we actually use?" now that Phases
1-3 have shipped.

The headline: the extractions hold up. No shipped component was a false
positive, and the reverse check -- were the donor copies actually deleted?
-- is clean except for billsplit's `_SectionHeader`, fixed separately in
testsAndMisc@54054e7. The one clear miss is `DeviceCodeDialog` x3, two
copies of which are byte-for-byte identical; it belongs in
`sync_settings_ui` rather than `design_system` because it depends on
GitHub auth types.

Also corrects two things the audit surfaced:

`nielsen-audit.md` described Phase 2 *and* Phase 3 as "not started". Both
shipped -- gatelock 0.5.0 and web_ui-v0.3.1. A planning doc that says "not
started" about finished work is worse than no doc, because the next
session reads it as a backlog and rebuilds what already exists. Both
headings now state what shipped, and the plan text is kept below as the
record of why. The Phase 2 note also spells out that screen-locker and
wake-alarm keeping local fan-out copies is deliberate (only two consumers
were bumped, to avoid restarting live systemd services) so it does not
read as a regression to be "fixed".

The audit also found a fourth shared package the phase briefs omit:
`sync_settings_ui`, consumed by four apps. It resolves most of the
`SettingsScreen` cluster, which the grid still listed as outstanding.

The remaining true duplicates (`_PrefsPersistence`, `Clock`, mulberry32
RNG) are non-UI utilities outside every package's remit, and are recorded
as such rather than as a reason to widen web_ui into a general utility
package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U4Rj3s7K2qzrLNgJqqPhno
This commit is contained in:
Krzysztof Rudnicki 2026-08-15 15:29:53 +02:00
parent ccef40e817
commit 0dd4229432
2 changed files with 139 additions and 2 deletions

View File

@ -0,0 +1,124 @@
# Extraction audit — did we extract everything? (2026-08-15)
Answers "are all the components we actually use extracted?" across all three
shared packages, after Phases 1–3 shipped. Companion to `nielsen-audit.md`
(the grid) and `phase3-record.md` (Phase 3's verdicts).
**Short answer: yes, with one leftover — now fixed — and one clear miss.**
The three packages' extractions hold up. No shipped component turned out to be
a false positive, and the reverse check (did the donor copies actually get
deleted?) is clean except for one case.
## There is a fourth shared package
The phase briefs name three. There are four:
| Package | Stack | Consumers |
| --- | --- | --- |
| `design_system` | Dart/Flutter | 6 repos |
| `gatelock` | Python/Tk | 4 guard apps (2 bumped) |
| `web_ui` | TS/React | dufs-cloud, awesome-mcp-explorer |
| **`sync_settings_ui`** | Dart/Flutter | todo, home_inventory, diet-guard, wake-alarm |
`sync_settings_ui` (`backup_slot.dart`, `firebase_sync_controller.dart`,
`sync_settings_screen.dart`) changes the coverage answer for the whole sync
cluster — four of the five `SettingsScreen`s already delegate to it, so that
cluster is largely resolved rather than outstanding.
## Fixed in this pass
**billsplit's `_SectionHeader`** — a genuine leftover donor copy.
`billsplit/lib/ui/people_screen.dart` imported `design_system` while still
defining and using its own `_SectionHeader`. The shared `SectionHeader`'s
own doc names billsplit as a donor and sizes `defaultPadding` as
`fromLTRB(md, md, md, xs)` precisely to reproduce billsplit's `(16,16,16,4)`.
The union shipped; this donor was never deleted. Retired in
`testsAndMisc@54054e7`, verified geometrically identical.
(untools' `_SectionHeading`, the other named donor, *was* correctly deleted.)
## The one clear miss — `DeviceCodeDialog` ×3
The strongest finding. A modal showing a GitHub device-flow user code:
copies to clipboard, launches the verification URL, polls for the token,
pops with it or renders an error.
| Repo | Path | Lines |
| --- | --- | --- |
| todo | `lib/ui/device_code_dialog.dart` | 110 (public) |
| wake-alarm | `phone_app/lib/screens/github_mirror_screen.dart:157-240` | ~84 (private) |
| diet-guard | `app/lib/screens/github_mirror_screen.dart:398-481` | ~84 (private) |
**The wake-alarm and diet-guard copies are byte-for-byte identical** (`diff`
returns empty). todo's differs only by being public/keyed with doc comments.
Same domain types (`DeviceCodeResponse`, `GitHubDeviceAuth`), same props, same
`String? _error` state, same `initState`→`_poll` lifecycle.
**Belongs in `sync_settings_ui`, not `design_system`** — it depends on GitHub
auth types, so it is not a generic widget. Not yet extracted.
Note this is a *leaf* of the `GitHubMirrorScreen` ×4 cluster, which is
deliberately app-local (each copy carries the "connecting here also triggers
an actual sync" comment). The screen stays local; the dialog need not.
## True duplicates outside every package's remit
Real duplication, but none of it is UI, so no existing package owns it.
**These are not a reason to widen `web_ui`'s remit** — it is a UI package, and
the games were explicitly out of Phase 3 scope. A separate shared utility
module would be the vehicle, in a future phase.
- **`_PrefsPersistence` ×3** — todo, home_inventory, diet-guard, all
`implements LogPersistence`, all at the identical line number. Belongs to
`crdt_sync_dart`'s remit.
- **`Clock` / `createRealClock` / `createManualClock` ×2** — konbini-67 (59
lines) and iron-and-anvil (49). Same interface, same `realClock` shared
instance with the same rationale, doc comments near-verbatim.
- **mulberry32 seeded RNG ×4** — konbini-67, sims3-clone, europe-county-map,
hotline3d. Identical `0x6d2b79f5` core in all four, but the *interfaces*
genuinely differ (closure-returning-methods vs mutable-state-plus-free-
functions; `float`/`next` naming; `chance` in only one). Extractable, but
it is an API reconciliation, not a lift-and-shift.
## Correctly not extracted (name collisions)
Confirmed structurally disjoint — do not re-raise these:
- **`SettingsScreen` ×5** — four already delegate to `sync_settings_ui`;
dufs-cloud's is a WebDAV connection form (`_url`/`_user`/`_pass`), not sync
preferences.
- **`FilterSheet` ×3** — todo and home_inventory are Stateful, dufs-cloud is
Stateless. The split alone rules out a common component.
- **`FilterBar`** — settled in `phase3-record.md`.
- **`EntryTile` ×2** — kuhylog log entries vs dufs-cloud `DirEntry` rows.
- **`HomeShell` ×2**, **`HomeScreen` ×2** — unrelated shells/screens.
- **`formatDuration` ×2** — personal-website takes a `Duration` (CV date
range), dufs-cloud takes `ms: number` (media length). Disjoint signatures.
- **`ConfirmDialog`, `PromptDialog`, `StatBar`, `SummaryCards`** — single-use.
## Reviewed and judged benign
**leetcode-guard's five raw `tk.Button(` sites** (`_view.py:160,190,247`,
`_escape_flow.py:219`, `_study_strip.py:184`) persist despite the repo
importing `make_button`. All five sit inside the gate/lock-window path marked
do-not-touch, and each already applies the `fg=config.on_fill` contrast
discipline manually with an explanatory comment — the exact bug `make_button`'s
`variant` API exists to prevent. Worth migrating opportunistically; the
invariant is currently upheld.
## Not fully verified
`WrapperServer` ×4 (habit_stack 155 / home_inventory 217 / todo 223 /
diet-guard 254) was assessed from line counts and in-file architectural
comments, not a full body diff. If a hard verdict is wanted, that one still
needs opening.
## Phase 4 inputs (deferred, per the Q2 decision)
`personal-website`, `konbini-67`, `europe-county-map`, `sims3-clone`,
`steam-backlog-enforcer/web` and `iron-and-anvil` do not consume
`@kuhyx/web-ui`. All the `Clock`/RNG duplication lives entirely in this group.
`europe-county-map` and `iron-and-anvil` still define **zero** custom
properties, so they are token-adoption candidates independent of any component
work.

View File

@ -178,7 +178,15 @@ dep, as todo, untools, focus_owner, kuhylog, epopeja_karta and billsplit now
do. These six were never off-palette — they transcribe the right values by
hand — so this is duplication cleanup, not a visual fix.
### Phase 2 — Python/Tk (`~/utils/gatelock`), not started
### Phase 2 — Python/Tk (`~/utils/gatelock`), SHIPPED (gatelock 0.5.0)
> **Status: done.** `make_button`, `heading`/`row`, `ScrollableSurface` and
> `WidgetGroup` all ship in gatelock 0.5.0 (`c963cd8`, `d82107a`, `d25f288`);
> diet-guard and leetcode-guard are the migrated proof consumers.
> screen-locker (v0.4.0) and wake-alarm (v0.4.1) keep local fan-out copies
> **by design** — only two consumers were bumped to avoid restarting live
> systemd services. That asymmetry is not a regression; do not "fix" it.
> Everything below this line is the original plan, kept as the record of why.
Not a new package: gatelock already owns the tokens (`LockConfig`), the Tk
plumbing and 4 consumers. Add composite widgets, which is where its scope
@ -208,7 +216,12 @@ gain.
> path. Then launch each gate under `xvfb-run -s "-screen 0 1366x768x24"` and
> confirm buttons render and **`<Return>` activates** them.
### Phase 3 — TypeScript (`~/utils/web_ui`), not started
### Phase 3 — TypeScript (`~/utils/web_ui`), SHIPPED (web_ui-v0.3.1)
> **Status: done** — see `phase3-record.md` for what shipped and, importantly,
> why `FilterBar` was *not* extracted (a name collision, not a duplicate).
> Both proof consumers (dufs-cloud, awesome-mcp-explorer) are migrated.
> Everything below this line is the original plan, kept as the record of why.
Lowest confirmed duplication (2 components, 2 repos) and the only stack needing
a brand-new consumption mechanism, so it carries the worst effort-to-payoff