# REPORT — ISO boot branding + single-entry GRUB menu (R-38) · website grid restored (2026-07-19)

> `REPORT-<topic>.md` per this repo's parallel-session rule: another session was writing in this
> clone tonight (CAMPAIGN 7 / `DIAG-immich-restore-2026-07-19.md`), so the shared `REPORT.md` is left
> untouched.

Parts 1 and 2 of the polish train. Parts 3 and 4 landed in `felhom-agent` and `felhom-controller`;
see their own `REPORT.md`.

---

## Part 1 — scripts v1.22.0: GRUB branding + single-entry safety (R-38)

### What shipped

Every ISO is now **repacked** after `prepare-iso`. `mkimage-surgery.sh` → **`iso-repack.sh`**:
branding and the slice-B loader swap need the same extract → modify → re-master cycle, so they share
one pass instead of re-mastering twice. **The mkimage recipe is untouched.**

**The safety half — the one that matters.** The stock PVE menu offers *Graphical*, *Terminal UI*, a
serial variant, and an **Advanced Options** submenu holding two `nomodeset` entries, three debug
entries, *Rescue Boot*, memtest and *UEFI Firmware Settings*. Every one of those reaches the
**manual** installer, whose first question is which disk to wipe. They are **not emitted** — not
hidden, not password-gated. What ships is one entry, „Felhom telepítés", default, 5 s.

**Boot behavior is unchanged.** The `linux`/`initrd` lines are lifted **verbatim at repack time**
from the ISO's own *Install Proxmox VE (Automated)* entry rather than frozen into a copy in this
repo, so a PVE bump that moves the kernel path or edits the append line tracks automatically. The
build **fails** if they cannot be found, if the append line has lost `proxmox-start-auto-installer`,
or if `auto-installer-mode.toml` is absent — that last one because without it the single
Felhom-labelled entry would boot a *manual* installer, i.e. exactly what this change prevents.

