Build the Wolfskin TV title, episode and person pages #34

Open
justinzeus wants to merge 13 commits from fm/tv-slice5-titles into main
Owner

Build native title, episode and person pages

AI-assisted (fork-internal)

Builds held/not-held film, series and explicit episode pages, playback-option sheets, cast/person pages and continuous filmography on the existing Wolfskin TV shell. All13 requested review fixes are implemented and measured below. All34 required title-family pairs are original1920×1080 at density2.0; all four complete native-Home launch paths now pass. Ready for re-review, not a release or device install.

Refs: approved TV specification, locked Opus v2 prototype. Scope: sections6.5,6.6,6.8–6.10,7.2 and title/person endpoints in3.6.

Base main; exact validated source cd625b4227ce8baa57512df50c8e74e57f5b42a9 on fm/tv-slice5-titles. Native Home main 9dd321f07e82393bd7afc2f470e4b61789907e13 is merged additively. Real Home, Discover, Requests and Library surfaces remain present; no unmerged sibling branch was imported.

Behaviour and integration

  • Film/series/exact-episode headers; Version, Audio and subtitles, Media info, watched toggle and whole-series confirmation; held trailer intent with calm unavailable-handler fallback.
  • Cast/person pages with actual credits/bios, missing portraits/art, inert loading skeletons, absent unavailable cast rows, recovery, saved/offline, signed-out and hidden/Undo states.
  • Existing shell/nav/back ladder and view memory are reused. Home Details now dispatches the real title route, not a title placeholder; Coming in retains its request overlay/expansion when returning from a title.
  • Held Jellyfin reads go through the facade with server/user-scoped saved state. Source UUIDs, non-contiguous track indexes, exact episode playback/watched scope and rejected/out-of-order optimistic choices are covered by consumer-visible tests. Subtitle Off persists DISABLED, not UNSPECIFIED.
  • Shared fractional CSS line boxes/columns preserve negative progress margins and accumulate media-row positions before rounding. Focused choices bring themselves into the actual scroll viewport. Mounted ready person pages own their first-credit focus transition.

All13 requested fixes — measured final result

Finding Frozen target / former defect Final observation and residual
D1 Header CSS geometry Preserve33.8px kicker,89.76px title line,38px facts,36.4px overview lines and actual negative progress margins; old native stack drifted8–18px. Shared fractional CSS layout restores facts y358 vs357.5, film actions y553 vs552.7, series actions y603 vs602.5. Native title ink y261–327 vs260–327; year ink367–387 vs364–385; overview ink416–443/453–480/490–511 vs414–442/450–478/487–509. Residual glyph baselines1–3px are explicit. The native heading TextLayoutResult natural box [96,245,327,349] is104px high and is NOT claimed to be its surrounding89.76px CSS line box. No fontflow excuse remains for stack geometry.
D2 Progress and episode marks Progress is26px regular400, not Medium500. Episode order is TMDB mark → Ep → score. Actual progress weight400; native progress bounds [96,539,457,573], source row y538.7. Native TMDB x578–625 → Ep x633–663 →7.8 x671–709. Source corresponding x645.6/700.4/744.8 differs because the Roboto progress string is361px wide versus DejaVu415.4px; order, weight and line box are fixed.
D3 Focused-button lift Canonical shadow0px x/12px y/40px blur/50% black was absent. TitleControl uses shared FocusLift.Button. Five pixels below the native Undo outer-ring run, y425, mean RGB is14.54/14.54/20.00, no longer flat19/19/26. Equivalent frozen Undo sample y472 is15/14/19. Absolute y differs because the approved Simulated disclaimer is removed. Native and Chromium kernels are not asserted identical.
D4 Focused selected radio inset Selected plus focused radio retains its3px dark inset stroke, not a flat accent disk. Both Version044 and Audio046 show native core RGB108/85/135 versus frozen core about106–111/82–89/129–148. Native horizontal dark-core runs are2px and3px plus AA; declared stroke is3px. Frozen fully dark runs are3px plus JPEG edges. Centre y286 vs285 for Version and298 vs298 for Audio. Approved row scale1.02 versus frozen1.03 changes transformed x bounds.
D5 Watched-eye ring Hide the inset watched-state ring while focused. Native eye-centre scan has exactly TWO accent runs, y543–547 and635–639; no extra inset pair. Frozen outer runs are about542–545 and635–638. Native four-pixel canonical stroke versus three thresholded JPEG pixels remains documented.
D6 Sheet button margins Restore12px top margin for Media info Close, confirmation actions, and Undo/Done. Native Close top845.1px versus reviewer's approximate image target844px; the scraper has no Close node. Native Mark as watched top283.1 vs Chromium282.5. Native Undo top332.1 vs frozen378.9 minus the approved31.2px disclaimer and16px gap =331.7; residual0.4px is consistent with approved1.05 versus1.06 button scaling.
D7 Fractional media table 31.2px lines +6px gaps; old independent rounding accumulated about12px. Container top263, Dutch top789: whole-table span526px versus source526.078125px. Fractional positions accumulate before rounding; adjacent observed pitch alternates37/38 instead of rounding every row to38. Existing pair assertion retained and a526.2±1 whole-span regression passes.
D8 Loading skeleton gaps Blocks y200/260/376/440/584 with gaps26/24/30/34, not a uniform-gap approximation. Native blocks [200,234], [260,352], [376,410], [440,550], [584,660], exactly the declared CSS geometry. JPEG thresholding yields441–549 and585–660 for the last two interiors. No spinner, loading copy or stale shelves.
D9 Cast and poster captions Preserve name/role/facts CSS line boxes; do not contract the stack by3–4px or damage siblings. Unfocused character box starts478 vs source477.8. Focused Ruth character ink493–512 vs491–515; INVINCIBLE facts ink673–690 vs671–689:2px top residual from glyph ink, not contracted layout. Shared Caption keeps merged Home's style/overflow API and role/ownership flex shrinking.84 fresh namespaced sibling captures were compared and inspected;36 are byte-identical to published native baselines,48 have explicit deltas. Family numbers are below; no missing poster, shifted shelf, lost focus state or clipping regression was observed.
D10 Rating-plate fade Preserve the inherited inline descender; focused fades formerly ended about8px too early. Unshrunk native plate100px versus source100.2, including shared6.6px inline descender. Focused clipped fade ends315.24 in056 vs reference about316, and320.64 in104 vs about321. At x330/y316, st104 RGB is EXACTLY179/191/117 in both; y300 is native138/145/87 vs136/146/85, y321 is191/204/125 vs190/202/126. Smaller multi-score plates retain their proportionate font sizing rather than forcing every row to100px.
F1 Subtitle Off persistence UI Off−1 must mean playback disabled, not upstream unspecified−1/preferred language. Actual facade regression proves UI−1 stores TrackIndex.DISABLED−2; UNSPECIFIED−1 is never written. A subsequent real subtitle index8 remains8. No live endpoint in the test.
F2 Focused sheet scrolling D-pad focus must remain visible beyond the initial viewport. Real sheet regression traverses20 focusable choices including Done with19 Down events. Every focused row's visible bounds stay inside the viewport with visible height>20px; Done closes the sheet without promoting nav focus. BringIntoViewRequester is attached to actual focused choices and footer controls.
F3 Async person focus A real delayed person load must focus first credit, not retain the nav/loading focus owner. Real TitleDestination + WolfskinTitleViewModel regression delays the facade reply, proves loading Down is inert, completes the person read, then observes credit0/nav false. Actual Right reaches credit1/nav false. Both async title/person scenarios pass; PersonSurface owns the mounted ready transition, not a racing parent effect.

Complete response: review-response.md. Native pixels/DOM/text/element details and residuals are retained in the evidence bundle; these numbers do not self-approve design fidelity.

Full launch paths — no deferred prefixes

The four TitleLaunchKeysTest cases use the actual Who's watching, native Home hero/shelves, title/person components and production focus handling. Every fixture key is replayed, including Home navigation. No direct row/focus assignment substitutes for input.

Path Actual key presses Observed outcome
Launch → title → nav 4 Nav owns the current tab while the title is retained
Launch → title → switch person 8 Mindel selected, chooser closed, and Home route active
Launch → Locke → second version → Play 15 Source v2 selected, sheet closed, and Play for Locke emitted
Launch → Locke → cast/person 11 Tom Hardy person route reached through the cast row

The Home prefix reaches Locke through its real current ordering: three Down events then four Right events in Your movies. No fixture reordering or placeholder hero remains. The older suffix-only tests are retained as additional coverage.

Commands and complete results

807 tests;0 failures,0 errors,0 skipped. Existing tests retained; no assertion weakened. One final-head make test passed in2m51 and one make apk-debug passed in1m14 after source was committed; no source edits followed. Debug artifact remains local only.

Module Tests Failures Errors Skipped
app 638 0 0 0
wolfskin-design 152 0 0 0
conductarr-client 17 0 0 0

Before the final pair, targeted shared-design smoke passed in1m03 and complete title-family smoke in2m43. All34 title captures and all four full launch cases are included in the unfiltered final XML. Tests use local artwork/debug fixtures and mocked SDK boundaries; no live Conductarr/Jellyfin calls. The inherited generated-client contract smoke uses a local fixture HTTP server, not Conductarr.

Evidence: test-results.md, final-cd625b42-make-test-and-apk.log, title-review-evidence.zip.

Required per-screen fidelity evidence

Frozen left / Compose right, each half1920×1080. MAE is mean absolute RGB error0–255; changed pixels have any channel >12. All34 full-resolution pairs were visually inspected in nine contact sheets. These are measurements, not an automatic pass.

Approved fonts are native platform Roboto versus frozen DejaVu Sans. No bundled font or per-title/row compensation. Native glyph widths can change wrapping; Spider-Man is two browser lines versus one native line,984.2px in1060px with no ellipsis. CSS stack geometry is explicit, not excused as fontflow. Residual ink baselines are1–3px. Approved focus scales1.05 buttons/1.02 rows differ from frozen1.06/1.03. Canonical shadows/rings are present; native/Chromium kernels and JPEG AA are not claimed identical.

Approved product exceptions are identified per frame: no sample/simulated labels or handoff screens, fake biography/credit filler, signed-out ratings or unheld trailer before Conductarr addition5. Frozen069 avatar and109 ratings take precedence over inconsistent metadata/filenames.

Required capture pair MAE Pixels >12 Nav MAE Explicit difference
held movie justin (st-042) 9.7712 7.7970% 5.7295 Held film: same backdrop/cast, two title lines on both. Native glyph widths and overview line breaks differ; no copy or size compensation. Native heading: 2 lines, widths [727.3, 542.3] px in 1060 px; ellipsis [False, False]; requested weight 800; bounds [96, 245, 1156, 439] px.
held film two versions options row locke note 3 (st-043) 6.6792 5.6362% 6.2012 Locke options: the prototype sample suffix is absent. Version-ring bounds, thresholded stroke and caption ink are measured separately. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
version list locke sample files (st-044) 5.3678 5.4471% 5.0046 Version sheet: sample badge/disclaimer absent by spec. Same sources and selected source; focused selected radio has the canonical dark 3 px inset ring. Native glyph widths can change description line breaks, but CSS line heights are explicit. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
version 2 remembered on reopening locke (st-045) 6.5884 5.5576% 6.2012 Second source selected and retained. Sample suffix absent; same Version focus and 1080p · SDR choice. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
audio and subtitles sample (st-046) 5.5583 5.3466% 8.7472 Audio/subtitles sheet: sample badge absent. Same real track labels, Off selection, fixed 84 px rows, canonical label margins and sheet gaps. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
more media info sample (st-047) 5.6527 5.6661% 6.1819 Media info: sample badge absent. Canonical 200 px key column, 20 px column gap, 6 px row gap and 650→700 key weight. Fractional 31.2 px lines plus 6 px gaps accumulate before rounding: Container-to-Dutch span is 526 px versus Chromium 526.078 px, not the old 12 px cumulative drift. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
trailer sample hand off open question (st-048) 29.1727 55.5980% 18.5438 Approved trailer exception: forbidden sample hand-off screen is not implemented. Native title stays visible with the calm no-YouTube-app fallback. Separate native-intent scenarios exercise installed/unavailable handlers. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
mark as watched toggled (st-049) 6.7013 5.6146% 6.2012 Watched film: same watched kicker and eye state. Focused watched eye has only the outer accent ring; the inset ring is suppressed. CSS title flow is explicit; remaining Roboto/DejaVu ink-baseline differences are measured rather than used to explain stack geometry. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
series mark the whole series confirm (st-050) 5.7011 6.0340% 9.5830 Whole-series confirmation: same destructive scope and two choices, with restored 12 px button top margin. Approved 1.02 row/1.05 button scales differ from frozen 1.03/1.06; native glyph widths still differ. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px.
episode page restart options mindel resuming (st-051) 9.0033 8.2749% 4.4927 Resuming episode: same S4 E9 target, progress, episode score and Restart focus; full household-action pill is not clipped. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px.
cast row locke portraits (st-052) 5.3153 5.5500% 5.8649 Ruth Wilson is focused after a real Right press. Same cast order, portrait slots, fixed backdrop and related order. Cast names and characters now retain their CSS line boxes; focused character ink starts 2 px below DejaVu, not a contracted stack. Prototype sample shelf badge absent.
cast row on a series after seasons andor (st-053) 5.5305 6.4872% 5.8512 Series cast: fixed Andor backdrop remains behind deep rows. Same cast/related geometry; recommendation posters do not acquire catalogue badges. Sample shelf badge absent.
cast no photo initials spirited away (st-054) 5.9542 7.2002% 5.1141 Missing portraits: same title-hashed initials/gradients and cast positions. Native name truncation differs; sample shelf badge absent and related posters have no catalogue badges.
cast loading placeholder circles (st-055) 2.5182 1.9871% 5.8649 Seven inert cast skeletons; same scrolled viewport with header focus retained. Surface RGB differs by JPEG/rendering noise; sample shelf badge absent.
cast unavailable row absent down skips to more like this (st-056) 2.8267 2.5209% 5.8649 Unavailable cast row is absent; Addicted is focused in More like this. Same pinned row and fixed backdrop. Restored inherited rating-line descender makes the native fade end at 315.24 px versus reference about 316 px. Sample shelf badge absent.
person page tom holland real credits sample fill (st-057) 7.0367 7.2671% 5.9918 Only real credits and biography are shown: fake fixture biography, sample filmography wording and More credits filler are absent. The shorter real header is vertically centred. Caption uses shared flex facts; native text clipping differs.
not interested receipt with undo g6 (st-058) 9.3362 11.9208% 6.8680 Hidden receipt: Simulated disclaimer absent, with real fixture Undo/Done state. No unheld trailer until addition 5; native heading wraps differently behind the scrim. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
hidden picks title page show again (st-059) 15.9075 12.9755% 7.3159 Hidden pick: same Show again focus and hidden kicker. Unheld trailer omitted pending addition 5; native title is one line instead of two. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
title page nav focused note 5 (st-060) 6.1965 5.2100% 6.2803 Nav focus: same retained title below Home. Native nav word/label widths differ; no title-specific positioning. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px.
title page loading no saved copy skeleton (st-061) 1.5925 0.6009% 6.2803 Initial load: only canonical skeletons; no stale cast/related rows. Home keeps focus and no loading copy/spinner is drawn.
title page error no saved copy try again (st-062) 5.1264 4.2787% 6.2012 No-copy error: same alert, recovery and Back labels without invented button glyphs. Native glyph widths produce fewer body lines, shortening this intrinsically sized panel; fixed line boxes are not a claim of matching line breaks.
not signed in a picks page without requests (st-063) 14.4291 9.2156% 7.3159 Approved sign-out exception: ratings and unheld trailer are absent, leaving Try again. Native heading/overview wrapping differs. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
held series with next up andor (st-064) 7.6326 5.9146% 6.2511 Held series: same S1 E7 next-up target, season selection and episode rating set. Complete household-action label remains visible; native copy line breaks differ. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px.
resume an episode mindel (st-065) 8.7626 8.2116% 4.4927 Resuming episode: same exact S4 E9, 28-minute remainder, progress and episode set. Complete household-action label remains visible. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px.
not here request (st-066) 15.4829 12.8931% 7.3159 Unheld title: same 420×630 one-sheet at 1320,150, canonical overscan/blur/shadow and cast initials. Trailer omitted pending addition 5. Frozen title has two lines; native has one. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
not here trailer focused round 4 sample hand off (st-067) 15.5408 12.9293% 7.3159 Approved trailer-field fallback: frozen Trailer cannot exist until Conductarr addition 5. Request stays focused instead; no invented hand-off. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
requested for you searching (st-068) 18.8214 14.7265% 7.3159 Requested title: same Searching stage and actor. Native heading is one line; complete Requested/household/Not interested pills remain visible. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
missing artwork (st-069) 14.5270 11.6913% 6.0286 No artwork: canonical radial background and no one-sheet. Frozen readout/avatar names Justin although screen JSON says Shared; capture follows the frozen avatar. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
offline request needs conductarr (st-070) 14.2860 9.1801% 8.1139 Offline saved copy: Saved 20:42, no saved rating set and Try again. No empty rating slot or unheld trailer. Native title wraps to one line. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.
poster in more like this title page (st-104) 3.2664 4.6222% 5.8766 Focused INVINCIBLE related poster: same 99% plate, Watched caption, pinned row and fixed backdrop. Restored rating fade ends at 320.64 px versus reference about 321 px; sampled RGB at x330/y316 is exactly 179,191,117 in both. No catalogue badge; sample shelf badge absent.
title page held film with rt critics shared (st-106) 8.9283 6.0311% 5.4595 Held watched film: same critic/audience/IMDb set, watched cue and cast fallback. Native headline/overview ink and button widths differ. Native heading: 1 lines, widths [738.0] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 836, 349] px.
title page series up next ep justin andor (st-107) 7.6326 5.9146% 6.2511 Series plus episode ratings: same series RT and next-up episode RT/TMDB with Ep label. Full options labels retained; native copy flow differs. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px.
title page resuming an episode mindel (st-108) 8.7626 8.2116% 4.4927 Resuming episode ratings: same series and episode sets, muted Ep label and locale-formatted one decimal. Native copy flow differs. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px.
title page not here no ratings request (st-109) 15.4829 12.8931% 7.3159 The frozen image contains RT/audience/IMDb despite its no-ratings filename. Capture follows the locked image, not that misleading label; true missing-rating geometry has a separate regression. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px.

