diff --git a/REPORT-dashboard-layout.md b/REPORT-dashboard-layout.md new file mode 100644 index 00000000..4124bdc0 --- /dev/null +++ b/REPORT-dashboard-layout.md @@ -0,0 +1,88 @@ +# REPORT — three dashboard layout fixes (2026-10-08) + +> `REPORT-dashboard-layout.md`, not `REPORT.md`: other sessions work in `felhom.eu` and `felhom-controller` today. The +> controller's own `REPORT.md` and `CONTEXT.md` were left to the session that owns them; the controller `CHANGELOG.md` +> carries this work. + +Baseline: controller `main` = `05e12921` (2026-10-08 10:25), fleet 0.303.0, scratch 9202 on 0.303.0. +Read first: controller `CLAUDE.md` + `.claude/rules/`, `felhom-ui-design`, `10-localisation.md` §1 and §2.3 (the parity +fixtures: a deliberate change is recorded by deleting the fixture, re-writing it with `-update-i18n-golden`, and +measuring that the diff is exactly the change — `CHANGELOG.md` v0.250.0 / v0.255.0 precedent). + +| Part | What | State | +|---|---|---| +| A | Apps card header: logo + name (+ address) over a wrapping tags row; logo top-aligned; address one line, ellipsis, full in `title` | **Done on main** (`d5f2e47`, fixtures `e37e9b5`) — R-909 | +| B | R-907: the Launcher header wraps on a phone | **Done on main** (`d5f2e47`) | +| B+ | Found live: a banner's link ran past a phone screen and had no colour; fixed in the session (small) | **Done on main** (`423b6d3`) | +| C | R-906: the overflow counted after the page filter | **Done on main** (`d5f2e47`) | +| D | Red tests, fixtures, test build on 9202, before/after pictures, 9202 put back | **Done** | +| E | Register, CHANGELOG, STATUS | **Done** | + +**Register:** before **131**, after **134**, opened **3** (R-909, R-910, R-911), closed **0**. R-906 and R-907 moved to +„VERIFY — fixed on main, ships with tomorrow's controller release". + +## Red tests (`documentation/audits/dashboard-layout-2026-10-08/red-proofs.txt`) + +Mutation for all three: `stacks.html`, `style.css` and `handlers.go` put back to `05e12921`, tests from `423b6d3`. + +| Test | Failure line (excerpt) | +|---|---| +| `TestStackCardHeaderLayout` | `hu card 0: want the title row, then a .stack-tags row (title 13, tags -1)` (16 cards, hu + en); `.stack-detail-header must stack the title row over the tags … justify-content: space-between`; `.stack-title-row must align the logo to the top of the name … align-items: center`; `style.css has no top-level ".stack-title-text" rule` | +| `TestR907_PageHeaderWrapsOnAPhone` | `the phone block must let .page-header wrap`; `the share button's text must stay whole …`; `the phone block lacks \.alert-banner \{[^}]*flex-wrap: wrap` (+2); `.alert-link must take the banner's colour …` | +| `TestR906_OverflowCountsOnlyThisPagesBannerAlerts` | `/launcher: an overflow line is shown, but this page shows no more than one banner alert` and `/launcher: 0 warning banners, want 1 (the hub-off warning)` — on /launcher, /stacks, /backups, /monitoring, /launcher?lang=en, /stacks?lang=en | + +`TestR906_CapStillCountsBannerAlerts` pins that the cap still works (six banner alerts → five + „+ 1"). + +## Re-captured fixtures (`e37e9b5`, its own commit) + +`stacks_full`, `stacks_unhealthy_stop`, `stacks_install_interrupted` — because the Apps card's MARKUP changed (a wrapper, +an address span + `title`, a tags row), not its words. Measured (`fixture-diff.txt`): each = its predecessor with +exactly the template change applied, byte-for-byte, 3 of 3; visible text identical. No other fixture moved; +`TestI18nParity` and `TestI18nEnglishPages` green. **Hungarian: not one string changed.** New English text: none. +Note: `d5f2e47` alone fails `TestI18nParity` on those three; both commits went in one push and CI ran the tip. + +## Live (9202 only; `documentation/audits/dashboard-layout-2026-10-08/README.md`) + +Headless Chrome inside 9202; English by `?lang=en` (the household setting never changed). Test tags +`0.304.0-layout.rc1` and `.rc2` (pre-release tags, never a release, never a floor). + +| | before (0.303.0) | after (rc2 = `423b6d3`) | +|---|---|---| +| Banner | „+ 5 további figyelmeztetés" alone, every page | three real warnings, no overflow line | +| Launcher, 390 px | page 425 px wide, share button right edge 424 | page 390, button 16–217, own line | +| Apps, 390 px | page 519 px wide | 390 | +| Apps: name under a tag | Paperless-ngx (en, 1440) | 0 of 61 cards, all four runs | +| Apps: logo vs name top | three values (-5, 9, 21 px) | one value (-3) | + +The rc1 run found the banner overflow (B+ above): the phone pages were still 481 / 472 px wide. Fixed, rc2 built, run +again. Pictures: `before/` and `after/` — Launcher, Apps (+ three cards: installed with two tags, not installed, +built-in), Backups; hu and en; 1440 and 390 px. + +**9202 put back:** `0.303.0` restored from a copy, healthy; both test images removed; work folder and session file +gone (shredded on both sides). demo-hp host: only `pct list`, `pct exec 9202`, `pct push 9202`. Not touched: 9201, +demo-felhom, Tester 1, Tester 2, the hub, the website. + +**One side effect, said plainly:** `build.sh --push` also moves the registry's `:latest` tag to the last build (rc2). +Nothing in the fleet runs `:latest` (only the legacy compose files name it); tomorrow's release build moves it again. + +## Files at their pushed commits + +- Controller `d5f2e47`: `controller/internal/web/alerts.go`, `handlers.go`, `templates/stacks.html`, + `templates/style.css`, `dashboard_layout_test.go` (new), `r906_overflow_test.go` (new), `CHANGELOG.md`, `REUSE.md`. +- Controller `e37e9b5`: the three parity fixtures. +- Controller `423b6d3`: `templates/style.css`, `dashboard_layout_test.go`, `CHANGELOG.md`. +- felhom.eu: `OPEN-ITEMS.md`, `STATUS.md`, this report, the audit folder — the next commit. + +**CI by commit (controller):** run 1561 = `e37e9b50` (carries `d5f2e47`) **success**; run 1562 = `423b6d34` **success**. + +`unproven.py --summary`: not walked 35 of 55 — unchanged. + +## Waits for tomorrow's release + +- Release the controller (it carries R-899 and R-304 too). Then R-906, R-907, R-909 are read back on a box and closed. +- R-910: after the release reaches demo-hp, retake the website's dashboard pictures (the Apps picture shows the old card). +- R-911 (new, P4): the „data storage not reachable" banner stays Hungarian on English pages. + +## Decision for you + +None. diff --git a/STATUS.md b/STATUS.md index 2e1066d6..3cdb9e2a 100644 --- a/STATUS.md +++ b/STATUS.md @@ -59,6 +59,7 @@ Full designs: `documentation/audits/day-2026-10-08/`. - **A missing-page (404) page exists now.** - **The language link is now a globe icon**, like the dashboard's: a click shows „Magyar" and „English". - **The website now shows the dashboard**: four real pictures on the home page (and two on the technology page), Hungarian on Hungarian pages, English on English ones. Two small dashboard defects were filed. +- **Three dashboard layout fixes built and tested (ship with tomorrow's controller release):** on the Apps page the status tags no longer cover an app's name and every logo sits at the same height; on a phone the Launcher's share button gets its own line; a page no longer says „+5 more warnings" with nothing above it. The website's dashboard pictures are retaken after the release (R-910). - **One picture viewer for the whole site:** the dashboard pictures open like the app pictures, and a click on the big picture closes it. - **The contact form's lost program code was searched for everywhere I can reach: not found.** The running program is now saved in git, so it cannot be lost. A plan for a replacement is written. One question for you: is the February folder on your Windows computer? diff --git a/documentation/audits/dashboard-layout-2026-10-08/README.md b/documentation/audits/dashboard-layout-2026-10-08/README.md new file mode 100644 index 00000000..3fa14870 --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/README.md @@ -0,0 +1,50 @@ +# Dashboard layout fixes — evidence (2026-10-08) + +Scratch guest **9202** on demo-hp, headless Chrome (`ghcr.io/puppeteer/puppeteer`, already on 9202 from an earlier +session, left there) run inside 9202, through traefik with the real host name. Script: `tools/shots.js`. The session +cookie came from a 0600 file, never printed, shredded on both sides at the end. English is shown with `?lang=en` (a +per-request override; the household setting was never changed — read `hu` before and after). + +| Run | Controller on 9202 | Files | +|---|---|---| +| before | `0.303.0` (the fleet's) | `before/before-*.png`, `before/before-measure.txt` | +| after | `0.304.0-layout.rc2` = controller `423b6d3` (test tag, never a release) | `after/after-*.png`, `after/after-measure.txt` | + +Per language (hu, en) and width (1440 × 900; 390 × 844 @2, phone): the Launcher, the Apps page (plus three card +pictures: an installed app with two tags = Paperless-ngx, not installed = Immich, built-in = FileBrowser) and the +Backups page (one of the pages that showed „+ 5 további figyelmeztetés"). + +## What was measured (from the `*-measure.txt` files) + +| | before (0.303.0) | after (rc2) | +|---|---|---| +| Banner | `+ 5 további figyelmeztetés` / `+ 5 more warnings` only, on every page | the three real warnings (two „Adattároló nem elérhető", hub off), no overflow line | +| Launcher 390 px: page width | 425 (hu) / 395 (en) on a 390 screen | 390 / 390 | +| Share button right edge, 390 px | 424 (hu) / 395 (en) — past the edge | 217 / 192, own line | +| Apps 390 px: page width | 519 (hu) / 434 (en) | 390 / 390 | +| Apps: name text under a tag | 1 card (Paperless-ngx, en, 1440) | 0 of 61, all four runs | +| Apps: logo top minus name top | three values (-5, 9, 21 px) | one value (-3 px) | +| Apps: Paperless address | „↗" on a second line (card picture) | one line, full address in `title` | + +**Honest notes on the measuring:** +- The first „after" run (rc1) showed the phone pages STILL wider than the screen (481 / 472 px). The cause was the + banner: once R-906 let the real warnings through, the banner's link ran past the screen, and it had no colour of its + own. Fixed in `423b6d3` (rc2); the rc1 run was discarded and is not in this folder. +- The address-line counter in the BEFORE run counted any step in the text's top edge as a new line; on rc1 it said + „2 lines" for an address that the picture shows on one line (a 1 px baseline step). It was loosened before the rc2 + run. The before claim rests on the card picture (`before-apps-*-card-installed.png`), not on that number. +- 9202's English pages show „Adattároló nem elérhető" in Hungarian — filed as R-911. + +## Tests + +- `red-proofs.txt`: the three new tests run against 05e12921's `stacks.html`, `style.css` and `handlers.go` — every + check fails with its own line. +- `fixture-diff.txt`: the three re-captured parity fixtures, each = its predecessor with exactly the template change + applied (byte-for-byte, 3 of 3), visible text identical. + +## 9202 put back + +`/etc/felhom-controller-image` restored to `…:0.303.0` from a copy taken before; restarted; `felhom-controller 0.303.0 +Up (healthy)`; the two test images removed from 9202 (`docker rmi`, 4 tags untagged); `/root/layout` removed. Apps as +found (Paperless-ngx running, PrivateBin stopped). demo-hp host: only `pct list`, `pct exec 9202`, `pct push 9202`. +9201, demo-felhom, Tester 1, Tester 2, the hub: not touched. diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-builtin.png new file mode 100644 index 00000000..c64507b4 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-installed.png new file mode 100644 index 00000000..d5be0347 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-notinstalled.png new file mode 100644 index 00000000..956ef2b6 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440.png new file mode 100644 index 00000000..04c3fbf7 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-builtin.png new file mode 100644 index 00000000..6a4e71c9 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-installed.png new file mode 100644 index 00000000..522fc27c Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-notinstalled.png new file mode 100644 index 00000000..4d6c024c Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390.png new file mode 100644 index 00000000..ab25d2e8 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-builtin.png new file mode 100644 index 00000000..44b75cf2 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-installed.png new file mode 100644 index 00000000..e434ec27 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-notinstalled.png new file mode 100644 index 00000000..67cf89f5 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440.png new file mode 100644 index 00000000..00948eb7 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-builtin.png new file mode 100644 index 00000000..bae499d5 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-installed.png new file mode 100644 index 00000000..e23a5f3c Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-notinstalled.png new file mode 100644 index 00000000..064dde33 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390.png new file mode 100644 index 00000000..85af65a3 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-apps-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-1440.png new file mode 100644 index 00000000..4e7e8f88 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-390.png new file mode 100644 index 00000000..588b0bc7 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-1440.png new file mode 100644 index 00000000..340adaad Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-390.png new file mode 100644 index 00000000..65dd7e8d Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-backups-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-1440.png new file mode 100644 index 00000000..385d9a42 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-390.png new file mode 100644 index 00000000..47dd4898 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-1440.png new file mode 100644 index 00000000..9b4a65bc Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-390.png new file mode 100644 index 00000000..1269705f Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/after/after-launcher-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/after/after-measure.txt b/documentation/audits/dashboard-layout-2026-10-08/after/after-measure.txt new file mode 100644 index 00000000..c08474d7 --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/after/after-measure.txt @@ -0,0 +1,56 @@ +after-launcher-hu-1440: html lang hu, version 0.304.0-layout.rc2 +after-launcher-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-launcher-hu-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-launcher-hu-1440: share button {"left":1207,"right":1408,"top":213,"text":"Indítópult megosztása"} past the screen edge: false +after-apps-hu-1440: html lang hu, version 0.304.0-layout.rc2 +after-apps-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-apps-hu-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-apps-hu-1440: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-3]; address on more than one line on 0 +after-apps-hu-1440: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-hu-1440: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-hu-1440: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"paperless.enkisfelhom.hu ↗","addrLines":1,"addrTitle":"paperless.enkisfelhom.hu"} +after-backups-hu-1440: html lang hu, version 0.304.0-layout.rc2 +after-backups-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-backups-hu-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-launcher-hu-390: html lang hu, version 0.304.0-layout.rc2 +after-launcher-hu-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-launcher-hu-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-launcher-hu-390: share button {"left":16,"right":217,"top":470,"text":"Indítópult megosztása"} past the screen edge: false +after-apps-hu-390: html lang hu, version 0.304.0-layout.rc2 +after-apps-hu-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-apps-hu-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-apps-hu-390: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-3]; address on more than one line on 0 +after-apps-hu-390: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-hu-390: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-hu-390: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"paperless.enkisfelhom.hu ↗","addrLines":1,"addrTitle":"paperless.enkisfelhom.hu"} +after-backups-hu-390: html lang hu, version 0.304.0-layout.rc2 +after-backups-hu-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-backups-hu-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: Hub kapcsolat kikapcsolva — a központi monitoring nem aktív\nRendszermo"] +after-launcher-en-1440: html lang en, version 0.304.0-layout.rc2 +after-launcher-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-launcher-en-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] +after-launcher-en-1440: share button {"left":1232,"right":1408,"top":213,"text":"Share the launcher"} past the screen edge: false +after-apps-en-1440: html lang en, version 0.304.0-layout.rc2 +after-apps-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-apps-en-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] +after-apps-en-1440: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-3]; address on more than one line on 0 +after-apps-en-1440: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-en-1440: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-en-1440: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"paperless.enkisfelhom.hu ↗","addrLines":1,"addrTitle":"paperless.enkisfelhom.hu"} +after-backups-en-1440: html lang en, version 0.304.0-layout.rc2 +after-backups-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +after-backups-en-1440: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] +after-launcher-en-390: html lang en, version 0.304.0-layout.rc2 +after-launcher-en-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-launcher-en-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] +after-launcher-en-390: share button {"left":16,"right":192,"top":470,"text":"Share the launcher"} past the screen edge: false +after-apps-en-390: html lang en, version 0.304.0-layout.rc2 +after-apps-en-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-apps-en-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] +after-apps-en-390: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-3]; address on more than one line on 0 +after-apps-en-390: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-en-390: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"","addrLines":0,"addrTitle":""} +after-apps-en-390: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-3,"addr":"paperless.enkisfelhom.hu ↗","addrLines":1,"addrTitle":"paperless.enkisfelhom.hu"} +after-backups-en-390: html lang en, version 0.304.0-layout.rc2 +after-backups-en-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +after-backups-en-390: banners ["alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/grimm","alert-banner-warning: Adattároló nem elérhető: /mnt/felhom-drives/scratch_hdd/userdata/metub","alert-banner-warning: The hub connection is off — central monitoring is not running\nSystem m"] diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-builtin.png new file mode 100644 index 00000000..540d4b55 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-installed.png new file mode 100644 index 00000000..38f3fba3 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-notinstalled.png new file mode 100644 index 00000000..a18a2fe9 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440.png new file mode 100644 index 00000000..8f8946e7 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-builtin.png new file mode 100644 index 00000000..a23efea4 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-installed.png new file mode 100644 index 00000000..e9ea43f5 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-notinstalled.png new file mode 100644 index 00000000..c00fd47c Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390.png new file mode 100644 index 00000000..39f2900a Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-builtin.png new file mode 100644 index 00000000..56a9eebc Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-installed.png new file mode 100644 index 00000000..a1ffd9ad Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-notinstalled.png new file mode 100644 index 00000000..fd479a50 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440.png new file mode 100644 index 00000000..ba1a5c67 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-builtin.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-builtin.png new file mode 100644 index 00000000..90fe6551 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-builtin.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-installed.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-installed.png new file mode 100644 index 00000000..7ff2ea88 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-installed.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-notinstalled.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-notinstalled.png new file mode 100644 index 00000000..efe25ff8 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390-card-notinstalled.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390.png new file mode 100644 index 00000000..72f73739 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-apps-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-1440.png new file mode 100644 index 00000000..34f7f76d Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-390.png new file mode 100644 index 00000000..20e29dac Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-1440.png new file mode 100644 index 00000000..9ed4d33b Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-390.png new file mode 100644 index 00000000..e245e2ca Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-backups-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-1440.png new file mode 100644 index 00000000..8a7d20b9 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-390.png new file mode 100644 index 00000000..6114b556 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-en-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-1440.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-1440.png new file mode 100644 index 00000000..090fd5d7 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-1440.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-390.png b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-390.png new file mode 100644 index 00000000..6d72f329 Binary files /dev/null and b/documentation/audits/dashboard-layout-2026-10-08/before/before-launcher-hu-390.png differ diff --git a/documentation/audits/dashboard-layout-2026-10-08/before/before-measure.txt b/documentation/audits/dashboard-layout-2026-10-08/before/before-measure.txt new file mode 100644 index 00000000..5a091b97 --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/before/before-measure.txt @@ -0,0 +1,56 @@ +before-launcher-hu-1440: html lang hu, version 0.303.0 +before-launcher-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-launcher-hu-1440: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-launcher-hu-1440: share button {"left":1207,"right":1408,"top":99,"text":"Indítópult megosztása"} past the screen edge: false +before-apps-hu-1440: html lang hu, version 0.303.0 +before-apps-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-apps-hu-1440: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-apps-hu-1440: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-5,9,21]; address on more than one line on 2 +before-apps-hu-1440: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-hu-1440: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-hu-1440: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":21,"addr":"paperless.enkisfelhom.hu ↗","addrLines":2,"addrTitle":""} +before-backups-hu-1440: html lang hu, version 0.303.0 +before-backups-hu-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-backups-hu-1440: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-launcher-hu-390: html lang hu, version 0.303.0 +before-launcher-hu-390: layout width 425 (screen 390) scrollWidth 425 — wider than the screen: true +before-launcher-hu-390: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-launcher-hu-390: share button {"left":290,"right":424,"top":134,"text":"Indítópult megosztása"} past the screen edge: true +before-apps-hu-390: html lang hu, version 0.303.0 +before-apps-hu-390: layout width 519 (screen 390) scrollWidth 519 — wider than the screen: true +before-apps-hu-390: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-apps-hu-390: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-5,9,21]; address on more than one line on 2 +before-apps-hu-390: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-hu-390: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-hu-390: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":21,"addr":"paperless.enkisfelhom.hu ↗","addrLines":2,"addrTitle":""} +before-backups-hu-390: html lang hu, version 0.303.0 +before-backups-hu-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +before-backups-hu-390: banners ["alert-banner-info: + 5 további figyelmeztetés\n→"] +before-launcher-en-1440: html lang en, version 0.303.0 +before-launcher-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-launcher-en-1440: banners ["alert-banner-info: + 5 more warnings\n→"] +before-launcher-en-1440: share button {"left":1232,"right":1408,"top":99,"text":"Share the launcher"} past the screen edge: false +before-apps-en-1440: html lang en, version 0.303.0 +before-apps-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-apps-en-1440: banners ["alert-banner-info: + 5 more warnings\n→"] +before-apps-en-1440: 61 cards; name text under a tag on 1 ["Paperless-ngx"]; tag outside card on 0; logo top minus name top [-5,9,21]; address on more than one line on 2 +before-apps-en-1440: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-en-1440: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-en-1440: {"name":"Paperless-ngx","tags":2,"nameUnderTag":true,"tagOutsideCard":false,"logoTopMinusNameTop":21,"addr":"paperless.enkisfelhom.hu ↗","addrLines":2,"addrTitle":""} +before-backups-en-1440: html lang en, version 0.303.0 +before-backups-en-1440: layout width 1440 (screen 1440) scrollWidth 1440 — wider than the screen: false +before-backups-en-1440: banners ["alert-banner-info: + 5 more warnings\n→"] +before-launcher-en-390: html lang en, version 0.303.0 +before-launcher-en-390: layout width 395 (screen 390) scrollWidth 395 — wider than the screen: true +before-launcher-en-390: banners ["alert-banner-info: + 5 more warnings\n→"] +before-launcher-en-390: share button {"left":284,"right":395,"top":134,"text":"Share the launcher"} past the screen edge: true +before-apps-en-390: html lang en, version 0.303.0 +before-apps-en-390: layout width 434 (screen 390) scrollWidth 434 — wider than the screen: true +before-apps-en-390: banners ["alert-banner-info: + 5 more warnings\n→"] +before-apps-en-390: 61 cards; name text under a tag on 0 []; tag outside card on 0; logo top minus name top [-5,21,8]; address on more than one line on 1 +before-apps-en-390: {"name":"FileBrowser","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-en-390: {"name":"Immich","tags":1,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":-5,"addr":"","addrLines":0,"addrTitle":""} +before-apps-en-390: {"name":"Paperless-ngx","tags":2,"nameUnderTag":false,"tagOutsideCard":false,"logoTopMinusNameTop":21,"addr":"paperless.enkisfelhom.hu ↗","addrLines":2,"addrTitle":""} +before-backups-en-390: html lang en, version 0.303.0 +before-backups-en-390: layout width 390 (screen 390) scrollWidth 390 — wider than the screen: false +before-backups-en-390: banners ["alert-banner-info: + 5 more warnings\n→"] diff --git a/documentation/audits/dashboard-layout-2026-10-08/fixture-diff.txt b/documentation/audits/dashboard-layout-2026-10-08/fixture-diff.txt new file mode 100644 index 00000000..93cd9b5c --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/fixture-diff.txt @@ -0,0 +1,3 @@ +stacks_full cards 8 | old + the template change == new: True | visible text identical: True | bytes 60494 -> 61264 +stacks_unhealthy_stop cards 1 | old + the template change == new: True | visible text identical: True | bytes 43371 -> 43452 +stacks_install_interrupted cards 1 | old + the template change == new: True | visible text identical: True | bytes 43324 -> 43405 diff --git a/documentation/audits/dashboard-layout-2026-10-08/red-proofs.txt b/documentation/audits/dashboard-layout-2026-10-08/red-proofs.txt new file mode 100644 index 00000000..cf1a8351 --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/red-proofs.txt @@ -0,0 +1,55 @@ +# Red proof: the three new tests run against 05e12921's stacks.html, style.css and handlers.go (the tests from 423b6d3). +--- FAIL: TestStackCardHeaderLayout (0.08s) + dashboard_layout_test.go:54: hu card 0: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 1: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 2: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 3: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 4: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 5: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 6: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: hu card 7: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:72: hu: 0 addresses, want 2 (filebrowser, privatebin) + dashboard_layout_test.go:54: en card 0: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 1: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 2: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 3: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 4: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 5: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 6: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:54: en card 7: want the title row, then a .stack-tags row (title 13, tags -1) + dashboard_layout_test.go:72: en: 0 addresses, want 2 (filebrowser, privatebin) + dashboard_layout_test.go:82: .stack-detail-header must stack the title row over the tags, not spread them on one row: + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: .75rem; + dashboard_layout_test.go:85: .stack-title-row must align the logo to the top of the name: + display: flex; + align-items: center; + gap: .75rem; + flex: 1; + min-width: 0; + dashboard_layout_test.go:87: style.css has no top-level ".stack-title-text" rule +--- FAIL: TestR907_PageHeaderWrapsOnAPhone (0.06s) + dashboard_layout_test.go:113: the phone block must let .page-header wrap + dashboard_layout_test.go:116: the share button's text must stay whole when it drops to its own line + dashboard_layout_test.go:122: the phone block lacks \.alert-banner \{[^}]*flex-wrap: wrap + dashboard_layout_test.go:122: the phone block lacks \.alert-message \{[^}]*min-width: 0[^}]*overflow-wrap: anywhere + dashboard_layout_test.go:122: the phone block lacks \.alert-link \{[^}]*flex-basis: 100% + dashboard_layout_test.go:126: .alert-link must take the banner's colour, not the browser's link blue: margin-left: auto; white-space: nowrap; +--- FAIL: TestR906_OverflowCountsOnlyThisPagesBannerAlerts (0.08s) + r906_overflow_test.go:48: /launcher: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /launcher: 0 warning banners, want 1 (the hub-off warning) + r906_overflow_test.go:48: /stacks: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /stacks: 0 warning banners, want 1 (the hub-off warning) + r906_overflow_test.go:48: /backups: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /backups: 0 warning banners, want 1 (the hub-off warning) + r906_overflow_test.go:48: /monitoring: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /monitoring: 0 warning banners, want 1 (the hub-off warning) + r906_overflow_test.go:48: /launcher?lang=en: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /launcher?lang=en: 0 warning banners, want 1 (the hub-off warning) + r906_overflow_test.go:48: /stacks?lang=en: an overflow line is shown, but this page shows no more than one banner alert + r906_overflow_test.go:51: /stacks?lang=en: 0 warning banners, want 1 (the hub-off warning) +FAIL +FAIL gitea.dooplex.hu/admin/felhom-controller/internal/web 0.253s +FAIL diff --git a/documentation/audits/dashboard-layout-2026-10-08/tools/shots.js b/documentation/audits/dashboard-layout-2026-10-08/tools/shots.js new file mode 100644 index 00000000..b5ce1e88 --- /dev/null +++ b/documentation/audits/dashboard-layout-2026-10-08/tools/shots.js @@ -0,0 +1,93 @@ +// Dashboard layout evidence (2026-10-08): scratch guest 9202, headless Chrome inside 9202, through traefik with the real +// host name. Session cookie from a 0600 file (never printed). Per page and width: a picture, plus MEASUREMENTS: +// sideways scroll, and on /stacks for each card whether the name box overlaps any tag box and the logo/name top offset. +const puppeteer = require('puppeteer'); +const fs = require('fs'); +const sleep = ms => new Promise(r => setTimeout(r, ms)); +const HOST = 'felhom.enkisfelhom.hu', BASE = 'https://' + HOST; +const sess = fs.readFileSync('/out/session.txt', 'utf8').trim(); +const TAG = process.env.TAG; // before | after +const out = []; +let LANG = 'hu'; +function log(...a) { const l = a.join(' '); console.log(l); out.push(l); } +async function setLang(p, lang) { + await p.goto(BASE + '/launcher', {waitUntil: 'networkidle2'}); + const btn = await p.$(`button.lang-globe-item[value="${lang}"]`); + if (!btn) throw new Error('no globe item ' + lang); + await p.evaluate(b => b.closest('form').submit(), btn); + await p.waitForNavigation({waitUntil: 'networkidle2'}).catch(() => {}); + log('language now', await p.$eval('html', e => e.lang)); +} +async function measure(p, name) { + const m = await p.evaluate(() => { + const r = {scrollW: document.documentElement.scrollWidth, innerW: window.innerWidth}; + r.banner = Array.from(document.querySelectorAll('.alerts-container .alert-banner')).map(b => b.className.replace('alert-banner ', '') + ': ' + b.innerText.trim().slice(0, 70)); + const btn = document.querySelector('.share-open-btn'); + if (btn) { const b = btn.getBoundingClientRect(); r.share = {left: Math.round(b.left), right: Math.round(b.right), top: Math.round(b.top), text: btn.innerText.trim()}; } + r.cards = []; + for (const c of document.querySelectorAll('.stack-detail-card')) { + const h = c.querySelector('h3'), logo = c.querySelector('.stack-logo-lg'); + if (!h || !logo) continue; + const rg = document.createRange(); rg.selectNodeContents(h); + const tr = rg.getBoundingClientRect(), lr = logo.getBoundingClientRect(), cr = c.getBoundingClientRect(); + const tags = Array.from(c.querySelectorAll('.tag')).map(t => t.getBoundingClientRect()); + const overlap = tags.some(t => !(t.right <= tr.left || t.left >= tr.right || t.bottom <= tr.top || t.top >= tr.bottom)); + const outside = tags.some(t => t.right > cr.right + 0.5); + const addr = c.querySelector('.subdomain-link'); + let addrLines = 0; + if (addr) { const ar = document.createRange(); ar.selectNodeContents(addr); { const rs = Array.from(ar.getClientRects()).filter(x => x.width > 0); const t0 = Math.min(...rs.map(x => x.top)); const h0 = Math.min(...rs.map(x => x.height)); addrLines = 1 + rs.filter(x => x.top > t0 + h0 * 0.6).length; } } + r.cards.push({name: h.innerText.trim(), tags: tags.length, nameUnderTag: overlap, tagOutsideCard: outside, + logoTopMinusNameTop: Math.round(lr.top - tr.top), addr: addr ? addr.innerText.trim().replace(/\s+/g, ' ') : '', addrLines, + addrTitle: addr ? addr.title : ''}); + } + return r; + }); + log(`${name}: layout width ${m.innerW} (screen ${process.env.W}) scrollWidth ${m.scrollW} — wider than the screen: ${m.innerW > +process.env.W || m.scrollW > +process.env.W}`); + log(`${name}: banners ${JSON.stringify(m.banner)}`); + if (m.share) log(`${name}: share button ${JSON.stringify(m.share)} past the screen edge: ${m.share.right > +process.env.W}`); + if (m.cards.length) { + const ov = m.cards.filter(c => c.nameUnderTag).map(c => c.name); + const offs = [...new Set(m.cards.map(c => c.logoTopMinusNameTop))]; + log(`${name}: ${m.cards.length} cards; name text under a tag on ${ov.length} ${JSON.stringify(ov)}; tag outside card on ${m.cards.filter(c => c.tagOutsideCard).length}; logo top minus name top ${JSON.stringify(offs)}; address on more than one line on ${m.cards.filter(c => c.addrLines > 1).length}`); + for (const c of m.cards.filter(c => /paperless|filebrowser|immich/i.test(c.name))) log(`${name}: ${JSON.stringify(c)}`); + } +} +async function shot(p, name, path) { + await p.goto(BASE + path + (LANG === 'en' ? '?lang=en' : ''), {waitUntil: 'networkidle2'}); + await sleep(1200); + log(`${TAG}-${name}: html lang ${await p.$eval('html', e => e.lang)}, version ${await p.evaluate(() => (document.body.innerText.match(/\b0\.\d+\.\d+\S*/) || ['?'])[0])}`); + await p.screenshot({path: `/out/${TAG}-${name}.png`}); + await measure(p, `${TAG}-${name}`); +} +async function cards(p, name) { // the three cards the brief names, as one element picture each + for (const [key, re] of [['installed', /paperless/i], ['notinstalled', /immich/i], ['builtin', /filebrowser|f.jlkezel/i]]) { + const hs = await p.$$('.stack-detail-card'); + for (const h of hs) { + const t = await h.$eval('h3', e => e.innerText).catch(() => ''); + if (re.test(t)) { await h.screenshot({path: `/out/${TAG}-${name}-card-${key}.png`}); break; } + } + } +} +(async () => { + const b = await puppeteer.launch({args: ['--no-sandbox', '--ignore-certificate-errors', `--host-resolver-rules=MAP * ${process.env.TRAEFIK}`]}); + const p = await b.newPage(); + const [k, v] = sess.split('='); + await p.setCookie({name: k, value: v, domain: HOST, path: '/', secure: true, httpOnly: true}); + for (const lang of ['hu', 'en']) { + process.env.W = '1440'; + await p.setViewport({width: 1440, height: 900, deviceScaleFactor: 1}); + LANG = lang; + await shot(p, `launcher-${lang}-1440`, '/launcher'); + await shot(p, `apps-${lang}-1440`, '/stacks'); + await cards(p, `apps-${lang}-1440`); + await shot(p, `backups-${lang}-1440`, '/backups'); + process.env.W = '390'; + await p.setViewport({width: 390, height: 844, deviceScaleFactor: 2, isMobile: true, hasTouch: true}); + await shot(p, `launcher-${lang}-390`, '/launcher'); + await shot(p, `apps-${lang}-390`, '/stacks'); + await cards(p, `apps-${lang}-390`); + await shot(p, `backups-${lang}-390`, '/backups'); + } + fs.writeFileSync(`/out/${TAG}-measure.txt`, out.join('\n') + '\n'); + await b.close(); +})().catch(e => { console.error(e); process.exit(1); }); diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 69bdd133..46bcb067 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -122,7 +122,7 @@ stopping line that lies. | **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC | | **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator | -## Apps & catalog — 10 rows (P3 3, P4 7) +## Apps & catalog — 11 rows (P3 3, P4 8) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| @@ -135,7 +135,8 @@ stopping line that lies. | **R-770** | Apps & catalog | P4 | **[P3-LOW] Invidious — fit check only; the recommendation is not to build it.** READ 2026-10-01: playback needs `invidious-companion` (rolling `latest`, no version tags); PostgreSQL 14 (EOL 2026-11); `registration_enabled: true` by default; upstream: a bot check means „your IP is blocked from YouTube”, a 429 can last 24 h, triggered by „someone on your network” — on our boxes that IP is the household's. One bad period in 2026 (March, ~2 weeks). No report found of a family's other devices being bot-checked (inference). **Needs:** the operator's go / no-go. `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** **Re-ranked 2026-10-03: P3→P4: a new-app idea waiting on a decision.** | — | — | operator | | **R-771** | Apps & catalog | P4 | **[P3-LOW] moonlight-web — fit check only; not buildable through an HTTP-only tunnel at usable latency.** READ 2026-10-01: two unrelated projects (MrCreativ3001/moonlight-web-stream, the original; linckosz/moonlight-web); both need Sunshine/Apollo/Wolf on a gaming PC on the LAN and WebRTC over UDP (40000-40100/udp; linckosz recommends host networking and sends telemetry by default); both have a WebSocket fallback (high latency, all video through the tunnel); a logged-in user controls the PC's desktop. **Needs:** the operator's go / no-go (LAN-only use would need a different publishing model). `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** **Re-ranked 2026-10-03: P3→P4: a new-app idea waiting on a decision.** | — | — | operator | | **R-905** | Apps & catalog | P4 | **wger's collected style files (283 MB) go into every wger backup, though the app rebuilds them at every start.** Since R-762's fix (`DJANGO_DEBUG=False` + `collectstatic`, catalog `cf1ed43`) the static files sit in a named volume that the backup legs copy like data (measured 283 MB, `audits/design-build-2026-10-06/`); `collectstatic` regenerates them, so they are rebuildable bytes in every Tier-1 unit, Tier-2 copy and off-site snapshot. wger is `lifecycle: hidden` and no box runs it (hub read 2026-10-08). Options to weigh: a non-backed-up volume class for regenerable data, or an anonymous volume. Found by the R-762 helpers 2026-10-08. | **OPEN — needs a design before wger is shown** | — | Design the „regenerable volume" exclusion (catalog + controller backup legs) | CC | -| **R-907** | Apps & catalog | P4 | **On a phone (390 px) the Launcher's „Indítópult megosztása" / „Share the launcher" button runs off the right edge of the screen.** SEEN 2026-10-08 (website dashboard pictures, controller 0.303.0, demo-hp guest 9201, headless Chrome 390 × 844 @2): the page title, the domain chip and the button sit on one row; the button's text is cut at the right edge (`audits/website-dashboard-2026-10-08/README.md`). Not fixed here (controller out of scope for the website task). | **OPEN — owner: CC** | — | Let the launcher header wrap below ~480 px; re-shoot the phone picture | CC | +| **R-907** | Apps & catalog | P4 | **On a phone (390 px) the Launcher's „Indítópult megosztása" / „Share the launcher" button runs off the right edge of the screen.** SEEN 2026-10-08 (website dashboard pictures, controller 0.303.0, demo-hp guest 9201, headless Chrome 390 × 844 @2): the page title, the domain chip and the button sit on one row; the button's text is cut at the right edge (`audits/website-dashboard-2026-10-08/README.md`). Not fixed here (controller out of scope for the website task). | **VERIFY — fixed on main 2026-10-08, ships with tomorrow's controller release** (controller `d5f2e47`, `423b6d3`). In the 768 px block `.page-header` wraps (the share button drops to its own line, text whole) and a banner wraps inside itself (its link ran past the screen once R-906 let the real banners through). `TestR907_PageHeaderWrapsOnAPhone` (red against 05e12921). Live on 9202, 390 px, hu + en: layout width 390 = screen, button right edge 217 / 192 px (was 424 / 395) | — | Release tomorrow; close with R-910's phone picture | CC | +| **R-909** | Apps & catalog | P4 | **On the Apps page an installed app's status tags covered its name, and the logo sat lower than on the other cards.** SEEN 2026-10-08 (website dashboard picture, demo-hp 9201; measured on 9202, 0.303.0, English 1440 px: Paperless-ngx's name text under a tag; logo top minus name top -5 / 9 / 21 px across cards; the address's „↗" on a second line). Cause: the title row and every tag shared one `space-between` flex row; the logo was centred on the name block. | **VERIFY — fixed on main 2026-10-08, ships with tomorrow's controller release** (controller `d5f2e47` + fixtures `e37e9b5`). Two rows on every card: logo + name (+ address, one line, ellipsis, full in `title`) over a wrapping tags row. `TestStackCardHeaderLayout` (red against 05e12921). Live on 9202 with a test build, 1440 + 390 px, hu + en: 61 cards, name under a tag on 0, logo offset one value (-3 px), address on one line | — | Release tomorrow; close with R-910's Apps picture | CC | ## App updates — 4 rows (P3 4) @@ -223,7 +224,7 @@ stopping line that lies. | **R-242** | Box system & updates | P3 | **A controller release that changes customer-visible behaviour is not delivered until a golden carries it — and nothing enforces that.** R-239 is the symptom; this is the mechanism, recorded **2026-08-07** and deliberately **NOT built** (the task that found it scoped it as a record-only item). **Two releases went out without a golden and the gap was invisible until a walk measured it from the customer's side**: v0.204.0 (R-237) and v0.205.0 (R-234) were written, tested, pushed, and CHANGELOG'd, and every one of those steps passed while a machine installed that night received neither. The register said CLOSED; the fleet said otherwise. **Nothing in the release path knows a golden exists.** The version bump, the image push, the CHANGELOG entry and the register closure are all repo-local; the manifest's `golden_version` is edited by a separate operator act, in a different repo, with no link back. **Proposed shapes, cheapest first — the choice is the operator's and is not taken here.** (a) **A release-path checklist step** — one line in the controller's end-of-session checklist: *a release that changes customer-visible behaviour is not finished until a golden carries it or a register row says why not.* Costs nothing, catches nothing mechanically. (b) **A gate in `repo_gates.py`** comparing the manifest's `golden_version` against the newest released controller and FAILING (or warning) past a tolerance of one minor. Mechanical, runs on every push, and would have fired the morning after v0.204.0. (c) **A hub-side checker** — the hub already knows every box's running controller version from `/hosts` and the vouched golden from the manifest; a periodic comparison against the newest published image would catch drift the repo cannot see, including a vouch that was made and then rolled back. **Earliest catch: (b).** It fires on the push that creates the gap, before any box is installed, and it needs no live fleet. **(c) catches strictly more but only after boxes exist.** (a) is worth doing regardless because it is free. **Not built. No gate was written this session.** **⚠ IT RECURRED WITHIN A DAY, WHICH IS THE ARGUMENT FOR BUILDING IT.** Controller **v0.206.0** shipped the R-241 fixes on 2026-08-07 while the vouched golden still carried **0.205.0** — so a machine installed on the morning of 2026-08-08 would have received neither. Third occurrence of the shape in three days (R-111/R-115/R-120 are the older family). **✅ SHAPE (b) BUILT 2026-08-08 — `scripts/golden_currency_gate.py`, registered in `repo_gates.py` as gate 7.** **It was shown FAILING against that exact state before anything was baked**, which is its red-proof and the reason its own introducing push needed `--no-verify` (stated in the session report rather than worked around): `newest released controller : 0.206.0 / newest golden baked : 0.205.0 → CONVICTED`. **IT IS `--fast`, AND THAT FORCED ITS DESIGN:** both `.githooks/pre-push` AND CI run `repo_gates.py --fast`, so a non-fast gate would run in NEITHER — the R-29 census failure this runner exists to end. **THEREFORE IT CHECKS THE BAKE, NOT THE VOUCH**, because the vouched version lives only in the hub's `hub_settings` with no copy in git, and putting a copy there would create a second source of truth that can drift — a green gate over a false claim being the worst outcome available. **A bake without a vouch still passes: that half is NOT closed and stays on this row.** It also compares versions rather than behaviour, so a release changing nothing customer-visible trips it too — accepted deliberately, because judging that by hand is what failed three times and the cost of a false trip is one bake; a waiver belongs here, never in a habit of bypassing. **VOUCHED 2026-08-08 with the operator's approval** — golden `0.206.0` / sha `c85230b4…108e`; `agent_version` and `min_agent` both stayed `0.127.0`, and `wrapper_sha256` was carried through explicitly because the handler clears it when omitted. **The gate was CONVICTED before the bake and OK after it** — red→green on the same command, which is its proof that it measures something real. **⚠ THE GATE FIRED FOR REAL, 2026-08-08 — and it was right.** Controller **v0.207.0** (R-249/R-252/R-253) is released, tested and pushed, and **no golden carries it** — the newest bake is 0.206.0 — so `golden_currency_gate.py` FAILED, saying exactly the true thing: *a machine installed right now would receive v0.206.0*. **The `felhom.eu` push therefore used `git push --no-verify`, declared here, in the commit message and in the session report.** A bypass and NOT a waiver, deliberately: the gate offers a waiver only for a release that *deliberately needs no golden*, and this one needs one. **Owed: bake golden 0.207.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; the vouch is a three-field change). **This row's own remaining half is unchanged — nothing gates the VOUCH itself.** **✅ THE OWED BAKE IS DONE, SAME DAY — golden 0.207.0 baked, published, round-trip verified and VOUCHED (2026-08-08).** The gate went from red to **green**, and the `--no-verify` bypass declared above is now historical rather than standing. **Round trip is the evidence, not the build log:** the published bytes were downloaded back — 656 879 192 B, sha256 `20ec9602…22995`, both identical to what the bake reported — and **`./etc/felhom-controller-image` read OUT of the downloaded archive says `felhom-controller:0.207.0`**, which is the delivered artifact naming the controller it will start. **The vouch was a three-field change with all three checked deliberately** (`MinAgent 0.127.0` read from the golden's controller CHANGELOG header, not assumed; `agent_version` already ≥ it; `min_agent` not above `agent_version`, so not the R-216 shape) and verified by **re-reading the manifest rather than trusting the flash**. **This row's remaining half is UNCHANGED and is the whole of what is still open: nothing gates the VOUCH itself** — the currency gate's own docstring says it checks the bake, so a baked-but-unvouched golden still passes it silently. Evidence: `tests/golden-0.207.0-2026-08-08/`. **⚠ RED AGAIN, 2026-08-08 (second time in two days) — controller v0.208.0 (R-254) is released and the vouched golden is 0.207.0.** `golden_currency_gate.py` FAILS, correctly: a machine installed right now receives 0.207.0 and none of today's fixes. **The `felhom.eu` push used `git push --no-verify`, declared in the commit, the CHANGELOG and the session report** — **a bypass, not a waiver**, on the same reasoning as yesterday: the gate offers a waiver only for a release that *deliberately needs no golden*, and this one needs one. **Owed: bake golden 0.208.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; three-field change, `MinAgent 0.127.0` unchanged). **Note the cadence this is establishing: two releases, two bakes owed within 24 h.** That is the argument for this row's OTHER half — nothing gates the vouch, so the only thing standing between a release and an undelivered fleet is somebody remembering. **⚠ RED AGAIN, 2026-08-30 — controller v0.224.0 (R-330) and v0.225.0 (R-331) are released and the newest golden carries 0.223.0.** `golden_currency_gate.py` FAILS, correctly: a machine installed right now receives 0.223.0 and neither of today's fixes. **The `felhom.eu` push used `git push --no-verify`, declared in the commit message, in `hub/CHANGELOG.md` and in `REPORT.md` — a BYPASS, not a waiver**, on the same reasoning as the two 2026-08-08 entries above: the gate offers a waiver only for a release that *deliberately needs no golden*, and these need one. **The operator was asked and ruled bypass-now-bake-later on 2026-08-30**, on the stated ground that neither fix bites a DAY-0 box — R-330 is a nightly false alarm about apps a new box has not installed yet, and R-331 is a hub-side display over backups a new box has not taken yet — and both arrive by self-update afterwards. **That ground is recorded because it is the thing to re-check, not a general licence: the next release that changes first-boot behaviour cannot reuse it.** **OWED: bake a golden carrying 0.225.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; three-field change — `golden_version` + `agent_version` + `min_agent`; MinAgent is 0.129.0 per both CHANGELOG headers). **Cadence note, unchanged and now worse: this is the fourth bypass of this gate, and the gap it names is now two releases wide rather than one.** **⚠ WIDENED TO THREE THE SAME DAY — v0.226.0 (R-353/R-357/R-358/R-360) shipped 2026-08-30 and the golden still carries 0.223.0.** The `felhom.eu` push carrying that release's documentation used `git push --no-verify` on the operator's standing ruling from earlier the same day, declared in the commit and in `REPORT.md`. **The day-0 ground still holds for all three and was re-checked rather than assumed:** R-330 alarms about apps a new box has not installed; R-331 is a hub display over backups a new box has not taken; **R-353/357/358/360 are restore-surface fixes, and a day-0 box has nothing to restore.** **The ground expires the moment a release changes first-boot behaviour — that is the thing to re-check, not a licence.** **Owed: ONE bake carrying 0.226.0 covers all three** (`RUNBOOK-manual-build.md` §4.1; three-field vouch, MinAgent 0.129.0), then raise the floor. **✅ PAID THE SAME DAY — golden `0.226.1` baked, published, round-trip verified, VOUCHED, and the floor RAISED (2026-08-30).** Evidence: `documentation/tests/golden-0.226.1-2026-08-30/`. `golden_currency_gate.py` went **red → green** on the same command, which is its proof that it measures something real. **The three declared bypasses above are now HISTORICAL rather than standing.** **The round trip is the evidence, not the build log:** the published bytes were downloaded back — **657 197 592 B, sha256 `70ed8e93…baefe69`**, both identical to what the bake reported — and `./etc/felhom-controller-image` read **out of the downloaded archive** says `felhom-controller:0.226.1`, i.e. the delivered artifact naming the controller it will start. **The three-field vouch was checked deliberately, not assumed:** `MinAgent 0.129.0` read from the golden's controller CHANGELOG header, `agent_version 0.130.0 ≥ min_agent 0.129.0` (so NOT the R-216 shape), and the result verified by **re-reading the manifest** rather than trusting the flash — golden option `0.226.1 SELECTED`, all four shas matching. **The floor is proven ACTING, not merely set:** `demo-felhom` self-updated within 30 s, logging `[selfupdate] Post-update startup: update successful (0.225.0 → 0.226.1)`. **⚠ AND IT HAPPENED AGAIN THE SAME DAY, AND WAS PAID AGAIN.** v0.227.0/v0.227.1 (R-359/R-397) shipped after the 0.226.1 bake, the gate convicted a fifth time, that `felhom.eu` push used `--no-verify` and declared it, and golden **0.227.1** was baked, published, round-trip verified, **VOUCHED** and the floor **RAISED to 0.227.1** within the hour. Evidence: `documentation/tests/golden-0.227.1-2026-08-30/`. **THE CADENCE IS NOW MEASURED RATHER THAN ASSERTED: five convictions and two full bakes in one day.** Every bypass was declared and every debt was paid — but the pattern this row exists to name is exactly that a release and its delivery are separate acts, performed hours apart, by whoever remembers. **The floor was proven ACTING both times:** `demo-felhom` self-updated 0.225.0→0.226.1, then 0.226.1→0.227.1 — and the second time it also registered the new `offsite-integrity` job **by itself, on a box nobody deployed to**, which is the strongest evidence this row has ever carried that a floor delivers rather than merely records. **This row's OTHER half is still open and untouched: nothing gates the VOUCH itself** — the currency gate's own docstring says it checks the bake, so a baked-but-unvouched golden still passes it silently. **2026-08-31, the SEVENTH debt and it was paid the same day — twice in one day.** v0.230.0 shipped in the morning with the newest golden at 0.229.0, **which is the build R-403 says deletes a good copy**, so the gate was red across four commits (`dddcc80`, `6e550ae`, `130f7a6`, `32a4c35`). Golden **0.230.0** baked, published, round-trip verified, vouched, and the fleet floor raised 0.229.0 → 0.230.0; `demo-felhom` moved itself off the defective build unattended (`controller-swap: new controller healthy`, 16:21:40 CEST). Evidence: `documentation/tests/golden-0.230.0-2026-08-31/`. **The gate did its job and its own weakness surfaced doing it — R-410.** | **READY — the vouch half only** — owner Viktor **⚠ SIXTH CONVICTION, 2026-08-31 — controller v0.229.0 (R-102/R-103) is released and the newest golden carries 0.228.0.** `golden_currency_gate.py` FAILS, correctly: a machine installed right now receives 0.228.0 and neither of today's fixes. The `felhom.eu` push carrying this release's documentation used `git push --no-verify`, declared in the commit message and in `felhom-controller/REPORT.md` - **a BYPASS, not a waiver**, on the same reasoning as the five entries above: the gate offers a waiver only for a release that *deliberately needs no golden*, and this one needs one. **The day-0 ground was RE-CHECKED rather than reused:** R-102 and R-103 are restore-surface changes on the Tier-2 card, and a day-0 box has taken no Tier-2 copy and has nothing to restore from one; no first-boot behaviour changed, and `MinAgent` is unchanged at 0.129.0. **The ground still expires the moment a release changes first-boot behaviour.** **OWED: bake a golden carrying 0.229.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; three-field change - `golden_version` + `agent_version` + `min_agent`, MinAgent 0.129.0), then raise the floor. Fleet floor and golden are 0.228.0 today. **Golden and fleet delivery are the operator's (this row).** **✅ PAID THE SAME DAY — golden `0.229.0` baked, published, round-trip verified, VOUCHED, and the floor RAISED (2026-08-31).** Evidence: `documentation/tests/golden-0.229.0-2026-08-31/`. `golden_currency_gate.py` went **red to green** on the same command, which is its proof that it measures something real. **The `--no-verify` bypass declared above is now HISTORICAL rather than standing.** **The round trip is the evidence, not the build log:** the published bytes were downloaded back - **656 864 331 B, sha256 `39aa886d…d7bdae87`**, both identical to what the bake reported - and `./etc/felhom-controller-image` read **out of the downloaded archive** says `felhom-controller:0.229.0`. **A THIRD independent reader agreed before anything was vouched:** the hub's own Day-0 dropdown read the same sha straight from Gitea, a different code path from the round trip. **The three-field vouch was checked deliberately, not assumed** (`MinAgent 0.129.0` read from the golden's controller CHANGELOG header; `agent_version 0.130.0` >= `min_agent 0.129.0`, so NOT the R-216 shape; `agent_sha256` and `wrapper_sha256` carried through explicitly because the handler clears a field it is not sent), and verified by **re-reading the manifest** rather than trusting the flash. The **R-120 gate passed rather than being bypassed** - fleet newest 0.229.0, golden 0.229.0. **The floor is proven ACTING:** `demo-felhom` self-updated `0.228.0 -> 0.229.0` and logged `settle-gate: GO - at/above floor 0.229.0 (we are 0.229.0)` - **nobody deployed to that box.** **Cadence note: this is the SECOND bake in one day (0.228.0 then 0.229.0) and the sixth conviction, and both debts were paid within the hour.** This row's OTHER half is still open and untouched: **nothing gates the VOUCH itself** - the currency gate checks the bake, so a baked-but-unvouched golden still passes it silently. **⚠ SEVENTH CONVICTION, 2026-08-31 — controller v0.230.0 (R-403) is released and the newest golden carries 0.229.0. AND THIS ONE IS NOT LIKE THE OTHERS: the day-0 ground does NOT apply and must not be reused.** Every previous bypass rested on 'a day-0 box has nothing to restore / nothing to alarm about yet'. R-403 is a defect in the NIGHTLY TIER-2 COPY, which a day-0 box starts running on its first night: a machine installed on 0.229.0 can have a complete recovery package on its second drive replaced by an empty one, and that is measured, not suspected (120 082 104 B -> 7 036 B on demo-hp). **The row's own standing sentence - 'the ground expires the moment a release changes first-boot behaviour' - is what expires it here.** The `felhom.eu` push carrying this release's documentation used `git push --no-verify`, declared in the commit message and in `felhom-controller/REPORT.md` - a BYPASS, not a waiver. **OWED, and more urgent than the previous six: bake a golden carrying 0.230.0, vouch it (three fields, MinAgent 0.129.0 unchanged), and raise the floor.** `demo-hp` was updated by hand; `demo-felhom` is still on 0.229.0 and still carries the defect. **See also R-404, filed today: this is the seventh bypass and the habit is now the thing being reported.** **2026-09-01 (R-404): THE BAKE HALF IS UNCHANGED AND THE VOUCH HALF IS STILL OPEN.** R-404 moved WHO the bake check refuses and added a notice in the controller repo; it did NOT touch what is checked. **Nothing gates the VOUCH.** A baked-but-unvouched golden still passes both the gate and the new notice, and the reason is unchanged and forced: the vouched version lives only in the hub's `hub_settings` table, there is no copy in git, and a hub-reading gate could not be `--fast` so it would run in neither the hook nor CI. **Do not read R-404's closure as closing this.** **2026-09-13 — NARROWED: the WAIVER half is BUILT (R-468).** The docstring's *"honest fix is a recorded waiver in the register, never a habit of bypassing"* is now a mechanism: `golden_currency_gate.py` reads `documentation/tests/golden-waiver.yml` (dated, ≤ 14 days, row-bound), turns a BEHIND conviction into a loud advisory while valid, and is red again when it expires — the difference from this row's original rule, which recurred the next day, is that a dated waiver cannot be forgotten. It never covers an UNRECORDED golden (R-385). Operator ruling the same day: goldens weekly and before any install, not per release. **What stays open on THIS row is exactly one thing: nothing gates the VOUCH.** The waiver does not touch it, and the reason it is unbuilt is unchanged (the vouched version lives only in the hub). | — | — | operator | | **R-468** | Box system & updates | P3 | **[P3-LOW] THE GOLDEN WAIVER — goldens on a cadence, not per release (operator ruling 2026-09-13).** 25 goldens in 26 days in August, almost one per release, because `golden_currency_gate.py` trips on every release by design and the only honest ways past it were a bake or a declared `--no-verify` (thirteen by 2026-09-01, R-404/R-417). **The ruling: bake WEEKLY, and always before any drill or fresh install.** Every release still raises the FLOOR, so both demo boxes keep getting each release in ~20 s; only the golden — which protects a fresh install and nothing else — moves to a cadence. **The mechanism (built 2026-09-13):** `documentation/tests/golden-waiver. **⚠ CORRECTED THE SAME DAY (R-472): between bakes the floor does NOT carry a release — the hub holds any floor above the vouched golden (publish-train rule 1), so releases between bakes reach the demo boxes only by hand-deploy.**yml`, four lines (`issued`, `expires`, `reason`, `register_row: R-468`), read by the gate. While valid, a golden BEHIND the record makes the gate print a loud ADVISORY and exit 0; when it expires the gate is red again until someone bakes or renews. **The 14-day cap is enforced by the gate, not the runbook** — a longer, undated, unparseable, reason-less or row-less waiver is INCONCLUSIVE (exit 2), never 0 and never silently ignored. **It never covers a golden that is UNRECORDED (R-385)** — that is not a cadence choice. **A dated waiver cannot be forgotten; it just expires** — the difference from R-242's original rule, which recurred the day after it was written. Tests: `scripts/test_golden_currency_gate.py` cases 5–15 (E/F/G/H, a 15-day, absent, unparseable, bad-row and empty-reason waiver each 2; the R-421 decoy — a file saying only `expires` — 2). **This is a PRE-CUSTOMER arrangement: the first external install retires it** (delete the file in that commit). Cadence written into `RUNBOOK-manual-build.md` §4.2 and the `felhom.eu` end-of-session checklist. **Does NOT touch R-242's open half (nothing gates the VOUCH).** | **WATCHING — rank P3-LOW; owner: CC (renew ≤ 14 days or bake); retire at the first external install** | — | — | CC | -## Monitoring & notifications — 16 rows (P2 2, P3 9, P4 5) +## Monitoring & notifications — 17 rows (P2 2, P3 9, P4 6) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| @@ -241,7 +242,8 @@ stopping line that lies. | **R-285** | Monitoring & notifications | P4 | **A planned, supervised reinstall pages the operator as if the machine had died — there is no notion of expected downtime anywhere.** During the 2026-08-09 rehearsal the hub sent, all `status: sent` to the operator channel: `host_stale` 08:58 UTC, `node_stale` 09:00, **`host_down` 09:28 (error)**, **`node_down` 09:30 (error)**, `host_leaf_changed` 09:31, `host_recovered` 09:31, `node_recovered` 09:34, `offsite_delivery_stuck` 09:34 — eight operator mails for work that was deliberate, attended and announced. **This is the OPPOSITE gap from the one R-281 filed:** the alarms are not missing, they are indiscriminate. `host_stale` at 30 min and `host_down` at 60 min (`monitor/host_staleness.go:22-23`, `downAfter = 2 * threshold`) cannot distinguish a wiped-on-purpose box from a dead one, and `host_leaf_changed` firing on a reinstall is correct-but-expected. **Note the interaction with the mute used on 2026-08-09 evening:** blocking a customer silences everything, so today the only two settings are *page me for planned work* and *tell me nothing at all*. **What is owed is a middle:** a maintenance window, or an operator-set expected-downtime flag, that suppresses staleness and leaf-change while leaving genuine faults audible | **READY (M) — NEW 2026-08-09** | — | The evidence is the operator's mailbox plus `events`/`notification_log` for 2026-08-09 | CC | | **R-886** | Monitoring & notifications | P3 | **DooPlex's Alertmanager cannot write its own state since the Longhorn restart of 2026-10-05 13:20Z** — every 15 min `Running maintenance failed … open /alertmanager/nflog.…: permission denied` (and the same for `silences`), 8 times by 14:21Z; the pod was recreated 13:20:48Z by that restart. Mail still goes out (`alertmanager_notifications_total{integration="email"}` 5 → 6, `failed_total` 0, 14:23Z), but a silence set now and the record of what was already sent do not survive the next pod restart — so a restart can re-send every active alarm or drop a silence. Likely collateral of the restart (volume ownership on re-attach), not measured. **Checked from source 2026-10-05 (burn-down round 2):** homelab-manifests@87dfc29 mon-system/alertmanager.yaml:137-247: the Deployment has NO securityContext / fsGroup / runAsUser at all (grep), runs prom/alertmanager:v0.34.1 (:199, non-root `nobody` image) with --storage.path=/alertmanager on the Longhorn PVC alertmanager-data (:202, :212-213, :245-247). The comment :239-244 asserts silences now survive a restart -- an invariant with no test, which is exactly what this row says broke. Last structural change 58d1cd2 (2026-08-14, 'give alertmanager re | **OPEN** | — | Compare the volume's file owner with the pod's `securityContext` (`fsGroup`/`runAsUser`); fix in homelab-manifests; prove with a silence that survives a pod restart | operator | | **R-884** | Monitoring & notifications | P4 | **ArgoCD app `monitoring` shows `Deployment/prometheus` OutOfSync** (seen 2026-10-05 while syncing the R-173 alarm rules; only the rules ConfigMap was synced, so the Deployment drift is untouched and its cause unknown). A full sync would change the running Prometheus in an unknown way. **Checked from source 2026-10-05 (burn-down round 2):** Strong lead from source: homelab-manifests@87dfc29 commit 53c6e99 (Renovate, 2026-10-03) changed ONLY mon-system/monitoring.yaml `prom/prometheus:v3.14.0` -> `v3.15.0` (monitoring.yaml:419), and the `monitoring` Application has no `automated` syncPolicy in git (argocd-apps/homelab.yaml:602-605). So the drift is most likely an unsynced Renovate bump, i.e. a full sync = Prometheus 3.14 -> 3.15 upgrade (plus pod restart; R-211: no reloader). Not confirmed live. | **OPEN** | — | `argocd app diff monitoring` (or the CR's resource diff) to see what differs, then decide git or live | operator | -| **R-906** | Monitoring & notifications | P4 | **A page can show „+ 5 további figyelmeztetés" (+5 more warnings) with no warning above it.** SEEN 2026-10-08 on scratch 9202 (controller 0.303.0): `GetAlerts` caps the list at 5 and counts the rest into the overflow line BEFORE the layout drops the alerts that belong on other pages (`disk-not-separate` is `Inline` + `PageOnly` dashboard/monitoring, `web/alerts.go` ~L281); on the launcher, apps, backups and system pages all five visible ones were such alerts, so only the overflow line rendered. Not fixed here (controller out of scope). | **OPEN — owner: CC** | — | Count the overflow after the page filter (a render test per page) | CC | +| **R-906** | Monitoring & notifications | P4 | **A page can show „+ 5 további figyelmeztetés" (+5 more warnings) with no warning above it.** SEEN 2026-10-08 on scratch 9202 (controller 0.303.0): `GetAlerts` caps the list at 5 and counts the rest into the overflow line BEFORE the layout drops the alerts that belong on other pages (`disk-not-separate` is `Inline` + `PageOnly` dashboard/monitoring, `web/alerts.go` ~L281); on the launcher, apps, backups and system pages all five visible ones were such alerts, so only the overflow line rendered. Not fixed here (controller out of scope). | **VERIFY — fixed on main 2026-10-08, ships with tomorrow's controller release** (controller `d5f2e47`). `AlertManager.GetBannerAlerts(page, lang)` filters with the layout's own rule BEFORE the cap; `baseData` and /monitoring use it. `TestR906_*` (the 9202 case: nine inline disk warnings + hub off, on /launcher, /stacks, /backups, /monitoring, hu + en — red against 05e12921). Live on 9202 with a test build: the overflow line is gone and the three real banner warnings show (`audits/dashboard-layout-2026-10-08/`) | — | Release tomorrow; read one page on a box with inline warnings; close | CC | +| **R-911** | Monitoring & notifications | P4 | **On an English page the „data storage not reachable" banner stays Hungarian: „Adattároló nem elérhető: ".** SEEN 2026-10-08 on 9202 (test build, `?lang=en`), once R-906 let the real banner warnings through. Cause: the health check appends `warnFmtStorageUnavailable` as plain text (`internal/monitor/healthcheck.go:322`) with no `MsgRef`, so the banner has no key to render in English (the R-516 item 10 pattern). | **OPEN — owner: CC** | — | Give the warning a `MsgRef` (new key `alert`/`health` hu + en, the wire text unchanged); English page test for the banner | CC | ## Hub & operator — 12 rows (P2 1, P3 5, P4 6) @@ -272,7 +274,7 @@ stopping line that lies. | **R-89** | Business & legal | P4 | Retention as a per-customer **commercial** policy on the hub | READY (increment 2) | — | Policy object + reconciler → ep0 prune job; keep box tokens write-only | CC | | **R-794** | Business & legal | P4 | **[P3-LOW] redis 7.4 (RSALv2 / SSPL, not OSI) runs as a private cache in seven apps: dawarich, docmost, immich, nextcloud, outline, paperless-ngx, romm.** READ 2026-10-02 (`audits/licences-2026-10-02/TABLE.md`). Read as permitted (a private cache only its app uses is not Redis offered as a service — inferred). Valkey (BSD-3) or redis 8 (AGPL option) removes the question. **Needs:** a ladder step per app to valkey or redis 8, through the harness — no hurry. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: the row itself says no hurry; usage read as permitted.** | — | — | CC | -## Process & tooling — 21 rows (P3 3, P4 18) +## Process & tooling — 22 rows (P3 3, P4 19) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| @@ -297,6 +299,7 @@ stopping line that lies. | **R-807** | Process & tooling | P4 | **[P3-LOW] 13 apps stay UNDETERMINED because their seeds write only to the database, so their upload / media / cache / redis volumes stay empty.** MEASURED 2026-10-02 (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): claper, crafty-controller, dawarich (redis), docmost, gramps-web, immich (ML cache), outline (+redis), sparkyfitness, tandoor, vikunja, wger, wishlist, zipline — plus plex (no seed route: a plex.tv claim token) and wanderer (no fixture; unhealthy under the gate). None is BROKEN: nothing was written outside a preserved folder. **Needs:** per app, a seed that uploads one file (or the volume named n/a with a reason: a redis cache, an ML model cache). | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: test coverage; nothing was found broken.** | — | — | CC | | **R-896** | Process & tooling | P4 | **The catalog's `volume-persistence` gate runs its canary and the apps on the LOCAL Docker, so a full gate run on DooPlex acts on the production host — and there its self-test fails for every app.** SEEN 2026-10-06 night: a helper's full `catalog_gates.py` run on DooPlex built `felhom-volgate-canary:1` and ran the canary pair locally (`scripts/check-volume-persistence.py` canary self-test, ~line 830); it reported the self-test failing for every app, also on `main` (checked with gokapi). Not re-run (it would act on DooPlex again). Same class as R-650 (tests that reach real Docker act on DooPlex). | **OPEN — filed 2026-10-06 night** | — | Point the gate at the bench guest (as the upgrade harness does) or refuse to run on a host without a marker; then find why the self-test fails | CC | | **R-903** | Process & tooling | P4 | **On a phone with JavaScript off, the website's menu does not open — so its links and the language globe cannot be reached there.** MEASURED 2026-10-08 (globe brief, headless Chrome at 376 px, JavaScript off): the three-line button is scripted (`#hamburger` toggles `.nav-links.is-open`), and below 900 px the nav list sits off-canvas until then. Older than the globe — the text switch had the same limit. The globe itself works with JavaScript off at desktop width (`audits/website-globe-2026-10-08/check.txt`). Fix direction: a CSS-only menu toggle (a `
` or a checkbox), a nav change on every page plus gate 3. | **OPEN — owner: CC** | — | Build the no-JS menu in a website task | CC | +| **R-910** | Process & tooling | P4 | **The website's dashboard pictures show the old Apps card (the tags crowd the name, R-909) and no phone view (R-907).** Filed 2026-10-08. | **OPEN — owner: CC** — waits for tomorrow's controller release to reach demo-hp | Tomorrow's controller release on demo-hp | After the release reaches demo-hp 9201: retake the 8 `dashboard-*.webp` pictures the way `audits/website-dashboard-2026-10-08/` did (read only, language round trip), consider adding the phone view, run `site_gates.py`; then close R-907, R-909 | CC |