From 3793c73c5d6d33db067cfd200e238452b3be7060 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Mon, 15 Jun 2026 21:53:09 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20REPORT.md=20=E2=80=94=20badge-clip=20fi?= =?UTF-8?q?x=20+=20M3=20live=20re-verify=20+=20refresh-loop=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- REPORT.md | 123 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 33 deletions(-) diff --git a/REPORT.md b/REPORT.md index 87613a8..97e76f3 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,39 +1,96 @@ -# REPORT — storage lifecycle completion + acceptance sweep (controller half) → v0.68.1 (2026-06-15) +# REPORT — two UI cleanups + M3 live re-verification (2026-06-15) -## Type -Finished the storage lifecycle on the intermediary-mount model + drove a full live acceptance sweep -(incl. a real host reboot). Built + Linux-gated + deployed to guest 9201 per commit. Pairs with agent -v0.36.7. Full audit: `felhom.eu/documentation/audits/storage-lifecycle-acceptance-2026-06-15.md`. +**Deployed:** controller **v0.68.3** on guest 9201 / felhom-pve (bootstrap-managed; healthy). +**Commits:** `37ed757` (v0.68.2 CSS) · `a821a9d` (v0.68.3 refresh fix). Trunk-based, pushed to `main`. -## What changed (controller) -- **v0.68.0** — H2/H3/M1/M3 + deterministic boot-id: - - **Boot-id determinism** (`processGuestBootChange` + `settings.LastGuestBootID`): the agent reports - `guest_boot_id`; on change the controller recreates drive-backed apps deterministically (replaces the - fragile container-uptime sample, which missed apps stopped at the sample instant). - - **H2** decommission UI button (migrate / decommission-anyway type-to-confirm); **H3** one-click - re-enroll of a decommissioned drive (`handleStorageReconnect` decommissioned branch); **M1** - `defaultPromotionTarget` (auto-promote on decommission-of-default, BLOCK if it's the only drive); - **M3** migrate merge-walk re-asserts 2775 setgid on userdata dirs (`isUserdataDir`). - - Fixed the H1 `{path}`/`{where}` JS body mismatch. -- **v0.68.1** — boot-id recreate ALL deployed drive-backed present apps **state-independent** (v0.68.0 - filtered on container state and **missed 5 apps** docker hadn't auto-restarted yet — caught in the live - host-reboot test). +--- -Non-hollow tests + companions: `TestShouldRecreateOnBoot` (old sample missed a stale/stopped app), -`TestDefaultPromotionTarget`, `TestIsUserdataDir`. +## Part 1 — stack-card state-badge clipping fix (v0.68.2, CSS-only) -## Acceptance sweep (live) — per-item PASS in the audit doc -HOST REBOOT (✅ ordering + auto-start + convergence, no manual intervention), guest reboot ×3, drive-yank -fail-close (no leak), capability-proof fail-close, confinement, controller redeploy (rslave explicit), -two-drive isolation, **H2 decommission**, **H3 re-enroll** (after the agent v0.36.1 raw-stays-mounted -fix), **M1 default reassignment**, rapid eject/reconnect (single bind after the agent double-bind -root-cause fix). E14/E15 migrate-all not re-run live (M3 unit-tested + 2775 convention verified live). +**Symptom:** on the /stacks page, an **unhealthy** app's state badge clipped — "Nem egészséges" rendered +as "Ner…". Healthy / not-deployed cards were fine. -## End state -Both external drives registered at `/mnt/felhom-drives/`, felhom-flash default, 25 containers -healthy. controller v0.68.1 deployed (golden/bootstrap image tag mechanism). +**Root cause (confirmed):** `.stack-detail-header` is `flex` / `space-between` holding `.stack-title-row` +(logo + title + subdomain link + the `route-unpublished` warning) and `.stack-state-badge`. Only on an +unhealthy app does the long "⚠ URL nem elérhető – útvonal nincs publikálva" warning render, inflating the +title-row. The title-row had no `min-width:0` (so it wouldn't shrink below its content) and the +`white-space:nowrap` badge had no `flex-shrink:0` (so the flexbox compressed the *badge* instead → clip). -## Known residuals -- Boot-id first-sight bounce (fresh controller recreates apps once on first start — one-time per - data-volume lifetime). -- E14/E15 full migrate-all round-trip not re-run live (data-merge risk; M3 unit-tested). +**Fix** (`internal/web/templates/style.css`): +```css +.stack-title-row { flex: 1; min-width: 0; } /* shrinks + wraps its own content */ +.stack-state-badge { flex-shrink: 0; } /* never compressed */ +``` + +**Browser-verified on /stacks (demo, guest 9201):** +- BEFORE (v0.68.1): komga badge clipped to "Ner…" at the card's right edge (screenshot captured live). +- AFTER (v0.68.2): komga badge reads the full **"Nem egészséges"**; the "⚠ URL nem elérhető…" warning + **wraps within the title column** (screenshot captured live). +- Regression: Mealie ("Fut", healthy) and all "Nincs telepítve" cards visually unchanged. + +**Defensive note (not applied):** the spec offered adding `flex-shrink:0` to +`.badge-missing-storage`/`.badge-orphaned` *if* a card showing BOTH unhealthy AND a missing-storage/ +orphaned badge still clipped. No such card exists live right now, and the primary fix's mechanism +(title-row `min-width:0` absorbing all shrink) already shields sibling badges, so it was left out — a +one-liner if that combo ever appears. + +--- + +## Part 2 — M3 live re-verification (no code change — the migrate path is correct) + +**Goal:** prove the migrate merge-walk re-asserts the `2775`-setgid/gid-1000 convention on a +**pre-existing STALE dir** (the B3 calibre case), which the acceptance audit had only covered by unit +test + static convention. + +**Method:** two full **migrate-all** runs driven through the dashboard UI (the real +`/api/storage/migrate` endpoint, all 8 flash apps: audiobookshelf, calibre-web, immich, jellyfin, komga, +paperless-ngx, radarr, romm): **flash→usb**, then **usb→flash** (restoring apps to the default drive). +Before each, the migrate **target** was pre-seeded host-side with deliberately-stale `0755`, +**non-setgid** userdata dirs; `stat` recorded. + +**Result — PASS (clean, non-hollow):** +- Pre-seeded `userdata/documents` at `755 gid1000` — a dir **no app mounts** — became + **`2775 gid1000`** after migrate (the `EnsureUserdataDir` re-assert in `walkMerge`, migrate.go:854-859). + Same for the seeded `userdata` root and `userdata/import`. Whole target userdata tree = `2775`. +- Integrity: static seeded files (`b3-document.txt`, `b3-movie.txt`, `b3-photo.txt`, `demo.jpg`) had + **identical sha256** before/after. +- All 8 apps redeployed **healthy** on the target (komga "unhealthy" = its pre-existing + `/api/v1/actuator/health` 401 — serving, unrelated to migration). + +**Secondary finding — the `import/calibre` 755 is an APP clobber, not a migrate bug.** In both runs +`userdata/import/calibre` ended at `755`. Isolated live: **calibre-web (CWA)** bind-mounts +`${USERDATA_PATH}/import/calibre` as `/cwa-book-ingest` and **chmods it to 755 (strips setgid) on every +startup** — proven by setting it to `2775` and restarting calibre-web alone (reverted to `755`, no +migration). The merge-walk *does* re-assert `2775` during copy; calibre-web re-clobbers it during the +flip/redeploy phase (this is also why the source flash calibre was already `755` pre-migration). +**migrate.go was therefore NOT modified** — the M3 path works. If the convention must hold for that +single-app ingest dir, the fix belongs in the catalog/app layer (an `umask`/entrypoint wrapper for CWA), +not in migrate. + +--- + +## Part 3 — Beállítások endless-refresh loop fix (v0.68.3) — found during Part 2 + +**Symptom (user-reported):** after any migration finished, the settings page reloaded itself every +~1.5 s, forever. + +**Root cause:** `MigrationStatus` keeps returning the last `done` job indefinitely (never cleared). The +settings page's resume-view IIFE called `migWatch()` for *any* returned job, and `migWatch`'s `done` +branch does `setTimeout(()=>location.reload(),1500)`. So every load saw the persisted `done` job → watched +it → reloaded → saw it again → endless loop. + +**Fix** (`internal/web/templates/settings.html`): the resume-view now starts the watcher **only for an +in-progress job** (`phase!=='done' && phase!=='aborted'`). The one-time post-completion reload still fires +from the *active* watcher started by `storageMigrateAll`, so a real migration refreshes drive state once +when it finishes — but a stale terminal job no longer triggers the loop. + +**Browser-verified on v0.68.3:** with a persisted `done` job present in the journal, the settings page +stayed put for 25 s (`navType:navigate`, planted marker survived, migration panel idle) — no reload. + +--- + +## Resting state +- Apps healthy on **felhom-flash** (the default drive); **felhom-usb** cleaned/empty. +- Controller **v0.68.3** deployed live on guest 9201. +- Build/deploy: built+pushed on 192.168.0.180, deployed via the bootstrap mechanism + (`docker pull` → `/etc/felhom-controller-image` → restart `felhom-controller-bootstrap.service`).