The shell loads gs-core.js and the other app scripts under fixed names,
and they are served with cache-control max-age=14400. After a push that
changes them, a browser runs the old scripts for up to four hours, so a
reader sees old behavior next to new data until a hard reload.
Expected: a deploy reaches readers on their next load, for example
through versioned script names or a short max-age with revalidation.
Fixed on main by #commit:cbf046b81285: the shell uploads to immutable .gitsocial/site/shell/<rev>/ directories, written before the no-cache root index.html flips its references, with the directory announced as window.GS_SHELL to the boot loader, the app's grammar fetches and the generated pages. The sweep keeps the current and previous revisions from a durable marker and retries after a crash, and the root-level copies survive the first transition as grace for pages the old binary generated. Edge policy no longer matters, per the direction to skip CDN-specific work. Two review rounds, all findings fixed in-branch with tests; browser battery green.
↩ Max Rakhimov · 2026-09-28
Diagnosed live 2026-09-28. GET gs-app.js serves cf-cache-status REVALIDATED with cache-control max-age=14400 and a day-old last-modified, while HEAD shows the stored object as no-cache: the edge cached the script under an old policy, revalidates it by etag against the current object (a 304 keeps the old headers when the body is unchanged), and hands browsers max-age=14400, so a client that visited before a deploy keeps the old script for up to four hours without revalidating. index.html and pages.json serve DYNAMIC and are fine. Two fixes: (1) infra, immediate: a Cloudflare cache rule for the zone that respects origin cache-control (or bypasses cache for the shell assets), plus one purge to evict the old-header entries; (2) code, durable: versioned asset URLs (immutable, referenced from the no-cache index.html), which makes any edge policy irrelevant; that one changes what the site stores and needs a design note.