Build Discover TV picks, hidden titles and request flows #33

Merged
justinzeus merged 12 commits from fm/tv-slice6-discover into main 2026-11-05 20:35:19 +01:00
Owner

Discover TV slice

AI-assisted (fork-internal).

Implements Discover picks, filters, complete lane order, service pages, See all, Hidden picks, Not interested/restore/Undo, recipient selection, request sheets and receipts. Reuses the shipped shell, one Android key/hold adapter, shared poster/caption/grid/header/focus primitives and debug-only fixture provider. Main includes the shell, Libraries and Requests; this PR targets main after preserving all three through an additive merge.

Refs: approved TV spec, sections6.3/6.7/7.6/7.7/8/9/10; locked Opus prototype.

Design review corrections and additive main integration

Merged origin/main additively (merge commit bfd4002d), including shipped Requests head 1c77f21df3383e2af7684a047dfeb2675f4f8bc6. Both screen families remain: the actual Coming-in Requests sheet replaces the old shell placeholder, Discover overlay still draws after nav, both destination branches and glyph sets survive, and shared avatar letterColor/caption separator tokens are reused. No force push or rebase.

Design blocker Correction Re-measured native / frozen pixels
Restore subtitle rhythm Source negative subtitle top margin and bottom gap; explicit per-line title/body CSS allocation. st-032 sub ink top 211 /209; outer ring top 274 /274 at RGB tolerance30 (review target273±2); white fill 282–361 /282–361.
Squashed release sign-in retry Recovery content measures unbounded while its scroll row stays300px. st-036 white button fill 80 /80px; panel bottom border 555–556 /553–554 (2px shift, first border pixel within553±2).
Inflated hero paragraph Natural font overflow is centred inside the exact per-line CSS allocation, without changing text, width, weight or font. Other large styles were checked: sheet44px/57.2px and state36px/46.8px are within native rounding; shared library styles untouched. st-037 allocated title 86px for85.68px CSS (was100px); kicker ink top 259 /258; solid digit baseline 366 /368, both within±2. Same title allocation fixes st-029/038.

measure-review-layout.py records the exact RGB thresholds, bounds and border-versus-fill definitions; its JSON retains all measured values and six passing acceptance checks. The 273px Restore target is the outer focus ring, not its white fill, which begins282px in the frozen image too. Panel bottom reports both border rows instead of obscuring the 2px difference.

Three permanent native geometry guards now cover Restore negative-margin layout, full-sized retry despite row overflow, and hero CSS allocation. The hero guard failed on a99px paragraph with LineHeightStyle.Mode.Tight; explicit layout allocation passed at86px. No existing assertion was weakened.

Non-blocking review observations N1/N2 remain outside this three-blocker pass: request/receipt stacks and shared page-header/caption facts have measured3–4px offsets. Those are now explicitly identified per affected capture and not attributed to font rasterisation. Other non-blocking observations (unused live adapter coverage, debug per-person fixture persistence, rare empty-hero focus, live Upcoming projection and Show-again/Undo toast wording) were not expanded into unrelated changes. The capture suite now guards these three layout boundaries; it is not a universal pixel-equality test.

Safety and integration

  • No live Conductarr writes or network calls in tests/captures. Typed Conductarr operations use the generated client only, behind injection; the debug fixture host never constructs that adapter. Release shows NeedsSignIn.
  • Unknown request outcomes fresh-read before any future write; close/reopen and recipient changes preserve uncertainty. Whole-series requests require known seasons. Per-recipient actor/stage receipts and dismissal rollback/Undo preserve exact order/focus.
  • Loading→ready host focus is exercised asynchronously: first hero accepts the next real Android OK; loading completion never steals a later user-selected nav target.
  • Hero resources are released offscreen; bounded offline poster/backdrop sizes, keyed lazy shelves/grids, shared180ms frame tween and draw-phase focus lift avoid per-frame layout work.

Upstream files touched

None. No files under upstream app/src/main, app/src/default, upstream Gradle configuration or version/signing/default resources changed relative to main. Existing hook remains unchanged. Changes are confined to fork-owned wolfskin-design, app/src/wolfskin, debug assets/tests and the fork documentation. Shared focus/caption/grid/header/scroll primitives were extended to reuse one convention, not duplicate the shell or Libraries.

Verification at 130697c2eb0e79c6257be20362c27572dd374eab

Command/scenario Observed result
make test PASS,2m24s;685 tests: design108,app560,client17; zero failures/errors/skips; every existing test retained, no weakened assertion.
make apk-debug PASS,54s after shared cache wait;4 debug APKs,1013 resources each passed branding/legal attribution checks. No install.
Native screenshot suite 28 required captures at1920×1080,density2.0,offline Roborazzi/SDK34.
Real Android key replay Open Discover4; request yourself7; request Mindel9; Not interested8.16 key/safety regressions plus2 projection tests.
Async real host regression 2 app tests: skeleton→first ready focus and retain user-selected nav target.
Native stroke/color probe Radio3px in native/reference; native card ring4px versus reference3px opaque plus fractional/JPEG edge, not doubled8px. Caption structural dotsRGB77/77/80, muted74%×40%; header initials muted206/232/248; nav/person initials dark11/11/16.

After design review, firstmate explicitly authorized one final make test / make apk-debug pair after the additive main merge and these three blocker corrections. The table above is that final pair at the pushed head. No runtime edits or extra full gate runs followed. The task-owned offline init hook was removed; shared dependency caches were left intact. No release/tag/asset/signing/version change, device operation or install.

Deliberately not done / integration boundaries

  • Full title pages belong to the Title slice. Pick OK reaches the explicitly named discover-title-destination-stub, with request/hide actions but no cast/playback/version fidelity claim. The high whole-frame differences behind sheets and st-059 reflect that authorized stub, not a finished title page.
  • Live Conductarr sign-in/provider integration is not available. This slice uses the supplied debug-only fixture seam. Requests now uses the merged household Coming-in sheet, including its own explicit title-destination integration stub.
  • The generated backend contract has no trailer field. The spec fallback hides trailers for unheld picks; conditional9-press trailer scenario was not run. No invented URL or hand-written HTTP.
  • No physical-TV performance/device proof. These are real native JVM-rendered surfaces and actual Android-event focus tests, not a physical TV session.
  • Firstmate approved platform Roboto optionA: no bundled font, per-title breaks or width compensation. Exact wrapping parity is not claimed. Every resulting layout difference is explicit below. Corrective shadow/fill/skeleton/margin/disabled-state omissions were fixed, not waived.

Exit-check evidence

All28 final pairs were visually audited. Final proof archive contains per-capture text metrics, review-target pixel measurements, full final logs, scripts, decisions and before/after hash manifests. All28 pairs were regenerated; only st-029/032/036/037/038 changed and have new head-suffixed attachments. The other23 are byte-identical to the previously audited set. Source/native files and heatmaps remain under /root/.treehouse/firstmate-7bab20/2/firstmate/data/tv-slice6-discover/. Each pair shows prototype left and native right, each half1920×1080. Numeric errors include approved font/layout differences and explicitly scoped title stubs; they are not pixel-equality assertions.

Discover acceptance comparison

Commit: 130697c2eb0e79c6257be20362c27572dd374eab. Every half is 1920×1080 at density2.0; prototype left, native Compose right. SDK34/Roborazzi renders the actual surface with offline fixture art. All pairs were visually audited; these measurements are not pixel-equality claims.

Approved differences: platform Roboto (no bundled font or per-title/width compensation); source600/650 maps to platform Bold700; focus scales fold source buttons1.06→approved1.05 and sheet rows1.03→1.02. Hero/Restore text explicitly allocates CSS line boxes; other Discover text retains its source leading. Merged shell/library line-height styles are unchanged. Prototype-only sample/Simulated labels do not ship. Authorized title stubs are not title-family fidelity claims. Native Gaussian shadows/blur, glyph rasterisation and JPEG compression are not browser-identical (spec10.2). Non-blocking review observations N1/N2 (3–4px request/header/caption offsets) remain explicitly identified below, not disguised as font-only differences.

Mean RGB error averages every channel/pixel on a 0–255 scale. >12 counts pixels whose maximum absolute channel error exceeds12. Nav error includes the underlying top150px background, not an isolated geometry mask. Sheet error covers x1120–1919 only; the whole-frame diff also includes its outside shadow. Text metrics are actual composed TextLayoutResult values, including any transparent composed plates; the linked JSON contains line strings and bounds.

Screen and capture pair Mean RGB >12 pixels Nav RGB Sheet RGB Native text measurements Explanation
Discover picks hero (st-029) 11.7660 8.8643% 7.4752 — “For you”: 1L, y252.0/h34.0, 13sp/700; “The Amazing Spider-Man”: 1L, y296.0/h86.0, 42sp/800; “Movies for you”: 1L, y844.0/h45.0, 17sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) Native Spider-Man hero is one line; the frozen title is two. Its bottom-anchored text stack consequently starts lower. The native title now allocates the CSS85.68px line box rather than the inflated100px paragraph. Poster, art and chips are unchanged; native blur/shadow kernels and JPEG also differ.
Movies-for-you See all (st-030) 5.3689 8.1263% 6.0590 — “Movies for you”: 1L, y150.0/h75.0, 32sp/800; “The Amazing Spider-Man”: 1L, y638.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) All 18 titles and six-column order match. Native header/caption advances and baselines, score marks and native shadow rasterisation differ; shared library text styles are retained. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation.
Hidden picks (st-031) 2.9819 2.8530% 5.8569 — “Hidden picks”: 1L, y168.0/h45.0, 17sp/700; “Avengers Confidential: Black Widow & Punisher”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The frozen long Avengers caption ellipsizes; narrower native Roboto fits more characters in the same 640px band. Hidden ordering and structural separator opacity match. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation.
Show again sheet (st-032) 4.0478 2.5487% 9.6588 6.6202 “Avengers Confidential: Black Widow & Punisher”: 2L, y79.0/h116.0, 22sp/800 (JSON in final proof archive) The long title still breaks later natively, but explicit per-line allocation and the source sub margins restore the rhythm: sub ink211 versus reference209; button outer ring274 versus274 at RGB tolerance30 (review target273±2); solid white fill282–361 in both. Native sheet/control shadow rasterisation and content-derived pill widths differ.
Failed request badge (st-033) 4.4702 6.0604% 6.0718 — “Movies for you”: 1L, y168.0/h45.0, 17sp/700; “The Avengers”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The caption sits under the second poster at x376. Native title/facts advances differ. Structural fact dots are muted×0.4; the dot inside the accented requested/stage phrase intentionally stays accented, as in source line1203. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation.
Trending retry tail (st-034) 3.9345 5.1997% 5.8582 — “Trending”: 1L, y168.0/h45.0, 17sp/700; “More titles couldn’t load”: 2L, y440.5/h64.8, 12sp/500; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The restored last-five-title window and tail match. The tail message has different native word breaks within its fixed card; prototype-only sample pills are omitted. Native heading/score advances and shadow rendering differ.
Duplicate-only Show more tail (st-035) 2.4140 1.7295% 5.8942 — “Show more”: 1L, y334.1/h35.6, 14sp/700; “Only titles already shown came back”: 2L, y380.5/h64.8, 12sp/500; “Show more”: 1L, y570.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The description wraps into two native lines versus three frozen lines, shifting its vertically centred icon/text group. The fixed tail bounds, separate row and caption location are unchanged.
Discover needs sign-in (st-036) 3.8713 2.6715% 5.8569 — “Discover”: 1L, y150.0/h75.0, 32sp/800; “Discover needs sign-in”: 1L, y292.0/h47.0, 18sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The sign-in panel now overflows its fixed300px scroll row instead of squashing Try again: white fill80px in both; native bottom border555–556 versus source553–554. Native word breaks and content-derived retry width differ; no ratings before sign-in. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction.
Mindel series filter (st-037) 6.8972 5.6934% 7.3750 — “For you”: 1L, y252.0/h34.0, 13sp/700; “56 Days”: 1L, y296.0/h86.0, 42sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The native hero now has an86px CSS title allocation, not the inflated100px paragraph. Kicker ink259 versus reference258; solid digit baseline366 versus368, within the requested±2. Overview elides later with native advances. One-sheet, selected Series chip and shelf art match; native blur/shadow rasterisation differs.
No-seeds trending fallback (st-038) 11.8908 8.2590% 7.4752 — “Trending”: 1L, y252.0/h34.0, 13sp/700; “The Amazing Spider-Man”: 1L, y296.0/h86.0, 42sp/800; “Netflix”: 1L, y1073.0/h7.0, 15sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The Spider-Man fallback title is one native line versus two frozen lines, moving its bottom-anchored copy. Its native title allocation is now CSS85.68px per line, not the inflated100px paragraph. The prototype sample pill is omitted. Eye note, service wordmarks and source8% entry borders remain.
Trending mixed See all (st-039) 4.3217 6.0096% 6.0590 — “Trending”: 1L, y150.0/h75.0, 32sp/800; “American Gods”: 1L, y742.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The full 63-title order and mixed-media chips match. The prototype-only “· sample” header suffix is omitted. Native header/caption/score advances differ; shared library line-box treatment is unchanged. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction.
Netflix service page (st-040) 5.1379 6.3048% 6.0590 — “Netflix”: 1L, y150.0/h75.0, 32sp/800; “General interest”: 1L, y181.0/h35.0, 15sp/400; “The Amazing Spider-Man 2”: 1L, y802.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) The shared baseline-aligned subtitle uses source 30px/400. Narrower native header/chip/caption advances differ. Prototype-only sample pills are omitted from service shelves; source order/art are retained. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction.
Discover unavailable (st-041) 3.2638 2.2333% 5.8569 — “Discover”: 1L, y150.0/h75.0, 32sp/800; “Discover is resting”: 1L, y292.0/h47.0, 18sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) Try again is secondary/dark, as in the frozen frame. The source bold title and stacked line boxes are preserved; native text advances and content-sized retry width differ.
Not-interested receipt (st-058) 11.1992 16.8157% 7.0633 9.0515 “Hidden from your picks”: 1L, y108.0/h47.0, 18sp/700 (JSON in final proof archive) Paragraph word breaks differ with Roboto. The prototype-only Simulated note is omitted, moving Undo/Done upward. The dimmed background is the explicitly authorized title-destination stub, not title-family parity.
Hidden pick title destination (st-059) 23.2277 23.2955% 8.1856 — “The Amazing Spider-Man”: 1L, y200.0/h103.0, 44sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) This is the explicitly authorized title-family destination stub: no cast, playback/version family or trailer parity is claimed. Show again is implemented. Native title wrapping and content-derived action widths differ.
Request for yourself (st-071) 10.3027 16.2917% 5.1036 6.9391 “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y474.0/h39.0, 15sp/700 (JSON in final proof archive) The fixed 800px sheet, 80/64 padding, source margins and radio geometry match. Native glyph advances/baselines and pill widths differ. The dimmed title background is the authorized title stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Request for Mindel (st-072) 10.8592 16.4070% 5.1036 8.2749 “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 (JSON in final proof archive) The focused recipient uses the approved 1.02 scale instead of the prototype 1.03; controls use 1.05 instead of 1.06. Native text advances and the authorized title-stub background also differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Add held title for someone (st-073) 20.4138 21.9544% 7.9087 30.6500 “Add for someone”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 (JSON in final proof archive) The frozen subtitle puts 2006 on a second line; native Roboto fits it on one, raising the following stack by one 36.4px line. The disabled reason is 24px/500 at inherited80% under disabled50%. The held title background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Request receipt for Mindel (st-074) 10.9910 16.4929% 5.1036 8.3086 “Request”: 1L, y80.0/h58.0, 22sp/800; “Requested”: 1L, y293.0/h47.0, 18sp/700 (JSON in final proof archive) For Mindel/by Justin is retained. Removing the prototype-only Simulated wording shortens the note and raises Done. Native text advances differ; readiness/cast in the dimmed title are outside the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Request not confirmed (st-075) 10.3836 16.3104% 5.1036 7.1334 “Request”: 1L, y80.0/h58.0, 22sp/800; “Not confirmed”: 1L, y293.0/h47.0, 18sp/700 (JSON in final proof archive) The warning paragraph breaks at different words with Roboto; native control widths and shadow rasterisation differ. Check again performs a fresh lookup, never another POST. Background title/cast are the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Request unresolved (st-076) 10.6485 16.5841% 5.1036 7.7691 “Request”: 1L, y80.0/h58.0, 22sp/800; “Request unresolved”: 1L, y293.0/h47.0, 18sp/700 (JSON in final proof archive) The unresolved paragraph has later native word breaks. Font-derived text/pill widths and native shadows differ; the title/cast background is the authorized stub. Checking does not resubmit. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Request refused (st-077) 7.0284 10.6775% 5.1570 4.9710 “Request”: 1L, y80.0/h58.0, 22sp/800; “Refused”: 1L, y293.0/h47.0, 18sp/700 (JSON in final proof archive) The one-line refusal retains the fail colour and primary Close. Native heading/body/pill advances differ; the dimmed title/cast background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Eligible recipients loading (st-078) 9.2667 15.8259% 5.1036 4.4529 “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700 (JSON in final proof archive) The three 60px-circle/260×34 skeleton rows use source surface tone; Close is secondary. Native header/label line-box metrics and the authorized title-stub background differ. No waiting message or live read is introduced. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Eligible recipients unavailable (st-079) 10.6809 16.7050% 5.1036 7.8470 “Request”: 1L, y80.0/h58.0, 22sp/800; “Can’t read who can receive requests”: 2L, y293.0/h94.0, 18sp/700 (JSON in final proof archive) The two-line warning heading breaks differently with native advances. The primary retry/secondary Close group has the source top margin. Native shadows and the authorized title-stub background differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Series seasons unknown (st-080) 11.5746 18.7593% 5.2940 12.2619 “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y474.0/h39.0, 15sp/700 (JSON in final proof archive) The frozen subtitle wraps 2017 onto a second line; native fits one, raising the following stack by one 36.4px line. Close is secondary and the warning is retained; unknown seasons never submit. Background is the authorized title stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Shared actor receipt (st-087) 10.1008 14.9034% 5.3110 8.1190 “Request”: 1L, y80.0/h58.0, 22sp/800; “Requested”: 1L, y293.0/h47.0, 18sp/700 (JSON in final proof archive) For you/by Shared is retained. Prototype-only Simulated wording is omitted. The native Requested·Waiting action follows spec7.6 rather than the frozen Request-button stand-in; the rest of the title background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
Focused pick source-rating plate (st-105) 4.1961 5.7936% 6.0718 — “Movies for you”: 1L, y168.0/h45.0, 17sp/700; “The Avengers”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 (JSON in final proof archive) Both source scores remain on one line after the shared dwell. Native score/caption advances and shadow rasterisation differ. Structural caption dots use muted×0.4; the focused second-slot geometry is unchanged. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation.
Held-title sheet source ratings (st-110) 20.4138 21.9544% 7.9087 30.6500 “Add for someone”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 (JSON in final proof archive) The same held-title scene also proves full source ratings in the sheet. Its frozen subtitle wraps 2006 to a second line while native fits one, raising the stack by 36.4px. Native text and the authorized held-title stub differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference.
## Discover TV slice AI-assisted (fork-internal). Implements Discover picks, filters, complete lane order, service pages, See all, Hidden picks, Not interested/restore/Undo, recipient selection, request sheets and receipts. Reuses the shipped shell, one Android key/hold adapter, shared poster/caption/grid/header/focus primitives and debug-only fixture provider. Main includes the shell, Libraries and Requests; this PR targets `main` after preserving all three through an additive merge. Refs: [approved TV spec](https://pages.justinvisser.org/wolfskin/tv-spec), sections6.3/6.7/7.6/7.7/8/9/10; [locked Opus prototype](https://pages.justinvisser.org/wolfskin/tv-prototype-v2-opus/). ### Design review corrections and additive main integration Merged `origin/main` additively (merge commit `bfd4002d`), including shipped Requests head `1c77f21df3383e2af7684a047dfeb2675f4f8bc6`. Both screen families remain: the actual Coming-in Requests sheet replaces the old shell placeholder, Discover overlay still draws after nav, both destination branches and glyph sets survive, and shared avatar `letterColor`/caption separator tokens are reused. No force push or rebase. | Design blocker | Correction | Re-measured native / frozen pixels | |---|---|---| | Restore subtitle rhythm | Source negative subtitle top margin and bottom gap; explicit per-line title/body CSS allocation. | st-032 sub ink top **211 /209**; outer ring top **274 /274** at RGB tolerance30 (review target273±2); white fill **282–361 /282–361**. | | Squashed release sign-in retry | Recovery content measures unbounded while its scroll row stays300px. | st-036 white button fill **80 /80px**; panel bottom border **555–556 /553–554** (2px shift, first border pixel within553±2). | | Inflated hero paragraph | Natural font overflow is centred inside the exact per-line CSS allocation, without changing text, width, weight or font. Other large styles were checked: sheet44px/57.2px and state36px/46.8px are within native rounding; shared library styles untouched. | st-037 allocated title **86px** for85.68px CSS (was100px); kicker ink top **259 /258**; solid digit baseline **366 /368**, both within±2. Same title allocation fixes st-029/038. | `measure-review-layout.py` records the exact RGB thresholds, bounds and border-versus-fill definitions; its JSON retains all measured values and six passing acceptance checks. The 273px Restore target is the outer focus ring, not its white fill, which begins282px in the frozen image too. Panel bottom reports both border rows instead of obscuring the 2px difference. Three permanent native geometry guards now cover Restore negative-margin layout, full-sized retry despite row overflow, and hero CSS allocation. The hero guard failed on a99px paragraph with `LineHeightStyle.Mode.Tight`; explicit layout allocation passed at86px. No existing assertion was weakened. Non-blocking review observations N1/N2 remain outside this three-blocker pass: request/receipt stacks and shared page-header/caption facts have measured3–4px offsets. Those are now explicitly identified per affected capture and **not** attributed to font rasterisation. Other non-blocking observations (unused live adapter coverage, debug per-person fixture persistence, rare empty-hero focus, live Upcoming projection and Show-again/Undo toast wording) were not expanded into unrelated changes. The capture suite now guards these three layout boundaries; it is not a universal pixel-equality test. ### Safety and integration - No live Conductarr writes or network calls in tests/captures. Typed Conductarr operations use the generated client only, behind injection; the debug fixture host never constructs that adapter. Release shows NeedsSignIn. - Unknown request outcomes fresh-read before any future write; close/reopen and recipient changes preserve uncertainty. Whole-series requests require known seasons. Per-recipient actor/stage receipts and dismissal rollback/Undo preserve exact order/focus. - Loading→ready host focus is exercised asynchronously: first hero accepts the next real Android OK; loading completion never steals a later user-selected nav target. - Hero resources are released offscreen; bounded offline poster/backdrop sizes, keyed lazy shelves/grids, shared180ms frame tween and draw-phase focus lift avoid per-frame layout work. ### Upstream files touched **None.** No files under upstream `app/src/main`, `app/src/default`, upstream Gradle configuration or version/signing/default resources changed relative to main. Existing hook remains unchanged. Changes are confined to fork-owned `wolfskin-design`, `app/src/wolfskin`, debug assets/tests and the fork documentation. Shared focus/caption/grid/header/scroll primitives were extended to reuse one convention, not duplicate the shell or Libraries. ### Verification at `130697c2eb0e79c6257be20362c27572dd374eab` | Command/scenario | Observed result | |---|---| | `make test` | PASS,2m24s;685 tests: design108,app560,client17; zero failures/errors/skips; every existing test retained, no weakened assertion. | | `make apk-debug` | PASS,54s after shared cache wait;4 debug APKs,1013 resources each passed branding/legal attribution checks. No install. | | Native screenshot suite |28 required captures at1920×1080,density2.0,offline Roborazzi/SDK34. | | Real Android key replay | Open Discover4; request yourself7; request Mindel9; Not interested8.16 key/safety regressions plus2 projection tests. | | Async real host regression |2 app tests: skeleton→first ready focus and retain user-selected nav target. | | Native stroke/color probe | Radio3px in native/reference; native card ring4px versus reference3px opaque plus fractional/JPEG edge, not doubled8px. Caption structural dotsRGB77/77/80, muted74%×40%; header initials muted206/232/248; nav/person initials dark11/11/16. | After design review, firstmate explicitly authorized **one final make test / make apk-debug pair** after the additive main merge and these three blocker corrections. The table above is that final pair at the pushed head. No runtime edits or extra full gate runs followed. The task-owned offline init hook was removed; shared dependency caches were left intact. No release/tag/asset/signing/version change, device operation or install. ### Deliberately not done / integration boundaries - Full title pages belong to the Title slice. Pick OK reaches the explicitly named `discover-title-destination-stub`, with request/hide actions but no cast/playback/version fidelity claim. The high whole-frame differences behind sheets and st-059 reflect that authorized stub, not a finished title page. - Live Conductarr sign-in/provider integration is not available. This slice uses the supplied debug-only fixture seam. Requests now uses the merged household Coming-in sheet, including its own explicit title-destination integration stub. - The generated backend contract has no trailer field. The spec fallback hides trailers for unheld picks; conditional9-press trailer scenario was not run. No invented URL or hand-written HTTP. - No physical-TV performance/device proof. These are real native JVM-rendered surfaces and actual Android-event focus tests, not a physical TV session. - Firstmate approved platform Roboto optionA: no bundled font, per-title breaks or width compensation. Exact wrapping parity is **not** claimed. Every resulting layout difference is explicit below. Corrective shadow/fill/skeleton/margin/disabled-state omissions were fixed, not waived. ### Exit-check evidence All28 final pairs were visually audited. [Final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51) contains per-capture text metrics, review-target pixel measurements, full final logs, scripts, decisions and before/after hash manifests. All28 pairs were regenerated; only st-029/032/036/037/038 changed and have new head-suffixed attachments. The other23 are byte-identical to the previously audited set. Source/native files and heatmaps remain under `/root/.treehouse/firstmate-7bab20/2/firstmate/data/tv-slice6-discover/`. Each pair shows prototype left and native right, each half1920×1080. Numeric errors include approved font/layout differences and explicitly scoped title stubs; they are not pixel-equality assertions. # Discover acceptance comparison Commit: `130697c2eb0e79c6257be20362c27572dd374eab`. Every half is 1920×1080 at density2.0; prototype left, native Compose right. SDK34/Roborazzi renders the actual surface with offline fixture art. All pairs were visually audited; these measurements are not pixel-equality claims. Approved differences: platform Roboto (no bundled font or per-title/width compensation); source600/650 maps to platform Bold700; focus scales fold source buttons1.06→approved1.05 and sheet rows1.03→1.02. Hero/Restore text explicitly allocates CSS line boxes; other Discover text retains its source leading. Merged shell/library line-height styles are unchanged. Prototype-only sample/Simulated labels do not ship. Authorized title stubs are not title-family fidelity claims. Native Gaussian shadows/blur, glyph rasterisation and JPEG compression are not browser-identical (spec10.2). Non-blocking review observations N1/N2 (3–4px request/header/caption offsets) remain explicitly identified below, not disguised as font-only differences. Mean RGB error averages every channel/pixel on a 0–255 scale. >12 counts pixels whose maximum absolute channel error exceeds12. Nav error includes the underlying top150px background, not an isolated geometry mask. Sheet error covers x1120–1919 only; the whole-frame diff also includes its outside shadow. Text metrics are actual composed TextLayoutResult values, including any transparent composed plates; the linked JSON contains line strings and bounds. | Screen and capture pair | Mean RGB | >12 pixels | Nav RGB | Sheet RGB | Native text measurements | Explanation | |---|---:|---:|---:|---:|---|---| | [Discover picks hero (st-029)](https://git.justinvisser.org/attachments/3f9d95e5-d5b3-4d8e-b7a7-5777ae99cd32) | 11.7660 | 8.8643% | 7.4752 | — | “For you”: 1L, y252.0/h34.0, 13sp/700; “The Amazing Spider-Man”: 1L, y296.0/h86.0, 42sp/800; “Movies for you”: 1L, y844.0/h45.0, 17sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | Native Spider-Man hero is one line; the frozen title is two. Its bottom-anchored text stack consequently starts lower. The native title now allocates the CSS85.68px line box rather than the inflated100px paragraph. Poster, art and chips are unchanged; native blur/shadow kernels and JPEG also differ. | | [Movies-for-you See all (st-030)](https://git.justinvisser.org/attachments/0f1f2059-aaac-4000-a219-656db9d9c85f) | 5.3689 | 8.1263% | 6.0590 | — | “Movies for you”: 1L, y150.0/h75.0, 32sp/800; “The Amazing Spider-Man”: 1L, y638.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | All 18 titles and six-column order match. Native header/caption advances and baselines, score marks and native shadow rasterisation differ; shared library text styles are retained. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation. | | [Hidden picks (st-031)](https://git.justinvisser.org/attachments/be2825b1-2bb8-48b1-88f5-3096f8896347) | 2.9819 | 2.8530% | 5.8569 | — | “Hidden picks”: 1L, y168.0/h45.0, 17sp/700; “Avengers Confidential: Black Widow & Punisher”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The frozen long Avengers caption ellipsizes; narrower native Roboto fits more characters in the same 640px band. Hidden ordering and structural separator opacity match. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation. | | [Show again sheet (st-032)](https://git.justinvisser.org/attachments/35da94a7-151f-4847-8f30-c67ec8e0fa56) | 4.0478 | 2.5487% | 9.6588 | 6.6202 | “Avengers Confidential: Black Widow & Punisher”: 2L, y79.0/h116.0, 22sp/800 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The long title still breaks later natively, but explicit per-line allocation and the source sub margins restore the rhythm: sub ink211 versus reference209; button outer ring274 versus274 at RGB tolerance30 (review target273±2); solid white fill282–361 in both. Native sheet/control shadow rasterisation and content-derived pill widths differ. | | [Failed request badge (st-033)](https://git.justinvisser.org/attachments/6e5f6646-7a9d-4964-b59a-a3908ca62293) | 4.4702 | 6.0604% | 6.0718 | — | “Movies for you”: 1L, y168.0/h45.0, 17sp/700; “The Avengers”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The caption sits under the second poster at x376. Native title/facts advances differ. Structural fact dots are muted×0.4; the dot inside the accented requested/stage phrase intentionally stays accented, as in source line1203. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation. | | [Trending retry tail (st-034)](https://git.justinvisser.org/attachments/08803317-4c8a-442a-b1b8-f077aeb8e82d) | 3.9345 | 5.1997% | 5.8582 | — | “Trending”: 1L, y168.0/h45.0, 17sp/700; “More titles couldn’t load”: 2L, y440.5/h64.8, 12sp/500; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The restored last-five-title window and tail match. The tail message has different native word breaks within its fixed card; prototype-only sample pills are omitted. Native heading/score advances and shadow rendering differ. | | [Duplicate-only Show more tail (st-035)](https://git.justinvisser.org/attachments/9c80ae7b-77e8-422a-86d8-8e7d91889001) | 2.4140 | 1.7295% | 5.8942 | — | “Show more”: 1L, y334.1/h35.6, 14sp/700; “Only titles already shown came back”: 2L, y380.5/h64.8, 12sp/500; “Show more”: 1L, y570.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The description wraps into two native lines versus three frozen lines, shifting its vertically centred icon/text group. The fixed tail bounds, separate row and caption location are unchanged. | | [Discover needs sign-in (st-036)](https://git.justinvisser.org/attachments/90f82317-30bd-43bb-8c94-2127441ce0d3) | 3.8713 | 2.6715% | 5.8569 | — | “Discover”: 1L, y150.0/h75.0, 32sp/800; “Discover needs sign-in”: 1L, y292.0/h47.0, 18sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The sign-in panel now overflows its fixed300px scroll row instead of squashing Try again: white fill80px in both; native bottom border555–556 versus source553–554. Native word breaks and content-derived retry width differ; no ratings before sign-in. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction. | | [Mindel series filter (st-037)](https://git.justinvisser.org/attachments/05aad581-b602-4275-856c-b2c915c19043) | 6.8972 | 5.6934% | 7.3750 | — | “For you”: 1L, y252.0/h34.0, 13sp/700; “56 Days”: 1L, y296.0/h86.0, 42sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The native hero now has an86px CSS title allocation, not the inflated100px paragraph. Kicker ink259 versus reference258; solid digit baseline366 versus368, within the requested±2. Overview elides later with native advances. One-sheet, selected Series chip and shelf art match; native blur/shadow rasterisation differs. | | [No-seeds trending fallback (st-038)](https://git.justinvisser.org/attachments/93d0f9cb-9039-49c8-8bf3-b8158331afb1) | 11.8908 | 8.2590% | 7.4752 | — | “Trending”: 1L, y252.0/h34.0, 13sp/700; “The Amazing Spider-Man”: 1L, y296.0/h86.0, 42sp/800; “Netflix”: 1L, y1073.0/h7.0, 15sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The Spider-Man fallback title is one native line versus two frozen lines, moving its bottom-anchored copy. Its native title allocation is now CSS85.68px per line, not the inflated100px paragraph. The prototype sample pill is omitted. Eye note, service wordmarks and source8% entry borders remain. | | [Trending mixed See all (st-039)](https://git.justinvisser.org/attachments/4c8d70b7-525e-4b91-9a89-43c700e7a6d3) | 4.3217 | 6.0096% | 6.0590 | — | “Trending”: 1L, y150.0/h75.0, 32sp/800; “American Gods”: 1L, y742.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The full 63-title order and mixed-media chips match. The prototype-only “· sample” header suffix is omitted. Native header/caption/score advances differ; shared library line-box treatment is unchanged. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction. | | [Netflix service page (st-040)](https://git.justinvisser.org/attachments/45306e84-9cf9-407d-9532-8553b53cfa5c) | 5.1379 | 6.3048% | 6.0590 | — | “Netflix”: 1L, y150.0/h75.0, 32sp/800; “General interest”: 1L, y181.0/h35.0, 15sp/400; “The Amazing Spider-Man 2”: 1L, y802.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The shared baseline-aligned subtitle uses source 30px/400. Narrower native header/chip/caption advances differ. Prototype-only sample pills are omitted from service shelves; source order/art are retained. Shared header ink remains3–4px high (non-blocking review observation N2); shared header line-box changes were outside this bounded correction. | | [Discover unavailable (st-041)](https://git.justinvisser.org/attachments/eb2d9799-b347-430a-be36-286062930f91) | 3.2638 | 2.2333% | 5.8569 | — | “Discover”: 1L, y150.0/h75.0, 32sp/800; “Discover is resting”: 1L, y292.0/h47.0, 18sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | Try again is secondary/dark, as in the frozen frame. The source bold title and stacked line boxes are preserved; native text advances and content-sized retry width differ. | | [Not-interested receipt (st-058)](https://git.justinvisser.org/attachments/cdee9ae4-49e1-4b37-a59b-758c544e6e92) | 11.1992 | 16.8157% | 7.0633 | 9.0515 | “Hidden from your picks”: 1L, y108.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | Paragraph word breaks differ with Roboto. The prototype-only Simulated note is omitted, moving Undo/Done upward. The dimmed background is the explicitly authorized title-destination stub, not title-family parity. | | [Hidden pick title destination (st-059)](https://git.justinvisser.org/attachments/12b11c58-c789-436e-bfe5-d120d8b5dcc5) | 23.2277 | 23.2955% | 8.1856 | — | “The Amazing Spider-Man”: 1L, y200.0/h103.0, 44sp/800; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | This is the explicitly authorized title-family destination stub: no cast, playback/version family or trailer parity is claimed. Show again is implemented. Native title wrapping and content-derived action widths differ. | | [Request for yourself (st-071)](https://git.justinvisser.org/attachments/5e22804c-638b-4f38-89a8-d0144642cca9) | 10.3027 | 16.2917% | 5.1036 | 6.9391 | “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y474.0/h39.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The fixed 800px sheet, 80/64 padding, source margins and radio geometry match. Native glyph advances/baselines and pill widths differ. The dimmed title background is the authorized title stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Request for Mindel (st-072)](https://git.justinvisser.org/attachments/bb409f23-24fb-40c0-883b-fe96b32081da) | 10.8592 | 16.4070% | 5.1036 | 8.2749 | “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The focused recipient uses the approved 1.02 scale instead of the prototype 1.03; controls use 1.05 instead of 1.06. Native text advances and the authorized title-stub background also differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Add held title for someone (st-073)](https://git.justinvisser.org/attachments/92fecf82-5f26-445a-8d5a-58cd2e2889ea) | 20.4138 | 21.9544% | 7.9087 | 30.6500 | “Add for someone”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The frozen subtitle puts 2006 on a second line; native Roboto fits it on one, raising the following stack by one 36.4px line. The disabled reason is 24px/500 at inherited80% under disabled50%. The held title background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Request receipt for Mindel (st-074)](https://git.justinvisser.org/attachments/8f9c392d-172a-486a-8b32-f4f1b50b30ba) | 10.9910 | 16.4929% | 5.1036 | 8.3086 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Requested”: 1L, y293.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | For Mindel/by Justin is retained. Removing the prototype-only Simulated wording shortens the note and raises Done. Native text advances differ; readiness/cast in the dimmed title are outside the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Request not confirmed (st-075)](https://git.justinvisser.org/attachments/ff1032c8-06ac-4d38-a028-f9a7ffd2eaa2) | 10.3836 | 16.3104% | 5.1036 | 7.1334 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Not confirmed”: 1L, y293.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The warning paragraph breaks at different words with Roboto; native control widths and shadow rasterisation differ. Check again performs a fresh lookup, never another POST. Background title/cast are the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Request unresolved (st-076)](https://git.justinvisser.org/attachments/7eadd66f-72d7-4f8d-b385-308bace04ab4) | 10.6485 | 16.5841% | 5.1036 | 7.7691 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Request unresolved”: 1L, y293.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The unresolved paragraph has later native word breaks. Font-derived text/pill widths and native shadows differ; the title/cast background is the authorized stub. Checking does not resubmit. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Request refused (st-077)](https://git.justinvisser.org/attachments/7f2b66b0-9f53-4945-b852-66f65c5d11fe) | 7.0284 | 10.6775% | 5.1570 | 4.9710 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Refused”: 1L, y293.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The one-line refusal retains the fail colour and primary Close. Native heading/body/pill advances differ; the dimmed title/cast background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Eligible recipients loading (st-078)](https://git.justinvisser.org/attachments/a26133a6-c25c-4017-80c8-b0dd61365fb3) | 9.2667 | 15.8259% | 5.1036 | 4.4529 | “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The three 60px-circle/260×34 skeleton rows use source surface tone; Close is secondary. Native header/label line-box metrics and the authorized title-stub background differ. No waiting message or live read is introduced. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Eligible recipients unavailable (st-079)](https://git.justinvisser.org/attachments/5fc4d9e0-4309-41fa-853a-56fa4175098c) | 10.6809 | 16.7050% | 5.1036 | 7.8470 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Can’t read who can receive requests”: 2L, y293.0/h94.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The two-line warning heading breaks differently with native advances. The primary retry/secondary Close group has the source top margin. Native shadows and the authorized title-stub background differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Series seasons unknown (st-080)](https://git.justinvisser.org/attachments/bad44007-9637-444e-b6b4-cf5d9a2d2bd0) | 11.5746 | 18.7593% | 5.2940 | 12.2619 | “Request”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y474.0/h39.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The frozen subtitle wraps 2017 onto a second line; native fits one, raising the following stack by one 36.4px line. Close is secondary and the warning is retained; unknown seasons never submit. Background is the authorized title stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Shared actor receipt (st-087)](https://git.justinvisser.org/attachments/fd4e7b80-eef8-4a4e-bf7f-0b8fec65bb2d) | 10.1008 | 14.9034% | 5.3110 | 8.1190 | “Request”: 1L, y80.0/h58.0, 22sp/800; “Requested”: 1L, y293.0/h47.0, 18sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | For you/by Shared is retained. Prototype-only Simulated wording is omitted. The native Requested·Waiting action follows spec7.6 rather than the frozen Request-button stand-in; the rest of the title background is the authorized stub. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. | | [Focused pick source-rating plate (st-105)](https://git.justinvisser.org/attachments/c53982b0-fce1-436d-9415-b22a4d437000) | 4.1961 | 5.7936% | 6.0718 | — | “Movies for you”: 1L, y168.0/h45.0, 17sp/700; “The Avengers”: 1L, y630.0/h33.0, 14sp/700; “Discover”: 1L, y80.0/h35.0, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | Both source scores remain on one line after the shared dwell. Native score/caption advances and shadow rasterisation differ. Structural caption dots use muted×0.4; the focused second-slot geometry is unchanged. Shared caption facts remain3–4px high (non-blocking review observation N2); this is not excused as font rasterisation. | | [Held-title sheet source ratings (st-110)](https://git.justinvisser.org/attachments/6ef1c13d-560c-4fc0-acca-a3d8781c251c) | 20.4138 | 21.9544% | 7.9087 | 30.6500 | “Add for someone”: 1L, y80.0/h58.0, 22sp/800; “For”: 1L, y273.0/h34.0, 13sp/700; “Justin (you)”: 1L, y356.0/h39.0, 15sp/700; “Mindel”: 1L, y473.6/h39.8, 15sp/700 ([JSON in final proof archive](https://git.justinvisser.org/attachments/012b9ace-6688-4c05-a754-2042ce0deb51)) | The same held-title scene also proves full source ratings in the sheet. Its frozen subtitle wraps 2006 to a second line while native fits one, raising the stack by 36.4px. Native text and the authorized held-title stub differ. Separate non-blocking review observation N1 remains: request/receipt stacks sit3–4px high after accounting for subtitle wrapping; this is not claimed as a font-only difference. |
justinzeus added 10 commits 2026-11-05 19:50:44 +01:00
Author
Owner

