# REPORT — v0.315.0: one app page with four tabs (2026-10-15) ## 1. Baselines | Repo | `main` at start | Version | → | |---|---|---|---| | felhom-controller | `85f65c7` | v0.314.0 | **v0.315.0** | | felhom.eu | `06e7cf8c` (the task said `3263bb2`; it had moved — two other sessions are pushing) | docs only | — | | app-catalog-felhom.eu | `e67aad4` | read only | — | Architecture read before any claim: `02-controller-module-map.md` (`web/`, the R-571 alert-placement rule), `10-localisation.md` §7, `15-media-library.md` (the Plex claim path), and the controller's `CLAUDE.md`, `REUSE.md` and `.claude/rules/{ui-hungarian,gates,unprompted-work}.md`. **Where the work ran.** Edited on the Windows workstation; **built and tested on DooPlex** in an isolated scratch clone (`/mnt/5_hdd/felhom.eu/scratch/cc-apptabs`) so the shared DooPlex checkout — which two other sessions are using tonight — was never dirtied. The sync normalises line endings to LF and restores any file it did not mean to touch, because this checkout has `core.autocrlf=true` while six files (`.gitignore`, `Dockerfile`, `Makefile`, the three infra templates) are stored in git WITH CRLF; a blanket normalisation rewrote all six on the first attempt. ## 2. Files created / modified **Created (11)** `controller/internal/web/app_page.go` · `after_start.go` · `app_page_test.go` · `guide_render_test.go` · `controller/internal/stacks/guide_test.go` · `templates/app_shell.html` · `app_settings.html` · `app_data.html` · `app_guide.html` · `app_logs.html` · `app_fields.html` **Deleted (4 templates)** `templates/app_info.html` · `logs.html` · `app_export.html` · `tier2_config.html` — with the three page handlers that rendered them (`logsHandler`, `exportPageHandler`, `tier2ConfigPageHandler`). The POST handlers beside them (`tier2ConfigSaveHandler`, `appEmailToggleHandler`) stay, with their URLs. **Modified (main ones)** `web/server.go` (routes + the GET-only redirects) · `web/handlers.go` (`appDetailHandler` → `appCommonData`; `deployHandler` → `deployPageData` + a thin renderer) · `web/media_events.go` (`applyPlexClaim` extracted) · `web/tier2_config_handler.go` (redirect targets) · `templates/deploy.html` (install-only; 1197 → 544 lines) · `stacks.html`, `dashboard.html`, `backups_apps.html`, `app_import.html` (links) · `stacks/metadata.go`, `metadata_i18n.go`, `appinfo_address.go`, `manager.go` (the guide) · both locale files · `scripts/template_id_gate.py`, `scripts/test_gate_decoys.py`, `scripts/i18n_go_keys.json`. ## 3. Commits on `main` | Commit | What | |---|---| | (see `git log`) | v0.315.0 — the whole slice in one commit, because the template split, the routes and the fixtures cannot be green apart | ## 4. Tests and red-proofs **New test groups (9 functions across 3 files), all through the production router where routing is the subject.** | Test | Asserts | |---|---| | `TestAppPage_DeployedDefaultsToSettings` | `/apps/{slug}` renders the settings tab (`data-tab="settings"` + `aria-selected="true"`), all four tabs present, Mukodes offers restart+stop and not start, **the header contains no `stackAction(`**, no Pi/x86 text | | `TestAppPage_NotDeployedShowsGuideOnly` | no tab bar, `data-install-cta`, every tab suffix → 302 `/apps/grocy`; unknown app 404; unknown suffix 404 | | `TestAppPage_OldURLsRedirectGETOnly` | the five old GETs → 302 with the right Location, query carried, `?raw=1` still text/plain, unknown name 404, **POST to `/backup` and `/app-email` reaches its handler** | | `TestAfterStart_PlexThroughRouter` | POST `/apps/plex/after-start/PLEX_CLAIM` → the real registry → a fake Plex, by ADDRESS; the gate moves; the card disappears | | `TestAfterStart_NoActionNoCard` | an unregistered field: no card, POST 404, **zero calls to the fake** | | `TestAfterStart_RegistryAloneIsNotAnInvitation` | a REGISTERED action whose field the catalog does not declare: 404, zero calls, the gate untouched | | `TestAfterStart_CardHiddenWhenNotNeeded` | registered + `Needed()==false` → no card | | `TestAfterStart_PlexClaimOldRouteStillWorks` | `/apps/plex/plex-claim` runs the same entry | | `TestInstallPage_BackGoesToApp` | Vissza and Mégsem href `= /apps/grocy`; the post-install links point at the tabs; unknown app 404 | | `TestAppPage_EachTabRendersItsOwnContent` | one marker per tab, and **the log poller on the log tab only** | | `TestGuide_RenderRules` | checked renders / empty does not / absent says so / `