Skip to content

refactor(api): move the Android clients to API v2 - #307

Merged
Quick104 merged 185 commits into
mainfrom
apiv2-cleanup
Sep 16, 2026
Merged

Quick104 merged 185 commits into
mainfrom
apiv2-cleanup

Conversation

@Quick104

@Quick104 Quick104 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Problem

The Android phone and TV clients talked to the server over /api/v1/. The
server has since redesigned those routes as /api/v2/ with different shapes,
stricter identity scoping, and — for playback — a genuinely different start
contract. Until the clients moved, every new server capability had to be
back-ported into v1 shapes, and a v1-only client could not express things the
v2 API makes explicit: which identity a read belongs to, which session owns a
playback mutation, and whether the server is new enough to serve the call at
all.

Solution

Every HTTP call the Android clients make now goes to /api/v2/, with three
documented exceptions. docs/api-v2/android-migration-status.md is the ledger
and lists each endpoint family and its status; it is verifiable against the
source with the grep recorded at the top of that file.

The substantive pieces:

  • Contract probing and gating. ApiV2Gate blocks v2 calls when the active
    server reports UPDATE_REQUIRED. The contract is probed at launch, on server
    switch, on identity refresh, and after pairing, with bounded waits so a stalled
    probe cannot hold up sign-in.
  • Identity-scoped reads and writes. Calls carry the authority they were
    issued under, so a profile or server change mid-flight cannot land a write
    under the wrong identity or publish a stale read.
  • Playback on the v2 PlaybackDecision shape. Start was redesigned
    server-side. Sessions are owner-bound, terminal decisions that allocate no
    session settle correctly, and recovery is durable across process death.
  • Dead v1 code removed rather than left dormant: the v1 playback transport,
    notification/section/recommendation/catalog remnants, the bound audiobook
    timeline, and the owner-loss recovery union.
  • Vendored v2 fixtures with scripts/sync-apiv2-fixtures.sh, so the contract
    tests check against real server responses rather than hand-written shapes.

This branch also carries unrelated UI work

Flagging this plainly rather than burying it: the last several commits are phone
and TV UI changes with no connection to the API migration — the fullscreen
player HUD insets, a flattened playback settings menu, the item detail page
becoming an ordinary full-screen page, server addresses in the TV setup sheet,
and season/episode numbers on TV Home cards. By the repository's own
one-concern-per-PR rule these belong in their own PR; they are here at the
author's direction.

Validation

  • ./gradlew :androidApp:testDebugUnitTest :androidTvApp:testDebugUnitTest
    green (1154 TV tests).
  • Optimized R8 release APKs built and smoke-tested on real hardware, not just
    the emulator: Pixel 11 Pro (phone) and a Google TV Streamer (kirkwood,
    Android 14, 32-bit armeabi-v7a). Launched clean, no crashes in logcat -b crash, TV cold start 679–705 ms across three runs with the baseline profile
    installing.
  • UI changes were checked on an emulator against fixed data before going to a
    device. Before/after screenshots are attached below.

Risks

  • Size. 166 commits, 537 files, +28.9k/−7.7k. This is not reviewable as a
    single unit in one sitting; the per-commit history is the useful granularity.
  • R8. Both apps minify with a shared proguard-rules.pro over a
    reflection-heavy stack. R8 breakage is runtime-only, so the release smoke
    tests above are load-bearing.
  • Server floor. Clients now require a server serving /api/v2/. Older
    servers are handled by UPDATE_REQUIRED, but that path is worth exercising
    deliberately before release.
  • Flaky test. TvNextUpSelectionHandoffTest.profileOrServerChangeClearsPendingNextUpHandoff
    failed once and passed on re-run with no change to it or its subject. It is a
    coroutine/ktor-mock timing test and looks order-dependent. Not introduced
    here, not fixed here, worth its own issue.

Follow-up

  • Split the UI commits out if this PR is meant to stay an API migration.
  • The TV app spells season/episode four different ways across the player,
    calendar, detail rail, and inbox. Only the Home screen was unified here.
  • The TV baseline profile is 9 KB and comes from whatever :baselineprofile-tv
    last generated; regenerating it against the current UI is the next real
    performance lever.

AI disclosure

Written with AI assistance.

  • Models: claude-fable-5-1 (Claude Fable 5.1) authored the bulk of the
    migration — 69 commits carry its Co-Authored-By trailer.
    claude-opus-5[1m] (Claude Opus 5, 1M context) authored 4 commits: the
    player HUD fix, the playback settings menu, the TV setup sheet addresses, and
    the TV Home episode numbers.
  • Harness: Claude Code, running in T3 Code.
  • Other tooling: one comment in TvMediaRow.kt is attributed to Codex from
    earlier work on this repository.

All changes were reviewed, built, and tested on real devices by a human-directed
process; the validation evidence above was produced by running the commands and
installs listed, not inferred.

Note

Move Android and TV clients from API v1 to API v2 with identity-fenced operations

  • Migrates all network APIs (auth, catalog, collections, downloads, notifications, playback, settings, subtitles, watch-together, ebook, recommendations, onboarding) from v1 routes to v2 routes, replacing offset pagination with cursor/continuation tokens throughout
  • Adds ApiV2Gate backed by a ServerContract probe; v2 operations are blocked with an update_server error when the active server is v1-only, while unauthenticated probes (branding, setup status) bypass the gate
  • Introduces AuthScopeSnapshot ownership checks and OwnerPolicy guards on v2 calls so captured identity (server, profile, credential epoch) is validated before and after each request; stale results return identity_changed instead of publishing
  • Replaces direct optimistic UI mutations for favorites/watchlist with a durable Room-backed MembershipPort outbox (MembershipOutbox, MembershipRuntime, RoomMembershipPort) that records commands, dispatches single-attempt mutations, and reconciles failures; watched/rating writes go through a PersonalWrite journal with the same ownership fencing
  • Adds sequenced playback via SequencedPlayback with a durable PlaybackJournalStore, v2 control tickets for realtime WebSocket connections, and proxy auxiliary header capture scoped to issued subtitle URLs
  • Raises Room schema from 8 to 11, adding membership projection/quarantine entities and download-deletion ownership fields
  • Risk: Many v1 API methods and models are removed (SettingsCapabilitiesResult, safeStatusCall, legacy personal-data operations, getReaderConfig/saveReaderConfig, updateAnnotation, getConversionCapability); any out-of-tree callers of these will break. PersonalDataApi no longer exposes favorites, watchlist, progress sync, ratings, or watched-state methods. EbookReaderApi no longer exposes config or annotation operations. SettingsApi no longer exposes per-setting CRUD or subtitle-appearance methods. User/request/notification/suggestion identifiers changed from numeric to string on the wire. Download and push responses now require additional fields (generation, server-device ID).