Design review (Claude Sonnet, cross-family): CHANGES REQUESTED

Reviewed head 12990349913f96bbd5723cd08040bccc1898279f. Compared all 28 pairs against the locked frames at 1920x1080 with pixel measurements, and read the Discover code, the four press-path tests and the async-focus test. No Gradle run.

Blocking (visible, not font-caused):

  1. Show again sheet (st-032): the "Hidden from Justin's picks." line is 12 px lower than the frame and the button 13 px closer to it. The prototype's .sub margin (-8 above, +14 below) is applied in the Request sheet but not in the Restore sheet (DiscoverOverlay.kt:57-58).
  2. Sign-in panel (st-036): the Try again button is 57 px tall instead of 80 px and the panel ends 18 px early. The panel sits in a 300 px row (row_state) that caps its Column, so the two-line body squeezes the button (DiscoverScreen.kt:166-181). This is the screen every release build shows. The one-line variant (st-041) fits.
  3. Picks hero (st-029/037/038): the title baseline is 8 px and the "For you" kicker 13 px higher than the frame relative to the facts row. The hero title text box is 100 px where the CSS line box is 85.7 px (DiscoverType.hero, Trim.None), so the bottom-anchored stack carries about 14 px extra. It applies with Roboto too, so ruling (a) does not cover it.

Measured numbers and crops are in the review report. Fix suggestions: give the Restore sub the same offset as the Request sheet, let the state panel overflow its row, and clamp the hero title to the CSS line box; then re-measure st-029/037, 032 and 036.

Non-blocking: request/receipt sheets sit 3-4 px high from the "For" label down; shared headers and caption facts lines are 3-4 px high (inherited shared code); the real Conductarr adapter is unwired and untested and omits poster_path; debug-fixture hides/receipts reset on person switch; capture tests assert nothing.

Checks that passed: the four press paths (4/7/9/8) are real key-event replays through the shell focus path and fail if behaviour breaks; held OK files 0 requests; no double-submit (uncertain outcome does a forced read, never a POST); the real async skeleton-to-loaded focus path is tested; receipts and Shared wording follow 3.5; no upstream file touched; the fixture host constructs no network client; hygiene clean (plain commits, no trailers, no test deleted or weakened, GPL headers present, no public design API removed). The Trailer press path is a stated gap (no trailer field in the generated client), which is acceptable.

**Design review (Claude Sonnet, cross-family): CHANGES REQUESTED** Reviewed head `12990349913f96bbd5723cd08040bccc1898279f`. Compared all 28 pairs against the locked frames at 1920x1080 with pixel measurements, and read the Discover code, the four press-path tests and the async-focus test. No Gradle run. **Blocking (visible, not font-caused):** 1. **Show again sheet (st-032):** the "Hidden from Justin's picks." line is 12 px lower than the frame and the button 13 px closer to it. The prototype's `.sub` margin (-8 above, +14 below) is applied in the Request sheet but not in the Restore sheet (`DiscoverOverlay.kt:57-58`). 2. **Sign-in panel (st-036):** the Try again button is 57 px tall instead of 80 px and the panel ends 18 px early. The panel sits in a 300 px row (`row_state`) that caps its Column, so the two-line body squeezes the button (`DiscoverScreen.kt:166-181`). This is the screen every release build shows. The one-line variant (st-041) fits. 3. **Picks hero (st-029/037/038):** the title baseline is 8 px and the "For you" kicker 13 px higher than the frame relative to the facts row. The hero title text box is 100 px where the CSS line box is 85.7 px (`DiscoverType.hero`, Trim.None), so the bottom-anchored stack carries about 14 px extra. It applies with Roboto too, so ruling (a) does not cover it. Measured numbers and crops are in the review report. Fix suggestions: give the Restore sub the same offset as the Request sheet, let the state panel overflow its row, and clamp the hero title to the CSS line box; then re-measure st-029/037, 032 and 036. **Non-blocking:** request/receipt sheets sit 3-4 px high from the "For" label down; shared headers and caption facts lines are 3-4 px high (inherited shared code); the real Conductarr adapter is unwired and untested and omits `poster_path`; debug-fixture hides/receipts reset on person switch; capture tests assert nothing. **Checks that passed:** the four press paths (4/7/9/8) are real key-event replays through the shell focus path and fail if behaviour breaks; held OK files 0 requests; no double-submit (uncertain outcome does a forced read, never a POST); the real async skeleton-to-loaded focus path is tested; receipts and Shared wording follow 3.5; no upstream file touched; the fixture host constructs no network client; hygiene clean (plain commits, no trailers, no test deleted or weakened, GPL headers present, no public design API removed). The Trailer press path is a stated gap (no trailer field in the generated client), which is acceptable.
justinzeus added 2 commits 2026-11-05 20:32:09 +01:00
Clamp Discover text boxes and preserve recovery button height
All checks were successful
review/design-claude B1-B3 re-measured by firstmate from the lane measurement file against the locked numbers (all within 2 px); delta is the three scoped fixes plus an additive main merge; N1/N2 3-4 px offsets remain as listed follow-ups
130697c2eb
justinzeus merged commit 86d3432fb0 into main 2026-11-05 20:35:19 +01:00
Sign in to join this conversation.
No reviewers
No labels
exploring
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: justinzeus/wolfskin#33
No description provided.