Skip to content

fix: resumable update pause and phase-aware cancel#91

Merged
Wangnov merged 1 commit into
mainfrom
fix/update-pause-resume
Jun 18, 2026
Merged

fix: resumable update pause and phase-aware cancel#91
Wangnov merged 1 commit into
mainfrom
fix/update-pause-resume

Conversation

@Wangnov

@Wangnov Wangnov commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Why

The in-app update flow had four problems (reported from the macOS updater UI):

  1. 「正在准备」phase — pause and cancel were both disabled, with no way out if it stalled.
  2. Pause dumped the user back to home and the "再次更新会继续下载" promise never actually resumed — the partial download was deleted every time.
  3. No colour distinction between pause/cancel, and no 〔继续〕 button.
  4. After the download finished, pause/cancel were disabled with no explanation.

Root cause of (2): downloads staged into a per-run unique staging dir that staging.discard() wiped on pause — so the engine's .part resume (curl -C -) could never find it, and pause/cancel were effectively identical.

What changed

Backend

  • Downloads now stage into a persistent, URL-keyed cache (staging::download_cache_path, FNV-1a key for cross-toolchain stability) instead of a per-run dir, so a paused .part survives and the next run resumes from where it stopped. Success clears the cache; failure/pause keeps it; the stale sweep prunes abandoned partials. clear_download_cache now returns Result so a paused-state cancel only reports success once the partial is actually gone.
  • A drop-guarded abort latch (AbortGuard / WinAbortGuard) makes the preparing phase cancellable. It resets the latch on op exit, never at entry — so a cancel racing the op's startup (the cancel command holds no op lock) isn't wiped, and the next op starts clean. Abort checkpoints sit before every destructive step and on the cache-hit path; perform/install/stage each own the guard so a cancel can't leak into the next operation.
  • New mac_discard_download / win_discard_download commands clear a paused partial when the user cancels from the paused state.

Frontend (Home.tsx + WinHome.tsx, symmetric)

  • Paused state: the progress screen stays up showing the paused byte count + a 〔继续〕 (primary) button that resumes; cancel is danger-styled and discards.
  • Cancel stays usable through preparing; the download-complete install phase shows 「正在安装,请勿关闭」 instead of dead, greyed buttons.
  • 4 new progress keys across all 11 locales; a pause glyph for the ring.

Verification

  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace ✅ (50 tests, incl. abort-guard race-free invariants for mac + win)
  • npm run check / npm run test (41) / npm run build
  • Reviewed with codex review --uncommitted, iterated to no comments (5 rounds — it surfaced a startup-window race, abort blind spots, latch residue across stage paths, and a cache-hit cancel window, all now closed).

Windows target can't be cross-compiled locally (ring C deps); win_update.rs is still type-checked + clippy'd + unit-tested on the macOS host, and the Windows CI matrix builds it.

Downloads now stage into a persistent, URL-keyed cache instead of a
per-run staging dir, so the engine's `.part` resume survives a pause —
pausing keeps the progress screen up with a 〔继续〕 that picks up from
the byte it stopped at, and cancel discards the partial. The cancel
button is danger-styled and usable through the preparing phase, while a
finished download shows "正在安装,请勿关闭" instead of dead, unexplained
buttons. Fixes the old behavior where pause dumped the user back to home
and "再次更新会继续下载" never actually resumed.

Backend:
- staging: persistent download cache (FNV-1a key, toolchain-stable) +
  Result-returning clear; stale sweep prunes abandoned partials
- mac/win: a drop-guarded abort latch (reset race-free on op exit, never
  at entry) makes the preparing phase cancellable; abort checkpoints sit
  before every destructive step and on the cache-hit path; perform/
  install/stage each own the guard so a cancel can't leak to the next op
- new mac_discard_download / win_discard_download commands clear a paused
  partial when the user cancels from the paused state

Frontend:
- paused state with resume + danger cancel + "finishing" install copy;
  discard wiring that only reports cancelled once the partial is gone
- 4 new progress keys across all 11 locales; a pause glyph for the ring
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Wangnov Wangnov merged commit b4b0eae into main Jun 18, 2026
5 checks passed
@Wangnov Wangnov deleted the fix/update-pause-resume branch June 18, 2026 06:39
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