static/index.html hardcodes ?v= query strings on style.css, the per-page
CSS files and loader.js, plus an app-version meta tag. Nothing kept them
in sync with VERSION, so they'd silently drifted to different stale
versions each (found live: style.css one release behind, loader.js
sixteen releases behind). The static-file middleware marks any CSS/JS
request with a ?v= query as max-age=31536000 immutable, so a browser
that had ever loaded e.g. magazine.js?v=1.26.10 kept serving that exact
response for a year no matter how many times the file changed on disk
or how hard the page was force-reloaded.
release.py now rewrites every ?v=X.Y.Z and the app-version meta tag to
the new version on every release, so a release is what invalidates the
cache rather than a manual, easy-to-forget edit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scripts/release.py:
- --patch/--minor/--major flag bumps VERSION file
- Renames [Unreleased] → [x.y.z] — date in CHANGELOG.md
- Inserts fresh [Unreleased] section + correct compare links
- Commits + creates annotated git tag
- --dry-run flag for preview without writes
- Prints git push + GitHub Releases URL on completion
scripts/hooks/pre-commit:
- Warns (exit 0, non-blocking) when source files are staged but
CHANGELOG.md or VERSION are not staged
- Already installed in .git/hooks/
scripts/install-hooks.sh:
- One-liner to install hooks after a fresh clone
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>