Skip to content

Revalidate static UI assets on every load (Cache-Control: no-cache) - #53

Merged
DaveHomeAssist merged 1 commit into
mainfrom
claude/static-no-cache
Oct 1, 2026
Merged

DaveHomeAssist merged 1 commit into
mainfrom
claude/static-no-cache

Conversation

@DaveHomeAssist

Copy link
Copy Markdown
Owner

Problem

app.mount("/", StaticFiles(...)) sent ETag and Last-Modified but no Cache-Control. static/index.html loads app.js, style.css, console.js, prompt-contract.js, anticipation.js, and vendored GSAP by fixed URLs, so browsers applied heuristic freshness and kept a cached app.js after DaveLLM was updated. While testing PR #48 (claude/notion-adapter-v1), the page kept running the old app.js (transferSize: 0, 173,254 bytes vs 175,326 live) through a plain reload and a direct navigation, until fetch('/app.js', {cache: 'reload'}). An open browser or Electron tab can therefore run stale JS against a newer API.

Change

  • app.py: a small RevalidatedStaticFiles(StaticFiles) subclass overrides file_response and sets Cache-Control: no-cache on every static file response, including 304s. Browsers still cache, but they revalidate each load: an unchanged asset costs a 304 on loopback and a changed one is fetched. It also covers edits that keep the same version, which versioned URLs from VERSION would miss. The class sits beside the mount at the bottom of app.py, below the pinned tool handlers.
  • API responses are unchanged. /tools/agent/runs/{id}/events?stream=true keeps no-store.
  • CLAUDE.md and README.md each get one line describing the header.

Tests

  • tests/test_api_contracts.py::test_static_bundle_revalidates_and_api_headers_are_unchanged: /, /index.html, /app.js, /style.css, /console.js, /prompt-contract.js, /anticipation.js, /vendor/gsap/gsap.min.js, and /monitoring.html return no-cache with an ETag and Last-Modified. A conditional If-None-Match request returns a 304 with no-cache and an empty body. /health, /nodes (authenticated and 401), and a 404 for /app.py carry no Cache-Control.
  • tests/test_agent_lifecycle_api.py: pins no-store on the SSE events stream.
  • The new test fails on main and passes on this branch.

Verification (local, macOS, Python 3.14 venv, Node 22)

  • py_compile (CI's file list), compileall daveharness, and mypy daveharness: clean.
  • pytest -q: 758 passed, 1 skipped.
  • node --check on all static and desktop JS, plus node --test for the run ledger, approval preview, and console suites (5/5): clean.
  • bash -n on the deploy and launcher scripts, npm ci, npm ls --depth=0 (electron@44.0.0), npm audit --audit-level=high (0 vulnerabilities), and git diff --check: clean.
  • Electron A/B smoke (desktop/main.js, DAVE_ELECTRON_SMOKE_EXIT_MS, CDP on a spare port, static mtimes backdated so heuristic freshness applies, app.js edited on disk after the first load):
    • main: after the edit, both the navigation and the reload kept running the old app.js. The backend log shows the assets were never requested again after the first load (only / revalidated once).
    • this branch: the navigation after the edit fetched the new app.js (200, edited body confirmed through CDP) and the other assets returned 304. The next reload returned 304 for everything. The UI rendered (title DaveLLM, composer present) with no page exceptions in both runs.

The static mount sent ETag and Last-Modified but no Cache-Control, so
browsers applied heuristic freshness and kept running a cached app.js
(and style.css, console.js, ...) after DaveLLM was updated. An open
browser or Electron tab could then run stale JS against a newer API.

Serve static/ through RevalidatedStaticFiles, which adds
Cache-Control: no-cache to every file response and 304. Browsers keep
their cached copy but revalidate each load, so an unchanged asset costs
a 304 on loopback and a changed one is fetched. API routes are
unchanged; the agent events stream keeps no-store.

Tests pin the header on /, the fixed-URL assets, and their 304s, assert
/health and /nodes carry no Cache-Control, and pin no-store on the SSE
events route.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@DaveHomeAssist
DaveHomeAssist merged commit 2182588 into main Oct 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant