# REPORT — Indítópult (app launcher) + universal app placeholder icon (v0.163.0) **Date:** 2026-07-24 · **Repo:** felhom-controller · **Target:** v0.163.0 · No agent coupling; MinAgent unchanged. ## 1. Baseline used - felhom-controller `main` @ `a71cc583272a`, version `v0.162.0` — **matched §1 exactly** (clean tree, HEAD == origin/main, CHANGELOG top = "v0.162.0 — R-71(a) settle-gate"). No drift. ## 2. Files created / modified **Backend** - `controller/internal/stacks/metadata.go` — new `Metadata.BrandColor` (`brand_color`, omitempty). - `controller/internal/web/funcmap.go` — new `tileColor` (returns `template.CSS`) + `initial` (rune-safe monogram) + `hexColorRe`; both registered in the funcmap. - `controller/internal/web/handlers.go` — extracted `Server.subdomainMap` (replaced BOTH inline copies in dashboard + stacks handlers); new `LauncherApp` type, `buildLauncherApps`, `launcherHandler`, `controllerStackName` const. - `controller/internal/web/inframeta.go` — new `AppPlaceholderSVG` constant. - `controller/internal/web/server.go` — `/launcher` route; `/static/app-placeholder.svg` route. **Templates / nav / CSS** - `controller/internal/web/templates/launcher.html` — NEW page template. - `controller/internal/web/templates/app_row.html` — `data-fallback` now defaults to `/static/app-placeholder.svg`; header comment updated. - `controller/internal/web/templates/icons.html` — new `#i-rocket` sprite (nav glyph — `layout-grid` is already the Vezérlőpult icon, so a distinct glyph was added rather than reused). - `controller/internal/web/templates/layout.html` — Indítópult as the FIRST nav `
  • `, above Vezérlőpult. - `controller/internal/web/templates/style.css` — `launch-grid` / `launch-cell` / `launch-tile` (+ `--off`) / `launch-mono` / `launch-logo` / `launch-name` / `empty-state-link`, tokens only. **Tests** - `controller/internal/web/launcher_test.go` — NEW (8 test functions). - `controller/internal/web/app_row_test.go` — 2 test functions added (Group D). **Docs** - `CHANGELOG.md` (v0.163.0 entry), `REPORT.md` (this file), `CONTEXT.md` (design rulings), `controller/README.md` (launcher + placeholder sections). - felhom.eu (separate repo): `documentation/architecture/00-capability-map.md` (launcher row, IMPLEMENTED), `documentation/backlog/ROADMAP.md` (R-15 launcher-as-member-landing coupling note + new R-72 "curate brand_color for top catalog apps"). ## 3. Commits pushed to `main` - `987e915` — Indítópult launcher + universal app placeholder (code + CHANGELOG + CONTEXT + README). - `` — this REPORT.md (see git log). - felhom.eu: `` — capability-map + ROADMAP (explicit paths only; shared worktree). ## 4. Tests + red-proofs All groups green. **4 red-proofs applied → observed FAIL → restored → re-green** (tree clean after each): | # | Group | Mutation | Test | Observed FAIL | |---|-------|----------|------|---------------| | 1 | A | dropped the `if !ok \|\| sd == ""` subdomain filter in `buildLauncherApps` | `TestBuildLauncherApps_SelectionAndOrder` | `apps = [filebrowser mealie paperless-ngx worker-app]`, want without worker-app | | 2 | B | forced the launcher `` branch (`{{if isOperational}}`→`{{if true}}`) | `TestLauncherTemplate_StoppedTileGreyedNoLink` | stopped tile became a link to `media.demo-felhom.eu`; greyed classes absent | | 3 | C | `tileColor` returns `brand` unvalidated (`if brand != ""`) | `TestLauncherTemplate_TileColorSafety` | injection payload reached the rendered style; hsl-fallback assertion failed | | 4 | D | reverted `app_row.html` `data-fallback` to `{{with .FallbackIcon}}…{{end}}` | `TestAppRow_DefaultPlaceholderFallback` | logo-less row had no `data-fallback` attribute | **New test functions (10):** Group A `TestBuildLauncherApps_SelectionAndOrder`, `TestLauncherTemplate_OpenableTiles`; Group B `TestLauncherTemplate_StoppedTileGreyedNoLink`; Group C `TestLauncherTemplate_TileColorSafety`, `TestTileColor`, `TestInitial`; Group E `TestLauncherTemplate_EmptyState`; wiring `TestLauncherRoute_EndToEnd`; Group D `TestAppRow_DefaultPlaceholderFallback`, `TestAppPlaceholderRoute`. Note on `tileColor` returning `template.CSS`: html/template's CSS value filter mangles a legitimate `hsl()` from a func pipeline to `ZgotmplZ` (verified live). The value is trusted-by-construction (the brand is validated against `hexColorRe` in Go; the hsl is fully computed by us), so `template.CSS` is correct — and red-proof #3 shows exactly why the Go-side validation is load-bearing (an unvalidated brand as `template.CSS` injects verbatim). ## 5. Test count & suite - `go build ./... && go vet ./... && go test ./...` — **all green, 25 packages OK, 0 FAIL/panic.** - Gates green: `app_row_dedup_gate.py`, `template_id_gate.py`, `emoji_gate.py`. - Web package: 335 `=== RUN` entries after (+10 top-level funcs + subtests). ## 6. Deployed version + verification _(filled in §8 after the deploy step.)_ Built + pushed `gitea.dooplex.hu/admin/felhom-controller:0.163.0`; deployed to guest 9201 via the bootstrap flow. ## 7. Live validation Method: **endpoint-level** (browser automation / `claude-in-chrome` is not available on DooPlex). Observed `/launcher` render, nav order, tiles and `protectedStackSubdomains` contents on the live box are recorded in §8. ## 8. Live results _(filled after deploy.)_ ## 9. Observations (not acted on) - Tile S/L constants (`hsl(H, 45%, 38%)`) are a first pick; worth eyeballing live and tuning while keeping S/L fixed (only H per app). Not changed. - `brand_color` curation for top catalog apps is parked as ROADMAP R-72 (catalog-only). - `protectedStackSubdomains` currently maps only `filebrowser → files`, so FileBrowser is the one protected stack eligible for the launcher.