Symptom
After a new version is deployed, an already-open session keeps running the
old frontend bundle while the footer shows the new version number — so
the UI looks updated but behaves like the old code until a manual hard
refresh. This has twice caused false "the fix doesn't work" reports during
testing (most recently the #52 variable-rename fix, which was actually fine —
the tab was running cached pre-1.8.3 JS).
The mismatch is the giveaway: the footer reads the live server /api/health
(client/src/components/Footer.tsx), which returns the new version instantly,
while the running JS/HTML come from the service-worker precache and don't swap
until the page reloads.
Why it happens
- The PWA already uses
registerType: "autoUpdate" (client/vite.config.ts:22)
with generateSW. The app shell + hashed JS are precached (cache-first);
only /api/* is NetworkFirst.
autoUpdate updates the service worker in the background, but an already-open
tab/PWA keeps its loaded assets — the new SW typically takes control on the
next navigation, not the current page. There's no explicit registration in
client/src (grep for registerSW/virtual:pwa is empty), so we rely on the
default injected registration and never force a reload on update.
Net: deploy → SW fetches new precache in the background → open session still runs
old code until the user happens to fully reload.
Options to fix
- Auto-reload on update (best for a kiosk-style household printer): register
via virtual:pwa-register and reload the page when the new SW activates
(listen for controllerchange / use the autoUpdate callback), so an open tab
refreshes itself shortly after a deploy.
- Reload prompt: use
onNeedRefresh to show a small "New version available —
reload" toast, letting the user choose. Less surprising than an auto-reload.
- Version-mismatch guard (belt-and-suspenders, directly kills the confusing
symptom): compare the build-time __APP_VERSION__ (already injected, see
vite.config.ts:17) against /api/health's version in Footer/app init;
if they differ, surface the reload prompt. Guarantees footer-version and
running-code can never silently disagree.
Suggest 1 or 3 (or both). Mostly a client/vite.config.ts + a small SW-registration
module change; low risk.
Impact
Not a user-data bug, but it repeatedly masks real fixes during testing and would
confuse end users after an update. Worth doing before the next round of
user-facing changes.
Symptom
After a new version is deployed, an already-open session keeps running the
old frontend bundle while the footer shows the new version number — so
the UI looks updated but behaves like the old code until a manual hard
refresh. This has twice caused false "the fix doesn't work" reports during
testing (most recently the #52 variable-rename fix, which was actually fine —
the tab was running cached pre-1.8.3 JS).
The mismatch is the giveaway: the footer reads the live server
/api/health(
client/src/components/Footer.tsx), which returns the new version instantly,while the running JS/HTML come from the service-worker precache and don't swap
until the page reloads.
Why it happens
registerType: "autoUpdate"(client/vite.config.ts:22)with
generateSW. The app shell + hashed JS are precached (cache-first);only
/api/*isNetworkFirst.autoUpdateupdates the service worker in the background, but an already-opentab/PWA keeps its loaded assets — the new SW typically takes control on the
next navigation, not the current page. There's no explicit registration in
client/src(grep forregisterSW/virtual:pwais empty), so we rely on thedefault injected registration and never force a reload on update.
Net: deploy → SW fetches new precache in the background → open session still runs
old code until the user happens to fully reload.
Options to fix
via
virtual:pwa-registerand reload the page when the new SW activates(listen for
controllerchange/ use the autoUpdate callback), so an open tabrefreshes itself shortly after a deploy.
onNeedRefreshto show a small "New version available —reload" toast, letting the user choose. Less surprising than an auto-reload.
symptom): compare the build-time
__APP_VERSION__(already injected, seevite.config.ts:17) against/api/health'sversioninFooter/app init;if they differ, surface the reload prompt. Guarantees footer-version and
running-code can never silently disagree.
Suggest 1 or 3 (or both). Mostly a
client/vite.config.ts+ a small SW-registrationmodule change; low risk.
Impact
Not a user-data bug, but it repeatedly masks real fixes during testing and would
confuse end users after an update. Worth doing before the next round of
user-facing changes.