Appearance
Signing out no longer leaves the previous account's holdings visible. Holdings and collection-bin snapshots use an owner-specific namespace; switching owners remounts private UI state, including navigation and stale drafts. Session refresh does not remount the account or clear a guest's scanning workflow.
Legacy signed-in data is moved into its proven owner's namespace before an account switch. Legacy data found after an earlier sign-out has no reliable owner: it is preserved under an unassigned backup key, not imported into the next login. Local read/modify/write operations are serialized to prevent concurrent additions from overwriting each other. No account records are merged or reassigned.
Incident diagnosis
The global local ledger, manual-only backup and sign-out retaining that ledger predate secure-session PR 684. A read-only production check found one account among the three address spellings in the report, with no active or deleted collection rows and no collection events. This establishes that the reported cards were not present in the server ledger at inspection time; it does not recover data erased by uninstalling an app before its first server backup.
Credentials, addresses, raw identifiers and collection contents are omitted. Different email spellings are not evidence that accounts belong together.
Verification
Eight data integration cases cover owner boundaries, legacy preservation, concurrent writes, credential-write failure and private bin snapshots. Six mounted React Native tests cover account changes, sign-out, stale reads, anonymous account minting, email claim and session refresh. At PR creation: lint and typecheck passed; 2,609 Jest tests passed with 11 existing skips. CI passed.
The Maestro sign-out/restart flow is committed but was not executed locally: the Maestro CLI is unavailable. No production writes, merge, deployment or emulator reset is part of these verification results.
Automatic acknowledged server writes are tracked separately in mobile issue 730 and backend PR 495. This isolation change alone is not a server-backup fix.
Autosave companion
The companion branch fix/account-collection-autosave adds server-first writes for linked accounts, with an owner-scoped durable retry journal. Additions, reductions, soft deletions, restores, annotations and favourites use the backend's atomic mutation endpoint. Lost responses retain the same operation ID; definitive rejections do not permanently block the next valid operation. Acknowledged writes update the account's local cache and successful-sync timestamp.
The first owner-cache import preserves a raw backup and existing favourites; certified slabs already on the server retain their server identity. Subsequent reads use the server as authority, including deletions from another device. Invalid snapshots reject without silently showing a false empty collection. Concurrent view reads share a short owner-bound cache; explicit refresh and a new installation fetch server state. Manual backup uses this same path for linked accounts instead of attempting another two-way upload.
At final local verification, 2,636 mobile tests passed with 11 existing skips, and lint and TypeScript checks passed. Twenty-three API-client integration journeys use real app collaborators with an HTTP fixture; they are not PostgreSQL tests. Backend PR 495 separately passed 29 real PostgreSQL integration tests and all six CI jobs. Rollout requires the API first, then an in-place mobile update. No installation reset is required or authorized by the correction itself.
The import freezes its initial server baseline before writing, so interruption cannot produce a new plan that mistakes its own saved item for an unrelated one. Old sealed/slab data without a reliable identity and with a matching server copy requires manual reconciliation: import stops before any server write and preserves the raw local cache and backup, rather than guessing or duplicating a physical item. The original shared signed-out cache similarly remains unassigned until ownership can be established. These are deliberate recovery limits, not discarded data.
Additional Red/Green regressions cover immediate reads after uncertain writes, subscriber notification after both replay paths, and stale reads across logout and relogin even with the same credential. Account revision guards also cover migration metadata: an obsolete initial GET must not seed a duplicate later import.
PR 729 was merged into staging on 2026-10-17 as 45097db. The autosave companion is PR 731, rebased onto that merge with an unchanged source tree and ten changed files. The complete local gates passed again: 2,636 tests, 11 existing skips, zero lint problems and a clean TypeScript check. GitHub CI and deployment are verified separately before distributing the in-place Android update.
PR: https://github.com/snag-tcg/poc-tcg-collector/pull/729
Autosave PR: https://github.com/snag-tcg/poc-tcg-collector/pull/731
Staging Rollout
After explicit approval, mobile PRs 729 and 731 were merged into staging on 2026-10-17 (45097db and 0050c42). Companion CI run 39051357559 passed. Backend PR 495 was merged as f855c67; its post-merge staging CI run 39051407976 also passed. Backend documentation PR 243 was merged as cff88f2.
The API was deployed without a database migration. Its readiness endpoint returned ok: true, and the public mutation endpoint returned HTTP 401 to an unauthenticated POST, confirming that the new route is present and remains protected. These probes did not create or change any user's collection.
The Android x86_64 release build succeeded. Its source tree is identical to mobile staging 0050c42; inspection of the bundled JavaScript confirmed account-scoped storage, the mutation endpoint and durable import metadata are included.
The initial in-place adb install -r attempt was refused by Android for insufficient internal storage before an installation session was created. The installed app's first-install and last-update timestamps remained unchanged at that point.
Android Delivery Follow-up
After explicit approval, the stopped emulator was backed up outside all repositories: 71 files, about 11.46 GiB, including encryption metadata and snapshots. All copied file sizes matched, as did SHA-256 hashes for the five essential disk/configuration files. The working virtual disks and AVD data-partition configuration were enlarged from 6 to 12 GiB. The old quick-boot snapshot remains in the backup; its working copy was removed to permit disk growth, followed by a clean QCOW2 consistency check.
The Google Play production image does not expose the maintenance access needed to grow the encrypted, mounted ext4 filesystem. Android still reports approximately 5.8 GiB of usable filesystem capacity. The larger virtual disk must not be confused with a completed filesystem expansion. No system-image replacement or data format was performed. Android's cache-trimming command found no additional space to reclaim.
The remaining free space was sufficient for the approximately 92 MiB APK, but below Android's normal installation reserve. That reserve was temporarily lowered only for the in-place update, then restored to its exact original settings in a finally block. The final installation used adb install -r --no-streaming to transfer the complete APK before installation. No uninstall, app-data clear or account reset was performed. The original application UID, data-directory identities and first-install timestamp were preserved. These metadata checks establish installation continuity; they do not establish equality of collection contents or replace account-login tests. The launcher resolved successfully and the application started after a complete, graceful Android reboot. The temporary maintenance console was closed before the normal emulator restart. Public API readiness also remained HTTP 200 with ok: true.
The destructive account sign-out E2E flow was not run on the personal installation.