**Gates, then a re-check against the shipped artifact.** The rendered menu is asserted to have
exactly 1 `menuentry`, 0 `submenu`s and no *live* reference to
`proxtui`/`proxdebug`/`nomodeset`/`Rescue Boot`/`memtest`/`fwsetup` (comments are stripped first —
the template's header names the dropped entries deliberately). Then the menu and theme background
are read back **out of `final.iso`**, not out of the extract tree.

**The boot card.** `grub/generate-grub-background.sh` letterboxes `website/assets/og-image_2.png`
onto a 1024×768 gfxterm canvas at repack time (ImageMagick added to the assistant image), so the boot
screen has **one source** and not a second pre-rendered PNG to drift. The card's own subtle grid
(measured: 4px lines of `#0D131A` on `#0D1117`, pitch 131px) is continued across the letterbox fill
**phase-locked** to where the card's grid lands, so the fill is seamless instead of a 500px square of
grid floating in flat navy. The generator refuses a source whose geometry no longer matches the
measured constants — a swapped asset would misplace every line, and that only shows up on a boot
screen nobody re-checks. Menu positioning needs a gfxmenu theme (plain `background_image` cannot move
the menu off the wordmark), so `grub/felhom-theme.txt` puts it in the lower third the layout leaves
empty, optically centered (measured off a canary screenshot; the comment records the measurement).

### Live validation — nested canary, UEFI/OVMF, PVE 9.2-1

Booted the built canary ISO under QEMU with OVMF and captured the framebuffer.

| Check | Result |
|---|---|
| GRUB renders the Felhom card | **PASS** — background + grid visible at 1024×768 |
| Exactly one entry, selected | **PASS** — „Felhom telepítés" only |
| Hungarian accents under gfxterm | **PASS** — „telepítés", „Indítás … másodperc múlva" render correctly |
| Countdown visible and counting | **PASS** — 5 → 0 |
| Auto-fires at 0 | **PASS** — serial shows ``Booting `Felhom telepítés'`` |
| Unattended install proceeds | **PASS** — „Fetching answers for automatic installation" → auto installer |
| Same abort as v1.21.0 | **PASS** — `ERROR: Installation failed: filter did not match any device` → `Installation aborted`; no disk touched |

A first build **correctly failed closed**: the banned-entry gate matched the template's own
explanatory header. Fixed to strip comments before matching (a comment naming a removed entry is the
point; a directive using one is the bug), which is a gate behaving as designed.

### Artifacts (rebuilt on 180, `/mnt/5_hdd/felhom.eu/felhom-iso/out/`)

| ISO | sha256 | bytes |
|---|---|---|
| `felhom-pve-9.2-1-v1.22.0-n100-generic-mkimage.iso` (safety) | `ff6f06ba1dbfe10f27d703afc29516001000349147426b43c9a424a0ea28bdbf` | 1 704 482 816 |
| `felhom-pve-9.2-1-v1.22.0-n100-demo-generic-mkimage.iso` (real) | `494db0ddf859b6b152cad4d0e0d9e9cefd27255cde07e2b41aba3ac12a217888` | 1 704 482 816 |
| `felhom-pve-9.2-1-v1.22.0-nested-canary-generic.iso` (validation) | `83c61c0413c84e27b26a37bb5dfaed2fcd44fd25e3e571c7310142bd305f2f9d` | 1 705 338 880 |

Both shipping ISOs: `embedding 60 modules`, `El Torito boot images=2`, fs-uuid preserved, and the
post-re-master verification confirming 1 entry + theme background inside the finished image.

**Deliberately not done** (per the task): no squashfs/initrd rebranding — post-GRUB screens are still
Proxmox-branded; no disk-setup or answer-generation change; the pairing banner is untouched.

---

## Part 2 — website: the index grid background, restored

**Archaeology.** Not a deliberate removal. The grid lived as a fixed `body::before` in
`index.html`'s inline `<style>` block and was dropped in **`bed8675`** ("D3 Part 2: index + kapcsolat
on design system v2"), the commit that migrated the page onto the shared `assets/site.css`.
`dd54e4c`, which *created* `site.css`, has no `body::before` at all — it was a porting omission and
nothing took its place. `ccbb13a` (the other five pages) never had it. No asset was lost: the
mechanism was pure CSS (two stacked `linear-gradient`s), which is why nothing looked missing in the
worktree.

**Restoration, not redesign.** Same 50px cells, same 1px lines, same 3% opacity, same
`position:fixed` / `z-index:-1`. One deliberate difference: the accent is the v2 `--blue` `#0083D8`
instead of the retired legacy `#0088cc`, which `site_gates.py` bans. Scoped to `body.page-index`,
because index is the only page that ever had it. `site.css` cache-bust bumped `?v=1` → `?v=2` across
all seven pages (nginx caches 7 days); BOM preserved on every file.

**Live verification** (felhom.eu, after git-sync deploy):

- Desktop: grid renders behind the hero, at its original subtlety.
- **376px viewport** (via a same-origin iframe — the browser window would not resize in this
  environment, so the narrow case was exercised for real rather than asserted): grid renders, mobile
  layout unchanged, `scrollWidth === clientWidth` so **no horizontal overflow**.
- Computed style confirmed live: `linear-gradient(rgba(0,131,216,0.03) 1px, …)`, `50px 50px`,
  `position: fixed`, `z-index: -1`, `pointer-events: none`.
- `python scripts/site_gates.py` — **OK** (BOM, no legacy tokens, no `<style>` blocks, cache-busted).

---

## Docs

- `scripts/CHANGELOG.md` — v1.22.0 entry.
- `website/CHANGELOG.md` — grid restoration entry.
- `documentation/backlog/ROADMAP.md` — **R-38 flipped to SHIPPED**; **R-45** (unified async-job
  feedback) and **R-46** (verification-copy browse + expiry) added; pre-invite checklist gained the
  "golden ≥ 0.147.x carries all four infra images" line.
- Capability map: **untouched** — no capability moved. These are UX and packaging.
