Skip to content

fix(desktop): keep a verified CUDA torch, and repair a missing torchvision - #895

Merged
thcp merged 1 commit into
big-releasefrom
fix/b24-torch-swap-repair
Nov 3, 2026
Merged

thcp merged 1 commit into
big-releasefrom
fix/b24-torch-swap-repair

Conversation

@thcp

@thcp thcp commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Three small repairs to how the desktop shell swaps torch for a CUDA build and checks torchvision on launch. Builds on #891.

What changes for the user

  • A CUDA install that verified stays put. After a good CUDA swap, the shell deletes the backup of the bundled CPU torch. If that delete stopped part way (a file held open, for example by an antivirus scan), the half-deleted backup still had its manifest.txt, so the next launch treated it as an interrupted swap and restored half a CPU torch over the working CUDA one. The commit now removes manifest.txt first, so a delete that stops part way leaves nothing that counts as a backup. The next snapshot clears that manifest-less leftover before it moves anything, so a stale entry cannot block the move or be restored later.
  • A damaged RECORD or manifest cannot point outside its folder. owned_entries takes the first path segment of each line in torch's RECORD and kept it if site.join(name).exists(). On Windows that is true for names that are not entries at all: C: (from a line such as C:\x,,) joins to that drive's current directory, and ... or . are trimmed to site-packages itself. discard_installed would then delete them. A name is now kept only if read_dir(site) lists it, which never returns any of those. restore uses the same rule for the names in manifest.txt, matched against read_dir of the backup, because a manifest written by an older build from such a RECORD could hold them.
  • A missing torchvision is put back. On a CPU torch install, the launch check only repaired a CUDA torchvision; a torchvision that was gone (a pip run that stopped between uninstall and install) was reported as healthy, and the karaoke split failed at import every time. A missing torchvision/version.py beside +cpu torch now gets the same CPU torchvision install. A version.py that exists but cannot be read is still left alone, so a healthy offline machine is not sent to the network on every launch. A CUDA torch is not touched.

How it was verified

  • Red then green on Windows, cargo test:
    • torchswap::tests::a_commit_that_stops_part_way_leaves_no_backup_to_restore (Windows only: it holds a backup file open with share_mode(0)). Before: assertion failed: !has_backup(&site). With only the commit change, the second snapshot failed on the leftover. After: passes.
    • torchswap::tests::a_record_line_naming_no_entry_in_site_is_not_owned. Before: owned entries included "C:", "...", ". " and ".. ". After: passes. The test only reads, so running it against the old code was safe.
    • torchswap::tests::a_manifest_line_that_names_no_entry_is_ignored (manifest lines ... and . ). Before: restore removed the test's site-packages and then failed. After: passes, CPU torch and the neighbour package are intact. It works in a temp dir, so the red run could only delete that.
    • tests::a_missing_torchvision_beside_cpu_torch_is_repaired and the extended tests::the_repair_decision_reads_the_installed_tree. The helper's signature changed to take Option<&str>, so the old code cannot compile these; the old behaviour is visible in the removed let (Some(torch), Some(vision)) = ... else { return "ok" }.
  • The manifest test has no C: line on purpose: against the old code it would make restore delete from the drive's current directory, so a red run is not safe on any machine. The same read_dir rule covers it.
  • Full cargo test: 209 passed, 0 failed, 2 ignored. cargo fmt --check and cargo clippy --tests -- -D warnings clean.
  • No Python, frontend or i18n change. No new control, so no Guide entry.

Update path

  • This changes the shell binary only. uv.lock is unchanged, so the runtimeId gate is not affected and existing installs receive it through the in-app update.

What I have to check by hand

On a Windows machine with an NVIDIA GPU, using a throwaway install of the desktop build:

  1. Run setup so the CUDA torch installs and verifies. Confirm setup.log says verified and that .stemdeck-torch-backup next to site-packages is gone.
  2. On a CPU install, delete site-packages/torchvision and torchvision-*.dist-info, then launch. Confirm setup.log says torchvision is missing beside torch ...; installing torchvision==...+cpu, and that the karaoke split works afterwards.
  3. Launch again and confirm no second repair runs.

…ision

A torch swap commit deleted the backup tree with the manifest still in
it, so a delete that stopped part way (a file held open) left a backup
that the next launch restored over the verified CUDA install. The
manifest is now removed first, and the next snapshot clears a
manifest-less leftover before moving anything into it.

owned_entries and restore kept RECORD and manifest names that exist()
accepts but that are not entries: on Windows "C:" joins to a drive's
current directory and "..." or ". " are trimmed to site-packages
itself, which discard_installed or restore would then delete. Names
are now matched against read_dir instead.

The launch check reported a missing torchvision beside +cpu torch as
healthy, so a pip run that stopped between uninstall and install left
the karaoke split broken for good. An absent version.py now gets the
CPU torchvision install; an unreadable one is still left alone.
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