New markup over an old stylesheet

Reported from a desktop browser: a stray close button beside the logo, badly
drawn, and a page that would not scroll. None of it was in the code that was
running -- it was the code the browser had not fetched.

The worker caches /static/ under a cache named for the release while the files
in it carried no version, and a page is fetched network-first. That only ever
worked because the worker used to seize every open tab the instant it installed
and wipe the old cache. 1.1.0 stopped it doing that, rightly -- it was swapping
stylesheets out from under a streaming reply -- and a momentary mismatch became
a permanent one: new markup over the previous release's CSS for as long as the
old worker lived. `.sidebar__close` had no rule there, so `.btn--icon` made it
inline-flex: visible everywhere, placed by nothing.

Every /static/ URL carries the release now, written by `templating.asset` and
precached by `sw.js:versioned` -- both halves, because caches.match compares the
query too and precaching the bare path would cache entries nothing requests.
Self-correcting: updating is enough.

The header was also a brand with a button appended and margin-left:auto doing
the placing, which holds exactly while that button is last. Two slots now: a
brand that shrinks and truncates, and a rail on the trailing edge.

Verified before changing anything: with the current stylesheet the button is
display:none at 1280 and, with thirty chats and forty messages, both scrollers
scroll. The first measurement said the thread did not -- that was
scroll-behavior: smooth reporting where it started.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 18:16:24 +00:00
co-authored by Claude Opus 5
parent 28390095a9
commit 201281d616
18 changed files with 216 additions and 48 deletions
+47
View File
@@ -264,3 +264,50 @@ def test_the_page_asks_for_the_whole_screen_and_then_pays_for_it(client, registe
app = (STATIC_DIR / "css" / "app.css").read_text()
assert "var(--safe-top)" in app
assert "var(--safe-bottom)" in app
# --- A release cannot be drawn with the previous release's stylesheet --------
def test_every_static_asset_carries_the_release(client: TestClient, registered):
"""The bug this is here to stop shipped in 1.1.0.
The worker caches `/static/...` under a cache named for the release, and a
page is fetched network-first while its assets come from that cache -- so
once the worker stopped claiming open tabs the instant it installed (which
it had to, or it swaps stylesheets under somebody mid-reply), new HTML and
old CSS were served together. What that looked like was a close button
meant for a phone drawer appearing, unstyled, on every desktop.
A version in the URL settles it: the new HTML asks for something the old
cache has never heard of.
"""
import re
for path in ("/chat", "/settings"):
page = client.get(path).text
bare = re.findall(r'(?:href|src)="(/static/[^"?]+)"', page)
assert not bare, f"{path} loads unversioned assets: {bare[:5]}"
def test_no_template_reaches_past_the_helper(client: TestClient):
"""`url_for('static', ...)` produces a URL with no version in it, so one
left behind is one asset that can still come from the wrong release."""
from pathlib import Path
import lembas
root = Path(lembas.__file__).parent / "web/templates"
offenders = [
str(p.relative_to(root))
for p in root.rglob("*.html")
if "url_for('static'" in p.read_text(encoding="utf-8")
]
assert not offenders, f"still using url_for for static assets: {offenders}"
def test_the_worker_precaches_what_a_page_will_ask_for():
"""`caches.match` compares the whole URL. Precaching the bare path fills the
cache with entries nothing requests, and every asset then goes to the
network on every load while looking perfectly cached."""
source = (STATIC_DIR / "js" / "sw.js").read_text()
assert 'path + "?v=" + VERSION' in source
assert "versioned(path)" in source
+6 -1
View File
@@ -324,7 +324,12 @@ def test_no_page_loads_a_script_that_the_base_template_already_loads():
from pathlib import Path
templates = Path(__file__).resolve().parents[1] / "src/lembas/web/templates"
pattern = re.compile(r"path='js/([a-z_]+\.js)'")
# Both spellings, because the way a static URL is written has changed once
# already: `url_for('static', path='js/x.js')` became `asset('js/x.js')`
# when assets started carrying the release. The assertion below that the set
# is non-empty is what turned that rename into a loud failure rather than a
# sweep that silently stopped sweeping -- keep it.
pattern = re.compile(r"(?:path=|asset\()'js/([a-z_]+\.js)'")
always = set(pattern.findall((templates / "base.html").read_text()))
assert always, "base.html stopped loading any script; this test is now blind"