Cache stale de base.js/alpine-components.js por versionado manual olvidado (v1.73.1)
Cuándo
20-08-2026, detectado y arreglado el mismo día en el commit 5d927b6 (v1.73.1, PR #403). La ventana de exposición real es más larga: cubre las 5 PRs anteriores del día que tocaron base.js o alpine-components.js sin subir su ?v=.
Síntomas visibles
Edu vio en producción comportamiento desactualizado pese a que el deploy en el servidor ya tenía el código correcto:
- El backup completo salía directamente, sin el diálogo de selección de vídeos de Signage (feature de la v1.72.0/#399).
- El modal de restore aparecía sin el inventario completo ni la opción de restaurar solo dominios (feature de la v1.73.0/#402).
Ambas features estaban bien desplegadas en el servidor — el fallo era que el navegador de Edu, con caché caliente, seguía sirviendo la copia anterior de base.js/alpine-components.js en vez de descargar la nueva.
Causa raíz
templates/base.html versiona a mano los dos scripts principales del frontend legacy con un querystring (?v=N) que actúa como cache-buster:
<script defer src="{% static 'js/alpine-components.js' %}"></script> <!-- sin versión -->
<script src="{% static 'js/base.js' %}?v=3" defer></script>
Nadie subió ese número en ninguna de las 5 PRs del día que modificaron esos ficheros — el paso es manual y no lo fuerza ningún check de CI ni pre-commit. Como el querystring no cambiaba, el navegador consideraba la URL idéntica a la ya cacheada y no volvía a pedir el archivo.
Es una causa raíz distinta pero de la misma familia que el incidente previo v1.63.2 (CHANGELOG.md, edge de Cloudflare cacheando una respuesta CSS rota durante un deploy) — ambos son fallos de la clase “deploy verificado en servidor ≠ código ejecutándose en el cliente”.
Matiz arquitectónico sin resolver: el proyecto ya tiene cache-busting automático por hash de fichero en producción vía [[entity—core—service—forgiving-manifest-storage]] (ForgivingManifestStaticFilesStorage, que cachea 1 año con immutable y cambia de nombre solo cuando cambia el contenido). El ?v=N manual en base.html es una capa aparte, redundante con ese mecanismo y con el mismo modo de fallo que pretendía evitar (versión olvidada = caché stale). El CHANGELOG deja explícito que la mitigación de fondo (querystring automático ligado a APP_VERSION, o depender solo del hashing ya existente) queda pendiente de decisión de Edu.
Fix aplicado (commit 5d927b6a)
Bump manual de los dos querystrings — alpine-components.js pasa de sin versión a ?v=2, base.js de ?v=3 a ?v=4 — más el bump de rutina de APP_VERSION a 1.73.1 en config/settings/base.py.
Lecciones
- El versionado manual de assets es tan fiable como acordarse de hacerlo: sin automatismo, falla con el tiempo suficiente (5 PRs en un solo día bastaron).
- Un deploy “correcto” en el servidor no garantiza que el cliente ejecute ese código — hay que verificar también el lado del navegador, no solo el lado del servidor.
- Footgun ya anotado en memoria del proyecto: tocar
static/jsobliga a subir su?v=en el mismo PR.
Preventivos futuros
- Decisión pendiente de Edu (anotada en el CHANGELOG de este commit): automatizar el
?v=ligándolo aAPP_VERSION, o retirarlo y confiar en el hashing de [[entity—core—service—forgiving-manifest-storage]] que ya cubre este caso en producción.
Véase también
- [[entity—core—service—forgiving-manifest-storage]]
- [[entity—frontend—service—escape-helpers]]
- [[feature—infra—fix-flash-tipografia-cache]]
- [[feature—core—help-widget-i4]]
- [[feature—core—help-widget-it-tutor]]