Files
felhom-controller/REPORT.md
T

11 KiB
Raw Blame History

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:

  1. layout.html asked for /static/style.css?v={{.Version}} → ?v=0.315.0 for all three different 0.315.0 builds (e66d466, fe0228c, db52f5a); the first had no .app-tabs rules.
  2. The origin sends no validator: measured on the box, ETag present = 0, Last-Modified present = 0. A cached copy cannot be revalidated — only expire.
  3. Cloudflare caches that URL (cf-cache-status: HIT, age: 99 → 100 over three requests) and rewrites the browser TTL from the origin's max-age=3600 to max-age=14400 — four hours.
  4. 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-tabs rules. This is the fix working: before it, the same page needed a hand-forced refetch.
  • .app-tab-active computes border-bottom: 2px rgb(46,168,245) = --blue-bright; the column is max-width: 820px; the hero has border-top-width: 0px.
  • Across settings / data / guide / logs / plex / grocy / install: 1 column each, 1 aria-current on 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

  1. The operator's look at AdventureLog and Plex on demo-hp through the public address — asked for in STATUS. The layout judgement is theirs.
  2. v0.316.3's two cosmetic fixes (§6) — committed, unbuilt, so unseen.
  3. 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.eu sibling 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.sh in 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.0 no longer holds the 0.315.0 release. I did this, and the cause is worth stating plainly: I put the guard in controller/build.sh in 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_ReachesTheRenderedPage now asserts the rendered value, not the markup.
  • NOT-A-FINDING (fixed in session): app_row_dedup_gate convicted 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 key app_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.md changed. Neither file edited; nothing added to STATUS about it.
  • Instruction files: .claude/rules/unprompted-work.md item 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.