Cuándo
20-08-2026, durante todo el día. El síntoma (“no veo los cambios”) persiguió al equipo desde la mañana; el fix definitivo llegó por la tarde-noche con v1.73.2 (commit f129a668, PR #404).
Síntomas visibles
Usuarios y el propio equipo viendo JavaScript viejo en el navegador pese a que el deploy en el servidor era correcto — verificado por fuera (estáticos servidos) y por dentro (template nuevo en el contenedor). Ni un Ctrl+F5 ni el bump del parámetro ?v= de cache-busting (aplicado unas horas antes en v1.73.1, commit 5d927b6a) resolvían el problema para los navegadores atrapados.
Causa raíz
CreaRack Pro tuvo en el pasado un service worker que cacheaba todas las respuestas de la API, causando una fuga de memoria de 7GB+ de RAM en el navegador. Al retirarlo, templates/base.html se quedó con un script inline que desregistra cualquier service worker y borra sus cachés — pero ese script corría una sola vez por navegador, marcado con la clave _sw_cleaned en localStorage.
El fallo: si esa marca quedaba puesta mientras el service worker seguía vivo (o el navegador lo resucitaba más tarde), ese navegador quedaba atrapado para siempre sirviendo JS congelado — el service worker intercepta las peticiones de red antes de que lleguen al servidor, así que ni el cache-busting de ?v= ni un hard refresh normal podían evitarlo. El bump de v1.73.1 atacaba la caché de assets, pero no el problema real: un service worker vivo interceptando en un navegador donde la limpieza ya se había “consumido”.
Fix aplicado
Commit f129a668668cf75f2b1f5a7ecd29a994b941d65e109 (v1.73.2). En templates/base.html, la limpieza deja de ser condicional: se elimina el guard !localStorage.getItem('_sw_cleaned') y el localStorage.setItem('_sw_cleaned', '1') posterior. Ahora el script desregistra cualquier service worker y borra sus cachés en cada carga de página — un no-op barato cuando no hay nada que limpiar.
Lecciones
- El bump de cache-busting de v1.73.1 era necesario pero no suficiente: resuelve la caché de assets estáticos, no un service worker vivo que intercepta la petición antes de que el
?v=importe. - Una limpieza “de un solo intento” con flag en
localStoragees frágil ante cualquier condición de carrera entre el flag y el ciclo de vida real del service worker. Cuando el coste del caso no-op es despreciable, incondicional > flag de una vez.
Preventivos futuros
- El usuario ya atrapado necesita una visita que ejecute el HTML nuevo (o un “Clear site data” manual) para liberarse; a partir de ahí, inmunidad permanente porque la limpieza corre en cada carga.
- Si se reintroduce cualquier service worker en el futuro, replicar el patrón de limpieza incondicional en vez de flags de un solo uso.
Véase también
- [[entity—core—service—forgiving-manifest-storage]]