Skip to content

uploader: resume a session interrupted before it ever reached publish - #29

Open
Chris-Schnaufer wants to merge 5 commits into
CulverLab:mainfrom
Chris-Schnaufer:fix/resume-before-bundle
Open

uploader: resume a session interrupted before it ever reached publish#29
Chris-Schnaufer wants to merge 5 commits into
CulverLab:mainfrom
Chris-Schnaufer:fix/resume-before-bundle

Conversation

@Chris-Schnaufer

Copy link
Copy Markdown
Contributor

A streamed upload cancelled before its blob queue closed (e.g. navigating to History mid-upload) has no bundle yet — resumeUpload used to flatly refuse and tell the user to start a fresh upload, but a fresh upload stamps a brand-new destination prefix and doesn't skip anything already sitting under the abandoned one, wasting a full re-upload of a large batch.

  • resume.ts's new ensureBundle: for a bundle-less session, resolves every still-awaiting-processing record from the same re-hash pass reconciliation already does (reconcileReselect/restoreFromHandle now expose full Inspect-equivalent results, not just sha256), persists them, and builds+attaches the bundle — reusing the original persisted uploadPrefix verbatim, never a new stamp. A file whose hash couldn't be resolved, or whose only capture-time source was a manual Assign entry (unrecoverable here), surfaces as a blocking problem instead of silently publishing incomplete metadata.
  • bundle.ts: extracted namingForUploadPath and added buildBundleFromRecords (bundle payloads from persisted record metadata alone).
  • db.ts: added updateFileRecords for one bulk write instead of one transaction per file.
  • History.tsx: all three resume entry points call ensureBundle first.
  • upload.ts: resumeUpload's "no bundle" error is now a rare fallback, reworded since it no longer tells the user to start a fresh upload.
  • New tests: naming reconstructed from persisted records matches the original run exactly (including a collision case), and buildBundleFromRecords emits a valid bundle.

Verified live against a real interrupted 17,952-file batch — resume now completes instead of erroring out.

Depends on #27 and #28 (both not yet merged) — this branch is based on top of fix/resume-large-batch-verify-progress, which is based on fix/resume-permission-lost-user-activation.

Chris-Schnaufer and others added 5 commits August 10, 2026 15:18
beginResume awaited loadSession(batch.id) — an IndexedDB read — before
touching the folder handle's requestPermission(), the native directory
picker, or the hidden <input> fallback's .click(). Firefox and Safari
require those to fire within the click's transient user-activation window;
an unrelated await ahead of them silently breaks it, so Resume appeared to
do nothing: no prompt, no error, no network activity.

Kick the session load off in parallel instead of awaiting it up front, so
the gated call is always the first await in the handler.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…esume

Resuming re-hashes every recorded file's content against the reselected
folder before anything else happens (reconcileReselect -> hashAll), with no
feedback on screen while that runs. For a batch of thousands of files this
takes real, noticeable time, and looked exactly like Resume doing nothing.
Root-caused live: a 17,952-file resume reached the re-hash step and simply
had no further observable state change until it finished.

Thread an optional, ~150ms-throttled onProgress callback from hashAll up
through reconcileReselect and restoreFromHandle, and surface it in History
as "Verifying N of M files against the original folder…".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Was a page-level note above the whole list; move it into the specific
batch's row (next to its Resume/Discard buttons), and label the button
"Verifying…" during that phase — matches "Resuming…" for the run itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ounts

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A streamed upload cancelled before its blob queue closed (e.g. navigating
to History mid-upload) has no bundle yet — resumeUpload used to flatly
refuse and tell the user to start a fresh upload, but a fresh upload stamps
a brand-new destination prefix and doesn't skip anything already sitting
under the abandoned one, wasting a full re-upload of a large batch.

Add resume.ts's ensureBundle: for a bundle-less session, resolve every
still-`awaiting-processing` record from the same re-hash pass reconciliation
already does (reconcileReselect/restoreFromHandle now expose full Inspect-
equivalent results, not just sha256), persist them, and build+attach the
bundle — reusing the original persisted uploadPrefix verbatim, never a new
stamp. A file whose hash couldn't be resolved, or whose only capture-time
source was a manual Assign entry (unrecoverable here — never persisted for
a file that hadn't finished Inspect), surfaces as a blocking problem instead
of silently publishing incomplete metadata.

- bundle.ts: extract namingForUploadPath (same sanitize/collision logic as
  resolveBatchNaming, against a fixed prefix instead of a fresh stamp) and
  add buildBundleFromRecords (bundle payloads from persisted record
  metadata alone, no file bytes needed).
- db.ts: add updateFileRecords for one bulk write instead of one
  transaction per file.
- History.tsx: all three resume entry points call ensureBundle first.
- upload.ts: resumeUpload's "no bundle" error is now a rare fallback;
  reworded since it no longer tells the user to start a fresh upload.
- New tests: naming reconstructed from persisted records matches the
  original run exactly (including a collision case), and
  buildBundleFromRecords emits a valid bundle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@Juli4nG Juli4nG left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really like this one. Three things before merge:

  1. ensureBundle only checks capture times on files that never finished Inspect. A no-EXIF file that did finish gets saved with an empty time, slips through, and publishes an empty timestamp column. Anything missing a time should block, not just the unfinished ones.
  2. If ensureBundle fails, the tile stays stuck on "Verifying…".
  3. This writes an empty observations.csv but #17 changes that to one row per file. Whichever merges second has to match the other. Left the same note there.

(reviewed with fable 5 + gpt-5.6-sol)

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.

2 participants