Skip to content

fix(version-switch): refuse vanished targets#1576

Merged
danielcopper merged 3 commits into
mainfrom
fix/1570-version-switch
Jul 24, 2026
Merged

fix(version-switch): refuse vanished targets#1576
danielcopper merged 3 commits into
mainfrom
fix/1570-version-switch

Conversation

@danielcopper

@danielcopper danielcopper commented Jul 24, 2026

Copy link
Copy Markdown
Owner

What this is

PR 4 of #1570. A version can disappear after the picker loads but before the user selects it. The backend now performs a fresh exact-id availability check immediately before moving a shortcut binding onto an already-local target.

A definitive target 404 refuses the switch. Temporary uncertainty still fails open so the offline Switch anyway path remains usable and bounded by one short attempt.

What changed

  • Local targets receive exactly one executor-backed get_rom_once(target_rom_id) after all non-writing guards pass and immediately before the binding-write path.
  • Only RommNotFoundError returns:
    {success: false, reason: "version_vanished", message: "This version is no longer available on RomM."}
  • Timeout, transport, authentication, 5xx, malformed, empty, and wrong-id probe responses log uncertainty and fail open.
  • allow_stranded bypasses only the save-stranding guard; both Sync now & switch and Switch anyway retries still receive the final liveness check.
  • Server-only targets keep their mandatory full-detail fetch. Its typed 404 maps to the same version_vanished refusal; other failures remain classified normally.
  • A refusal leaves the binding, appId, launch options, artwork, cache, collections, playtime, rows, installs, saves, sync state, and completion stamps untouched.
  • All three frontend switch paths use the same failure handling. Only explicit server_unreachable updates global connection state.
  • Switch success no longer claims that RomM is connected when the optional probe may have failed open.
  • A vanished-target refusal reloads the picker directly without emitting version_switched.
  • All list loads share latest-request-wins publication, so a late refusal reload cannot overwrite a newer successful switch, another app's state, or a newer connection verdict.

The active-target no-op is not probed, and no network request is held inside a SQLite unit of work.

Safety

The check narrows the stale-list window but does not pretend RomM and SQLite share a transaction. RomM can still delete an id after a successful response; retained local identity and the PR-3 recovery path keep that state recoverable.

No library sync or fetch-stamp invalidation is introduced.

Docs

Updated backend architecture, Steam shortcut architecture, and the managing-games user guide. Frozen ADR history remains unchanged.

Verification

The full mise run gate passed on the implementation state:

  • 6395 backend tests
  • 2267 frontend tests
  • Ruff and basedpyright clean
  • TypeScript, ESLint, Prettier, Deno, build, size limit, import contracts, and repository invariant checks passed

Review fixes then passed 100 focused backend/contract tests, 57 frontend tests, changed-file static/type/format checks, and an independent review with no remaining findings.

On-device verification

Passed in Game Mode with a safely staged local binding: a retained id that RomM answers with 404 was temporarily bound to the existing Steam appId, remained visible and disabled, and returned an honest not_found response on Download. Switching back to its live local sibling succeeded through the new pre-write target probe; the appId stayed stable, RomM was not reported offline, the picker guard released, and the database binding returned to the live id. No RomM data was deleted for the test.

Closes part of #1570.

@github-actions github-actions Bot added the area:ui Frontend UI, game detail page label Jul 24, 2026
@sonarqubecloud

Copy link
Copy Markdown

@danielcopper
danielcopper merged commit ee40268 into main Jul 24, 2026
20 checks passed
@danielcopper
danielcopper deleted the fix/1570-version-switch branch July 24, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui Frontend UI, game detail page

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant