From 006ceab58a15a83079a4d29b326e69377a4aa195 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Thu, 15 Oct 2026 20:41:09 +0200 Subject: [PATCH] v0.316.1: the asset tag actually reaches the dashboard pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.316.0 shipped it dead. Every shell asked for `?v={{.AssetTag}}`, TestAssetTag_EveryShellUsesIt passed, and the first live load through the public address rendered `/static/style.css?v=` — EMPTY. addShellAssetData was called only by executeTemplateLang (the sign-in, claim, recovery, guest-share and catch-all pages). The dashboard path had never needed it, because layout.html used to ask for {{.Version}}, which baseData supplies; moving it to {{.AssetTag}} left nothing to supply the value. An empty buster is a CONSTANT. It would never have changed again — worse than the per-version one it replaced, and invisible to a test that only checks the markup. executeTemplate now calls the same function executeTemplateLang does, which is literally what the v0.255.0 comment above that call asked for: "setting it at the single choke point every shell renders through means a new shell cannot miss it". There were two choke points. TestAssetTag_ReachesTheRenderedPage renders six real URLs through the production router and asserts the buster is non-empty, 8 hex, and not a version. Red-proved by removing the call again: four pages fail naming the empty value. A PATCH VERSION, not a second push of 0.316.0 — the rule this release added, applied to its own mistake rather than around it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y --- CHANGELOG.md | 16 +++++++++ controller/internal/web/asset_tag_test.go | 42 +++++++++++++++++++++++ controller/internal/web/server.go | 6 ++++ 3 files changed, 64 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e1fdd..0d062ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## v0.316.1 — the asset tag actually reaches the dashboard pages (2026-10-15) + +**MinAgent: 0.155.0** (unchanged). A patch release because **a version number is pushed once** — the +rule this release added, applied to its own mistake rather than around it. + +- **v0.316.0 shipped the asset tag dead on every dashboard page.** Every shell asked for + `?v={{.AssetTag}}` and the template test passed, but `addShellAssetData` was only ever called by + `executeTemplateLang` — the stranger-facing pages. The dashboard path had never needed it, because + `layout.html` used to ask for `{{.Version}}`, which `baseData` supplies. The first live page load + rendered `/static/style.css?v=` — **empty, which is a constant, and a constant busts nothing.** + Found in a browser on the public address; the markup was right and the value was absent. + `executeTemplate` now calls the same one function, which is what v0.255.0's own comment asked for. +- `TestAssetTag_ReachesTheRenderedPage` asserts the rendered buster is NON-EMPTY, 8 hex and not a + version, on six real URLs through the production router. `TestAssetTag_EveryShellUsesIt` checks the + markup; it could not see this, and asserting a link exists proves nothing about what it carries. + ## v0.316.0 — the app page as one column; a version number is pushed once (2026-10-15) **MinAgent: 0.155.0** (unchanged — no new agent call). No golden (`09` §3 decision 250). Design: diff --git a/controller/internal/web/asset_tag_test.go b/controller/internal/web/asset_tag_test.go index 197c929..9d8ad70 100644 --- a/controller/internal/web/asset_tag_test.go +++ b/controller/internal/web/asset_tag_test.go @@ -1,6 +1,7 @@ package web import ( + "regexp" "strings" "testing" ) @@ -64,6 +65,47 @@ func TestAssetTag_ChangesPerBuild(t *testing.T) { } } +// TestAssetTag_ReachesTheRenderedPage — the half TestAssetTag_EveryShellUsesIt cannot see. +// +// THIS TEST EXISTS BECAUSE THE SUITE MISSED A DEAD FIX. Every shell asked for +// `?v={{.AssetTag}}`, the template test passed, and the first live page load rendered +// `/static/style.css?v=` — empty. `addShellAssetData` was called only by executeTemplateLang (the +// stranger-facing pages); the dashboard path had never needed it, because layout.html used to ask +// for `{{.Version}}`, which baseData supplies. The markup was right and the value was absent, which +// is worse than the defect it replaced: an empty buster is a CONSTANT and never busts anything. +// +// Asserting the link EXISTS proves nothing. Assert it carries a value. +// +// RED-PROOF (REPORT.md): remove the addShellAssetData call from executeTemplate -> this fails with +// an empty tag on every dashboard page. Reverted. +func TestAssetTag_ReachesTheRenderedPage(t *testing.T) { + s := appPageHarness(t) + re := regexp.MustCompile(`/static/style\.css\?v=([^"']*)`) + for _, p := range []string{"/apps/adventurelog", "/apps/adventurelog/data", "/apps/grocy", + "/apps/grocy/install", "/stacks", "/dashboard"} { + rec := appGet(t, s, p) + if rec.Code != 200 { + continue // a page this harness cannot render is another test's problem + } + m := re.FindStringSubmatch(rec.Body.String()) + if m == nil { + t.Errorf("%s: no stylesheet link at all", p) + continue + } + if m[1] == "" { + t.Errorf("%s: the stylesheet buster is EMPTY (%q) — an empty buster is a constant and "+ + "never busts anything", p, m[0]) + continue + } + if m[1] == "0.316.0" || strings.Contains(m[1], ".") { + t.Errorf("%s: the buster looks like a version (%q); it must be the per-build tag", p, m[1]) + } + if len(m[1]) != 8 { + t.Errorf("%s: buster %q is %d chars, want the 8-hex tag", p, m[1], len(m[1])) + } + } +} + // TestAssetTag_EveryShellUsesIt — the fix is only a fix if the shells ask for it. Before v0.316.0 // two stranger-facing pages used the tag and the other seven shells used `?v={{.Version}}`, which // is exactly the address that did not change between the three 0.315.0 builds. diff --git a/controller/internal/web/server.go b/controller/internal/web/server.go index 32ab9cd..8a35b82 100644 --- a/controller/internal/web/server.go +++ b/controller/internal/web/server.go @@ -1093,6 +1093,12 @@ func (s *Server) executeTemplate(w http.ResponseWriter, r *http.Request, name st // fence for the guest share page, which has no admin session. s.addEscrowBanner(data, r) s.addMissedBackupBanner(data, r) // R-871 (decision 110) + // The stylesheet cache-buster, for the DASHBOARD shell. executeTemplateLang has called this for + // the stranger-facing pages since v0.255.0; this path did not need it while layout.html asked for + // `?v={{.Version}}`, which baseData supplies. v0.316.0 moved it to `{{.AssetTag}}` and the first + // live load rendered `?v=` — a constant, which is worse than the version it replaced. Both render + // paths now go through the one function, which is what v0.255.0's own comment asked for. + s.addShellAssetData(data) lang := s.langFor(r) s.addLanguageData(data, r, lang) var buf bytes.Buffer