Files
dorado-hd/docs/ui-parity-audit.md
T

176 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI parity audit — Phase 1 (Dorado-HD)
**Date:** 2026-09-11 · **Scope:** all 63 registry apps + the shared UI harness
**Companion register:** [`ui-parity-gaps.json`](ui-parity-gaps.json) (machine-readable)
**Program:** UI Parity Test, Audit & Remediation (goal: *usable + spec-faithful*)
## 1. Method & evidence
| Evidence | What it covers | Result |
|---|---|---|
| `AppSmokeSuite` (Robolectric Compose) | every registry app renders through the real host path at DEVICE 480×272, adaptive PORTRAIT 400×800, adaptive LANDSCAPE 800×360 | **63/63 green** |
| `tools/emulator_crawl.py` (Android 16 x86_64) | deep-link launch + screenshot + package-filtered crash scan per app | **63/63 launch, 0 crashes, 0 blank** |
| Static audit passes (3 parallel) | W1/W2/W7 apps + harness; W3/W4 casual; W5/W6 engines — flow, scaling, input keys, loops, persistence, concrete `file:line` defects | 63 open findings |
| Screenshot triage | all 63 entry screens read for clipping, overlap, overflow, empty layout | 15 ranked visual failures + cross-app patterns |
Baseline artifacts: `reports/emulator/crawl.json`, `crawl-summary.json`,
`shots/<slug>.png` (git-ignored; regenerate with the tool).
## 2. Severity & acceptance bar
- **P0** crash, softlock, dead-end, lost save, or input that breaks the run.
- **P1** systemic/visible defect: unreachable or off-screen controls, dropped
input, stuck overlay, IME-obscured fields, silent SFX, layout overflow.
- **P2** fidelity/polish gap: empty-state copy, duplicated headings,
inconsistent button rhythm, two-line row clashes.
- **Acceptance:** zero open P0/P1; every P2 closed or documented N/A; every app
has a smoke test and crawl evidence; back/IME/layout contracts pass at all
three configurations.
**Canon clarification.** Cropped typography (`alarm cloc`, `run and jun`) is the
Zune HD design language (canon §7) and is **not** a defect. The audit
distinguishes it from *functional* overflow: controls clipped off-screen,
words wrapped inside a button, or rows hidden under system bars.
## 3. Systemic classes (fix once, benefits everything)
| # | Class | Register | Notes |
|---|---|---|---|
| H-01 | Back always exits the mini-app | `DoradoRoot.kt:122`; no per-screen `BackHandler` | Every multi-screen app and overlay loses state on system back |
| H-02/03 | Context menu top-left + prompt text leak | `Common.kt:131-195` | Menus render over the header; nested prompts inherit text |
| H-04 | No IME insets | `MainActivity.kt:28`, `DoradoRoot.kt:102` | Compose/search fields covered by the keyboard |
| H-05 | Adaptive width ~227 units vs 480-authored rows | `DeviceCanvas.kt:42-57` | Root of the off-screen-control class (msnmoney, alarm, fan-prediction, shell game, solitaire, messenger) |
| H-06 | Bottom content under the system nav bar | weather/calendar | Reserve insets inside apps, not just at the root |
| H-07 | Wake shade raised on any `onPause` | `DoradoRoot.kt:83-92` | Crawl-confirmed: shade over the *next* app after a deep-link relaunch |
| H-08 | Room writes keyed on ticking state | stopwatch 30 Hz, drum per frame, alarm 1 Hz, messenger per keystroke | Jank/battery; debounce helper |
| H-09 | Composition side effects / double records | Sudoku, Reversi, Hearts, Spades, Solitaire | Move to effects with once-guards |
| H-10 | Silent SFX | Checkers, Chess, Hexic (`synth.start()` missing) | One-line each |
| H-11 | Bars/toasts overlay content | Facebook/Twitter shells, MiniPlayer | Reserve padding |
| H-12/13/14 | Menu rhythm, duplicate headings, empty states | across apps | P2 polish |
## 4. P0 game-breakers (per-app)
Run-and-jump death softlock · Hairball + Splatter Bug frozen retry ·
Vine Climb tap rewind · BBQ + Bees per-frame gesture churn · Slider Puzzle
pause/autosave · WordMonger save-on-exit · Castles stale camera ·
Texas Hold'em buy-in lock · Facebook About trap · Twitter orphan DMs ·
Piano octave snap · Hearts PassPicker duplicates · Metronome dial drag ·
Calendar month grid + nested prompt · Shell Game/Snowball/Tug-O-War
no-sensor dead input · AlarmManager exact-alarm crash + lost sleep timer ·
3D Picture Puzzle GL rebuild leak · PGR per-frame mesh leak + background
audio · Echoes countdown input · plus the cold-start state-clobber races
(Supernova, Snowball, RunAndJump, SpaceBattle, Penalty, VineClimb,
DecoderRing).
## 5. Visual failures from the emulator baseline
Ranked in the register (`V-01`…`V-15`): msnmoney tab bar, solitaire action
column, fan-prediction overflow, shell-game shop column, shuffle track rows,
zunesocial/zunereader truncation, audiosurf row clash, drummachine dead
panel, calculator scientific default + wrapped key, alarm tab clip, messenger
presence clip, 3D puzzle double title, weather nav-bar collision, twitter
banner crop.
## 6. Remediation waves
1. **W-H (harness):** H-01…H-11 + the adaptive overflow contract.
2. **W-P0:** every P0 row, each with a regression test (UI test where
applicable, engine test otherwise).
3. **W-P2:** fidelity rows + spec-checklist closure, then record goldens.
4. **W-V:** full unit + UI suites, lint, emulator re-crawl, closeout docs.
Every fix updates its register row to `fixed` with the commit SHA; the
register is the falsifiable progress tracker for the program.
---
## 7. Remediation results (Phase 2/4 closeout)
**Register:** all P0/P1 rows are `fixed`; the two remaining P2 rows are closed
(H-13 duplicate headings) or documented as accepted variation (H-12 menu
rhythm, matching both rhythms present in the device-era UI).
**Harness (W-H):** hardware back follows the in-app header contract on every
sub-screen (`DetailScaffold(onBack)` → `BackHandler`); the context
menu/prompt anchors bottom-center and resets per request; the root applies
IME insets with `adjustResize`; the wake shade triggers on `ON_STOP` only;
`MiniSynth.start()` added where missing; Room writes throttled.
**Per-app (W-P0):** the 24 game-breakers (softlocks, frozen retries, dropped
inputs, stale captures, lost saves, buy-in loss, GL rebuild storms, sensorless
dead ends, exact-alarm crashes, cold-start clobber races) and the layout
overflow class are fixed, each with a regression test where the defect is
engine-expressible.
**Verification layers now in CI (`testDebugUnitTest`):**
| Layer | Coverage | Result |
|---|---|---|
| Unit/engine suites | 1,406 tests | green |
| `AppSmokeSuite` | 63 apps × 3 parity configs | green |
| `LayoutBoundsTest` | 63 apps × portrait/landscape: no unreachable clickables | 126 green |
| `BackContractTest` | system back on game sub-screens returns in-app | green |
| `GoldenCaptureTest` | 59 apps' device-mode entry frames, pixel tolerance | green |
| `DesignInvariantTest` | token/corner/motion invariants + silent-synth, unkeyed-pointer-input, onBack-forwarding and IME structure guards | 0 violations |
| Emulator crawl | 63 apps deep-link launch, crash + blank scan | 63/63 ok |
**Coverage checklist:** `docs/ui-parity-checklists.md` (63 apps × spec/smoke/bounds/golden/crawl).
**Residual, documented:** four clock/date screens (alarm, calendar, weather,
notes) are excluded from pixel goldens because their entry frame renders the
wall clock/date — they stay covered by smoke, layout, back and crawl layers;
GL resource behaviour is guarded by pure cache-policy tests rather than GL
instrumentation; IME correctness is structural (root insets + `adjustResize`)
and visually spot-checked on the emulator rather than simulated in Robolectric;
canvas-drawn text is not exposed to the accessibility tree (a standing
fidelity/accessibility opportunity, not a parity regression).
---
## 8. Track A hardening (post-program)
| Item | Result |
|---|---|
| A1 CI guards | `lintDebug`, `tools/ui_lint.py` and a `py_compile` check now run in the HD CI workflow alongside the unit/UI suites |
| A2 Interactive flow crawl | `tools/emulator_flow_crawl.py` drives each app's primary flow, then backgrounds/relaunches and cold-restarts it, scanning logcat per step: **63/63 apps, 0 crashes** (evidence: `reports/emulator/flows/`) |
| A3 Accessibility | `AppTouch.kt` (`appTap` / `appHold` / `appDescription`) applied across the catalog: every button/row/tab exposes a TalkBack click action, boards and readouts carry state descriptions, press-and-hold controls expose a click. The flow crawl had to tap text bounds because pointerInput controls previously exposed no click action — that gap is closed |
| A4 Golden expansion | Wall-clock seam `AppClock` (production = system clock; tests frozen at 2024-06-15T12:34Z) enables deterministic goldens for the alarm/calendar/weather/notes entry frames; the suite now records **126 goldens** (device 480x272 + adaptive landscape for all 63 apps) and verifies within tolerance. Suite total: **1,477 tests** |
The four clock/date exclusions from the original golden set are gone: the
seam makes those entry frames deterministic. Second-screen goldens remain
the one deferred item — mid-flow visuals are covered by the interactive
flow crawl's per-step screenshots.
---
## 9. Next-steps program results (Tracks B/C/D)
**Verified on-device:** entry crawl **63/63** (0 crashes, 0 blank) and
interactive flow crawl **63/63** (primary flow → pause → background/relaunch →
cold restart), latest run 2026-09-12. Accessibility semantics from Track A also
made the UI tree readable to the crawl (text nodes now present).
| Item | Evidence |
|---|---|
| Fade-through crossfade + sleep timer | `media/FadeRamp.kt` + controller tests; settings ▸ playback |
| Live FFT visualizer (C5) | `TapRenderersFactory`/`VisualizerBus`/`SpectrumBands`; PCM-tap integration test |
| Richer lock-screen (M10) | `DoradoMediaNotificationProvider` + Robolectric provider test |
| Smart DJ, podcast search, Zune-Card export (M5) | music crossbar menu; `PodcastSearch`/`CloudPodcastDirectory`; `CardRenderer`/`CardExporter` |
| Radio + picture Now Playing, photo grid (M13) | `RadioNowPlaying`, `PicturePresentation`, `ArtistPhotos` |
| Inbox/user card (D2) | `CloudSocialClient` + local-first `inbox_messages` (DB v8) |
| Audiobooks (D4) | model/scanner/grouping, DB v7, parts player with speed ladder |
| Queue engine, built-in playlists, PIN lock | `QueueEngine` + showlist move/next/clear; `BuiltInPlaylists`; `PinLock` + shade keypad |
| Roster uplift | PGR 12 cars/6 tracks/18 events; Echoes 24 arenas; bowling 8 lanes; Labyrinth at parity |
**Regression the crawl caught:** the audiobook speed restore in
`PlaybackController.connect()` ran off the application thread, crashing every
launch (63/63 crawl failures); fixed by resuming controller calls on
`Dispatchers.Main`. Unit/UI suites did not catch it because tests disable the
service connection — the on-device gate is the reason this shipped correctly.
**Documented N/A / deferred:** live-radio ±5-min rewind (needs a time-shifting
server); the four remaining emulator titles (two corpus gaps, two dead
services); desktop i18n remaining locales (deferred registry, 20 shipped);
organization repository pinning (profile-level manual step).