Reports: capture-diffs.md, native-measurements.md, review-gates.md, review-response.md, sibling-captures.md, title-review-evidence.zip.

Shared sibling surfaces and APIs

measure_siblings.py selects fresh home/, library/, discover/ captures and the actual root Requests/shell outputs. It deliberately ignores stale flat root Library/Discover staging copies. Before is each sibling lane's published native PNG; all full hashes, baseline paths, thresholded pixel deltas and locked-reference errors are in sibling-captures.json and .md. The92-file pre-review flat staging hash record is preserved separately and its exact-match flags are explicit. Published-baseline identity is not misrepresented as a particular source-commit guarantee.

Family Byte-identical Changed PNGs Largest native mean RGB delta / pixels >12
Native Home 11 11 0.5234 /0.7032%
Library 1 15 0.5024 /0.9496%
Discover 18 10 0.6950 /0.7205%
Requests 0 8 7.1176 /5.9045%
Shell fixtures 6 4 0.0112 /0.0072%

All48 changed before/after pairs were visually inspected in eight contact sheets; all34 title-family pairs were inspected in nine sheets. Shared changes are not claimed absent: poster captions/fade bands, regular progress/episode-mark ordering and canonical focused-button lift are visible corrections. The Requests published baseline also differs in the Settings-header initials and row text/readiness wrapping; its largest delta is the expanded Downloading row in086. These differences remain numeric and visible, not hidden under an unchanged-hash claim or an assertion of pixel identity. Nav-only shell fixtures are distinct from the actual Home captures.

  • WolfskinImages is the only image-loader seam. request(context, source: Any, width: Int, height: Int, poster: Boolean) accepts file/local-art sources with pixel dimensions; the old LibraryImages class is gone, not resurrected as an alias. The owner-isolated cache remains wolfskin-images.
  • Home's Dp-sized TitleArtwork overload converts dimensions then delegates to the same raw renderer; it does not construct another image loader. LibraryPoster uses shared TitleCard; captions retain Home's optional style/overflow API and the role/ownership shrink rules.
  • Shared exactLineBox(style, top, bottom) now uses one fractional CSS layout engine; type sizes/fonts are unchanged. SourceRatings fixes episode order and inherits the canonical plate descender. Shared focused-button lift is intentionally visible on Home/recovery surfaces too.
  • Removed eight unreachable duplicate glyph branches and seven obsolete resources without changing the first-selected canonical glyph mappings.

Upstream files touched

Upstream file Why
app/src/default/AndroidManifest.xml Add package visibility for resolving installed vnd.youtube handlers. This is an upstream default-flavour file, NOT one of the section2.7 hook files; appstore/firetv visibility is not asserted covered. The earlier body's “none” statement was wrong and is corrected here.

All other changed production paths are fork-owned app source sets/debug assets or wolfskin-design; existing fork build documentation is in docker/README.md. The existing single upstream hook is reused, not duplicated. GPL notices remain intact. No version/signing/release configuration or tracked offline workaround changed.

Not done / residual limitations

Native Roboto versus frozen DejaVu glyph widths/ink and line breaks remain approved platform differences; per-title offsets or fake copy were not added. Unheld trailers remain omitted until Conductarr addition5; held trailers use the installed YouTube-family app intent or calm unavailable-app fallback, never the prototype's sample hand-off screen. Conductarr TV sign-in is not built, so its reads use the debug fixture seam, not handwritten HTTP or live tests. Device performance and blur below Android31 were not proven here.

The review's nonblocking observations remain: releasing a held OK outside the title can leave okHeld true; Play has no in-flight guard; non-UUID release IDs remain loading until their Conductarr read exists; duplicate cast-person IDs can collide in LazyRow keys; signed-in Request/household CTAs still show the current toast rather than dispatching into slice6; production Hide/More like this remain unavailable without the Conductarr integration; pre31 blur is a no-op; debug fixture assets are about67MB; the locked recovery copy names Conductarr even for a Jellyfin failure. The unreachable duplicate glyph branches and seven obsolete resources were removed during this merge cleanup.

The13 requested findings have no unimplemented item. The remaining observations above are disclosed rather than silently expanded into this revision. Full launch replay is DONE, not deferred. No ADB/device access, install, release/tag/asset/signing/version change, merge, shared-service administration or Forgejo status manipulation. Firstmate arranges re-review/merge; release and TV installation still require Justin's word.

## Build native title, episode and person pages AI-assisted (fork-internal) Builds held/not-held film, series and explicit episode pages, playback-option sheets, cast/person pages and continuous filmography on the existing Wolfskin TV shell. All13 requested review fixes are implemented and measured below. All34 required title-family pairs are original1920×1080 at density2.0; all four complete native-Home launch paths now pass. Ready for re-review, not a release or device install. Refs: [approved TV specification](https://pages.justinvisser.org/wolfskin/tv-spec), [locked Opus v2 prototype](https://pages.justinvisser.org/wolfskin/tv-prototype-v2-opus/). Scope: sections6.5,6.6,6.8–6.10,7.2 and title/person endpoints in3.6. Base `main`; exact validated source `cd625b4227ce8baa57512df50c8e74e57f5b42a9` on `fm/tv-slice5-titles`. Native Home main `9dd321f07e82393bd7afc2f470e4b61789907e13` is merged additively. Real Home, Discover, Requests and Library surfaces remain present; no unmerged sibling branch was imported. ## Behaviour and integration - Film/series/exact-episode headers; Version, Audio and subtitles, Media info, watched toggle and whole-series confirmation; held trailer intent with calm unavailable-handler fallback. - Cast/person pages with actual credits/bios, missing portraits/art, inert loading skeletons, absent unavailable cast rows, recovery, saved/offline, signed-out and hidden/Undo states. - Existing shell/nav/back ladder and view memory are reused. Home Details now dispatches the real title route, not a title placeholder; Coming in retains its request overlay/expansion when returning from a title. - Held Jellyfin reads go through the facade with server/user-scoped saved state. Source UUIDs, non-contiguous track indexes, exact episode playback/watched scope and rejected/out-of-order optimistic choices are covered by consumer-visible tests. Subtitle Off persists DISABLED, not UNSPECIFIED. - Shared fractional CSS line boxes/columns preserve negative progress margins and accumulate media-row positions before rounding. Focused choices bring themselves into the actual scroll viewport. Mounted ready person pages own their first-credit focus transition. ## All13 requested fixes — measured final result | Finding | Frozen target / former defect | Final observation and residual | |---|---|---| | D1 Header CSS geometry | Preserve33.8px kicker,89.76px title line,38px facts,36.4px overview lines and actual negative progress margins; old native stack drifted8–18px. | Shared fractional CSS layout restores facts y358 vs357.5, film actions y553 vs552.7, series actions y603 vs602.5. Native title ink y261–327 vs260–327; year ink367–387 vs364–385; overview ink416–443/453–480/490–511 vs414–442/450–478/487–509. Residual glyph baselines1–3px are explicit. The native heading TextLayoutResult natural box `[96,245,327,349]` is104px high and is NOT claimed to be its surrounding89.76px CSS line box. No fontflow excuse remains for stack geometry. | | D2 Progress and episode marks | Progress is26px regular400, not Medium500. Episode order is TMDB mark → Ep → score. | Actual progress weight400; native progress bounds `[96,539,457,573]`, source row y538.7. Native TMDB x578–625 → Ep x633–663 →7.8 x671–709. Source corresponding x645.6/700.4/744.8 differs because the Roboto progress string is361px wide versus DejaVu415.4px; order, weight and line box are fixed. | | D3 Focused-button lift | Canonical shadow0px x/12px y/40px blur/50% black was absent. | TitleControl uses shared FocusLift.Button. Five pixels below the native Undo outer-ring run, y425, mean RGB is14.54/14.54/20.00, no longer flat19/19/26. Equivalent frozen Undo sample y472 is15/14/19. Absolute y differs because the approved Simulated disclaimer is removed. Native and Chromium kernels are not asserted identical. | | D4 Focused selected radio inset | Selected plus focused radio retains its3px dark inset stroke, not a flat accent disk. | Both Version044 and Audio046 show native core RGB108/85/135 versus frozen core about106–111/82–89/129–148. Native horizontal dark-core runs are2px and3px plus AA; declared stroke is3px. Frozen fully dark runs are3px plus JPEG edges. Centre y286 vs285 for Version and298 vs298 for Audio. Approved row scale1.02 versus frozen1.03 changes transformed x bounds. | | D5 Watched-eye ring | Hide the inset watched-state ring while focused. | Native eye-centre scan has exactly TWO accent runs, y543–547 and635–639; no extra inset pair. Frozen outer runs are about542–545 and635–638. Native four-pixel canonical stroke versus three thresholded JPEG pixels remains documented. | | D6 Sheet button margins | Restore12px top margin for Media info Close, confirmation actions, and Undo/Done. | Native Close top845.1px versus reviewer's approximate image target844px; the scraper has no Close node. Native Mark as watched top283.1 vs Chromium282.5. Native Undo top332.1 vs frozen378.9 minus the approved31.2px disclaimer and16px gap =331.7; residual0.4px is consistent with approved1.05 versus1.06 button scaling. | | D7 Fractional media table |31.2px lines +6px gaps; old independent rounding accumulated about12px. | Container top263, Dutch top789: whole-table span526px versus source526.078125px. Fractional positions accumulate before rounding; adjacent observed pitch alternates37/38 instead of rounding every row to38. Existing pair assertion retained and a526.2±1 whole-span regression passes. | | D8 Loading skeleton gaps | Blocks y200/260/376/440/584 with gaps26/24/30/34, not a uniform-gap approximation. | Native blocks `[200,234]`, `[260,352]`, `[376,410]`, `[440,550]`, `[584,660]`, exactly the declared CSS geometry. JPEG thresholding yields441–549 and585–660 for the last two interiors. No spinner, loading copy or stale shelves. | | D9 Cast and poster captions | Preserve name/role/facts CSS line boxes; do not contract the stack by3–4px or damage siblings. | Unfocused character box starts478 vs source477.8. Focused Ruth character ink493–512 vs491–515; INVINCIBLE facts ink673–690 vs671–689:2px top residual from glyph ink, not contracted layout. Shared Caption keeps merged Home's style/overflow API and role/ownership flex shrinking.84 fresh namespaced sibling captures were compared and inspected;36 are byte-identical to published native baselines,48 have explicit deltas. Family numbers are below; no missing poster, shifted shelf, lost focus state or clipping regression was observed. | | D10 Rating-plate fade | Preserve the inherited inline descender; focused fades formerly ended about8px too early. | Unshrunk native plate100px versus source100.2, including shared6.6px inline descender. Focused clipped fade ends315.24 in056 vs reference about316, and320.64 in104 vs about321. At x330/y316, st104 RGB is EXACTLY179/191/117 in both; y300 is native138/145/87 vs136/146/85, y321 is191/204/125 vs190/202/126. Smaller multi-score plates retain their proportionate font sizing rather than forcing every row to100px. | | F1 Subtitle Off persistence | UI Off−1 must mean playback disabled, not upstream unspecified−1/preferred language. | Actual facade regression proves UI−1 stores TrackIndex.DISABLED−2; UNSPECIFIED−1 is never written. A subsequent real subtitle index8 remains8. No live endpoint in the test. | | F2 Focused sheet scrolling | D-pad focus must remain visible beyond the initial viewport. | Real sheet regression traverses20 focusable choices including Done with19 Down events. Every focused row's visible bounds stay inside the viewport with visible height>20px; Done closes the sheet without promoting nav focus. BringIntoViewRequester is attached to actual focused choices and footer controls. | | F3 Async person focus | A real delayed person load must focus first credit, not retain the nav/loading focus owner. | Real TitleDestination + WolfskinTitleViewModel regression delays the facade reply, proves loading Down is inert, completes the person read, then observes credit0/nav false. Actual Right reaches credit1/nav false. Both async title/person scenarios pass; PersonSurface owns the mounted ready transition, not a racing parent effect. | Complete response: [review-response.md](https://git.justinvisser.org/attachments/497fb56f-ff05-453c-8e24-11171d507ca3). Native pixels/DOM/text/element details and residuals are retained in the evidence bundle; these numbers do not self-approve design fidelity. ## Full launch paths — no deferred prefixes The four TitleLaunchKeysTest cases use the actual Who's watching, native Home hero/shelves, title/person components and production focus handling. Every fixture key is replayed, including Home navigation. No direct row/focus assignment substitutes for input. | Path | Actual key presses | Observed outcome | |---|---:|---| | Launch → title → nav |4| Nav owns the current tab while the title is retained | | Launch → title → switch person |8| Mindel selected, chooser closed, and Home route active | | Launch → Locke → second version → Play |15| Source v2 selected, sheet closed, and Play for Locke emitted | | Launch → Locke → cast/person |11| Tom Hardy person route reached through the cast row | The Home prefix reaches Locke through its real current ordering: three Down events then four Right events in Your movies. No fixture reordering or placeholder hero remains. The older suffix-only tests are retained as additional coverage. ## Commands and complete results **807 tests;0 failures,0 errors,0 skipped.** Existing tests retained; no assertion weakened. One final-head `make test` passed in2m51 and one `make apk-debug` passed in1m14 after source was committed; no source edits followed. Debug artifact remains local only. | Module | Tests | Failures | Errors | Skipped | |---|---:|---:|---:|---:| | app | 638 | 0 | 0 | 0 | | wolfskin-design | 152 | 0 | 0 | 0 | | conductarr-client | 17 | 0 | 0 | 0 | Before the final pair, targeted shared-design smoke passed in1m03 and complete title-family smoke in2m43. All34 title captures and all four full launch cases are included in the unfiltered final XML. Tests use local artwork/debug fixtures and mocked SDK boundaries; no live Conductarr/Jellyfin calls. The inherited generated-client contract smoke uses a local fixture HTTP server, not Conductarr. Evidence: [test-results.md](https://git.justinvisser.org/attachments/538c8a09-880c-46a8-918b-befdad862eae), [final-cd625b42-make-test-and-apk.log](https://git.justinvisser.org/attachments/fe8cb6d7-47cd-4168-a04e-35209999ed4b), [title-review-evidence.zip](https://git.justinvisser.org/attachments/f937d01b-6209-4536-9c4e-1f74327ab637). ## Required per-screen fidelity evidence **Frozen left / Compose right**, each half1920×1080. MAE is mean absolute RGB error0–255; changed pixels have any channel >12. All34 full-resolution pairs were visually inspected in nine contact sheets. These are measurements, not an automatic pass. Approved fonts are native platform Roboto versus frozen DejaVu Sans. No bundled font or per-title/row compensation. Native glyph widths can change wrapping; Spider-Man is two browser lines versus one native line,984.2px in1060px with no ellipsis. CSS stack geometry is explicit, not excused as fontflow. Residual ink baselines are1–3px. Approved focus scales1.05 buttons/1.02 rows differ from frozen1.06/1.03. Canonical shadows/rings are present; native/Chromium kernels and JPEG AA are not claimed identical. Approved product exceptions are identified per frame: no sample/simulated labels or handoff screens, fake biography/credit filler, signed-out ratings or unheld trailer before Conductarr addition5. Frozen069 avatar and109 ratings take precedence over inconsistent metadata/filenames. | Required capture pair | MAE | Pixels >12 | Nav MAE | Explicit difference | |---|---:|---:|---:|---| | [held movie justin (st-042)](https://git.justinvisser.org/attachments/ca9db36d-1cea-4823-9374-c8d4e5abf3a4) | 9.7712 | 7.7970% | 5.7295 | Held film: same backdrop/cast, two title lines on both. Native glyph widths and overview line breaks differ; no copy or size compensation. Native heading: 2 lines, widths [727.3, 542.3] px in 1060 px; ellipsis [False, False]; requested weight 800; bounds [96, 245, 1156, 439] px. | | [held film two versions options row locke note 3 (st-043)](https://git.justinvisser.org/attachments/bb7f697d-5b7c-4e6f-925d-abe50e628340) | 6.6792 | 5.6362% | 6.2012 | Locke options: the prototype sample suffix is absent. Version-ring bounds, thresholded stroke and caption ink are measured separately. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [version list locke sample files (st-044)](https://git.justinvisser.org/attachments/818fd062-cfdf-4b44-8370-0ee82c319ea8) | 5.3678 | 5.4471% | 5.0046 | Version sheet: sample badge/disclaimer absent by spec. Same sources and selected source; focused selected radio has the canonical dark 3 px inset ring. Native glyph widths can change description line breaks, but CSS line heights are explicit. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [version 2 remembered on reopening locke (st-045)](https://git.justinvisser.org/attachments/67a9b546-7d01-435c-a413-6dec38688643) | 6.5884 | 5.5576% | 6.2012 | Second source selected and retained. Sample suffix absent; same Version focus and 1080p · SDR choice. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [audio and subtitles sample (st-046)](https://git.justinvisser.org/attachments/e494c8ec-9a2e-44fc-9ae9-8b05e418953d) | 5.5583 | 5.3466% | 8.7472 | Audio/subtitles sheet: sample badge absent. Same real track labels, Off selection, fixed 84 px rows, canonical label margins and sheet gaps. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [more media info sample (st-047)](https://git.justinvisser.org/attachments/32f8cfd6-1068-4d49-9c7a-4db81fbf8492) | 5.6527 | 5.6661% | 6.1819 | Media info: sample badge absent. Canonical 200 px key column, 20 px column gap, 6 px row gap and 650→700 key weight. Fractional 31.2 px lines plus 6 px gaps accumulate before rounding: Container-to-Dutch span is 526 px versus Chromium 526.078 px, not the old 12 px cumulative drift. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [trailer sample hand off open question (st-048)](https://git.justinvisser.org/attachments/ca9dc2ec-1894-44df-b237-8572fe2a83ca) | 29.1727 | 55.5980% | 18.5438 | Approved trailer exception: forbidden sample hand-off screen is not implemented. Native title stays visible with the calm no-YouTube-app fallback. Separate native-intent scenarios exercise installed/unavailable handlers. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [mark as watched toggled (st-049)](https://git.justinvisser.org/attachments/4ffdaacf-968d-4a32-9427-ebd6fc4977b5) | 6.7013 | 5.6146% | 6.2012 | Watched film: same watched kicker and eye state. Focused watched eye has only the outer accent ring; the inset ring is suppressed. CSS title flow is explicit; remaining Roboto/DejaVu ink-baseline differences are measured rather than used to explain stack geometry. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [series mark the whole series confirm (st-050)](https://git.justinvisser.org/attachments/c7e77749-c343-4ef9-98c3-922137f89a67) | 5.7011 | 6.0340% | 9.5830 | Whole-series confirmation: same destructive scope and two choices, with restored 12 px button top margin. Approved 1.02 row/1.05 button scales differ from frozen 1.03/1.06; native glyph widths still differ. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px. | | [episode page restart options mindel resuming (st-051)](https://git.justinvisser.org/attachments/e1a528f6-ce14-4cdc-8b53-663b67463827) | 9.0033 | 8.2749% | 4.4927 | Resuming episode: same S4 E9 target, progress, episode score and Restart focus; full household-action pill is not clipped. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px. | | [cast row locke portraits (st-052)](https://git.justinvisser.org/attachments/784b8ce6-38b5-4d0b-aecd-93d63d2fe848) | 5.3153 | 5.5500% | 5.8649 | Ruth Wilson is focused after a real Right press. Same cast order, portrait slots, fixed backdrop and related order. Cast names and characters now retain their CSS line boxes; focused character ink starts 2 px below DejaVu, not a contracted stack. Prototype sample shelf badge absent. | | [cast row on a series after seasons andor (st-053)](https://git.justinvisser.org/attachments/c4e8e1fd-4e25-4607-bdab-cfd169ebf842) | 5.5305 | 6.4872% | 5.8512 | Series cast: fixed Andor backdrop remains behind deep rows. Same cast/related geometry; recommendation posters do not acquire catalogue badges. Sample shelf badge absent. | | [cast no photo initials spirited away (st-054)](https://git.justinvisser.org/attachments/d5ad6305-281f-4c5d-b8d5-b75c72cf4893) | 5.9542 | 7.2002% | 5.1141 | Missing portraits: same title-hashed initials/gradients and cast positions. Native name truncation differs; sample shelf badge absent and related posters have no catalogue badges. | | [cast loading placeholder circles (st-055)](https://git.justinvisser.org/attachments/bbe5bb87-12d3-4d2e-b3d0-36fd65d4a3b3) | 2.5182 | 1.9871% | 5.8649 | Seven inert cast skeletons; same scrolled viewport with header focus retained. Surface RGB differs by JPEG/rendering noise; sample shelf badge absent. | | [cast unavailable row absent down skips to more like this (st-056)](https://git.justinvisser.org/attachments/428cbf96-69a6-4b1b-8529-5a92b864aac4) | 2.8267 | 2.5209% | 5.8649 | Unavailable cast row is absent; Addicted is focused in More like this. Same pinned row and fixed backdrop. Restored inherited rating-line descender makes the native fade end at 315.24 px versus reference about 316 px. Sample shelf badge absent. | | [person page tom holland real credits sample fill (st-057)](https://git.justinvisser.org/attachments/63401205-ea54-4eab-a3fb-96557406b79d) | 7.0367 | 7.2671% | 5.9918 | Only real credits and biography are shown: fake fixture biography, sample filmography wording and More credits filler are absent. The shorter real header is vertically centred. Caption uses shared flex facts; native text clipping differs. | | [not interested receipt with undo g6 (st-058)](https://git.justinvisser.org/attachments/0425e0e0-bd15-47d0-9771-65f26548db33) | 9.3362 | 11.9208% | 6.8680 | Hidden receipt: Simulated disclaimer absent, with real fixture Undo/Done state. No unheld trailer until addition 5; native heading wraps differently behind the scrim. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [hidden picks title page show again (st-059)](https://git.justinvisser.org/attachments/c683b21f-ec26-40ee-8cd8-ab94b882a3f6) | 15.9075 | 12.9755% | 7.3159 | Hidden pick: same Show again focus and hidden kicker. Unheld trailer omitted pending addition 5; native title is one line instead of two. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [title page nav focused note 5 (st-060)](https://git.justinvisser.org/attachments/4046ed57-9d6c-4cee-bac1-47d0ffe5cbd4) | 6.1965 | 5.2100% | 6.2803 | Nav focus: same retained title below Home. Native nav word/label widths differ; no title-specific positioning. Native heading: 1 lines, widths [229.3] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 327, 349] px. | | [title page loading no saved copy skeleton (st-061)](https://git.justinvisser.org/attachments/46a437a3-5203-4961-8167-277661a57ac3) | 1.5925 | 0.6009% | 6.2803 | Initial load: only canonical skeletons; no stale cast/related rows. Home keeps focus and no loading copy/spinner is drawn. | | [title page error no saved copy try again (st-062)](https://git.justinvisser.org/attachments/f67ec1ee-0c6b-4b49-aad2-3cf07abd8ab9) | 5.1264 | 4.2787% | 6.2012 | No-copy error: same alert, recovery and Back labels without invented button glyphs. Native glyph widths produce fewer body lines, shortening this intrinsically sized panel; fixed line boxes are not a claim of matching line breaks. | | [not signed in a picks page without requests (st-063)](https://git.justinvisser.org/attachments/e3932826-6ed1-4b62-8465-3620eeecb262) | 14.4291 | 9.2156% | 7.3159 | Approved sign-out exception: ratings and unheld trailer are absent, leaving Try again. Native heading/overview wrapping differs. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [held series with next up andor (st-064)](https://git.justinvisser.org/attachments/665a690f-8f07-4c30-93af-2116b0d16626) | 7.6326 | 5.9146% | 6.2511 | Held series: same S1 E7 next-up target, season selection and episode rating set. Complete household-action label remains visible; native copy line breaks differ. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px. | | [resume an episode mindel (st-065)](https://git.justinvisser.org/attachments/e57baad3-0713-4846-a7bd-52f675adece1) | 8.7626 | 8.2116% | 4.4927 | Resuming episode: same exact S4 E9, 28-minute remainder, progress and episode set. Complete household-action label remains visible. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px. | | [not here request (st-066)](https://git.justinvisser.org/attachments/5ba294e3-eeca-46d8-bed6-d8943caeff7a) | 15.4829 | 12.8931% | 7.3159 | Unheld title: same 420×630 one-sheet at 1320,150, canonical overscan/blur/shadow and cast initials. Trailer omitted pending addition 5. Frozen title has two lines; native has one. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [not here trailer focused round 4 sample hand off (st-067)](https://git.justinvisser.org/attachments/634cce60-e627-4301-b3a1-1c61ba09dd68) | 15.5408 | 12.9293% | 7.3159 | Approved trailer-field fallback: frozen Trailer cannot exist until Conductarr addition 5. Request stays focused instead; no invented hand-off. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [requested for you searching (st-068)](https://git.justinvisser.org/attachments/ecf8dc22-1588-4f09-9048-f9781ad6c340) | 18.8214 | 14.7265% | 7.3159 | Requested title: same Searching stage and actor. Native heading is one line; complete Requested/household/Not interested pills remain visible. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [missing artwork (st-069)](https://git.justinvisser.org/attachments/aefc2170-c980-4d55-adb9-9479608a4fa8) | 14.5270 | 11.6913% | 6.0286 | No artwork: canonical radial background and no one-sheet. Frozen readout/avatar names Justin although screen JSON says Shared; capture follows the frozen avatar. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [offline request needs conductarr (st-070)](https://git.justinvisser.org/attachments/a7560a2f-e458-4a92-a3eb-0bb4df76f70d) | 14.2860 | 9.1801% | 8.1139 | Offline saved copy: Saved 20:42, no saved rating set and Try again. No empty rating slot or unheld trailer. Native title wraps to one line. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | | [poster in more like this title page (st-104)](https://git.justinvisser.org/attachments/5cde106f-5434-4a0a-8897-575d19ca638e) | 3.2664 | 4.6222% | 5.8766 | Focused INVINCIBLE related poster: same 99% plate, Watched caption, pinned row and fixed backdrop. Restored rating fade ends at 320.64 px versus reference about 321 px; sampled RGB at x330/y316 is exactly 179,191,117 in both. No catalogue badge; sample shelf badge absent. | | [title page held film with rt critics shared (st-106)](https://git.justinvisser.org/attachments/045b6705-728b-4ca9-bab9-804d476df704) | 8.9283 | 6.0311% | 5.4595 | Held watched film: same critic/audience/IMDb set, watched cue and cast fallback. Native headline/overview ink and button widths differ. Native heading: 1 lines, widths [738.0] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 836, 349] px. | | [title page series up next ep justin andor (st-107)](https://git.justinvisser.org/attachments/7a47ade9-fe0e-48b2-9335-9d64acee0a10) | 7.6326 | 5.9146% | 6.2511 | Series plus episode ratings: same series RT and next-up episode RT/TMDB with Ep label. Full options labels retained; native copy flow differs. Native heading: 1 lines, widths [231.1] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 329, 349] px. | | [title page resuming an episode mindel (st-108)](https://git.justinvisser.org/attachments/bc97c5a6-d2ec-4b1c-a63f-bfa638cbe833) | 8.7626 | 8.2116% | 4.4927 | Resuming episode ratings: same series and episode sets, muted Ep label and locale-formatted one decimal. Native copy flow differs. Native heading: 1 lines, widths [618.8] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 716, 349] px. | | [title page not here no ratings request (st-109)](https://git.justinvisser.org/attachments/ee9fe6f7-4592-4820-ac93-a6900af4c56e) | 15.4829 | 12.8931% | 7.3159 | The frozen image contains RT/audience/IMDb despite its no-ratings filename. Capture follows the locked image, not that misleading label; true missing-rating geometry has a separate regression. Unheld trailer omitted pending addition 5. Native heading: 1 lines, widths [984.2] px in 1060 px; ellipsis [False]; requested weight 800; bounds [96, 245, 1082, 349] px. | Reports: [capture-diffs.md](https://git.justinvisser.org/attachments/4d237b2a-94a9-418b-95e7-3e2957de5fe8), [native-measurements.md](https://git.justinvisser.org/attachments/7762af3e-a1e4-4788-876c-be690dcb3473), [review-gates.md](https://git.justinvisser.org/attachments/23bc3c19-4ca6-4570-a6e1-affe89a1d05b), [review-response.md](https://git.justinvisser.org/attachments/497fb56f-ff05-453c-8e24-11171d507ca3), [sibling-captures.md](https://git.justinvisser.org/attachments/11667124-fc3f-4aec-a6c3-1145d8a00404), [title-review-evidence.zip](https://git.justinvisser.org/attachments/f937d01b-6209-4536-9c4e-1f74327ab637). ## Shared sibling surfaces and APIs `measure_siblings.py` selects fresh `home/`, `library/`, `discover/` captures and the actual root Requests/shell outputs. It deliberately ignores stale flat root Library/Discover staging copies. Before is each sibling lane's published native PNG; all full hashes, baseline paths, thresholded pixel deltas and locked-reference errors are in `sibling-captures.json` and `.md`. The92-file pre-review flat staging hash record is preserved separately and its exact-match flags are explicit. Published-baseline identity is not misrepresented as a particular source-commit guarantee. | Family | Byte-identical | Changed PNGs | Largest native mean RGB delta / pixels >12 | |---|---:|---:|---| | Native Home |11|11|0.5234 /0.7032% | | Library |1|15|0.5024 /0.9496% | | Discover |18|10|0.6950 /0.7205% | | Requests |0|8|7.1176 /5.9045% | | Shell fixtures |6|4|0.0112 /0.0072% | All48 changed before/after pairs were visually inspected in eight contact sheets; all34 title-family pairs were inspected in nine sheets. Shared changes are not claimed absent: poster captions/fade bands, regular progress/episode-mark ordering and canonical focused-button lift are visible corrections. The Requests published baseline also differs in the Settings-header initials and row text/readiness wrapping; its largest delta is the expanded Downloading row in086. These differences remain numeric and visible, not hidden under an unchanged-hash claim or an assertion of pixel identity. Nav-only shell fixtures are distinct from the actual Home captures. - **WolfskinImages** is the only image-loader seam. `request(context, source: Any, width: Int, height: Int, poster: Boolean)` accepts file/local-art sources with pixel dimensions; the old LibraryImages class is gone, not resurrected as an alias. The owner-isolated cache remains `wolfskin-images`. - Home's Dp-sized TitleArtwork overload converts dimensions then delegates to the same raw renderer; it does not construct another image loader. LibraryPoster uses shared TitleCard; captions retain Home's optional style/overflow API and the role/ownership shrink rules. - Shared `exactLineBox(style, top, bottom)` now uses one fractional CSS layout engine; type sizes/fonts are unchanged. SourceRatings fixes episode order and inherits the canonical plate descender. Shared focused-button lift is intentionally visible on Home/recovery surfaces too. - Removed eight unreachable duplicate glyph branches and seven obsolete resources without changing the first-selected canonical glyph mappings. ## Upstream files touched | Upstream file | Why | |---|---| | `app/src/default/AndroidManifest.xml` | Add package visibility for resolving installed `vnd.youtube` handlers. This is an upstream default-flavour file, NOT one of the section2.7 hook files; appstore/firetv visibility is not asserted covered. The earlier body's “none” statement was wrong and is corrected here. | All other changed production paths are fork-owned app source sets/debug assets or `wolfskin-design`; existing fork build documentation is in `docker/README.md`. The existing single upstream hook is reused, not duplicated. GPL notices remain intact. No version/signing/release configuration or tracked offline workaround changed. ## Not done / residual limitations Native Roboto versus frozen DejaVu glyph widths/ink and line breaks remain approved platform differences; per-title offsets or fake copy were not added. Unheld trailers remain omitted until Conductarr addition5; held trailers use the installed YouTube-family app intent or calm unavailable-app fallback, never the prototype's sample hand-off screen. Conductarr TV sign-in is not built, so its reads use the debug fixture seam, not handwritten HTTP or live tests. Device performance and blur below Android31 were not proven here. The review's nonblocking observations remain: releasing a held OK outside the title can leave okHeld true; Play has no in-flight guard; non-UUID release IDs remain loading until their Conductarr read exists; duplicate cast-person IDs can collide in LazyRow keys; signed-in Request/household CTAs still show the current toast rather than dispatching into slice6; production Hide/More like this remain unavailable without the Conductarr integration; pre31 blur is a no-op; debug fixture assets are about67MB; the locked recovery copy names Conductarr even for a Jellyfin failure. The unreachable duplicate glyph branches and seven obsolete resources were removed during this merge cleanup. The13 requested findings have no unimplemented item. The remaining observations above are disclosed rather than silently expanded into this revision. Full launch replay is DONE, not deferred. No ADB/device access, install, release/tag/asset/signing/version change, merge, shared-service administration or Forgejo status manipulation. Firstmate arranges re-review/merge; release and TV installation still require Justin's word.
justinzeus added 8 commits 2026-11-05 20:37:29 +01:00
justinzeus added 1 commit 2026-11-05 20:46:59 +01:00
Integrate reviewed Requests and Discover surfaces
Some checks failed
review/design-claude Changes requested: 13 blocking design/behaviour findings (see PR comment)
e243894a20
Author
Owner