Macroscope summarized a22c27b.

Quick104 and others added 30 commits September 4, 2026 17:05
Adds scripts/sync-apiv2-fixtures.sh, which copies the pilot-operation and
generic problem fixtures plus fixtures.schema.json and a filtered index.json
from a silo-server checkout into shared/src/commonTest/resources/api/v2/fixtures,
recording the exact server commit in SOURCE (key=value convention).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New lenient @serializable models under network/apiv2 for the four Android
pilot operations plus Problem and SystemInfo. Enums are string-backed value
classes whose unknown values stay observable (known == null) instead of
collapsing to a default under coerceInputValues. PATCH bodies use a Patch
wrapper so omitted members are absent and cleared members are literal null
despite explicitNulls=false. ApiV2ContractTest decodes every vendored fixture
with the production SiloJson and pins null/absence semantics, defaults,
unknown fields and enum values, and the encoded PATCH shape.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ApiV2Probe GETs /api/v2/system/info once per established connection and
returns V2(info) / UpdateServer / Failure(kind). Only a 404 with a text/plain
Content-Type (the legacy listener's http.NotFound) is UpdateServer; HTML or
problem+json 404s, HTML 200s, malformed JSON, 401/403, 429, 5xx, timeouts and
connect/TLS exceptions each stay their own failure kind. ServerEntry gains a
ServerContract state (UNKNOWN / V2 / UPDATE_REQUIRED) persisted through the
registry; both server-setup view models probe the candidate on connect and
show the update-server message. ApiV2Gate blocks pilot v2 calls in the
UPDATE_REQUIRED state without ever redirecting to a v1 path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… to v2

getSetupStatus, getMe, listProgress, and updateProfile now call the v2
endpoints only (GET /api/v2/system/setup, GET /api/v2/account/me,
GET /api/v2/progress walked by cursor, PATCH /api/v2/profiles/{id}) with no
v1 fallback and no replay of a failed mutation. The API layer adapts the v2
models back to the v1-shaped User/Profile/ProgressListResponse so repositories
and screens keep compiling; User.id and impersonator_user_id become opaque
strings. DI wires ApiV2Gate and ApiV2Probe. ApiV2NoFallbackTest proves a failed
PATCH is not retried against /api/v1 and that UPDATE_REQUIRED blocks the call
before any request.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… refresh

The v2 contract probe only ran inside AuthRepository.setServerUrl, so servers
saved before this build stayed ServerContract.UNKNOWN forever: nothing probed
when the app restored its active server on launch, when the user picked a
server from the server list, when device pairing switched servers, or when the
server identity was refreshed. A v1-only server could never reach the
update-server state on those paths (F5).

AuthRepository now mirrors Apple's AuthService: refreshActiveServerName()
(re)establishes the contract verdict before reading the branding name, with an
optional knownContract so the connect path records the verdict it already has
instead of probing twice. A new switchToServer(id) is the single entry point
for activating a registered server (registry switch + token scope + refresh);
ServerListViewModel, TvServerListViewModel, and the pairing wrong-server screen
in AppNavigation use it. RegistryPairingAuthPort cannot reach the repository
during its restore-on-failure switch, so it calls refreshServerContract()
after the switch. Both MainActivity variants refresh once when the launch
path restores an active server.

Recording is guarded against a switch racing the probe: the active server id
is captured before the request and the verdict is dropped if it changed, so a
slow answer never overwrites the newer server's state. Failures still record
nothing (UNKNOWN is non-blocking).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RegistryPairingAuthPort re-probes the v2 contract after restoring the
previous server on a failed account replacement, but every rollback test
built the port without an AuthRepository, so that line never ran.

Fail the token manager's post-commit hook so the registry has already
switched to the new server when the error surfaces, then assert that a
supplied AuthRepository issues exactly one GET /api/v2/system/info and
records the verdict against the restored server. A second test pins the
no-repository path: rollback still restores the old identity and leaves
the contract UNKNOWN.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server pilot branch gained schema examples, which changed the
contract digest carried in get_system_info_ok.json. Re-run the sync script
against that commit; SOURCE now pins 17380cbca59dff44d8e1454fd07ece37efc26afe.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rver

AuthApi.getSetupStatus(serverUrl) targets a candidate server the app is
not connected to, but it was wrapped in the ACTIVE entry's ApiV2Gate. A
user whose active server is UPDATE_REQUIRED therefore could not add a
different, supported server (both Add Server flows probe the candidate
first), and could not reconnect to the same server after upgrading it.

Explicit-server (absolute URL) v2 overloads now use ApiV2Gate.Unrestricted;
the candidate's own gate is the contract probe ServerSetupViewModel runs
before switching. The relative form still targets the active server and
stays gated. getSetupStatus(serverUrl) is the only explicit-server v2
overload today; the other absolute-URL calls in network/api are v1.

Test: ApiV2NoFallbackTest.explicitServerSetupStatusBypassesTheActiveServersUpdateRequiredVerdict

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RegistryPairingAuthPort refreshed the contract verdict only in the
rollback branch. After a successful replaceAccountSession the newly
paired server stayed at UNKNOWN (or a stale UPDATE_REQUIRED saved by an
older build), so gated startup consumers acted on a verdict that was
never established for it.

Pairing is a server switch like every other path: after the session
commits and before the port reports SignedIn, run refreshServerContract()
for the now-active server. The rollback call is kept. The probe never
throws on a failed request, so this cannot undo a committed session.
The override now declares `: Unit` so the added tail expression does not
change the port's return type.

Test: RegistryPairingAuthPortTest.successfulPairingProbesTheNewServerExactlyOnce

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…aunch

The launch probe in resolveStartDestination was fire-and-forget, so gated
startup consumers (ProfileSelectionViewModel's admin lookup, and anything
else behind ApiV2Gate) could run against a stale UPDATE_REQUIRED verdict
saved before the server was upgraded, and fail without a request.

Only UPDATE_REQUIRED is harmful: UNKNOWN and V2 both pass the gate. So
resolveStartDestination (already a suspend fun inside a LaunchedEffect on
both phone and TV) now calls the new
AuthRepository.awaitContractRefreshIfUpdateRequired(), which probes and
waits at most 3 s only when the stored verdict is UPDATE_REQUIRED, and
passes the result to refreshActiveServerName(knownContract) so the
background identity refresh does not probe twice. No runBlocking on the
main thread; the wait runs under Dispatchers.IO.

Consumers are made robust as well: AuthRepository exposes
activeServerContractFlow (from registry.activeEntry), and
ProfileSelectionViewModel collects it once in init and re-runs
loadProfiles() when the contract moves from UPDATE_REQUIRED to V2.

Tests:
- AuthRepositoryContractTest: awaitContractRefreshIfUpdateRequired
  replaces a stale UPDATE_REQUIRED verdict / does not probe when the gate
  would pass
- ProfileSelectionViewModelContractTest.v2VerdictArrivingAfterUpdateRequiredRetriggersTheGatedLoad

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Removing the ACTIVE server makes the registry promote the next-MRU entry,
but neither server-list view model probed it: TV only called
tokenManager.switchActiveServer(promotedId), and the phone did nothing
after remove(). The promoted server kept whatever contract verdict an
older build had stored (or UNKNOWN), and its fetched name was never
refreshed.

TV: replace the bare token switch with authRepository.switchToServer
(registry switch + token switch + refreshActiveServerName). It is
idempotent when the registry already points at promotedId —
AndroidServerRegistry.switchTo just bumps lastUsedAt and re-applies the
same active id — and the navigation logic after it is unchanged.

Phone: mirror the TV wasActive/promoted logic and call
authRepository.refreshActiveServerName() when the active server was
removed and another one was promoted.

Test: AuthRepositoryContractTest `switchToServer on the already-active id
still probes` (no ServerList view-model tests exist).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…test

ProfileSelectionViewModelContractTest asserted on a plain list the mock
engine appended to from its own thread, so under the full unit-test run
the first assertion could observe an empty list before the v1 request
had landed. Requests now go through a channel and each expected request
is awaited on a real clock (bounded), so the test observes network
arrivals instead of assuming them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server pilot branch documented updateProfile's real statuses and media
type (contract digest moved) and switched listProgress to keyset cursors
(list_progress_ok.json next_cursor changed). SOURCE now pins
74fe2b4ac97349167d1bb85892d7f0566bd43d49.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The probe mapped any 404 with a text/plain Content-Type to UpdateServer,
so a reverse proxy's own plain-text "Not Found" would tell the user to
update a server that may not be a Silo at all.

Decide on the body instead: only Go's http.NotFound text
("404 page not found", tolerating the single trailing newline it writes)
is UpdateServer. Leading whitespace, extra newlines, or any other body
stays Failure(UNEXPECTED_STATUS, 404). This matches the Apple client's
isLegacyNotFound rule so both clients reach the same verdict.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…efresh

Both server-list view models await AuthRepository.switchToServer behind a
spinner. It ran refreshActiveServerName, which awaits the contract probe
plus the branding and health requests, so a saved server that accepts
the TCP connection but never answers held the spinner for the full
client timeouts (about 72 s).

switchToServer now switches the registry and token scope, awaits only
the contract probe bounded by the same 3 s used on the launch path
(recording V2/UPDATE_REQUIRED, leaving UNKNOWN alone), and launches the
display-name refresh on an injected process-lifetime backgroundScope
without awaiting it. The scope is optional; without one the name
refresh runs inline after the probe (single-server hosts, tests). The
active-id guard still drops a late name for a server that is no longer
active.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
listProgress promises the full progress list, but the cursor walk
stopped at PROGRESS_MAX_PAGES and returned ApiResult.Success with only
the prefix, so continue-watching consumers treated older entries as
absent without any signal.

The page size stays at the server maximum (200). PROGRESS_MAX_PAGES is
now a runaway guard only (100 pages, 20,000 entries): reaching it with
has_more still true returns ApiResult.Error(code = 0,
error = "progress_incomplete") instead of Success. The function now
documents that the result is complete or an error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
185d1e0 made the probe accept only Go's exact "404 page not found" text, so
the two pairing tests that mocked a bare "not found" started recording
UNKNOWN instead of UPDATE_REQUIRED. Mock the real legacy body.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server pilot branch constrained the profile PIN schema (contract digest
moved) and bound listProgress cursors to the viewer's access policy
(list_progress_ok.json carries a new next_cursor). SOURCE now pins
c9e5a4e376f919a90e76aa8408892ba925d5661e.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… lifts

When an upgraded server still holds a stored UPDATE_REQUIRED verdict and
the launch probe outlasts its 3 s bound, MainTvActivity routes on the
stale verdict and the background refresh records V2 afterwards. The TV
profile picker called getCurrentUser() exactly once in loadProfiles(),
so the gated admin lookup failed locally and the admin lost the
profile-management controls for the ViewModel's lifetime.

Mirror the phone ProfileSelectionViewModel: collect
activeServerContractFlow in init and re-run loadProfiles() only when the
verdict moves from UPDATE_REQUIRED to V2. The initial emission is
skipped, so a fresh V2 or UNKNOWN start does not double-load.

TvProfileSelectionViewModelContractTest proves a V2 verdict arriving
after an UPDATE_REQUIRED first load re-triggers the load and lets the
v2 admin lookup reach the network.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…time

After a successful replaceAccountSession the pairing port awaited
refreshServerContract() unbounded. The client request and socket
timeouts are ~60 s while the companion waits only 30 s for
PairingMessage.ServerResult, so a stalled /api/v2/system/info made the
phone report failure after the credentials were already committed.

Add AuthRepository.refreshServerContractBounded(timeoutMs = 3_000L) as
the single home for the probe bound, and route switchToServer,
awaitContractRefreshIfUpdateRequired, and both PairingAuthPort probe
sites (success and rollback) through it. A timeout leaves the verdict
UNKNOWN, which passes the gate and is re-probed on the next switch or
launch.

RegistryPairingAuthPortTest gains
successfulPairingReportsSignedInWhenTheProbeNeverAnswers: a MockEngine
that suspends until cancelled still lets the port return within the
bound on runTest's virtual clock, with the session intact and no verdict
recorded. The two existing probe-counting tests now run the port on a
real-clock dispatcher, because under the test scheduler the new
virtual-time bound fires before the engine thread can answer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…imes out

switchToServer bounds the contract probe to SWITCH_PROBE_TIMEOUT_MS so the
switch spinner stays responsive. When the target entry held a stale
UPDATE_REQUIRED verdict and /api/v2/system/info answered after that bound,
withTimeoutOrNull cancelled the only contract refresh; the background task
afterwards refreshed just the display name, so the gate kept rejecting v2
calls for the switched session until the next launch even though the
upgraded server would have answered moments later.

Keep the switch bounded, but when the bounded probe returns null and a
backgroundScope is wired in, launch a replacement unbounded probe (the
client's own timeouts still apply) pinned to the target serverId. The
existing active-id guard in recordServerContract drops the result if the
user switched again meanwhile. Without a scope (tests, single-server
hosts) behavior is unchanged. The launch path in MainActivity and
MainTvActivity already follows awaitContractRefreshIfUpdateRequired with
a fire-and-forget refreshActiveServerName(knownContract = null) that runs
a full unbounded probe, so it needs no replacement.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
refreshServerContractBounded mapped a transient probe Failure (connection
error, 5xx) to the non-null ServerContract.UNKNOWN. refreshServerContract
correctly recorded nothing for it, but both callers read any non-null
result as "verdict obtained": the launch path passed knownContract =
UNKNOWN into refreshActiveServerName, which then skipped its own probe,
and switchToServer skipped its background replacement probe. With a
stored UPDATE_REQUIRED and a server briefly unreachable at launch or
switch, the gate kept rejecting pilot calls for the whole session.

Make "no verdict" uniform: the bounded probe now returns null unless the
probe produced a real V2 or UPDATE_REQUIRED verdict, so a failure is
handled exactly like a timeout. The launch path (MainActivity,
MainTvActivity) already falls back to refreshActiveServerName(null),
which re-probes unbounded on lifecycleScope; switchToServer now launches
its background replacement for failures too. recordServerContract keeps
ignoring UNKNOWN. PairingAuthPort discards the return value and needs no
change.

Tests cover a 503 answer returning null and switchToServer's replacement
recording V2 once the server recovers, a connection failure on the launch
guard returning null, and real V2 / UPDATE_REQUIRED answers still
returning their verdict without a replacement probe.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…talls

Both pairing probe sites (the newly paired server and the rollback
restore) awaited refreshServerContractBounded() and discarded the
result, so a timeout or transient failure while re-pairing a server
that carried a stale UPDATE_REQUIRED left the session gated with no
replacement probe — unlike switchToServer, which already handed the
retry to the background scope.

Extract that "bounded probe, then background replacement on no
verdict" step into AuthRepository.refreshServerContractWithFallback
(serverId) and use it from switchToServer and both pairing sites, so
every path that activates a server shares one fallback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…failed

The connect path mapped a transient candidate-probe Failure through
toServerContract() into a non-null UNKNOWN and passed that to
setServerUrl once /api/v2/system/setup succeeded. refreshActiveServerName
neither records UNKNOWN nor re-probes when handed a known contract, so a
stale UPDATE_REQUIRED on the registry entry survived a successful v2
connection and kept gating the session.

A successful v2 setup answer is proof of v2 on its own, so both setup
view models now pass CONTRACT_PROVEN_BY_V2_RESPONSE (ServerContract.V2)
on the success path and keep the probe only for the UpdateServer early
return. The shared value is documented in AuthRepository.kt so the
reasoning lives next to toServerContract().

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
probeServerContract ran the candidate probe unbounded, so a server that
accepted the socket but stalled held the setup spinner for the client's
~60 s timeout per candidate (HTTPS then HTTP for a bare host) before
the setup call waited again.

Bound it with withTimeoutOrNull(SWITCH_PROBE_TIMEOUT_MS) behind a
timeoutMs parameter. A timeout returns null, which the connect path
already treats as "no verdict"; getSetupStatus carries the client's own
timeout and decides reachability, so no second bound is added there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On the first launch after upgrading the app, restored server entries
carry ServerContract.UNKNOWN, which passes ApiV2Gate. Authenticated
startup consumers (getCurrentUser and friends) then raced the background
probe and could receive raw v2 404s from a saved v1-only server before
UPDATE_REQUIRED was recorded; those one-shot loads do not retry.

Generalize awaitContractRefreshIfUpdateRequired into
awaitContractRefreshIfUnsettled: only a stored V2 skips the bounded
wait; UNKNOWN and UPDATE_REQUIRED both probe before routing. Both
activities keep passing the result as knownContract to the
fire-and-forget refreshActiveServerName, so a null (V2, timeout, or
failure) still re-probes off the critical path. No other callers used
the old name, so it is renamed rather than aliased.

Tests: UNKNOWN awaits and records UPDATE_REQUIRED from a v1-only
server; UNKNOWN awaits and records V2; V2 returns null with zero
requests; existing UPDATE_REQUIRED cases unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Removing the ACTIVE server on the phone promotes the next-MRU entry
inside the registry, and ServerListViewModel.onRemove re-probed it via
refreshActiveServerName in viewModelScope. Popping the server list
cancels that scope, so the promoted entry could keep UNKNOWN or a stale
UPDATE_REQUIRED for the rest of the session.

Route the promotion through AuthRepository.switchToServer(promotedId)
like the TV client already does. The registry already points at the
promoted id, so the switch is idempotent there:
EncryptedTokenManagerImpl.switchActiveServer returns early when the id
is unchanged (no credential-epoch bump, no cache reload), and the
bounded probe hands its replacement to the repository's process-lifetime
backgroundScope with the display-name refresh off the critical path.

Test: promoted server re-probe survives cancellation of the calling
scope (AuthRepositoryContractTest).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The server pilot branch paginated listAdminUsers, declared the statuses the
listener really emits, relaxed the profile quality/subtitle enums, and bound
listProgress cursors to the library-restriction flag (contract digest and
list_progress_ok.json next_cursor moved). SOURCE now pins
7bd3af1430962a8b6662510caffe49e2954c4055.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
listProgress let every page's client.get resolve the active auth scope
afresh, so a server or profile switch mid-walk sent later pages under a
different identity while `entries` kept the earlier pages. The server
binds the cursor to user + profile and answers 400 invalid_cursor, so
mixed results could not surface today, but the walk relied on that
server behavior and reported a confusing cursor error.

The walk now captures one AuthScopeSnapshot up front (or takes an
explicit `scope`, mirroring addFavorite/syncProgress) and pins every
page request with authScope(), so SiloAuthPlugin sends the same server,
profile, and credential slot on each page. Before each later page the
captured scope is compared with the live one via the new
AuthScopeSnapshot.isSameIdentityAs (extracted from
ProfileRepository.identityScopeUnchanged); a generation or epoch change
aborts with ApiResult.Error(error = "identity_changed") instead of
continuing. PersonalDataApi now takes the TokenManager from Koin; the
default null keeps single-scope test constructions unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ncel it

refreshServerContractWithFallback awaited the bounded probe in the caller's
coroutine and only then launched the unbounded replacement on
backgroundScope. A server-list viewModelScope cancelled by Back during the
bound propagated CancellationException out of the bounded call, so the
replacement never started and the switched-to server stayed UNKNOWN or a
stale UPDATE_REQUIRED for the session.

With a backgroundScope and probe wired in, the whole bounded-then-unbounded
sequence now runs in a job launched on backgroundScope before the caller
waits; the caller only awaits the bounded verdict through a
CompletableDeferred under its own withTimeoutOrNull. Cancelling the caller
cancels that await, not the probe. The active-id guard stays inside the
background job, and recordServerContract still drops verdicts for a server
that is no longer active. Without a scope the inline behavior is unchanged.

Tests cover a caller cancelled mid-bound whose late answer is still
recorded, and a caller cancelled with the server switched meanwhile that
records nothing. Three existing tests moved their background scope to a
real-clock dispatcher because the probe no longer runs in the caller.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick104 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T15:20:43.312454Z a22c27b New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 539 files, which is 439 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 92d5e9d7-0774-4251-a90d-e9184c9cb537

📥 Commits

Reviewing files that changed from the base of the PR and between 81d7a78 and a22c27b.

📒 Files selected for processing (539)
  • README.md
  • android-shared/schemas/org.siloserver.silo.common.data.db.SiloDatabase/10.json
  • android-shared/schemas/org.siloserver.silo.common.data.db.SiloDatabase/11.json
  • android-shared/schemas/org.siloserver.silo.common.data.db.SiloDatabase/9.json
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/SiloDatabase.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/dao/DirtyOperationDao.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/dao/MembershipProjectionDao.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/dao/ServerPurgeDao.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/dao/UserItemStateDao.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/entity/DirtyOperationEntity.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/entity/DownloadDeletionEntity.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/db/entity/MembershipProjectionEntity.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/repository/IdentityCacheKey.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/repository/RoomCatalogCacheRepository.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/repository/RoomDownloadDeletionStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/repository/RoomHomeCacheRepository.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/repository/RoomUserItemStateRepository.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/AndroidNotificationSyncStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/ContentProjectionRevert.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/MembershipOutbox.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/MembershipRuntime.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/OutboxOperation.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/RoomMembershipPort.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/data/sync/SyncEngine.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/di/PlayerInfraModule.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/di/PlayerModule.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/diagnostics/DiagnosticsUploadWorker.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/diagnostics/DiagnosticsUploader.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/diagnostics/DiagnosticsViewModel.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/diagnostics/PendingReportStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/downloads/DownloadEnqueuer.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/downloads/DownloadMetadataStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/downloads/DownloadSubscriptionEvaluatorFactory.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/downloads/DownloadWorker.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ebook/EbookLocalStateStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/network/ServerReachabilityMonitor.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/pairing/PairingAuthPort.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/pairing/PairingReceiver.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/AndroidPlaybackJournalStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/AuthenticatedDataSourceFactory.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/MediaAuthInterceptor.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/MediaAuthSession.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlaybackRecoverySettings.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlaybackSessionLifecycle.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlaybackSessionManager.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlaybackTeardownGate.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/PlayerOkHttpClient.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/SiloPlayerFactory.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/player/cast/CastPlaybackPreparer.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/settings/AndroidPlayerSettingsStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/settings/CardPresentationStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/settings/LibraryPlaybackPrefsStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/settings/PlayerSettingsStore.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/settings/ServerSettingsFlusher.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/startup/StartupWarmup.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ui/MembershipStatusBanner.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ui/components/ProfileAvatarSupport.kt
  • android-shared/src/androidMain/kotlin/org/siloserver/silo/common/ui/components/ThumbhashImage.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/db/SiloDatabaseMigrationTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/repository/PersonalWriteJournalTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/repository/RoomCatalogCacheRepositoryTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/repository/RoomHomeCacheRepositoryTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/repository/RoomUserItemStateRepositoryTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/sync/DormantMembershipTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/sync/MembershipActivationTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/sync/MembershipOutboxTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/sync/OrderedEbookProgressTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/data/sync/SyncEngineTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/diagnostics/DiagnosticsUploaderTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/downloads/DownloadByteOwnershipTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/downloads/DownloadMetadataStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/downloads/DownloadSubscriptionV2ReadTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/downloads/DownloadWorkerHttpStatusTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/ebook/EbookLocalStateStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/network/DurableLoginAuthorityTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/network/ServerReachabilityMonitorTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/network/WatchTogetherRealtimeWebSocketTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/pairing/PairingReceiverTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/pairing/RegistryPairingAuthPortTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/AndroidPlaybackJournalStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/AuthenticatedDataSourceFactoryTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/MediaAuthInterceptorTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/MediaAuthSessionTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackMetadataMutexTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackPublicationSettlementIntegrationTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackSessionLifecycleTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackSessionManagerSeekReanchorTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackSessionManagerStagedReplanTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/PlaybackStreamUrlV2Test.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/ProxyAuxiliaryDataSourceTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/SubtitleManagerTrackSelectionTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/cast/CastPlaybackPreparerTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/player/video/PlaybackReachabilityGateTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/settings/AndroidPlayerSettingsStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/settings/LibraryPlaybackPrefsStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/settings/OverlayPrefsStoreTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/settings/ServerSettingsFlusherTest.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/startup/StartupHomeV2Test.kt
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/ui/components/ProfileAvatarSupportTest.kt
  • androidApp/src/androidMain/AndroidManifest.xml
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/MainActivity.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/di/AndroidModule.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/downloads/AppWorkerFactory.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/push/AndroidPushRegistrar.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/push/AndroidPushTokenProvider.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/push/PushInstallationStore.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/CardActionsHelpers.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/MainAppTopBar.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/MediaRow.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/Skeleton.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/SwipeBack.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/components/TopBarActions.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/navigation/AppNavigation.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/navigation/BottomNavBar.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/navigation/SharedElementTransition.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/MainHeaderViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/MainScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/audiobook/AudiobookDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/InviteClaimScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/InviteClaimViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/LoginViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/ServerSetupViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/SetupViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/auth/SignupViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/book/BookDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/browse/BrowseScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/browse/BrowseViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/browse/CatalogGrid.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/browse/FilterSheet.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/calendar/CalendarScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/collections/CollectionDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/collections/CollectionDetailViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/DetailPageSurface.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/DetailScrollState.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/DetailSharedComponents.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/ItemDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/ItemDetailViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/MovieDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/SeriesDetailContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/detail/SimilarRail.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/downloads/DownloadsViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/home/HomeScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/libraries/LibrariesScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/notifications/InboxScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/onboarding/OnboardingTourViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/pairing/CompanionPairingBottomOverlay.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/people/PersonDetailScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/people/PersonDetailViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/personal/PersonalListControls.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/personal/PersonalMediaGridContent.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/LocalWatchMetadata.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/MobileSubtitleTransactionAdapter.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/MobileVideoPlaybackStarter.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/OnDeckItems.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerControls.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerOverlay.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerSettingsSheet.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerSheetSupport.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/player/ReadyWatchMetadata.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/ComicReader.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/PdfReader.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderEngineHost.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderFileCache.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/reader/reflow/ReflowableReader.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/search/SearchResults.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/search/SearchScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/search/SearchViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/servers/ServerListViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/SettingsScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/SettingsViewModel.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/settings/diagnostics/DiagnosticsReportScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/theme/Color.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/theme/PageBackdrop.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/push/AndroidPushRegistrarTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/push/PushInstallationStoreTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/auth/InviteClaimV2Test.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/auth/LoginV2ConsumerTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/auth/ServerSetupPersistenceTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/browse/CatalogLetterIndexViewModelTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/detail/MobileDetailActionsTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/downloads/DownloadStatusLabelTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/libraries/LibrariesViewModelTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/notifications/InboxFormattersTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/onboarding/OnboardingRecoveryTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/people/PersonDetailViewModelTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/people/PersonRefreshOwnershipTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/player/LocalWatchMetadataTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/player/OnDeckItemsTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/player/PlayerViewModelLoadOwnershipIntegrationTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/player/ReadyWatchMetadataTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionAdminGateTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionGridScopeTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/profiles/ProfileSelectionViewModelContractTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderFileCacheTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderFileResolverTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/ui/screens/reader/ReaderViewModelReaderTargetSourceTest.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/MainTvActivity.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/cast/RemotePlaybackIdentityManager.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/data/preferences/LegacyTvPrefsMigration.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/data/preferences/TvLibrarySelectionStore.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/di/AndroidTvModule.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/components/TvCardActionsHelpers.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/components/TvEpisodeCard.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/components/TvEpisodeTag.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/components/TvFocusMarqueeModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/components/TvMediaRow.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/navigation/TvAppNavigation.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvLoginViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvServerSetupViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/browse/TvBrowseScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/browse/TvBrowseViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/collections/TvCollectionDetailScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/collections/TvCollectionDetailViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvItemDetailViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/home/TvUpcomingViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryBrowseControls.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryCollectionDetailScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryCollectionDetailViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryDetailScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibraryDetailViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/notifications/TvInboxScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/people/TvPersonDetailScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/people/TvPersonDetailViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/personal/TvPersonalScreens.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvPlayerScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvPlayerSubtitlePolicy.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvPlayerViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvSubtitleTransactionAdapter.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvVideoPlaybackStarter.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/search/TvSearchScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/search/TvSearchViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/servers/TvServerListViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/TvSettingsViewModel.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/settings/diagnostics/TvDiagnosticsReportScreen.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/watchnext/WatchNextHomeSync.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/watchnext/WatchNextRepository.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/watchnext/WatchNextSeeder.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/watchnext/WatchNextSyncWorker.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/watchnext/WatchNextWriteGate.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/data/preferences/LegacyTvPrefsMigrationTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/testing/FakePlayerSettingsStore.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/components/TvFocusMarqueeModelTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvAuthSingleFlightTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvLoginViewModelRaceTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvServerSetupPersistenceTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/auth/TvServerSetupUrlProbeTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvItemDetailSubtitlePreferenceTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvMembershipConsumerTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvNextUpSelectionHandoffTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvPersonalMutationOwnershipTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/detail/TvSimilarCardsTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/home/TvUpcomingOwnershipTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/library/TvAudiobookCursorTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/library/TvCollectionCursorTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibrarySectionOwnershipTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/library/TvLibrarySubdestinationViewModelTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/notifications/TvInboxFormattersTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/people/TvPersonDetailViewModelTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/player/SubtitleTransactionIntegrationTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/player/TvStartupMetadataOwnerTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionAdminGateTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionGridScopeTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/profiles/TvProfileSelectionViewModelContractTest.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/watchnext/WatchNextHomeSyncTest.kt
  • docs/README.md
  • docs/api-v2/android-migration-status.md
  • docs/api-v2/android-push-registration.md
  • docs/auth-api-v2.md
  • docs/catalog-api-v2.md
  • docs/collections-api-v2.md
  • docs/ebook-reader-api-v2.md
  • docs/history-api-v2.md
  • docs/membership-api-v2.md
  • docs/notifications-api-v2.md
  • docs/playback/sequenced-api-v2.md
  • docs/requests-api-v2.md
  • docs/subtitle-ai-api-v2.md
  • scripts/sync-apiv2-fixtures.sh
  • shared/src/androidMain/kotlin/org/siloserver/silo/network/AndroidServerRegistry.kt
  • shared/src/androidMain/kotlin/org/siloserver/silo/network/EncryptedTokenManagerImpl.kt
  • shared/src/androidUnitTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2FixtureSupport.android.kt
  • shared/src/androidUnitTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2NoV1TransportSourceTest.kt
  • shared/src/androidUnitTest/kotlin/org/siloserver/silo/network/apiv2/EventsSocketLoopbackTest.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/di/NetworkModule.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/di/RepositoryModule.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/domain/ManagePlaybackUseCase.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/domain/MediaActionsCoordinator.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/domain/settings/ProfileSettingsController.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/metadata/DescriptionTranslationController.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/auth/AuthModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/auth/InvitationModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/calendar/CalendarModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/catalog/CatalogModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/download/DownloadModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/ebook/EbookConversionCapability.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/ebook/EbookReaderModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/feature/MetadataAiFeatureStore.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/feature/RequestsFeatureStore.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/metadata/MetadataAiModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/notifications/NotificationModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/notifications/PushRegistrationModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/personal/PersonalDataModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/playback/PlaybackProtocolV3.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/playback/PlaybackSubtitleChoices.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/playback/SubtitleTrackMerge.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/profile/ActiveProfileStore.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/recommendation/RecommendationModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/request/RequestModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/section/SectionModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/server/ServerContract.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/server/ServerEntry.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/settings/SettingValueModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/settings/SettingsModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/subtitles/SubtitleModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/model/watchtogether/WatchTogetherModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/AuthInterceptorImpl.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/AuthScopeSnapshot.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/DurableLoginAuthority.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/ExpectedMetadataOwner.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/HomeRealtimeClient.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/NotificationsRealtimeClient.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/PlaybackRealtimeClient.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/ServerRegistry.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/TokenManager.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/TokenManagerImpl.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/WatchTogetherRealtimeClient.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/AuthApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/AuthWireV2.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/BrandingApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/CalendarApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/CatalogApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/CollectionApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/DeviceLoginApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/DiagnosticsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/DownloadsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/EbookReaderApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/HealthApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/LibraryPlaybackPrefsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/MetadataAiApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/NotificationsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/OnboardingApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/PersonalDataApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/PlaybackApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/ProfileApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/PushRegistrationApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/RecommendationApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/RequestsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/SectionApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/SettingsApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/SubtitlesApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/api/WatchTogetherApi.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ApiV2Call.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ApiV2Enums.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ApiV2Gate.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ApiV2Models.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ApiV2Probe.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogDetailReadV2Models.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogV2Models.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/DiscoverV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/DownloadByteRequest.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/DownloadCreationV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/DownloadRegistryV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/EbookReaderV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/EventsSocketV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/HistoryV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/HistoryV2Models.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/HomeSectionsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/LibrarySectionItemsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/MembershipV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/NotificationsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/PersonRefreshV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/PlaybackV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/ProxyAuxiliaryV2.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SettingsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SimilarCardsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SubtitleAiCreateV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SubtitleAiReadsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SubtitleDownloadV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/SubtitleReadsV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/TasteProfileV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/UserLibrariesV2.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/WatchDetailV2Api.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/overlays/OverlayTypes.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/AuthRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/CalendarRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/CatalogRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/CollectionRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/DeviceLoginRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/DownloadsRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/EbookConfigSession.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/EbookReaderRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/MembershipActions.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/MetadataAiRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/NotificationSyncStore.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/NotificationsRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/OnboardingRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/PersonalDataRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/PlaybackRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/ProfileRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/PushRegistrationRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/RecommendationRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/SectionRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/SequencedPlayback.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/SettingsRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/SubtitlesRepository.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/CatalogCachePort.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/DownloadDeletionPort.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/HomeCachePort.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/MembershipPort.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/PersonalWrite.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/repository/port/UserItemStatePort.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/viewmodel/CalendarViewModel.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/viewmodel/HistoryViewModel.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/viewmodel/HomeViewModel.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/viewmodel/PersonalListViewModels.kt
  • shared/src/commonMain/kotlin/org/siloserver/silo/viewmodel/RecommendationsViewModel.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/domain/settings/ProfileSettingsControllerTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/metadata/DescriptionTranslationControllerTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/feature/MetadataAiFeatureStoreTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/notifications/NotificationModelsSerializationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/personal/PersonalDataModelsSerializationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/playback/PlaybackSubtitleChoicesTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/profile/ActiveProfileStoreTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/request/RequestModelsSerializationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/request/RequestPresentationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/model/watchtogether/WatchTogetherModelsSerializationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/NotificationRealtimeDecoderTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/PlaybackControlV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/PlaybackRealtimeClientTicketTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/RoomFrameDecoderTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/SiloAuthPluginPinTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/SiloAuthPluginProactiveRefreshHazardTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/SiloAuthPluginProactiveRefreshTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/SiloAuthPluginRefreshFailureTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/WatchTogetherRealtimeClientTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/AuthApiUnauthenticatedProbeTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/AuthV2OperationsTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/BrandingApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/CatalogApiQueryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/CatalogApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/DeviceLoginApiCandidateServerTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/DiagnosticsApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/HealthApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/InvitationV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/NotificationsApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/PersonalDataApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/PlaybackApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/PushRegistrationApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/SectionApiCollectionItemsTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/SettingsApiValuesTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/SubtitlesApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/api/WatchTogetherApiTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2ContractTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2FixtureSupport.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2NoFallbackTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ApiV2ProbeTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/CalendarV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/CatalogDetailReadV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/CatalogV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/CollectionsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/DiscoverV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/DownloadCreationV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/DownloadRegistryV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/EbookAnnotationsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/EbookReaderV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/EventsSocketV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/FreshDiscoveryProbeTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/HistoryV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/HomeSectionV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/HomeSectionsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/LibrarySectionItemsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/LibrarySectionListV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/MembershipV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/MetadataAiV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/NotificationsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/OnboardingV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/PersonRefreshV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/PlaybackMutationRoutingTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ProfileHouseholdV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/ProxyAuxiliaryV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/RequestsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SimilarCardsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SubtitleAiCancelV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SubtitleAiCreateV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SubtitleAiReadsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SubtitleDownloadV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/SubtitleReadsV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/TasteProfileV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/network/apiv2/UserLibrariesV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/playback/NextEpisodeV2ReadsTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/AuthRepositoryAccountReplacementTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/AuthRepositoryContractTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/AuthRepositoryServerNameTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/CalendarRepositoryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/CatalogRepositoryDetailCacheTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/DownloadsRepositoryOfflineDeleteTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/DownloadsRepositoryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/NotificationsRepositoryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/PersonalDataRepositoryCacheTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/PersonalDataRepositoryPortTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/PlaybackMetadataAdmissionTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/ProfileIdentityCommitTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/ProfileRepositoryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/ProxyAuxiliaryDecisionTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/SectionRepositoryCacheTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/SequencedPlaybackTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/repository/SubtitlesRepositoryTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/CalendarViewModelTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/HistoryViewModelV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/HomeDismissalV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/HomeScreenV2Test.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/HomeViewModelCacheIdentityTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/PersonalListViewModelGenerationTest.kt
  • shared/src/commonTest/kotlin/org/siloserver/silo/viewmodel/RecommendationsTasteOwnershipTest.kt
  • shared/src/commonTest/resources/api/v2/fixtures/SOURCE
  • shared/src/commonTest/resources/api/v2/fixtures/authentication_required.json
  • shared/src/commonTest/resources/api/v2/fixtures/fixtures.schema.json
  • shared/src/commonTest/resources/api/v2/fixtures/get_current_user_ok.json
  • shared/src/commonTest/resources/api/v2/fixtures/get_setup_status_ok.json
  • shared/src/commonTest/resources/api/v2/fixtures/get_system_info_ok.json
  • shared/src/commonTest/resources/api/v2/fixtures/index.json
  • shared/src/commonTest/resources/api/v2/fixtures/list_progress_offset_rejected.json
  • shared/src/commonTest/resources/api/v2/fixtures/list_progress_ok.json
  • shared/src/commonTest/resources/api/v2/fixtures/list_progress_profile_header_required.json
  • shared/src/commonTest/resources/api/v2/fixtures/not_acceptable.json
  • shared/src/commonTest/resources/api/v2/fixtures/not_found.json
  • shared/src/commonTest/resources/api/v2/fixtures/profile_verification_required.json
  • shared/src/commonTest/resources/api/v2/fixtures/rate_limited.json
  • shared/src/commonTest/resources/api/v2/fixtures/update_profile_null_not_clearable.json
  • shared/src/commonTest/resources/api/v2/fixtures/update_profile_ok.json
  • shared/src/commonTest/resources/api/v2/fixtures/validation_failed_body.json
  • shared/src/commonTest/resources/playback/v3/SOURCE
  • shared/src/commonTest/resources/playback/v3/native-decision_response.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@macroscopeapp

macroscopeapp Bot commented Sep 16, 2026

Copy link
Copy Markdown

Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting).

This review would cost an estimated $10.90, which exceeds your per-review limit of $10.00.

The top 3 files driving up this estimate:

File Diff Size Estimate
shared/src/commonMain/kotlin/org/siloserver/silo/repository/SequencedPlayback.kt 32.13KB $0.80
shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogDetailReadV2Models.kt 13.69KB $0.34
shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogV2Api.kt 11.55KB $0.29

Tip

To get this pull request reviewed, you can:

  1. Comment @macroscope-app on this PR to request a manual review (monthly spend limits still apply).
  2. Exclude the file(s) above from review by adding a pattern to your .macroscope/ignore.md — note that creating this file replaces Macroscope's built-in default ignores rather than extending them.
  3. Raise your cost limit in your workspace billing settings.

Turn off this reminder going forward

@macroscopeapp

macroscopeapp Bot commented Sep 16, 2026

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a 539-file API migration that substantially changes production networking, identity scoping, playback recovery, downloads, persistence migrations, notifications, and UI behavior. Its broad runtime, schema, and authentication-sensitive impact is beyond a low-risk refactor or bounded additive change.

Not approved because:

  • Per-review cost limit exceeded (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings, or comment @macroscope-app review this PR to bypass the limit and review now. You can add or adjust custom eligibility rules. Learn more.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01a6a92896

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Quick104 and others added 11 commits September 16, 2026 08:52
`main` replaced season and episode detail pages in place inside the browse
deck (#302) while this branch removed the deck and made item detail an
ordinary full-screen page. Keep the full-screen page and carry the in-place
series redirect onto it:

- The route holds the resolved series id, season, and episode id, feeds a
  keyed `ItemDetailViewModel` through `DEFAULT_ARGS_KEY`, and publishes the
  displayed identity so external links still compare against what is on
  screen rather than the original route argument.
- A pending redirect selects the loading branch of the detail crossfade, so
  the season or episode detail on its way out never dissolves into view.
- Drop `videoCastRequest`; `main` removed its only caller with the
  detail-page "Play on device" action. The overlay remote-control button
  this branch added keeps its own picker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both are unbounded-retention defects found by Codex on this PR.

`ActiveProfileStore` kept a cached profile across an identity change. The
session-expiry observer routes to Login without resetting this singleton, so
the next account to sign in and fail its first profile fetch drew the
previous user's name and avatar in the header. The cache now belongs to the
profile it was loaded for and is dropped the moment the active profile moves.

`SequencedPlayback` never pruned settled attempts. The journal is serialized
whole on every write and progress writes land every ten seconds, so each
finished playback made every later write larger than the last — retained
replan responses carry full subtitle inventories. Settled attempts are now
compacted to tombstones that keep only what still answers attempt-id fencing
and `owns`, capped at the eight most recent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DownloadWorker derived a client with followRedirects = false and closed it
in finally. Ktor propagates manageEngine through config {}, so that close
shut down the app-wide OkHttp engine and every later request failed until
restart. Use the shared client directly; redirects on the file route are
followed again and Ktor already strips Authorization across origins.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ocking the item

A failed or lost v2 personal write left a personal_uncertain outbox row that
nothing cleared, so every later watched or rating write for that item was
refused. These are idempotent desired-state writes: abandon the row on any
non-success and let a newer write supersede a stale v2 row. Legacy v1 rows
still block as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mutex

ProxyAuxiliaryRequestHeaders.isCurrent took SequencedPlayback's mutex, which
start, replan, progress and stop hold across their HTTP calls, and it runs
under runBlocking on a Media3 loader thread. Read volatile snapshots of the
auxiliary generation and live attempts instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A single 5xx or network error ended the flow with PollUncertain even though
the pending authorization was still valid. Retry on the poll interval; a
consumed code already terminates through the 404 branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rly return

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he inbox

Every non-close event ran a full refresh (unread count, first page, up to 20
sync pages). Fold created/read/readAll locally through applyEvent and reread
only on the connection snapshot. markRead no longer rereads either.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 045bfe0059

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread shared/src/commonMain/kotlin/org/siloserver/silo/network/api/ProfileApi.kt Outdated
Quick104 and others added 3 commits September 16, 2026 10:29
…URLs

Two tests still asserted the IllegalArgumentException that 1c5a9bf removed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
updateProfile was the only profile mutation that bypassed the captured-scope
exchange helper, so a PATCH overlapping a profile, account or server switch
could be sent under the new identity and reported as saved for the old one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ownership check

An identity change between completeWrite and the sidecar write left the
original owner's sidecar at downloading with a staged URI that had already
moved, and the terminal failure meant WorkManager never retried.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ce7be0988

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Quick104 and others added 2 commits September 16, 2026 10:48
The stub clients for catalog, downloads, reader, recommendations and
metadata ran on MockEngine's default dispatcher, so a response could land
on a real thread after Dispatchers.resetMain() and fail the test with a
DispatchException on CI. Pin them to the scheduler and cancel the view
models before Main is reset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…etry

The v2 file route sends one bare conflict problem for preparing, cancelled,
failed and revoked downloads alike, so the client cannot distinguish them.
Rewrite the comment that still described the v1 download_inactive code and
remove the unused v1 error-code extractor and its tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

state.copy(rows = merged, unreadCount = recomputeUnread(merged))

P2 Badge Preserve the server-wide unread count during event folds

When unreadCount comes from /unread-count, it can exceed the 25 notifications loaded into rows. Folding a Created or Read event recomputes the count from only those visible rows, so an account with 100 unread notifications can suddenly show at most 26 after one realtime event or local mark-read action. Apply an event delta to the existing total, or refresh the authoritative count, rather than replacing it with the partial-page count.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/CatalogV2Api.kt Outdated
Comment thread shared/src/commonMain/kotlin/org/siloserver/silo/network/apiv2/MembershipV2Api.kt Outdated
Quick104 and others added 3 commits September 16, 2026 11:13
Content ids may contain reserved path characters. The watch route already
encoded them; the catalog item, series, version, episode, favorites and
watchlist routes interpolated the raw id and split it into extra segments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion

A cross-device mark-all-read arrives as Invalidate because its signed cutoff
cannot be folded locally. Treat it like the connection snapshot and refresh.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The mock engine evaluates handlers on its own threads, so a request issued
just before an identity transition could be checked after it and fail the
owner assertion on CI. A request that carries an older generation is
expected to name the old owner.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Quick104
Quick104 merged commit a5c9329 into main Sep 16, 2026
6 checks passed
@Quick104
Quick104 deleted the apiv2-cleanup branch September 16, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant