Repository navigation
Tag the UI assets so a rebuilt server changes what the reader sees - #75
Merged
Merged
Conversation
style.css and app.js went out with no Cache-Control, no ETag and no Last-Modified. embed.FS reports a zero modtime, so net/http had nothing to put in Last-Modified either, and a browser handed a response with no freshness information at all is free to invent its own. That breaks the loop this lab is built around: edit, rebuild control, look at the page. A rebuilt server that does not change the page reads as a broken fix, and it cost a session on #68 debugging code that was correct the whole time. It lands on readers too, not only editors — anyone who opened the lab before pulling can be looking at a new app.js against an old style.css with nothing indicating it. Every embedded asset now carries an ETag over its own bytes and Cache-Control: no-cache, which does not mean "do not store", it means store it and ask first. The hash is computed once at startup because the bytes are in the binary and cannot change while it runs. The header is set before handing off to http.FileServer, which is not a trick: http.ServeContent reads the ETag already on the ResponseWriter when it evaluates If-None-Match, so 304 handling, ranges and content sniffing all stay where they were. No build step, no dependency, no version in index.html. Measured against the running lab on 2026-09-12. Before, style.css answered with no cache headers of any kind. After, it answers with Cache-Control: no-cache and an ETag; a matching If-None-Match returns 304 with no body, a stale one returns the full 37646 bytes. Driving a browser: with style.css already cached, editing it and rebuilding control made a plain reload pick up the change, and a second reload with nothing changed moved 300 bytes on the wire for a 37692-byte stylesheet. AGENTS.md and CLAUDE.md both told agents a hard reload was part of the loop. It no longer is, so they say so. Closes #70
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Target
develop: the normal case for a fix, a chapter, alab change, a typo
develop→main, shipping to the published guidemain, because published advice iscurrently locking readers out or leaving them exposed
What this changes
The embedded UI went out with no
Cache-Control, noETagand noLast-Modified—embed.FSreports a zero modtime, sonet/httphad nothing toput in
Last-Modifiedeither — and a browser handed a response with no freshnessinformation at all is free to invent its own. Rebuilding the control server
changed what the server had; it did not change what the reader saw. Now every
asset carries an
ETagover its own bytes andCache-Control: no-cache, so anordinary reload revalidates and a 304 keeps that cheap.
Closes #70
Kind
If this changes advice or a command
AGENTS.mdandCLAUDE.mdboth told an agent that a hard reload was part of theedit → rebuild → look loop. That was true and this change makes it untrue, so
both now say so. The rebuild step itself is unchanged and still required.
What it was measured against:
What I ran, and what it printed.
Before, against the lab as it was running:
After
docker compose up -d --build --no-deps control:Revalidation, and that nothing else moved:
The bug itself, end to end, in a browser — which is the only place it was ever
visible. Load the page so Chromium caches
style.css, append a marker rule tolab/control/ui/style.css, rebuildcontrol, then reload normally, noCmd+Shift+R:
That is the reported bug gone. The other half — that revalidating on every load
stays cheap — from the PerformanceResourceTiming on a second plain reload with
nothing changed:
300 bytes on the wire for a 37 KB stylesheet: the 304.
The marker rule was then reverted,
controlrebuilt again,style.cssconfirmedbyte-identical to
develop(git diffempty) and its ETag confirmed back tob075419be128ece483bbe3dca3480ada. The page reloads with no console errors.grepfor"hard reload" and "caches" across the repo found only
AGENTS.mdandCLAUDE.md, both updated here. The guide's own pages are opened fromfile://and are not served by this handler.Checks
make -C lab checkpassesgofmtis clean (make checkfails on any output at all)SPDX-License-IdentifierheaderIf this touches the guide's pages
Not touched. The handler serves the lab UI only; the guide still opens from
file://with no server, no build step and no new dependency.If this touches the lab or sandbox engine
The engine is not touched — this is the static file handler in front of it. The
/api/*routes are untouched and still answer with no cache headers, confirmedabove.
dangerstill outranksoklab/README.mdmoved with it — no score movedNot in this change
Two things noticed while measuring, neither touched:
GET /index.html301-redirects to/. That ishttp.FileServer's ownbehaviour and predates this change; a test notes it rather than asserting it
away.
go vet-adjacent linters flag pre-existing issues indocker.go(abufio.Scannerloop with no finalsc.Err()check) andstream.go(stringconcatenation inside
WriteString). Unrelated to this branch.Licence
CC BY-SA 4.0 (guide) or GPL-3.0-or-later (code), per the file I touched