Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit6345c3eunknown_key

fix(pwa): infinite reload loop on first SW install (incognito, fresh browser)

fix(pwa): infinite reload loop on first SW install (incognito, fresh browser)

User reported gluecron.com looping the refresh button in incognito tab.
Root cause: pwaRegisterScript checked
\`navigator.serviceWorker.controller\` at statechange-time as the
"is this an update, not first install" guard. But the SW's activate
handler calls \`clients.claim()\` which SETS the controller during
first install — so the guard was always truthy on the first visit,
fired window.location.reload(), the new page re-registered the SW,
which triggered another updatefound (subtle browser quirks around SW
identity), which fired the reload again. Infinite loop.

New guard: snapshot
\`hadControllerAtLoad = !!navigator.serviceWorker.controller\`
BEFORE registration. Only reload when there was a previously-
controlling SW at page load — i.e. this is genuinely a deploy update.
First-install path now renders the page exactly once.

This was visible on EVERY first visit to the live site since BLOCK S2
landed. The "flashing" symptom the user has been chasing for hours.

Test suite still 1994 pass / 0 fail / 2 skip.

https://claude.ai/code/session_01QFLWDxWw65DX6enMcS5Lwe
Test User committed on May 15, 2026Parent: 81201cc
1 file changed+1786345c3e0c63cd7ecc863e683accb2028b6a044bf
1 changed file+17−8
Modifiedsrc/views/layout.tsx+17−8View fileUnifiedSplit
537537// Block G1 — register service worker for offline / install support.
538538// Kept inline (and tiny) so we don't block first paint.
539539//
540// Block S2 extension — when an updated SW activates we force a single
540// Block S2 extension — when an UPDATED SW activates we force a single
541541// reload so the user picks up the fresh HTML immediately instead of
542// being stuck on the previous deploy's cached page (the "I saw /login's
543// old version even after merging" bug that triggered S2 in the first
544// place). The reload only fires when `controller` is already set, which
545// means this is NOT the first-ever install (no double-load on first
546// visit). A page-scoped guard prevents reload loops if the SW updates
547// twice in quick succession.
542// being stuck on the previous deploy's cached page.
543//
544// Critical: snapshot \`navigator.serviceWorker.controller\` BEFORE
545// registration. The SW's activate handler calls clients.claim() which
546// sets the controller during first install — so checking the controller
547// at statechange-time was always truthy and produced an infinite reload
548// loop on every first visit (incognito tab, fresh browser, etc).
549//
550// New guard: only reload if there was a previously-controlling SW at
551// page load. First-install path stays a single page render.
548552const pwaRegisterScript = `
549553 if ('serviceWorker' in navigator) {
554 var hadControllerAtLoad = !!navigator.serviceWorker.controller;
550555 window.addEventListener('load', function(){
551556 navigator.serviceWorker.register('/sw.js').then(function(reg){
552557 var reloaded = false;
554559 var newSW = reg.installing;
555560 if (!newSW) return;
556561 newSW.addEventListener('statechange', function(){
557 if (newSW.state === 'activated' && navigator.serviceWorker.controller && !reloaded) {
562 if (
563 newSW.state === 'activated' &&
564 hadControllerAtLoad &&
565 !reloaded
566 ) {
558567 reloaded = true;
559568 window.location.reload();
560569 }
561570