Skip to content

feat(sandbox): resilient image downloads with progress and cancel - #208

Merged
quickbeard merged 2 commits into
mainfrom
image-download-resume
Aug 13, 2026
Merged

quickbeard merged 2 commits into
mainfrom
image-download-resume

Conversation

@quickbeard

@quickbeard quickbeard commented Aug 13, 2026 •

Copy link
Copy Markdown
Owner

Problem

Creating a sandbox on an unstable connection fails with Error invoking remote method 'sandbox-add': TypeError: terminated. The storage host cuts long transfers at arbitrary points, and stageAsset had no retry: the whole ~800 MB download restarted from zero on every Create click, and each failed attempt leaked an orphaned partial-<uuid> file in sandbox-images/ (observed: 25 MB, 203 MB, and 673 MB orphans from three attempts). On top of that, the Add sandbox dialog gave no feedback beyond a spinner during the download, its Cancel button was disabled the whole time, and the dialog body had no horizontal padding.

Download resume (commit 1)

  • Resumable, content-addressed partials — in-flight downloads live at <cacheDir>/<sha256>.partial; an interrupted download resumes via Range: bytes=<offset>- (the storage host advertises accept-ranges: bytes), both across retries within one call and across separate Create attempts.
  • Stall-based retry budget — give up only after 5 consecutive attempts with no new bytes; progress resets the counter, so a flaky link can finish a large image through many resumed connections. Linear backoff, abort-signal aware.
  • Correct restart handling — a 200 that ignores the range truncates and restarts; a 416 discards the partial and restarts; a clean-but-short stream fails verification immediately (the server's file genuinely differs from the manifest) instead of retrying forever.
  • Partial cleanup — every staging run sweeps partials belonging to other digests, including the legacy partial-<uuid> orphans; verification failure still deletes the partial. Verification now hashes in a separate pass after download (required for resume).

Progress + cancel UI (commit 2)

  • Progress bar with elapsed size — stageAsset reports throttled {bytes, totalBytes} progress (at most every 200 ms), threaded through every provider's create/reset into the controller's job state and over the existing sandbox-event push; the dialog renders a progress bar plus 123/761 MB.
  • Cancel while downloading — the Cancel button stays enabled and aborts the in-flight job over a new sandbox-cancel IPC channel. A cancelled create resolves to undefined: nothing is persisted or started and the dialog stays open (previously an aborted create still registered the sandbox — fixed); a cancelled reset stays stopped instead of starting from a half-reset image. The kept partial means the next attempt resumes where the cancelled one stopped.
  • Dialog margins — the dialog body gets px-4 to match the 16 px padding the header and footer already had.

Testing

  • image.test.ts: mid-stream cut → range resume, resume of a pre-existing partial, stall give-up preserving the partial, 416 restart, stale-partial sweep, progress reporting (initial 0, final total, monotonic).
  • controller.test.ts: job progress events, cancel aborts + persists nothing, cancel for a different sandbox is a no-op.
  • bun test and bun typecheck clean in packages/codev-sandbox (84 tests) and packages/app (657 tests); bun typecheck clean in packages/desktop and packages/ui.
  • Fork-only package + fork feature UI, so no DIVERGENCES.md rows; CHANGELOG.md updated.

🤖 Generated with Claude Code

Quickbeard and others added 2 commits August 13, 2026 10:40
…ials

The storage host cuts long transfers at arbitrary points, so the
multi-hundred-MB image download used to die with undici's
"TypeError: terminated" and start over on every create attempt,
leaking an orphaned partial-<uuid> file each time.

stageAsset now stages in-flight downloads content-addressed at
<cacheDir>/<sha256>.partial and resumes them with HTTP range requests,
retrying until five consecutive attempts make no progress. The partial
survives failures and aborts so the next create continues where the
last one stopped; verification failures still delete it, and every run
sweeps partials that belong to other digests, including the legacy
partial-<uuid> orphans.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Add sandbox dialog used to show only a static "Creating..." while
the multi-hundred-MB image downloaded, with Cancel disabled and no way
out but waiting or an error.

stageAsset now reports throttled {bytes, totalBytes} progress, threaded
through every provider's create/reset into the controller's job state,
so the dialog renders a progress bar with downloaded/total MB. A new
sandbox-cancel IPC channel aborts the in-flight job: a cancelled create
persists and starts nothing (previously an aborted create still
registered the sandbox), a cancelled reset stays stopped instead of
starting from a half-reset image, and the kept partial means the next
attempt resumes. The dialog body also gains the horizontal padding the
header and footer already had.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard quickbeard changed the title fix(sandbox): resume interrupted image downloads and sweep stale partials feat(sandbox): resilient image downloads with progress and cancel Aug 13, 2026
@quickbeard
quickbeard merged commit e2d0416 into main Aug 13, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the image-download-resume branch August 13, 2026 04:05
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