ec438479e2
gates / gates (push) Failing after 48s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
178 lines
11 KiB
Markdown
178 lines
11 KiB
Markdown
# 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.
|