Design review (Claude Sonnet, cross-family): CHANGES REQUESTED. Reviewed head e243894a209a11eed96a9843fb1a26455b796b6a. Commit status review/design-claude = failure on that SHA.

Press-count suffix tests, async title focus test, hold guard (7.1), back ladder (7.2), ratings timing (7.8), data boundaries and hygiene check out. Blocking findings, all measured at native 1920x1080 against the locked frames and the prototype DOM (px, ref -> native):

Design (not font-caused; fixed CSS line-heights make box positions font-independent)

  1. Title header stack drifts in every header frame (st-042..051, 058-060, 063-070, 106-109): title ink +4, facts +11/12, overview +10/11, film action row +8 (Version ring [284,542,380,638] -> [280,550,376,646]), series action row +18. Cause: TitleHeader.kt:49-69 uses default token text styles, not TitleTextStyles; offset(progressLift) does not reclaim layout height.
  2. Series/episode progress line (st-050, 051, 064, 065, 107, 108): "Up next"/"min left" weight 700 vs 400; Ep drawn before the TMDB mark (prototype: mark, Ep, score); bar +6.
  3. Focused title buttons have no lift shadow (TitleControl never passes FocusLift.Button; ref 40px blur +12y 50%): st-058, 062 and every focused control.
  4. Selected+focused radio loses its 3px inset ring (st-044, 046). 5. Focused watched eye keeps an extra inner ring (st-049).
  5. Sheet buttons lack the 12px top margin (.sheet .btn): st-050 -9/-10, st-058 -10/-12, st-047 Close clearance 32 -> 20.
  6. Media info row pitch 38 vs 37.2 accumulates to +12 (st-047).
  7. Loading skeleton gaps uniform 14 vs 26/24/30/34: title -12, facts -22, overview -38, buttons -58 (st-061).
  8. Caption line boxes contract 3-4px: cast character lines (st-052/053/054), poster facts (st-056, 104, 057).
  9. Focused rating plate fades ~8px early (st-056, 104).
    st-055 has no non-font blocker. Font-caused wrap/width items are accepted but the PR only gives heading widths: add the pill/chip/caption/panel numbers (e.g. st-062 panel 299.6 -> 264).

Behaviour
11. Subtitle Off is saved as -1 (UNSPECIFIED) instead of TrackIndex.DISABLED (-2); playback re-enables preferred subtitles. No test.
12. Tracks/Media info sheets never scroll the focused row/Done into view; real files with many subtitles put focus off-screen.
13. Person page async path: the Loading title state focuses nav and nothing resets it when the person loads (TitleDestination.kt:235-237, ready-only); spec entry is the first credit. Untested (the async test covers titles only).

Non-blocking (details in the report): OK key-up lost after trailer/player leaves okHeld stuck; no in-flight guard on Play; non-UUID ids sit on the skeleton forever in release; duplicate cast ids would crash the LazyRow; Request buttons toast "needs sign in" while signed in; duplicate Glyph labels; new app/src/default/AndroidManifest.xml is an undisclosed addition outside spec 2.7's list (the PR says none); LibraryImages.request signature changed; no before/after evidence for sibling-slice frames though Caption/LibraryPoster/rating plate changed.

Full report: data/review-wolfskin-34/report.md in the firstmate home. No Gradle was run; native captures are the lane's saved ones.

**Design review (Claude Sonnet, cross-family): CHANGES REQUESTED.** Reviewed head `e243894a209a11eed96a9843fb1a26455b796b6a`. Commit status `review/design-claude` = failure on that SHA. Press-count suffix tests, async title focus test, hold guard (7.1), back ladder (7.2), ratings timing (7.8), data boundaries and hygiene check out. Blocking findings, all measured at native 1920x1080 against the locked frames and the prototype DOM (px, ref -> native): **Design (not font-caused; fixed CSS line-heights make box positions font-independent)** 1. Title header stack drifts in every header frame (st-042..051, 058-060, 063-070, 106-109): title ink +4, facts +11/12, overview +10/11, film action row +8 (Version ring `[284,542,380,638] -> [280,550,376,646]`), series action row +18. Cause: `TitleHeader.kt:49-69` uses default token text styles, not `TitleTextStyles`; `offset(progressLift)` does not reclaim layout height. 2. Series/episode progress line (st-050, 051, 064, 065, 107, 108): "Up next"/"min left" weight 700 vs 400; Ep drawn before the TMDB mark (prototype: mark, Ep, score); bar +6. 3. Focused title buttons have no lift shadow (`TitleControl` never passes `FocusLift.Button`; ref 40px blur +12y 50%): st-058, 062 and every focused control. 4. Selected+focused radio loses its 3px inset ring (st-044, 046). 5. Focused watched eye keeps an extra inner ring (st-049). 6. Sheet buttons lack the 12px top margin (`.sheet .btn`): st-050 -9/-10, st-058 -10/-12, st-047 Close clearance 32 -> 20. 7. Media info row pitch 38 vs 37.2 accumulates to +12 (st-047). 8. Loading skeleton gaps uniform 14 vs 26/24/30/34: title -12, facts -22, overview -38, buttons -58 (st-061). 9. Caption line boxes contract 3-4px: cast character lines (st-052/053/054), poster facts (st-056, 104, 057). 10. Focused rating plate fades ~8px early (st-056, 104). st-055 has no non-font blocker. Font-caused wrap/width items are accepted but the PR only gives heading widths: add the pill/chip/caption/panel numbers (e.g. st-062 panel 299.6 -> 264). **Behaviour** 11. Subtitle Off is saved as `-1` (UNSPECIFIED) instead of `TrackIndex.DISABLED` (-2); playback re-enables preferred subtitles. No test. 12. Tracks/Media info sheets never scroll the focused row/Done into view; real files with many subtitles put focus off-screen. 13. Person page async path: the Loading title state focuses nav and nothing resets it when the person loads (`TitleDestination.kt:235-237`, ready-only); spec entry is the first credit. Untested (the async test covers titles only). Non-blocking (details in the report): OK key-up lost after trailer/player leaves `okHeld` stuck; no in-flight guard on Play; non-UUID ids sit on the skeleton forever in release; duplicate cast ids would crash the LazyRow; Request buttons toast "needs sign in" while signed in; duplicate Glyph labels; new `app/src/default/AndroidManifest.xml` is an undisclosed addition outside spec 2.7's list (the PR says none); `LibraryImages.request` signature changed; no before/after evidence for sibling-slice frames though Caption/LibraryPoster/rating plate changed. Full report: `data/review-wolfskin-34/report.md` in the firstmate home. No Gradle was run; native captures are the lane's saved ones.
justinzeus added 3 commits 2026-11-05 22:29:18 +01:00
Author
Owner

