Merge upstream Wholphin v1.1.1 #53

Open
justinzeus wants to merge 4 commits from fm/wolfskin-upstream-1-1-1 into main
Owner

Outcome and approval gate

Merge Wholphin v1.1.1 into current Wolfskin main as a real two-parent merge. Wait for Justin's explicit GO before merging. Unit validation is RED (missing offline Robolectric runtimes); details and every failing name below. No release, asset upload, tag, optimized-variant build, installation, ADB or device/box action was performed.

AI-assisted (fork-internal). Ready for firstmate's independently arranged cross-family review; this worker posts no review status and performs no merge.

  • Wolfskin base: 332fc6476d00264a573864f9bcf8c0569d95d36b (includes the image-sizing/lean-read, DNS/saved-people and picker-test synchronization work).
  • Upstream tag peels to 159edc875d10a667c25598a2cbfac8c123fe5bff.
  • Merge commit: b80b67f7363562da57ce2815216a7f23272eeb21; parents are the base above and upstream tag commit.
  • Final head: 00e8e83bb64d7a3ea57310846ed106c9fd621f53. The only subsequent commit documents the integration; compiled source is identical to the APK build head.

Conflicts, behavior and fork preservation

No conflicts. No ours/theirs override, rebase, history rewrite or force-push. The sole upstream source change imports unchanged. Every existing Wolfskin implementation and assertion is retained.

Changed file Change / resolution
app/src/main/java/com/github/damontecres/wholphin/ui/setup/seerr/SwitchSeerrViewModel.kt Three upstream added lines: without the optional plugin, mark loading Success immediately. This file was not previously changed by the fork.
docker/README.md Nine documentation lines explaining the patch and unchanged database/layout/player/session/network/signing scope. No runtime change.

The three initialization paths, checked against the code:

  1. No plugin: previously Pending indefinitely; now synchronously Success, allowing the manual Seerr chooser instead of the loading dialog. initialUrl stays null, retaining Wolfskin's default URL and Jellyfin-first login choice.
  2. Plugin installed, empty settings response: unchanged asynchronous lookup; no URL prefill, then the existing finally sets Success so manual setup is available.
  3. Plugin settings lookup throws: unchanged catch/log and finally → Success; manual setup remains available. A successful response still prefills its plugin URL.

No existing app/design test directly references SwitchSeerrViewModel or SwitchSeerrState. Build plus the existing suite provide integration/regression coverage, not direct runtime proof of these three paths. Two attempts to register an isolated external smoke never executed an application test (source discovery, then read-only Groovy property); firstmate explicitly directed dropping that disproportionate harness. Its source/configuration/runner were removed; there was no third attempt and no new or weakened permanent test.

Preservation: baseline v1.1.0 → frozen Wolfskin main contains 2,832 paths. All 2,832 still differ from upstream v1.1.1; 2,831 are byte-identical to the frozen main. The remaining file is the additive build README note. No Wolfskin-modified source/resource/test file changes behavior through an upstream edit. The upstream Seerr setup fix affects the legacy setup dialog; the owned Conductarr Discover/request path is unchanged.

Application ID/branding, original com.github.damontecres.wholphin namespace/packages, GPL notices, Conductarr integration and all rating badges, Seerr defaults, TV layouts, Forgejo updater, image sizing/lean reads, saved-person/session fixes, DNS/OkHttp graph, Docker wrapper, Makefile and CI choices survive unchanged. No upstream workflows were enabled.

Restricted git diff --stat 332fc6476d00264a573864f9bcf8c0569d95d36b HEAD -- <all Wolfskin baseline paths>:

 docker/README.md | 9 +++++++++
 1 file changed, 9 insertions(+)

The full PR delta is two files, twelve insertions, zero deletions. Baseline inventories, input refs, patches, stats and identity/build logs are retained at /root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-upstream-1-1-1/.

Database, tokens and toolchain

  • Room remains version 41; no migration, entity/DAO, migration registration or app/schemas/*.json change. No conflicting fork migration or database decision is introduced. The older 35→40→41 install migration already in main remains unchanged.
  • No design tokens or generated token files changed. The standard build's :wolfskin-design:checkTokenDrift task passed; no token regeneration was needed.
  • No new dependency/wrapper/image/verification/lock changes. Existing container build tooling works unchanged. Fetch imported the release tag, while refusing to overwrite unrelated pre-existing mutable develop tags; none was forced. Git cannot independently verify the signed upstream tag without an allowedSignersFile; this PR makes no signature-verification claim and changes no shared trust settings.

Build, identity and final-head test evidence

Unit gate is RED, not merge-ready: the sole final-head make test invocation failed during Robolectric SDK startup for 109 tests. These are new failures observed in this lane, not asserted to be baseline failures. No second suite run, assertion weakening, test exclusion or unrelated tooling fix was performed.

Command / check Observed result
make apk → bash docker/gradle.sh release clean assembleDefaultRelease PASS, BUILD SUCCESSFUL in 5m 48s; 111 tasks executed. Standard default release variant, not the separate optimized variant.
make apk-debug → bash docker/gradle.sh debug assembleDefaultDebug PASS, BUILD SUCCESSFUL in 3m 6s; 97 tasks. Existing packaged branding check passed on all four debug APKs, including legal attribution. Debug uses the existing MPV stub; it is identity proof, not native-playback proof.
make test → bash docker/gradle.sh debug testDefaultDebugUnitTest, exactly once on 00e8e83bb64d7a3ea57310846ed106c9fd621f53 FAIL, BUILD FAILED in 3m 58s. App: 111 suites, 714 tests, 109 failures, 0 errors/skipped. Design: 27 suites, 194 tests, 0 failures/errors/skipped. Total: 908 tests, 799 pass, 109 fail.
All eight APKs: apksigner verify --verbose --print-certs + aapt2 dump badging in the build image PASS. Release: org.justinvisser.wolfskin.tv, label Wolfskin, code 65. Debug: existing org.justinvisser.wolfskin.tv.debug, label Wolfskin (Debug), code 65. Neither package became Wholphin.
Release signer SHA-256 5cd708ffa94cc22fa41648377a305af3afe0f6da394c57e67024df43c1fcb5a0 — unchanged from the v1.1.0 release-build evidence.
Persistent debug signer SHA-256 182bac72e292b9ea4d3d4e6383f92eb2161fa28ba5afcff0337cb7d5a9d5341d — matches the existing persistent debug keystore certificate. Keystore bytes unchanged before/after, SHA-256 9b3030cb7e5c19a3a7e498c5ad16f4a9d5ac80ffbce8dd7ad9def6680f339d35.

make apk intentionally signs with the release key, not the persistent debug key. The extra standard debug build proves the debug signer separately; Makefile/signing semantics were not changed.

APK version metadata is 1.1.1-153-gb80b67f7 from the committed merge. The later final-head commit is documentation only. All eight exact names (local proof only; not published):

Wholphin-default-release-1.1.1-153-gb80b67f7-65.apk
Wholphin-default-release-1.1.1-153-gb80b67f7-65-arm64-v8a.apk
Wholphin-default-release-1.1.1-153-gb80b67f7-65-armeabi-v7a.apk
Wholphin-default-release-1.1.1-153-gb80b67f7-65-x86_64.apk
Wholphin-default-debug-1.1.1-153-gb80b67f7-65.apk
Wholphin-default-debug-1.1.1-153-gb80b67f7-65-arm64-v8a.apk
Wholphin-default-debug-1.1.1-153-gb80b67f7-65-armeabi-v7a.apk
Wholphin-default-debug-1.1.1-153-gb80b67f7-65-x86_64.apk

The source package and artifact filename convention remain upstream-compatible; the installed application IDs/labels remain Wolfskin.

Exact test failure evidence

All 109 failures have IllegalArgumentException: Path is not a file before their test bodies, naming one of these absent offline runtimes:

wolfskin-design/build/robolectric-sdk/android-all-instrumented-9-robolectric-4913185-2-i7.jar
wolfskin-design/build/robolectric-sdk/android-all-instrumented-11-robolectric-6757853-i7.jar

Observed prepared directory contains only android-all-instrumented-14-robolectric-10818077-i7.jar, despite all three versions being declared on the single robolectricSdk configuration. [INFERENCE] Gradle resolves that configuration to one module version, so the clean build lacks the older SDK files that another checkout may have retained. This merge does not alter that configuration. The missing-file exception is observed; no application regression or baseline-green equivalence is claimed, and no rerun was used to confirm it.

The unchanged existing picker/identity/ratings/updater tests that did run include PersonPickerSessionTest (7 pass), SavedPeoplePickerTest (1 pass), ServerSavedSessionTest (5 pass), SeerrRatingsServiceTest (12 pass), and TestUpdateChecker (17 pass). These do not directly test the changed Seerr view-model initializer.

Every failing test name, exactly as reported (109)
com.github.damontecres.wholphin.test.TestVoiceInputManager > initial state is Idle
com.github.damontecres.wholphin.test.TestVoiceInputManager > callbacks from previous recognizer are ignored after cleanup
com.github.damontecres.wholphin.test.TestVoiceInputManager > onPartialResults updates partialResult
com.github.damontecres.wholphin.test.TestVoiceInputManager > audio focus is abandoned when recognizer is destroyed
com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults resets soundLevel to zero
com.github.damontecres.wholphin.test.TestVoiceInputManager > onReadyForSpeech transitions state to Listening
com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged clamps low values to 0
com.github.damontecres.wholphin.test.TestVoiceInputManager > acknowledge works from Error state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults transitions to Result state with correct text
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails when audio focus not granted
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening is ignored when already listening
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_SERVER to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > initial partialResult is empty
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_RECOGNIZER_BUSY to correct resource after retry exhausted
com.github.damontecres.wholphin.test.TestVoiceInputManager > audio focus error is retryable
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening calls recognizer startListening
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening does not create recognizer when no network
com.github.damontecres.wholphin.test.TestVoiceInputManager > acknowledge resets state to Idle
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening creates SpeechRecognizer
com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged updates soundLevel with normalized value
com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets state to Idle
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening sets recognition listener
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no network capabilities
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening requests audio focus
com.github.damontecres.wholphin.test.TestVoiceInputManager > initial soundLevel is zero
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_AUDIO to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup calls destroy on recognizer
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening resets partialResult
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening transitions state to Starting
com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged clamps high values to 1
com.github.damontecres.wholphin.test.TestVoiceInputManager > network error from fast-fail is retryable
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError resets soundLevel to zero
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_SPEECH_TIMEOUT to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionDenied sets correct error resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no internet capability
com.github.damontecres.wholphin.test.TestVoiceInputManager > onEndOfSpeech transitions to Processing state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NETWORK_TIMEOUT to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError transitions to Error state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NETWORK to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening does not create recognizer when audio focus denied
com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets soundLevel to zero
com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup calls cancel on recognizer
com.github.damontecres.wholphin.test.TestVoiceInputManager > isAvailable returns mocked value
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_CLIENT to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NO_MATCH to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening is ignored when in Starting state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_INSUFFICIENT_PERMISSIONS to correct resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no network
com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionGranted calls startListening
com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets partialResult to empty
com.github.damontecres.wholphin.test.TestVoiceInputManager > stopListening triggers cleanup
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps unknown error to unknown resource
com.github.damontecres.wholphin.test.TestVoiceInputManager > onError ERROR_SPEECH_TIMEOUT is retryable
com.github.damontecres.wholphin.test.TestVoiceInputManager > onPartialResults ignores blank text
com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with null bundle transitions to Error state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with blank text transitions to Error state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionDenied transitions to Error state
com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with empty text transitions to Error state
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Direct play includes the opened live stream ID
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Playback ends with no next up
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up never without automatic play
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > playItemInPlaylist
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up never with automatic play
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment not at current position
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play previous
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ignore
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip and clicked
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up at end of playback
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play two intros first
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip and dismissed
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play next up
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segments auto skip
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play intro first
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Don't play intro
com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Intro playback ends always play next
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force enable overrides AV1 Profile 5 device support check
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force disable overrides AV1 Profile 5 device support check
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force enable overrides HEVC Profile 7 device support check
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test AV1 Profile 5 unsupported when device does not support it
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force disable overrides HEVC Profile 7 device support check
com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test HEVC Profile 7 unsupported when device does not support it
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view, itemId, and switch user
com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, cold load, with protected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view and switch user
com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign disabled in with unprotected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play itemId and params
com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, hot load, with protected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with search
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play but no itemId
com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified with unprotected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view and no itemId
com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified does not exist
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play itemId
com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, no current user
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with search query
com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in with unprotected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified with protected profile
com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view item
com.github.damontecres.wholphin.test.TestVoiceInputManagerAutoFocus > permanent audio focus loss stops listening
com.github.damontecres.wholphin.test.TestVoiceInputManagerAutoFocus > transient audio focus loss is ignored
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Explicit VLC starts from beginning at zero position
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Implicit player preserves positive resume position
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Playback list starts from beginning
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Unavailable selected player falls back to implicit start from beginning
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > VLC starts from beginning without subtitles or duration
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Explicit VLC preserves positive resume position
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Implicit player starts VLC from beginning at zero position
com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Existing player extras and playback-start reporting remain unchanged

Full test log, archived XML/binary results, six failing-suite XML files, counts, exact failure inventory and APK checksums are retained under /root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-upstream-1-1-1/. Generated build/capture scratch outputs are removed after evidence collection to keep disk use lean.

Release scope in plain words

Wholphin v1.1.1 release notes explicitly call this a quick release fixing one bug. The page repeats the v1.1.0 feature list; those earlier changes already landed through the v1.1.0 integration.

Area Published release-note items New in this merge?
Seerr / Discover Make manual Seerr setup work when the optional companion plugin is absent. The old state stayed Pending, keeping the loading dialog visible; the new no-plugin branch marks setup ready. Yes: the entire v1.1.0→v1.1.1 code delta. No credentials, requests, ratings or identity precedence change.
Seerr / Discover, carried forward Companion-plugin support, plugin-supplied Discover configuration, finer movie/TV and 4K request permissions and small Discover UI changes. No; already in main. The pre-existing caution about plugin-provided administrator/shared credentials displacing household identities remains. Wolfskin-owned Discover continues through Conductarr.
TV layout / music, carried forward Playlist pages use the library-grid layout; subfolders inherit parent view/sort; album cards show album artist; music's Top Rated Unwatched row becomes Top Rated; Artists can show artist or album artist. No; no new TV layout changes.
Player, carried forward HDR/Dolby Vision force-enable/disable; experimental DV7→8.1; steadier per-series episode audio/subtitle choices; Fire OS media voice permission; in-play bitrate changes; AC3 setting, VLC restart, audio-focus/background-music, controller play/pause and HDHomeRun live-stream-ID fixes; detected HDR information in diagnostics. No; no new player code. Physical decoder/passthrough behavior remains unverified.
Jellyfin support / sessions, carried forward Warn about future unsupported servers, revise session restoration, add image tags to artwork URLs, plus the release's generic earlier-version/minor fixes. This is the final release supporting Jellyfin 10.10/10.11; v1.2.0 is planned to require 12+. No; minimum/version-warning logic and saved-person launch are unchanged. No new restriction on Jellyfin 12.
Toolchain / maintenance, carried forward Kotlin, coroutine/dependency, Gradle, OpenAPI-generator and GitHub Actions updates, plus translations. No; no dependency, wrapper, image, lock, generated API, workflow or translation changes in v1.1.1. Forgejo Actions remains unchanged.

Per-file overlap with work in flight

Upstream changes only app/src/main/java/com/github/damontecres/wholphin/ui/setup/seerr/SwitchSeerrViewModel.kt, by adding three lines. Both live-lane snapshots were inspected read-only; neither branch was checked out or changed. The motion snapshot and available card-branch tip currently point to the same commit, 166844541821b155673b8d3a4e06af7b76f2c231; no uncommitted sibling-worktree content was inspected. The upstream one-file delta does not overlap either snapshot or any named planned card surface.

Paths below are individual filenames within the stated source directories; every named file is unchanged by this tag.

Lane / source directory File v1.1.1 touch / predicted conflict
Motion — wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/ WolfskinImages.kt None; image request sizing/loaders untouched.
Motion — app upstream ui/ CoilConfig.kt None; shared image loader, client, crossfade and transition policy untouched.
Motion — design LoadingSurface.kt None; shaped loading states untouched.
Motion — design NetworkArtTransition.kt None; image reveal transitions untouched.
Motion — design Motion.kt None; motion durations/policy untouched.
Motion — design HeroMotion.kt None; hero motion untouched.
Motion — design HomeHeader.kt None; hero/header unchanged.
Motion — design HomeScreen.kt None; Home layout and focus unchanged.
Motion — design HomeShelf.kt None; shelf unchanged.
Motion — design DiscoverScreen.kt None; owned Discover layout/loading unchanged.
Motion — design DiscoverShelf.kt None; shelf unchanged.
Motion — design TitleHeader.kt None; title hero unchanged.
Motion — design TitleShelf.kt None; shelf unchanged.
Motion — design Focus.kt None; focus rings/lifts unchanged.
Motion — design FrameScroll.kt None; focus/scroll unchanged.
Motion — design ShelfScroll.kt None; focus/scroll unchanged.
Cards — design TitleCard.kt None; card metadata, caption and progress-bar work not imported or reimplemented.
Cards — design Caption.kt None; caption work untouched.
Cards / motion — design LibraryScreen.kt None; library grid/caption layout unchanged.
Cards — design LibraryGridState.kt None; grid state unchanged.
Cards — app upstream ui/components/ ItemGrid.kt None; grid unchanged.
Cards — app upstream ui/components/ CollectionFolderGrid.kt None; grid unchanged.
Cards — app upstream ui/components/ CollectionFolderList.kt None; list unchanged.
Cards — app upstream ui/main/settings/ HomeRowPresets.kt None; home row presets unchanged.
Sessions — app upstream data/ ServerRepository.kt None; saved users, concurrent session revalidation and profile isolation unchanged.
Sessions — app upstream package root MainActivity.kt None; launch/session/picker entry unchanged.
Person-picker surfaces — design WhosWatching.kt None; actual PeoplePicker/RecoveryPicker rendering unchanged.
Person-picker surfaces — design ShellState.kt None; person selection, remembered view and picker focus unchanged.
Person-picker session — app/src/wolfskin/java/org/justinvisser/wolfskin/tv/ WolfskinApp.kt None; saved-user first paint, household refresh and person switching unchanged.
Saved-person projection — app Wolfskin ShellPeople.kt None; locally saved names and identity-derived colors unchanged.
Person-picker surfaces — app tests PersonPickerSessionTest.kt None; existing person-picker session behavior/assertions unchanged.
Saved-person surfaces — app tests SavedPeoplePickerTest.kt None; saved-user first paint/name refresh unchanged.
Networking — app upstream services/hilt/ AppModule.kt None; DNS cache and OkHttp client graph unchanged.
Only changed upstream file — app upstream ui/setup/seerr/ SwitchSeerrViewModel.kt Manual Seerr setup stops waiting when the plugin is absent. No direct lane conflict. Existing plugin-present pending/finally handling remains unchanged.

This tag introduces no direct merge conflict for the motion/shaped-loading lane (ready motion review) or the stacked card metadata/caption/progress/grid lane. Those branches may already conflict with each other or moving main independently of this tag; that is not assessed or changed here.

Verification limit and next approval

Not smoke-tested on the TV. No live Jellyfin/Conductarr/Seerr calls or companion-plugin configuration were performed. Source inspection plus builds/existing suites do not prove the no-plugin dialog, decoder behavior or physical focus/layout.

A later explicitly authorized device window should open manual Seerr setup without the plugin and reach the existing Jellyfin-first chooser/default URL; inspect plugin-prefilled setup if used, current-person Conductarr identity/ratings, saved-person cold/hot launch, Home/Libraries/Discover and focus, Forgejo updater metadata, and existing ExoPlayer/MPV playback. The carried-forward HDR/Dolby Vision and database-install risks are unchanged by this patch.

This PR is ready for review, not permission to merge or publish. Hold for Justin's GO; the local unit gate is RED and must be accounted for before any merge decision. Firstmate arranges the separate cross-family review. Nothing is published or installed.

## Outcome and approval gate Merge [Wholphin v1.1.1](https://github.com/damontecres/Wholphin/releases/tag/v1.1.1) into current Wolfskin main as a real two-parent merge. **Wait for Justin's explicit GO before merging. Unit validation is RED (missing offline Robolectric runtimes); details and every failing name below.** No release, asset upload, tag, optimized-variant build, installation, ADB or device/box action was performed. **AI-assisted (fork-internal).** Ready for firstmate's independently arranged cross-family review; this worker posts no review status and performs no merge. - Wolfskin base: `332fc6476d00264a573864f9bcf8c0569d95d36b` (includes the image-sizing/lean-read, DNS/saved-people and picker-test synchronization work). - Upstream tag peels to `159edc875d10a667c25598a2cbfac8c123fe5bff`. - Merge commit: `b80b67f7363562da57ce2815216a7f23272eeb21`; parents are the base above and upstream tag commit. - Final head: `00e8e83bb64d7a3ea57310846ed106c9fd621f53`. The only subsequent commit documents the integration; compiled source is identical to the APK build head. ## Conflicts, behavior and fork preservation **No conflicts.** No ours/theirs override, rebase, history rewrite or force-push. The sole upstream source change imports unchanged. Every existing Wolfskin implementation and assertion is retained. | Changed file | Change / resolution | | --- | --- | | `app/src/main/java/com/github/damontecres/wholphin/ui/setup/seerr/SwitchSeerrViewModel.kt` | Three upstream added lines: without the optional plugin, mark loading Success immediately. This file was not previously changed by the fork. | | `docker/README.md` | Nine documentation lines explaining the patch and unchanged database/layout/player/session/network/signing scope. No runtime change. | The three initialization paths, checked against the code: 1. **No plugin:** previously Pending indefinitely; now synchronously Success, allowing the manual Seerr chooser instead of the loading dialog. `initialUrl` stays null, retaining Wolfskin's default URL and Jellyfin-first login choice. 2. **Plugin installed, empty settings response:** unchanged asynchronous lookup; no URL prefill, then the existing `finally` sets Success so manual setup is available. 3. **Plugin settings lookup throws:** unchanged catch/log and `finally` → Success; manual setup remains available. A successful response still prefills its plugin URL. No existing app/design test directly references `SwitchSeerrViewModel` or `SwitchSeerrState`. Build plus the existing suite provide integration/regression coverage, **not direct runtime proof of these three paths**. Two attempts to register an isolated external smoke never executed an application test (source discovery, then read-only Groovy property); firstmate explicitly directed dropping that disproportionate harness. Its source/configuration/runner were removed; there was no third attempt and no new or weakened permanent test. **Preservation:** baseline `v1.1.0` → frozen Wolfskin main contains 2,832 paths. All 2,832 still differ from upstream `v1.1.1`; 2,831 are byte-identical to the frozen main. The remaining file is the additive build README note. No Wolfskin-modified source/resource/test file changes behavior through an upstream edit. The upstream Seerr setup fix affects the legacy setup dialog; the owned Conductarr Discover/request path is unchanged. Application ID/branding, original `com.github.damontecres.wholphin` namespace/packages, GPL notices, Conductarr integration and all rating badges, Seerr defaults, TV layouts, Forgejo updater, image sizing/lean reads, saved-person/session fixes, DNS/OkHttp graph, Docker wrapper, Makefile and CI choices survive unchanged. No upstream workflows were enabled. Restricted `git diff --stat 332fc6476d00264a573864f9bcf8c0569d95d36b HEAD -- <all Wolfskin baseline paths>`: ```text docker/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) ``` The full PR delta is two files, twelve insertions, zero deletions. Baseline inventories, input refs, patches, stats and identity/build logs are retained at `/root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-upstream-1-1-1/`. ## Database, tokens and toolchain - **Room remains version 41; no migration, entity/DAO, migration registration or `app/schemas/*.json` change.** No conflicting fork migration or database decision is introduced. The older 35→40→41 install migration already in main remains unchanged. - No design tokens or generated token files changed. The standard build's `:wolfskin-design:checkTokenDrift` task passed; no token regeneration was needed. - No new dependency/wrapper/image/verification/lock changes. Existing container build tooling works unchanged. Fetch imported the release tag, while refusing to overwrite unrelated pre-existing mutable `develop` tags; none was forced. Git cannot independently verify the signed upstream tag without an `allowedSignersFile`; this PR makes no signature-verification claim and changes no shared trust settings. ## Build, identity and final-head test evidence **Unit gate is RED, not merge-ready:** the sole final-head `make test` invocation failed during Robolectric SDK startup for 109 tests. These are new failures observed in this lane, not asserted to be baseline failures. No second suite run, assertion weakening, test exclusion or unrelated tooling fix was performed. | Command / check | Observed result | | --- | --- | | `make apk` → `bash docker/gradle.sh release clean assembleDefaultRelease` | **PASS**, `BUILD SUCCESSFUL in 5m 48s`; 111 tasks executed. Standard default release variant, not the separate optimized variant. | | `make apk-debug` → `bash docker/gradle.sh debug assembleDefaultDebug` | **PASS**, `BUILD SUCCESSFUL in 3m 6s`; 97 tasks. Existing packaged branding check passed on all four debug APKs, including legal attribution. Debug uses the existing MPV stub; it is identity proof, not native-playback proof. | | `make test` → `bash docker/gradle.sh debug testDefaultDebugUnitTest`, exactly once on `00e8e83bb64d7a3ea57310846ed106c9fd621f53` | **FAIL**, `BUILD FAILED in 3m 58s`. App: 111 suites, 714 tests, 109 failures, 0 errors/skipped. Design: 27 suites, 194 tests, 0 failures/errors/skipped. Total: 908 tests, 799 pass, 109 fail. | | All eight APKs: `apksigner verify --verbose --print-certs` + `aapt2 dump badging` in the build image | **PASS**. Release: `org.justinvisser.wolfskin.tv`, label `Wolfskin`, code `65`. Debug: existing `org.justinvisser.wolfskin.tv.debug`, label `Wolfskin (Debug)`, code `65`. Neither package became Wholphin. | | Release signer SHA-256 | `5cd708ffa94cc22fa41648377a305af3afe0f6da394c57e67024df43c1fcb5a0` — unchanged from the v1.1.0 release-build evidence. | | Persistent debug signer SHA-256 | `182bac72e292b9ea4d3d4e6383f92eb2161fa28ba5afcff0337cb7d5a9d5341d` — matches the existing persistent debug keystore certificate. Keystore bytes unchanged before/after, SHA-256 `9b3030cb7e5c19a3a7e498c5ad16f4a9d5ac80ffbce8dd7ad9def6680f339d35`. | `make apk` intentionally signs with the **release key**, not the persistent debug key. The extra standard debug build proves the debug signer separately; Makefile/signing semantics were not changed. APK version metadata is `1.1.1-153-gb80b67f7` from the committed merge. The later final-head commit is documentation only. All eight exact names (local proof only; not published): ```text Wholphin-default-release-1.1.1-153-gb80b67f7-65.apk Wholphin-default-release-1.1.1-153-gb80b67f7-65-arm64-v8a.apk Wholphin-default-release-1.1.1-153-gb80b67f7-65-armeabi-v7a.apk Wholphin-default-release-1.1.1-153-gb80b67f7-65-x86_64.apk Wholphin-default-debug-1.1.1-153-gb80b67f7-65.apk Wholphin-default-debug-1.1.1-153-gb80b67f7-65-arm64-v8a.apk Wholphin-default-debug-1.1.1-153-gb80b67f7-65-armeabi-v7a.apk Wholphin-default-debug-1.1.1-153-gb80b67f7-65-x86_64.apk ``` The source package and artifact filename convention remain upstream-compatible; the installed application IDs/labels remain Wolfskin. ### Exact test failure evidence All 109 failures have `IllegalArgumentException: Path is not a file` before their test bodies, naming one of these absent offline runtimes: ```text wolfskin-design/build/robolectric-sdk/android-all-instrumented-9-robolectric-4913185-2-i7.jar wolfskin-design/build/robolectric-sdk/android-all-instrumented-11-robolectric-6757853-i7.jar ``` Observed prepared directory contains only `android-all-instrumented-14-robolectric-10818077-i7.jar`, despite all three versions being declared on the single `robolectricSdk` configuration. **[INFERENCE]** Gradle resolves that configuration to one module version, so the clean build lacks the older SDK files that another checkout may have retained. This merge does not alter that configuration. The missing-file exception is observed; no application regression or baseline-green equivalence is claimed, and no rerun was used to confirm it. The unchanged existing picker/identity/ratings/updater tests that did run include `PersonPickerSessionTest` (7 pass), `SavedPeoplePickerTest` (1 pass), `ServerSavedSessionTest` (5 pass), `SeerrRatingsServiceTest` (12 pass), and `TestUpdateChecker` (17 pass). These do not directly test the changed Seerr view-model initializer. <details><summary>Every failing test name, exactly as reported (109)</summary> ```text com.github.damontecres.wholphin.test.TestVoiceInputManager > initial state is Idle com.github.damontecres.wholphin.test.TestVoiceInputManager > callbacks from previous recognizer are ignored after cleanup com.github.damontecres.wholphin.test.TestVoiceInputManager > onPartialResults updates partialResult com.github.damontecres.wholphin.test.TestVoiceInputManager > audio focus is abandoned when recognizer is destroyed com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults resets soundLevel to zero com.github.damontecres.wholphin.test.TestVoiceInputManager > onReadyForSpeech transitions state to Listening com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged clamps low values to 0 com.github.damontecres.wholphin.test.TestVoiceInputManager > acknowledge works from Error state com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults transitions to Result state with correct text com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails when audio focus not granted com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening is ignored when already listening com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_SERVER to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > initial partialResult is empty com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_RECOGNIZER_BUSY to correct resource after retry exhausted com.github.damontecres.wholphin.test.TestVoiceInputManager > audio focus error is retryable com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening calls recognizer startListening com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening does not create recognizer when no network com.github.damontecres.wholphin.test.TestVoiceInputManager > acknowledge resets state to Idle com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening creates SpeechRecognizer com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged updates soundLevel with normalized value com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets state to Idle com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening sets recognition listener com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no network capabilities com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening requests audio focus com.github.damontecres.wholphin.test.TestVoiceInputManager > initial soundLevel is zero com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_AUDIO to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup calls destroy on recognizer com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening resets partialResult com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening transitions state to Starting com.github.damontecres.wholphin.test.TestVoiceInputManager > onRmsChanged clamps high values to 1 com.github.damontecres.wholphin.test.TestVoiceInputManager > network error from fast-fail is retryable com.github.damontecres.wholphin.test.TestVoiceInputManager > onError resets soundLevel to zero com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_SPEECH_TIMEOUT to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionDenied sets correct error resource com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no internet capability com.github.damontecres.wholphin.test.TestVoiceInputManager > onEndOfSpeech transitions to Processing state com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NETWORK_TIMEOUT to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > onError transitions to Error state com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NETWORK to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening does not create recognizer when audio focus denied com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets soundLevel to zero com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup calls cancel on recognizer com.github.damontecres.wholphin.test.TestVoiceInputManager > isAvailable returns mocked value com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_CLIENT to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_NO_MATCH to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening is ignored when in Starting state com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps ERROR_INSUFFICIENT_PERMISSIONS to correct resource com.github.damontecres.wholphin.test.TestVoiceInputManager > startListening fails immediately when no network com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionGranted calls startListening com.github.damontecres.wholphin.test.TestVoiceInputManager > cleanup resets partialResult to empty com.github.damontecres.wholphin.test.TestVoiceInputManager > stopListening triggers cleanup com.github.damontecres.wholphin.test.TestVoiceInputManager > onError maps unknown error to unknown resource com.github.damontecres.wholphin.test.TestVoiceInputManager > onError ERROR_SPEECH_TIMEOUT is retryable com.github.damontecres.wholphin.test.TestVoiceInputManager > onPartialResults ignores blank text com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with null bundle transitions to Error state com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with blank text transitions to Error state com.github.damontecres.wholphin.test.TestVoiceInputManager > onPermissionDenied transitions to Error state com.github.damontecres.wholphin.test.TestVoiceInputManager > onResults with empty text transitions to Error state com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Direct play includes the opened live stream ID com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Playback ends with no next up com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up never without automatic play com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > playItemInPlaylist com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up never with automatic play com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment not at current position com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play previous com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ignore com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip and clicked com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Show next up at end of playback com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play two intros first com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segment ask to skip and dismissed com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play next up com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Media segments auto skip com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Play intro first com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Don't play intro com.github.damontecres.wholphin.ui.playback.PlaybackViewModelTests > Intro playback ends always play next com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force enable overrides AV1 Profile 5 device support check com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force disable overrides AV1 Profile 5 device support check com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force enable overrides HEVC Profile 7 device support check com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test AV1 Profile 5 unsupported when device does not support it com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test force disable overrides HEVC Profile 7 device support check com.github.damontecres.wholphin.util.profile.TestDeviceProfileUtils > Test HEVC Profile 7 unsupported when device does not support it com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view, itemId, and switch user com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, cold load, with protected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view and switch user com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign disabled in with unprotected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play itemId and params com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, hot load, with protected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with search com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play but no itemId com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified with unprotected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view and no itemId com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified does not exist com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with play itemId com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in, no current user com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with search query com.github.damontecres.wholphin.services.IntentServiceTest > Test auto sign in with unprotected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test user specified with protected profile com.github.damontecres.wholphin.services.IntentServiceTest > Test parseIntent with view item com.github.damontecres.wholphin.test.TestVoiceInputManagerAutoFocus > permanent audio focus loss stops listening com.github.damontecres.wholphin.test.TestVoiceInputManagerAutoFocus > transient audio focus loss is ignored com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Explicit VLC starts from beginning at zero position com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Implicit player preserves positive resume position com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Playback list starts from beginning com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Unavailable selected player falls back to implicit start from beginning com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > VLC starts from beginning without subtitles or duration com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Explicit VLC preserves positive resume position com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Implicit player starts VLC from beginning at zero position com.github.damontecres.wholphin.ui.playback.PlayExternalViewModelTest > Existing player extras and playback-start reporting remain unchanged ``` </details> Full test log, archived XML/binary results, six failing-suite XML files, counts, exact failure inventory and APK checksums are retained under `/root/.treehouse/firstmate-7bab20/2/firstmate/data/wolfskin-upstream-1-1-1/`. Generated build/capture scratch outputs are removed after evidence collection to keep disk use lean. ## Release scope in plain words [Wholphin v1.1.1 release notes](https://github.com/damontecres/Wholphin/releases/tag/v1.1.1) explicitly call this a quick release fixing one bug. The page repeats the v1.1.0 feature list; those earlier changes already landed through [the v1.1.0 integration](https://git.justinvisser.org/justinzeus/wolfskin/pulls/27). | Area | Published release-note items | New in this merge? | | --- | --- | --- | | Seerr / Discover | Make manual Seerr setup work when the optional companion plugin is absent. The old state stayed Pending, keeping the loading dialog visible; the new no-plugin branch marks setup ready. | **Yes: the entire v1.1.0→v1.1.1 code delta.** No credentials, requests, ratings or identity precedence change. | | Seerr / Discover, carried forward | Companion-plugin support, plugin-supplied Discover configuration, finer movie/TV and 4K request permissions and small Discover UI changes. | No; already in main. The pre-existing caution about plugin-provided administrator/shared credentials displacing household identities remains. Wolfskin-owned Discover continues through Conductarr. | | TV layout / music, carried forward | Playlist pages use the library-grid layout; subfolders inherit parent view/sort; album cards show album artist; music's Top Rated Unwatched row becomes Top Rated; Artists can show artist or album artist. | No; no new TV layout changes. | | Player, carried forward | HDR/Dolby Vision force-enable/disable; experimental DV7→8.1; steadier per-series episode audio/subtitle choices; Fire OS media voice permission; in-play bitrate changes; AC3 setting, VLC restart, audio-focus/background-music, controller play/pause and HDHomeRun live-stream-ID fixes; detected HDR information in diagnostics. | No; no new player code. Physical decoder/passthrough behavior remains unverified. | | Jellyfin support / sessions, carried forward | Warn about future unsupported servers, revise session restoration, add image tags to artwork URLs, plus the release's generic earlier-version/minor fixes. This is the final release supporting Jellyfin 10.10/10.11; v1.2.0 is planned to require 12+. | No; minimum/version-warning logic and saved-person launch are unchanged. No new restriction on Jellyfin 12. | | Toolchain / maintenance, carried forward | Kotlin, coroutine/dependency, Gradle, OpenAPI-generator and GitHub Actions updates, plus translations. | No; no dependency, wrapper, image, lock, generated API, workflow or translation changes in v1.1.1. Forgejo Actions remains unchanged. | ## Per-file overlap with work in flight Upstream changes only `app/src/main/java/com/github/damontecres/wholphin/ui/setup/seerr/SwitchSeerrViewModel.kt`, by adding three lines. Both live-lane snapshots were inspected read-only; neither branch was checked out or changed. The motion snapshot and available card-branch tip currently point to the same commit, `166844541821b155673b8d3a4e06af7b76f2c231`; no uncommitted sibling-worktree content was inspected. The upstream one-file delta does not overlap either snapshot or any named planned card surface. Paths below are individual filenames within the stated source directories; every named file is unchanged by this tag. | Lane / source directory | File | v1.1.1 touch / predicted conflict | | --- | --- | --- | | Motion — `wolfskin-design/src/main/java/org/justinvisser/wolfskin/design/` | `WolfskinImages.kt` | None; image request sizing/loaders untouched. | | Motion — app upstream `ui/` | `CoilConfig.kt` | None; shared image loader, client, crossfade and transition policy untouched. | | Motion — design | `LoadingSurface.kt` | None; shaped loading states untouched. | | Motion — design | `NetworkArtTransition.kt` | None; image reveal transitions untouched. | | Motion — design | `Motion.kt` | None; motion durations/policy untouched. | | Motion — design | `HeroMotion.kt` | None; hero motion untouched. | | Motion — design | `HomeHeader.kt` | None; hero/header unchanged. | | Motion — design | `HomeScreen.kt` | None; Home layout and focus unchanged. | | Motion — design | `HomeShelf.kt` | None; shelf unchanged. | | Motion — design | `DiscoverScreen.kt` | None; owned Discover layout/loading unchanged. | | Motion — design | `DiscoverShelf.kt` | None; shelf unchanged. | | Motion — design | `TitleHeader.kt` | None; title hero unchanged. | | Motion — design | `TitleShelf.kt` | None; shelf unchanged. | | Motion — design | `Focus.kt` | None; focus rings/lifts unchanged. | | Motion — design | `FrameScroll.kt` | None; focus/scroll unchanged. | | Motion — design | `ShelfScroll.kt` | None; focus/scroll unchanged. | | Cards — design | `TitleCard.kt` | None; card metadata, caption and progress-bar work not imported or reimplemented. | | Cards — design | `Caption.kt` | None; caption work untouched. | | Cards / motion — design | `LibraryScreen.kt` | None; library grid/caption layout unchanged. | | Cards — design | `LibraryGridState.kt` | None; grid state unchanged. | | Cards — app upstream `ui/components/` | `ItemGrid.kt` | None; grid unchanged. | | Cards — app upstream `ui/components/` | `CollectionFolderGrid.kt` | None; grid unchanged. | | Cards — app upstream `ui/components/` | `CollectionFolderList.kt` | None; list unchanged. | | Cards — app upstream `ui/main/settings/` | `HomeRowPresets.kt` | None; home row presets unchanged. | | Sessions — app upstream `data/` | `ServerRepository.kt` | None; saved users, concurrent session revalidation and profile isolation unchanged. | | Sessions — app upstream package root | `MainActivity.kt` | None; launch/session/picker entry unchanged. | | Person-picker surfaces — design | `WhosWatching.kt` | None; actual PeoplePicker/RecoveryPicker rendering unchanged. | | Person-picker surfaces — design | `ShellState.kt` | None; person selection, remembered view and picker focus unchanged. | | Person-picker session — `app/src/wolfskin/java/org/justinvisser/wolfskin/tv/` | `WolfskinApp.kt` | None; saved-user first paint, household refresh and person switching unchanged. | | Saved-person projection — app Wolfskin | `ShellPeople.kt` | None; locally saved names and identity-derived colors unchanged. | | Person-picker surfaces — app tests | `PersonPickerSessionTest.kt` | None; existing person-picker session behavior/assertions unchanged. | | Saved-person surfaces — app tests | `SavedPeoplePickerTest.kt` | None; saved-user first paint/name refresh unchanged. | | Networking — app upstream `services/hilt/` | `AppModule.kt` | None; DNS cache and OkHttp client graph unchanged. | | Only changed upstream file — app upstream `ui/setup/seerr/` | `SwitchSeerrViewModel.kt` | Manual Seerr setup stops waiting when the plugin is absent. No direct lane conflict. Existing plugin-present pending/finally handling remains unchanged. | This tag introduces no direct merge conflict for the motion/shaped-loading lane ([ready motion review](https://git.justinvisser.org/justinzeus/wolfskin/pulls/51)) or the stacked card metadata/caption/progress/grid lane. Those branches may already conflict with each other or moving main independently of this tag; that is not assessed or changed here. ## Verification limit and next approval **Not smoke-tested on the TV.** No live Jellyfin/Conductarr/Seerr calls or companion-plugin configuration were performed. Source inspection plus builds/existing suites do not prove the no-plugin dialog, decoder behavior or physical focus/layout. A later explicitly authorized device window should open manual Seerr setup without the plugin and reach the existing Jellyfin-first chooser/default URL; inspect plugin-prefilled setup if used, current-person Conductarr identity/ratings, saved-person cold/hot launch, Home/Libraries/Discover and focus, Forgejo updater metadata, and existing ExoPlayer/MPV playback. The carried-forward HDR/Dolby Vision and database-install risks are unchanged by this patch. This PR is ready **for review**, not permission to merge or publish. Hold for Justin's GO; the local unit gate is RED and must be accounted for before any merge decision. Firstmate arranges the separate cross-family review. Nothing is published or installed.
justinzeus added 4 commits 2026-11-07 02:38:32 +01:00
## Description
If the server plugin is not installed, trying to add Seerr server will
show an endless loading page. This PR fixes that.

### Related issues
Fixes #2177

### Testing
Emulator against server with & without the plugin

## Screenshots
N/A

## AI or LLM usage
None
-----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgrqffh93Kw9xITbVISXK/jkih9K
 Q1NSQeC6TBqiLCg2oAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 AAAAQMNewXnxMn5HlyJcrG3vlqPg4lBjF2q9uyedLQQ2S+G4PZQf1XxPTy4q9DKBcT8UIN
 XSrDmvm0HJci43S6FMTgE=
 -----END SSH SIGNATURE-----

Merge tag 'v1.1.1' into fm/wolfskin-upstream-1-1-1

v1.1.1

# -----BEGIN SSH SIGNATURE-----
# U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgrqffh93Kw9xITbVISXK/jkih9K
# Q1NSQeC6TBqiLCg2oAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
# AAAAQMNewXnxMn5HlyJcrG3vlqPg4lBjF2q9uyedLQQ2S+G4PZQf1XxPTy4q9DKBcT8UIN
# XSrDmvm0HJci43S6FMTgE=
# -----END SSH SIGNATURE-----
# gpg verification failed.
Document upstream v1.1.1 setup fix
All checks were successful
review/code-claude No blocking findings on 00e8e83b; waits for Justin go
00e8e83bb6
Author
Owner

Cross-family review (Claude) of the v1.1.1 merge, head 00e8e83bb6: PASS, no blocking finding. NOT to be merged before Justin's go.

Read the whole PR delta: 2 files, +12. The only upstream source change is three lines in SwitchSeerrViewModel.kt: when the Seerr companion plugin is absent the setup state is now marked Success immediately instead of staying Pending (the loading dialog never cleared). The plugin-present, empty-response and error paths are unchanged (existing finally sets Success). docker/README.md gets nine documentation lines. Real two-parent merge, no conflicts, no Room change, no tokens, dependency, wrapper or workflow change; all Wolfskin paths preserved (restricted diff against the base shows only the README note). make apk and make apk-debug pass; release and persistent debug signers unchanged; packages remain org.justinvisser.wolfskin.tv(.debug).

On the red unit gate: all 109 failures are IllegalArgumentException: Path is not a file for the offline Robolectric Android 9 and 11 runtime jars that the fresh lane worktree lacks (the same infrastructure artifact the motion lane hit and cured by copying the jars; main itself is green at 714/714 since PR 52). None executes application code, and the diff cannot influence them. I did not require a rerun for three lines in a legacy dialog; it is cheap to do before a merge if Justin wants the number green.

Non-blocking: no existing test exercises SwitchSeerrViewModel (build plus suite only); the no-plugin dialog should be looked at once on the TV.

Cross-family review (Claude) of the v1.1.1 merge, head 00e8e83bb64d7a3ea57310846ed106c9fd621f53: PASS, no blocking finding. NOT to be merged before Justin's go. Read the whole PR delta: 2 files, +12. The only upstream source change is three lines in `SwitchSeerrViewModel.kt`: when the Seerr companion plugin is absent the setup state is now marked Success immediately instead of staying Pending (the loading dialog never cleared). The plugin-present, empty-response and error paths are unchanged (existing `finally` sets Success). `docker/README.md` gets nine documentation lines. Real two-parent merge, no conflicts, no Room change, no tokens, dependency, wrapper or workflow change; all Wolfskin paths preserved (restricted diff against the base shows only the README note). make apk and make apk-debug pass; release and persistent debug signers unchanged; packages remain org.justinvisser.wolfskin.tv(.debug). On the red unit gate: all 109 failures are `IllegalArgumentException: Path is not a file` for the offline Robolectric Android 9 and 11 runtime jars that the fresh lane worktree lacks (the same infrastructure artifact the motion lane hit and cured by copying the jars; main itself is green at 714/714 since PR 52). None executes application code, and the diff cannot influence them. I did not require a rerun for three lines in a legacy dialog; it is cheap to do before a merge if Justin wants the number green. Non-blocking: no existing test exercises SwitchSeerrViewModel (build plus suite only); the no-plugin dialog should be looked at once on the TV.
All checks were successful
review/code-claude No blocking findings on 00e8e83b; waits for Justin go
This pull request can be merged automatically.
This branch is out-of-date with the base branch
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/wolfskin-upstream-1-1-1:fm/wolfskin-upstream-1-1-1
git checkout fm/wolfskin-upstream-1-1-1
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#53
No description provided.