mirror of
https://github.com/project-dorado/dorado-hd.git
synced 2026-10-12 02:21:19 +00:00
176 lines
11 KiB
Markdown
176 lines
11 KiB
Markdown
# 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).
|