Design re-review (round 2) of head cd625b4227ce8baa57512df50c8e74e57f5b42a9: no blockers; commit status review/design-claude = success. I do not merge or approve; merge stays with Justin.

All 13 round-1 blockers are fixed. I re-measured the refreshed native captures against the locked frames myself (native 1920x1080 px, ref -> native):

  • Header stack: title ink 260 -> 261, facts 364 -> 366, overview lines 414/450/487 -> 416/453/490, film action row 552 -> 553, series row 600 -> 601 (was +4/+11/+10/+8/+18). Remaining 1-3 px is glyph ink.
  • Progress line: weight 400, order TMDB mark, Ep, score (x578-625, 633-663, 671-709).
  • Focused button lift: 5 px under the ring (13,14,18) -> (14,14,20); was flat (19,19,26).
  • Radio ring: selected+focused row keeps the dark 3 px inset ring. Eye button: one accent run only, no inner ring.
  • Sheet buttons: confirm top 282.5 -> 283.1, Undo 331.7 -> 332.1 (disclaimer removed). Media info: 17 rows, constant +2 px offset, no accumulation (was +4 to +12).
  • Skeleton: transitions 199/233/259/351/375 identical. Captions: cast character box 477.8 -> 478, poster facts ink 671 -> 673 (was -3). Rating plate: per-channel diff 0-3 along the fade on st-056/104.
  • F1: Off writes TrackIndex.DISABLED, covered by a facade test. F2: real D-pad test over 3 audio + Off + 15 subtitles + Done stays inside the viewport. F3: real async person test lands on the first credit, not the nav. Home press counts 4/8/15/11 replay through real keys.

