fix(sw): whoever promotes the waiting worker owns the reload - #199
Closed
chaxus wants to merge 2 commits into
Closed
fix(sw): whoever promotes the waiting worker owns the reload#199chaxus wants to merge 2 commits into
chaxus wants to merge 2 commits into
Conversation
CI kept failing the @serial silent-update case with a 90s timeout, and the screenshot was a blank white page. The trace says why: the editor iframe's own document request came back -1, and nothing retries it. Activating a worker terminates the outgoing one, and every fetch it still had in flight fails -- including that iframe navigation. That is survivable only because a reload follows. The reload did not follow. promoteWaitingWorker runs the moment register() resolves, which on the editor route is a few hundred milliseconds before the editor instance exists. It therefore sees "no document open" and promotes. By the time controllerchange arrives the document is open, shouldReloadOnControllerChange asks the same question again, gets the other answer, and refuses -- with the page already torn in half. wireServiceWorkerUpdates was dropping promoteWaitingWorker's return value, so the page never knew it had asked for the swap itself. It now reports it. index.ts keeps one flag for both promotion paths (the ordinary update and the stale-build heal), renamed promotedFromThisTab, and a tab that asked for the swap reloads regardless of what is open -- unsaved edits still veto. Refusing the reload does not undo the swap; it only leaves the reader on a blank editor. Reverse-verified: stashing lib/sw-update.ts and index.ts turns all four new unit cases red. sw-silent-update and sw-warm pass locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chaxus
enabled auto-merge (rebase)
August 23, 2026 07:24
Deploying document with
|
| Latest commit: |
fa8dd65
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://4068b206.document-7hm.pages.dev |
| Branch Preview URL: | https://sw-promote-reload.document-7hm.pages.dev |
The first half of this change turned every promotion into a reload, which the Cloudflare Pages E2E caught: autosave-recovery's reload case went red on the shard and stayed red on retry, while the same shard was green on a branch without it. CLAUDE.md already says why. A worker waiting is not a new version: the vendored editor registers a worker of its own into this scope from inside the iframe, so the scope's script alternates and ours sits in `waiting` on almost every editor load with no new build anywhere. "Refuse the reload when a document is open" was doubling as the brake on that. Removing it made those routine swaps reload the page every time. So the other half: a page that is about to open a document does not promote at all. hasOpenDocument() reads the store, which fills a few hundred milliseconds after register() resolves -- the URL knows sooner, and ?new= / ?file= / ?src= / ?open= / ?saved= all say a document is coming. Embed mode counts too, and for a stronger reason: the host can push a document at any moment, and the reload would throw away something the host page owns. A worker left waiting on those routes is the case the silent heal exists for, and that path checks isUnseenBuild() first, so the vendor's worker shuffling cannot fool it. Reverse-verified: stashing lib/sw-update.ts turns the three new documentIsExpected cases red. autosave-recovery (all three), sw-silent-update and sw-warm pass locally -- the reload case being the one that was red with only the first half. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Author
|
Superseded by #201, which carries these commits unchanged alongside the structured-data work. |
auto-merge was automatically disabled
August 23, 2026 07:45
Pull request was closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CI kept failing the @serial silent-update case with a 90s timeout, and the
screenshot was a blank white page. The trace says why: the editor iframe's own
document request came back -1, and nothing retries it.
Activating a worker terminates the outgoing one, and every fetch it still had
in flight fails -- including that iframe navigation. That is survivable only
because a reload follows. The reload did not follow.
promoteWaitingWorker runs the moment register() resolves, which on the editor
route is a few hundred milliseconds before the editor instance exists. It
therefore sees "no document open" and promotes. By the time controllerchange
arrives the document is open, shouldReloadOnControllerChange asks the same
question again, gets the other answer, and refuses -- with the page already
torn in half. wireServiceWorkerUpdates was dropping promoteWaitingWorker's
return value, so the page never knew it had asked for the swap itself.
It now reports it. index.ts keeps one flag for both promotion paths (the
ordinary update and the stale-build heal), renamed promotedFromThisTab, and a
tab that asked for the swap reloads regardless of what is open -- unsaved
edits still veto. Refusing the reload does not undo the swap; it only leaves
the reader on a blank editor.
Reverse-verified: stashing lib/sw-update.ts and index.ts turns all four new
unit cases red. sw-silent-update and sw-warm pass locally.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com