fix(sw): complete cache-clearing + add controllerchange auto-reload
All cache-clearing buttons and the IDAA clear-caches page previously cleared IDB/localStorage but left service worker registrations and Cache Storage intact. On the next reload the SW re-served the old JS bundle, leaving users stuck on stale code despite appearing to reload. This caused recurring stale-state reports from IDAA and other clients for 4+ months. Two gaps closed: 1. Every clear path (root page buttons, sys bar, help tech, idaa/clear-caches) now unregisters SW registrations and clears Cache Storage before touching IDB and localStorage. Order: SW → Cache Storage → IDB → localStorage. 2. Added controllerchange listener in +layout.svelte effect 4. When the new SW activates and calls clients.claim(), this listener reloads the page so open tabs run the new JS bundle instead of keeping old code in memory indefinitely. Without this, skipWaiting + clients.claim work correctly on the SW side but the page side never picks up the update. Also added thorough code comments and updated REFERENCE__Common_Agent_Mistakes (#15) and BOOTSTRAP doc (#8) to document the full root cause so this cannot be silently re-broken by a future agent or refactor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -299,7 +299,11 @@ Read this section first, then open the reference doc when your task touches one
|
||||
5. **Sorting and search correctness** — `tmp_sort_*` comparator direction and Dexie sorting caveats.
|
||||
6. **Network reliability** — retry classification in `api_*_object.ts` and timeout behavior.
|
||||
7. **JSON field safety** — `*_json` null reads/writes and wrapper serialization behavior.
|
||||
8. **Service worker rollout behavior** — stale-tab symptoms, activation expectations, and trade-offs.
|
||||
8. **Service worker + cache clearing — MANDATORY four-layer wipe** — clearing IDB/localStorage
|
||||
alone is a placebo. The SW Cache Storage is separate and must be cleared first or the SW
|
||||
re-serves the old JS bundle on the very next reload. The `controllerchange` event listener in
|
||||
`+layout.svelte` (effect 4) is also required or open tabs stay on old code after deploys.
|
||||
See mistake #15 in the reference doc — this caused a recurring client issue for 4+ months.
|
||||
9. **Local/remote config sync** — shadow fields that silently bypass an admin-synced master
|
||||
field, SWR-await-after-write races, and stateful/conditional sync gates that desync local
|
||||
state from history rather than current config. See the Pres Mgmt Config sync overhaul
|
||||
|
||||
@@ -141,14 +141,55 @@ Review policy: balanced curation.
|
||||
|
||||
**Verify:** Audit reads/writes for `cfg_json`, `data_json`, `poc_kv_json`, and similar nullable blob fields.
|
||||
|
||||
### 15) Service worker stale-tab behavior misunderstood
|
||||
**Impact:** users run old code longer than expected, “can’t reproduce” bug reports.
|
||||
### 15) Incomplete cache clearing — SW and Cache Storage omitted
|
||||
|
||||
**What happened:** deployment assumptions ignored SW activation lifecycle.
|
||||
**Impact:** Sev-1 class. Users stuck on old JS for weeks after deployments. “Fixed” bugs
|
||||
reappear because the affected user never loaded the fix. Nearly cost the IDAA client after
|
||||
4+ months of recurring reports that were repeatedly declared resolved.
|
||||
|
||||
**Rule:** Keep SW activation behavior explicit (`skipWaiting`, `clients.claim`) and evaluate trade-offs for session-heavy flows.
|
||||
**What happened (two separate gaps, both required to fix):**
|
||||
|
||||
**Verify:** After deploy, validate that long-lived tabs pick up new SW behavior as intended.
|
||||
**Gap A — Clear buttons did not clear the SW or Cache Storage.**
|
||||
The app has a service worker that caches every JS/CSS/HTML asset in its own Cache Storage
|
||||
(separate from IndexedDB and localStorage). All “clear caches” buttons only cleared IDB,
|
||||
localStorage, and sessionStorage — leaving the SW and its Cache Storage intact. On the next
|
||||
reload, the SW intercepted the request and returned the OLD cached JS bundle. The user appeared
|
||||
to reload but was still running old code. Clicking “Clear Storage and Reload” appeared to work
|
||||
but did nothing to break the stuck state.
|
||||
|
||||
**Gap B — No `controllerchange` listener in the layout.**
|
||||
`service-worker.js` correctly calls `skipWaiting()` on install (new SW activates immediately)
|
||||
and `clients.claim()` on activate (new SW takes control of all open tabs). But the page-side
|
||||
half of this contract was missing: there was no `controllerchange` event listener to trigger
|
||||
a reload when the new SW took over. Result: every deployment correctly installed and activated
|
||||
a new SW, but any tab that was already open kept running the old JS bundle from memory —
|
||||
indefinitely, until the user happened to close and reopen the browser.
|
||||
|
||||
**Who was affected:** Windows users who leave Chrome/Edge running for days or weeks with the
|
||||
tab pinned or restored via “continue where you left off.” Most users were never affected
|
||||
because normal browser habits (restart, close/reopen tab) naturally clear the stuck state.
|
||||
The pattern looked random but was actually a long-session-duration problem.
|
||||
|
||||
**The fix (2026-06-22):**
|
||||
1. Every “clear” button and automated heal path now clears SW registrations + Cache Storage
|
||||
BEFORE clearing IDB/localStorage. Order matters: SW → Cache Storage → IDB → storage.
|
||||
2. `controllerchange` listener added to `+layout.svelte` effect 4 — reloads the page when
|
||||
a new SW takes control, so open tabs automatically get fresh JS after any deployment.
|
||||
3. `idaa/clear-caches/+page.svelte` (IDAA iframe tool) updated with the same fix.
|
||||
|
||||
**Rule:** Any code that clears caches MUST clear all four layers in this order:
|
||||
1. `navigator.serviceWorker.getRegistrations()` → unregister each
|
||||
2. `caches.keys()` → delete each key
|
||||
3. `indexedDB.databases()` → delete each database
|
||||
4. `localStorage.clear()` + `sessionStorage.clear()`
|
||||
|
||||
Clearing IDB/localStorage WITHOUT clearing SW and Cache Storage is not a fix — it is a
|
||||
placebo that makes the user think something changed while the old JS bundle is served again
|
||||
on the very next reload.
|
||||
|
||||
**Verify:** After any future changes to cache-clearing paths, confirm all four layers are
|
||||
cleared. The `controllerchange` listener in `+layout.svelte` effect 4 must not be removed.
|
||||
`/testing/fix-sw` is the emergency escape route for users who are stuck on pre-fix code.
|
||||
|
||||
### 16) Local "shadow field" silently bypasses the admin-synced master field
|
||||
**Impact:** an admin config toggle appears to do nothing — the UI updates fine when a local
|
||||
|
||||
Reference in New Issue
Block a user