Scope: no public design API removed or renamed, WolfskinImages is used (no LibraryImages), no test deleted or weakened, upstream files only docker/README.md and the disclosed app/src/default/AndroidManifest.xml. Shared Caption/exactLineBox/plate changes move sibling Home/Library/Discover pixels toward the locked frames (e.g. Library facts ink ref 782-800, before 779-797, after 784-800). The Requests pairs differ from the lane's published baseline although ComingInSheet is untouched; I read that as baseline drift, not this PR (inference, not rendered).

Non-blocking: (1) a tall Media info list opens scrolled to the bottom (Close is focused first) with no way to scroll up; (2) add numbers for st-062 to the PR: ref body 2 lines, native 1, Try again fill top 357.6 -> 322 = -35.6 px = one body line; (3) the round-1 non-blockers listed in the PR stay open.

Full report: data/review-wolfskin-34-r2/report.md (firstmate data dir).

**Design re-review (round 2) of head `cd625b4227ce8baa57512df50c8e74e57f5b42a9`: no blockers; commit status `review/design-claude` = success.** I do not merge or approve; merge stays with Justin. All 13 round-1 blockers are fixed. I re-measured the refreshed native captures against the locked frames myself (native 1920x1080 px, ref -> native): - **Header stack:** title ink 260 -> 261, facts 364 -> 366, overview lines 414/450/487 -> 416/453/490, film action row 552 -> 553, series row 600 -> 601 (was +4/+11/+10/+8/+18). Remaining 1-3 px is glyph ink. - **Progress line:** weight 400, order TMDB mark, Ep, score (x578-625, 633-663, 671-709). - **Focused button lift:** 5 px under the ring (13,14,18) -> (14,14,20); was flat (19,19,26). - **Radio ring:** selected+focused row keeps the dark 3 px inset ring. **Eye button:** one accent run only, no inner ring. - **Sheet buttons:** confirm top 282.5 -> 283.1, Undo 331.7 -> 332.1 (disclaimer removed). **Media info:** 17 rows, constant +2 px offset, no accumulation (was +4 to +12). - **Skeleton:** transitions 199/233/259/351/375 identical. **Captions:** cast character box 477.8 -> 478, poster facts ink 671 -> 673 (was -3). **Rating plate:** per-channel diff 0-3 along the fade on st-056/104. - **F1:** Off writes `TrackIndex.DISABLED`, covered by a facade test. **F2:** real D-pad test over 3 audio + Off + 15 subtitles + Done stays inside the viewport. **F3:** real async person test lands on the first credit, not the nav. Home press counts 4/8/15/11 replay through real keys. Scope: no public design API removed or renamed, `WolfskinImages` is used (no `LibraryImages`), no test deleted or weakened, upstream files only `docker/README.md` and the disclosed `app/src/default/AndroidManifest.xml`. Shared Caption/exactLineBox/plate changes move sibling Home/Library/Discover pixels toward the locked frames (e.g. Library facts ink ref 782-800, before 779-797, after 784-800). The Requests pairs differ from the lane's published baseline although `ComingInSheet` is untouched; I read that as baseline drift, not this PR (inference, not rendered). Non-blocking: (1) a tall Media info list opens scrolled to the bottom (Close is focused first) with no way to scroll up; (2) add numbers for st-062 to the PR: ref body 2 lines, native 1, Try again fill top 357.6 -> 322 = -35.6 px = one body line; (3) the round-1 non-blockers listed in the PR stay open. Full report: `data/review-wolfskin-34-r2/report.md` (firstmate data dir).
justinzeus added 1 commit 2026-11-05 23:15:40 +01:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fm/tv-slice5-titles:fm/tv-slice5-titles
git checkout fm/tv-slice5-titles
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#34
No description provided.