Files
felhom-controller/REPORT.md
T

178 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.