Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
11 KiB
REPORT — v0.316.x: the app page as one column; one image per version number (2026-10-15)
1. Baselines
| Repo | main at start |
Version | → |
|---|---|---|---|
| felhom-controller | 8339206 |
v0.315.0 | v0.316.0 → .1 → .2 built; .3 committed, NOT built (§6) |
| felhom.eu | 340804f9 (docs only) |
— | — |
Architecture read first: 02-controller-module-map.md „The app page", 09-update-architecture.md,
the felhom-ui-design skill, and the controller's CLAUDE.md + .claude/rules/*.
Built and tested on DooPlex in an isolated scratch clone; edited on the Windows workstation. The browser work is on the workstation, which is the reason CC runs here.
2. PART 0 — the diagnosis, before any fix
Verdict: (a) a cache. Full evidence: felhom.eu/documentation/audits/app-page-2026-10-15b/P0/VERDICT.md.
(b) is ruled out. Exactly one guest ran 0.315.0 — demo-hp 9201 — on digest
sha256:8887343011d6…, identical to the registry's 0.315.0 at the time of measuring. The global
floor is 0.314.0 (read from the hub's /configuration), so no box could have taken 0.315.0 by
itself. demo-hp 9202 and felhom-pve (N100) 9201 are both on 0.314.0; Tester 1 (c11-appliance) was
unreachable and, by the floor, cannot be on 0.315.0.
The mechanism, in four measured facts:
layout.htmlasked for/static/style.css?v={{.Version}}→?v=0.315.0for all three different 0.315.0 builds (e66d466,fe0228c,db52f5a); the first had no.app-tabsrules.- The origin sends no validator: measured on the box,
ETag present = 0,Last-Modified present = 0. A cached copy cannot be revalidated — only expire. - Cloudflare caches that URL (
cf-cache-status: HIT,age: 99 → 100over three requests) and rewrites the browser TTL from the origin'smax-age=3600tomax-age=14400— four hours. - The session's first request returned
cf-cache-status: EXPIRED, which only happens when the edge holds an entry that has aged past its TTL.
What is true now, and the caveat stated rather than glossed: origin, edge and box agree
byte-for-byte (sha256 c1774f63… both sides) and all carry the rules; the page itself is never
cached (DYNAMIC). The specific stale object is no longer observable — the first probe forced
its revalidation, which is what EXPIRED records — so I cannot show a HIT whose body lacks
.app-tabs. What is shown is every precondition, the proof an aged entry existed, and the
elimination of the only other candidate. No cache was purged by hand.
3. Boxes on 0.315.0, with digests
| Box | tag | digest | now |
|---|---|---|---|
| demo-hp 9201 | 0.315.0 | 8887343011d6 |
0.316.2 |
| demo-hp 9202 | 0.314.0 | — | unchanged |
| felhom-pve (N100) 9201 | 0.314.0 | — | unchanged |
Tester 1 (c11-appliance) |
unreachable | — | cannot be on 0.315.0 (floor 0.314.0) |
4. Files changed, commits on main
| Commit | What |
|---|---|
9cc7ffc |
build.sh refuses a repeated push; assetTag per build; every shell uses it; harness normalises it |
7f3e58f |
the one-column layout (the approved mockup) |
48d8674 |
v0.316.0 CHANGELOG + the push-once fact in the rule file |
006ceab |
v0.316.1 — the asset tag reaches the dashboard pages |
f908eee |
the settings tab loses the install page's boxes |
9e68e52 |
two more data-tab leftovers |
8c7aa10 |
v0.316.3 CHANGELOG |
felhom.eu b6638ac4, 22cd7c51 |
the design docs, the audit, R-1080 closed, R-1081 filed |
Main files: controller/build.sh, scripts/test_build_push_once.sh, internal/web/server.go,
asset_tag_test.go, app_layout_test.go, templates/{style.css,app_shell,app_settings,app_data,app_guide,app_logs,app_fields,deploy}.html,
both locale files, i18n_parity_test.go, i18n_wiring_test.go.
5. Tests and red-proofs
| # | Break | Result |
|---|---|---|
| B1 | the whole registry check deleted | 5 of 6 cases fail; a present tag builds AND pushes |
| B2 | an unreachable registry treated as absent | the two unanswered-registry cases fail |
| C1 | assetTag from the version only |
TestAssetTag_ChangesPerBuild fails on both same-version cases |
| C2 | one shell back on ?v={{.Version}} |
TestAssetTag_EveryShellUsesIt fails naming layout.html |
| C3 | addShellAssetData removed from executeTemplate |
TestAssetTag_ReachesTheRenderedPage fails: empty buster on four pages |
| D1 | the auto-update success banner restored | TestAppPage_AutoUpdateSuccessIsMetaNotBanner fails |
| D2 | one tab loses its column | TestAppPage_OneColumnLayout fails naming the tab |
| D3 | a section drawn as a card again | same test fails on two tabs |
| E | an informational section drawn as a blue callout | TestAppPage_CalloutsOnlyForAskAndWarn fails on both counts |
New: TestBuildScript_RefusesExistingTag (six cases, real script, fake docker),
TestAssetTag_{ChangesPerBuild,EveryShellUsesIt,ReachesTheRenderedPage},
TestAppPage_{OneColumnLayout,AutoUpdateSuccessIsMetaNotBanner,CalloutsOnlyForAskAndWarn}.
Full gate: go build ./... && go vet ./... && go test ./... → exit 0, 37 packages ok, zero
failures, measured with go test's own exit code (not a pipeline's). All controller gates OK; 53
decoys behaved.
6. Deployed — and what is NOT
demo-hp guest 9201 runs 0.316.2, healthy. Tester 1 and demo-felhom untouched. The floor stays
0.314.0 — raising it needs all three test boxes and a green box walk.
v0.316.3 is committed and pushed to git but NOT built. Between the 0.316.2 build and this one,
another session's work in progress appeared in the shared DooPlex build clone (15 modified files,
the physical-disk storage work, R-1074..R-1079). The clean-tree gate forbids building over it and I
will not stash or revert another session's work. Every build I did make printed porcelain=0 first.
What 0.316.3 adds, and therefore what the operator will NOT see tonight: on the data tab, „Biztonsági mentés" is still drawn as a card, and the 2nd-copy heading still reads „2. mentés beállítása — AdventureLog". Both are cosmetic; everything else is live.
7. The browser check, through the PUBLIC address
Chrome on the workstation, https://felhom.enkisfelhom.hu, normal loads — no hard refresh:
- The stylesheet URL is
?v=20540d81(an 8-hex per-build tag, not a version, not empty), and the loaded sheet carries the.app-tabsrules. This is the fix working: before it, the same page needed a hand-forced refetch. .app-tab-activecomputesborder-bottom: 2px rgb(46,168,245)=--blue-bright; the column ismax-width: 820px; the hero hasborder-top-width: 0px.- Across settings / data / guide / logs / plex / grocy / install: 1 column each, 1
aria-currenton an installed app and 0 on a not-installed one, 0.app-info-card, 0 success banners. - Data tab: exactly 1 amber callout (the same-disk warning). Settings, guide, grocy: 0 blue, 0 amber — correct, nothing is asked and nothing is risky.
- Console clean on a fresh load.
Screenshots: felhom.eu/documentation/audits/app-page-2026-10-15b/browser/.
Scenario E's blue callout was NOT exercised live. Plex's setup gate is open (the operator's real
claim, R-1060), so Needed() is false and the card is correctly hidden. Showing it live would mean
un-claiming the operator's real Plex server; not done. The branch is covered by
TestAppPage_CalloutsOnlyForAskAndWarn against a harness Plex with a closed gate.
8. Fixtures replaced, by name
Recaptured (the pages this task restyles, per the standing exception): app_settings_* ×21,
app_data_* ×9, app_guide_* ×4, app_logs, deploy_new{,_blocked,_memory_blocked}. All other
fixtures are byte-identical — and now permanently so against build identity: the harness
normalises the asset tag on both sides of the comparison, as it already did for the per-session CSRF
token, so a build can no longer churn 148 files.
9. NOT yet validated
- The operator's look at AdventureLog and Plex on demo-hp through the public address — asked for in STATUS. The layout judgement is theirs.
- v0.316.3's two cosmetic fixes (§6) — committed, unbuilt, so unseen.
- A blue callout in a real browser (§7).
10. Teardown — three layers
- Machine: nothing provisioned, no app installed, no throwaway Plex. Probe scripts and fetched
pages removed from DooPlex
/tmp. - Host: the scratch clone and the
felhom.eusibling symlink are deleted. The shared DooPlex checkout was never written to by me — which is how I could tell the other session's work from mine.build.shin the build directory is now a symlink to the repo's (deliberate, §11), with the stale copy kept beside it. - Hub: nothing created; no floor change.
11. Observations
- FILED: R-1081 — the registry's
0.315.0no longer holds the 0.315.0 release. I did this, and the cause is worth stating plainly: I put the guard incontroller/build.shin the repo, but the documented release command runs$FELHOM_ROOT/build/felhom-controller/build.sh, which was a separate copy taken 2026-07-18. The very first thing I did with the new refusal — run it against a real existing tag to prove it fires — therefore rebuilt and re-pushed 0.315.0 a fourth time, with v0.316.0's code under it. A guard added to a script the runbook does not execute is the seam-built-but-never-wired class, and I shipped one while fixing another. Root cause fixed in the session (symlink), then the refusal proven from the documented path. No box is affected. - NOT-A-FINDING (fixed in session): the asset tag shipped dead in 0.316.0 — every shell asked for
it, the template test passed, and the dashboard path never set it, so the first live load rendered
?v=(empty, i.e. a constant). Found in a browser;TestAssetTag_ReachesTheRenderedPagenow asserts the rendered value, not the markup. - NOT-A-FINDING (fixed in session):
app_row_dedup_gateconvicted my.app-row, correctly — that class already means „a row in the app list". Renamed.app-field-row; an over-broad rename briefly took the list's own rule with it and was restored. - NOT-A-FINDING:
page.title.tier2_config-style leftovers — the 2nd-copy heading's locale key carries „— {{.DisplayName}}" because it was a page title. New keyapp_page.second_backup. - Register: 169 before → 170 after; 1 opened (R-1081), 1 opened-and-closed (R-1080).
- System poster: no fact in
felhom-system-poster.facts.mdchanged. Neither file edited; nothing added to STATUS about it. - Instruction files:
.claude/rules/unprompted-work.mditem 6 in all four versioned copies gains „a version number is pushed once", stated as a fact about what the build now enforces, not as a new rule. The workspace-root copy is unversioned and not on this machine.