perf(ui): cache busting — HTML no-cache + versioned asset URLs (?v=) with immutable 1y asset caching

Phase 33 (story: .agent/user_stories/cache-busting.md).

- app/core/caching.py: asset_version() — git short SHA (a commit is a
  deploy), stable content-hash fallback for non-git checkouts, "dev"
  for a missing static dir; computed once per process. CachingMiddleware
  — the five HTML pages revalidate (no-cache) with ?v=<token> asset refs
  rewritten in flight; /assets/* is public, max-age=31536000, immutable;
  everything else (all /api/*, the SSE chat stream in particular) passes
  through byte-identical.
- tests/e2e/test_cache_busting.py: fresh-Chromium wire assertions —
  document no-cache, versioned CSS/JS request URLs sharing one token,
  immutable asset headers, /api/health baseline headers, SSE chat to
  done (mock LLM).
- README 'Caching / deploys' section + story file.

Also fixed two prod-image defects surfaced by this phase's podman smoke
(the full app would not boot):
- Containerfile: ship the scripts/ package — app/api/sync.py (phase 32)
  imports scripts.git_sync / scripts.import_docs at module level, so the
  container crashed on boot (ModuleNotFoundError: No module named
  'scripts').
- compose.yaml: pass BOR_ADMIN_PASSWORD / BOR_SESSION_SECRET through to
  the app service (:- defaults keep 'podman compose up -d db' working;
  the app's own fail-loud gate still names missing admin auth).

Smoke: podman compose --profile prod up -d on a fresh image + a fresh
Chromium profile — /, /sources.html and /login.html all served
Cache-Control: no-cache; all 8 asset requests versioned with one shared
token (content-hash fallback inside the image — no .git there);
/assets/* immutable for a year.
This commit is contained in:
2026-08-25 22:42:10 -04:00
parent 52136fe307
commit 8fabb7efda
12 changed files with 988 additions and 0 deletions
+7
View File
@@ -42,6 +42,13 @@ services:
BOR_DATABASE_URL: postgresql+psycopg://reese:reese@db:5432/brain_of_reese
BOR_LLM_BASE_URL: https://aipi.reeseapps.com/v1
# BOR_LLM_API_KEY: provide via shell env or your own env file — never commit it
# Single-admin auth (phase 16) is a fail-loud boot gate: set both via
# your shell env or an env file for the prod profile (the app refuses
# to boot without them). `:-` defaults keep `podman compose up -d db`
# (the dev workflow) parseable without them. Values are secrets:
# never commit them.
BOR_ADMIN_PASSWORD: ${BOR_ADMIN_PASSWORD:-}
BOR_SESSION_SECRET: ${BOR_SESSION_SECRET:-}
ports:
- "8000:8000"