REPORT-app-page-2026-10-15.md: the app-page arc, in a file a second session cannot overwrite
gates / gates (push) Successful in 50s
gates / gates (push) Successful in 50s
REPORT.md is per-session and overwritten, and tonight two sessions released this repo within an hour — the second (v0.317.0, the storage work) took it. This sibling holds the app-page arc: Part 0's measurement and its caveat, what shipped, every red-proof, the three incidents, what is deployed and what must never be, the browser check, and what is still owed. The convention is felhom.eu/CLAUDE.md's, for exactly this case; this is the first use of it in this repo. Whether REPORT.md should stay a single overwritten file in a repo two sessions can release is a question for the operator, and is left as one rather than answered by a change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
# REPORT — the app page in one column, and one image per version number (2026-10-15)
|
||||
|
||||
**A non-overwritten sibling of `REPORT.md`.** Two sessions released this repo on the same evening;
|
||||
`REPORT.md` is per-session and overwritten, and it now holds v0.317.0's (the storage work). This
|
||||
file holds the app-page arc so it stops being clobberable — the convention `felhom.eu/CLAUDE.md`
|
||||
prescribes for parallel sessions, applied here.
|
||||
|
||||
## 1. Baselines
|
||||
|
||||
| Repo | `main` at start | Version at start |
|
||||
|---|---|---|
|
||||
| felhom-controller | `8339206` | v0.315.0 |
|
||||
| felhom.eu | `340804f9` | docs only |
|
||||
|
||||
Architecture read before any claim: `02-controller-module-map.md` „The app page",
|
||||
`09-update-architecture.md`, the `felhom-ui-design` skill, `CLAUDE.md` + `.claude/rules/*`.
|
||||
Edited on the Windows workstation; built and tested on DooPlex in an isolated clone; the browser
|
||||
work is on the workstation, which is why CC runs here.
|
||||
|
||||
## 2. PART 0 — the diagnosis, before any fix
|
||||
|
||||
**Verdict: (a) a cache.** 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 `8887343011d6`,
|
||||
identical to the registry's 0.315.0 at the time of measuring. The **global floor was 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 were both on 0.314.0; Tester 1 (`c11-appliance`) was unreachable and, by the
|
||||
floor, cannot have had it.
|
||||
|
||||
**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**: 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 3600 to 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.
|
||||
|
||||
**The caveat, stated rather than glossed:** origin, edge and box now agree byte-for-byte
|
||||
(`sha256 c1774f63…`) and all carry the rules. The stale object itself is **no longer observable** —
|
||||
that first probe forced its revalidation, which is what `EXPIRED` records — so no `HIT` with a bad
|
||||
body can be produced. 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. What shipped
|
||||
|
||||
| Area | What |
|
||||
|---|---|
|
||||
| Push-once | `build.sh` refuses `--push`/`--multiarch` for a tag already in the registry, before building. A registry it cannot reach is a refusal too. Local builds untouched. |
|
||||
| Clean tree | `build.sh` also refuses a tree that is dirty, not at `origin/main`, or not a git checkout. Added after it was needed — see §6. |
|
||||
| Asset tag | `assetTag()` hashes version + `GitCommit` + `BuildTime`; **all nine shells** use it (seven asked for `?v={{.Version}}`). Opaque 8 hex, because the stranger-facing share pages use the same value. `Cache-Control` unchanged — the new address is the fix. |
|
||||
| Layout | The approved one-column mockup: 820px column, hairline sections, underlined tabs, no hero box, 200px label grid. Two boxes only: blue „the app is waiting for you", amber „this is risky". |
|
||||
| Banner | The auto-update SUCCESS banner is meta text in the hero. Every banner that needs a decision is untouched above the tabs (R-571). |
|
||||
| Leftovers | „2. mentés beállítása — <app>" and „<app> — Exportálás" removed; „Cím és kulcsok" with one address row replaces the duplicate disabled subdomain field; the „already installed, read-only" banner is now the hint under the fields; the install wizard gets the same back link, hero and column. |
|
||||
|
||||
## 4. Commits on `main`
|
||||
|
||||
`9cc7ffc` push-once + per-build asset tag · `7f3e58f` the one-column layout · `48d8674` v0.316.0
|
||||
CHANGELOG + the rule-file fact · `006ceab` v0.316.1 (the tag reaches the dashboard) · `f908eee`
|
||||
settings tab de-boxed · `9e68e52` two data-tab leftovers · `8c7aa10` v0.316.3 CHANGELOG · `f9151f0`
|
||||
**the padding fix** · `4261a38` `FELHOM_REPO_DIR` + the build names its tree · `c6a5671` v0.316.4
|
||||
the clean-tree gate · `e7b64af` `workspace/` gitignored.
|
||||
felhom.eu: `b6638ac4` (design docs + the audit), `22cd7c51` (R-1080 closed, R-1081 filed).
|
||||
|
||||
## 5. Tests and red-proofs — every break seen failing, then reverted
|
||||
|
||||
| # | Break | Result |
|
||||
|---|---|---|
|
||||
| B1 | the 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 |
|
||||
| B3 | the clean-tree gate deleted | all four of its 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`: empty buster on four pages |
|
||||
| D1 | the 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: `scripts/test_build_push_once.sh` (**ten** cases, driving the real `build.sh` with a fake
|
||||
`docker` on PATH — nothing is ever pushed),
|
||||
`TestAssetTag_{ChangesPerBuild,EveryShellUsesIt,ReachesTheRenderedPage}`,
|
||||
`TestAppPage_{OneColumnLayout,AutoUpdateSuccessIsMetaNotBanner,CalloutsOnlyForAskAndWarn}`.
|
||||
|
||||
Full gate: `go build ./... && go vet ./... && go test ./...` → **exit 0, 37 packages ok, zero
|
||||
failures**, read from `go test`'s own exit code, not a pipeline's. All controller gates OK; 53
|
||||
decoys behaved.
|
||||
|
||||
## 6. THREE INCIDENTS, one shape
|
||||
|
||||
All three are the same defect: **a guard that is not on the path the work actually takes.** Two of
|
||||
them I caused while fixing the first.
|
||||
|
||||
**(i) I re-pushed 0.315.0 a fourth time.** The push-once guard went into
|
||||
`controller/build.sh` in the repo; the documented release command runs
|
||||
`$FELHOM_ROOT/build/felhom-controller/build.sh`, which was a **separate copy taken 2026-07-18** with
|
||||
no guard. The 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 with v0.316.0's code under it
|
||||
(`8887343011d6` → `b809201841f7`). Root cause fixed: the build directory's script is a symlink to
|
||||
the repo's, the stale copy kept as `build.sh.stale-copy-2026-07-18`, and the refusal then proven
|
||||
from the documented path. **R-1081.**
|
||||
|
||||
**(ii) The asset tag shipped dead in 0.316.0.** 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 load rendered `/static/style.css?v=` —
|
||||
**empty, which is a constant, and a constant busts nothing.** Found in a browser. Fixed in 0.316.1;
|
||||
`TestAssetTag_ReachesTheRenderedPage` now asserts the rendered value, not the markup.
|
||||
|
||||
**(iii) Building 0.316.3 published another session's unreleased work.** The symlink from (i) points
|
||||
into the shared clone's **working copy**, which predated the `FELHOM_REPO_DIR` override — so the
|
||||
override was silently ignored and the build copied the shared tree, which held 31 uncommitted files
|
||||
of the physical-disk storage work. The banner said `Commit: 0225255` with no `Repo:` line; the
|
||||
evidence was on screen and the image was already pushed. **Nobody deployed it — that was luck, not a
|
||||
control.** The clean-tree rule had existed only as a sentence in a runbook. It is now two questions
|
||||
the build asks and refuses on, plus a refusal for a source that is not a git checkout at all; it
|
||||
immediately caught its own next build (the scratch `workspace/` beside the script, now gitignored).
|
||||
|
||||
**`0.316.3` must never be deployed.** It is neither re-pushed nor deleted: a version number is
|
||||
pushed once, and that applies to a number I spoiled.
|
||||
|
||||
## 7. Deployed, and what happened to 0.316.4
|
||||
|
||||
**demo-hp guest 9201 runs `0.317.0`, healthy.** That is the other session's release, built from
|
||||
`main` at a commit that contains all of this work — verified: `f9151f0` (the padding fix) and
|
||||
`c6a5671` (the clean-tree gate) are both ancestors of it.
|
||||
|
||||
**`0.316.4` was built but never published, and was then dropped.** Its first build attempt was
|
||||
refused by its own new clean-tree gate (the `workspace/` directory); by the time that was fixed,
|
||||
`0.317.0` was already in the registry. Publishing 0.316.4 would have put a *lower* number in the
|
||||
registry carrying nothing 0.317.0 does not already have. The household's button offers the highest
|
||||
tag, so it would have been invisible anyway.
|
||||
|
||||
**The floor stays 0.314.0** — raising it needs all three test boxes and a green box walk, and this
|
||||
evening two sessions released the same component.
|
||||
|
||||
| tag | state |
|
||||
|---|---|
|
||||
| 0.316.0 / .1 / .2 | published, superseded |
|
||||
| **0.316.3** | published, **CONTAMINATED — never deploy** |
|
||||
| 0.316.4 | never published (superseded) |
|
||||
| **0.317.0** | published, deployed on demo-hp, carries this work |
|
||||
|
||||
## 8. The browser check, through the PUBLIC address
|
||||
|
||||
Chrome on the workstation, `https://felhom.enkisfelhom.hu`, **normal loads — no hard refresh**:
|
||||
|
||||
- The stylesheet URL is a real 8-hex per-build tag 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`); column
|
||||
`max-width: 820px`; hero `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: none —
|
||||
correct, nothing is asked and nothing is risky.
|
||||
- **After the operator's report:** `.app-page` padding `0px 0px 32px`, `.content` padding `32px`,
|
||||
measured gap **32px from the sidebar** (was 80) and **36px from the top** (was 64+).
|
||||
- **Console clean on a fresh load**, on 0.316.2 and again on 0.317.0.
|
||||
|
||||
Screenshots: `felhom.eu/documentation/audits/app-page-2026-10-15b/browser/` —
|
||||
`01-settings-boxes-still-there`, `02-settings-one-column-0.316.2`, `03-data-tab-amber-callout`,
|
||||
`04-guide-tab-padding-fixed-0.317.0`.
|
||||
|
||||
**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.
|
||||
|
||||
## 9. Parity fixtures
|
||||
|
||||
Recaptured (the pages this task restyles): `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 independent of 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. The same normalisation was applied at the second comparison site in `i18n_wiring_test.go`,
|
||||
which had the same assumption.
|
||||
|
||||
## 10. NOT yet validated
|
||||
|
||||
1. **The operator's own look** at AdventureLog and Plex on demo-hp through the public address. The
|
||||
layout judgement is theirs. (The padding complaint from the first look is fixed and live.)
|
||||
2. **A blue callout in a real browser** (§8).
|
||||
3. **A catalog guide rendered from a real template** — no template carries `app_info.guide` yet.
|
||||
|
||||
## 11. Teardown — three layers
|
||||
|
||||
- **Machine:** nothing provisioned, no app installed, no throwaway Plex. Probe scripts, fetched
|
||||
pages and the password file removed from guest 9201, demo-hp and DooPlex `/tmp`; the dashboard
|
||||
password went file→file, was never printed, and no copy reached Windows or git.
|
||||
- **Host:** both scratch clones and the `felhom.eu` sibling symlink deleted. The shared DooPlex
|
||||
checkout was never written to by me — which is how the other session's work was distinguishable
|
||||
from mine. `build.sh` in the build directory is now a symlink to the repo's (deliberate), with the
|
||||
stale July copy kept beside it.
|
||||
- **Hub:** nothing created; no floor change.
|
||||
|
||||
## 12. Observations
|
||||
|
||||
- **FILED: R-1081** — the registry's `0.315.0`, and now also `0.316.3`, hold content that is not
|
||||
what their numbers say. Both caused by this session; both fixed at the root; no box affected.
|
||||
- **NOT-A-FINDING (fixed in session):** the asset tag shipped dead — incident (ii).
|
||||
- **NOT-A-FINDING (fixed in session):** the clean-tree rule had no mechanism — incident (iii).
|
||||
- **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 (fixed in session):** the one-column padding doubled against `.content`'s own
|
||||
2rem. The mockup's numbers were the total the page should show, not an amount to add.
|
||||
- **NOT-A-FINDING:** `build.sh`'s `workspace/` dirtied any clone it was run from; gitignored.
|
||||
- **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.
|
||||
- **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.
|
||||
- **Worth the operator's attention:** two sessions released this repo within an hour, and the
|
||||
second overwrote the first's `REPORT.md`. This file is the workaround; whether `REPORT.md` should
|
||||
stay a single overwritten file in a repo two sessions can release is a question for the operator,
|
||||
not a change I should make unasked.
|
||||
Reference in New Issue
Block a user