# The new-app checklist — review, gate, wger pilot, gap page (2026-10-01) Operator request 2026-10-01: a checklist every new app passes before it reaches the live catalog. Reviewer's draft the same day (the brief's appendix; pushed by the operator as `app-catalog-felhom.eu/NEW-APP-CHECKLIST.md`, `6f18f74`). Architecture read: `documentation/architecture/09-update-architecture.md` §3 decisions 13, 22, 37, 42, 45–50, 61; §6.5 (drill catalog). Baselines: catalog `main` `6d72c09` (the draft's merge on top of `9c5eae9`), 53 templates; felhom.eu `b63654a`; register 392 rows, highest R-757; controller on 9202 `0.285.0`. | part | done? | what | |---|---|---| | A — the checklist | done, changed | 60 rows in 10 groups (draft: 53 in 10). 7 rows added, 16 "hows" sharpened, 9 citations fixed. `app-catalog-felhom.eu/NEW-APP-CHECKLIST.md`; template `onboarding/_TEMPLATE.md`; pointers in CLAUDE.md, REUSE.md §5, README "Adding a New App" | | B — the gate | done | `scripts/check-onboarding.py`, gate `onboarding` in `catalog_gates.py --fast` (hook + CI); 16 decoy cases, 5 mutants of the gate each turn the suite red (`A/`) | | C — the wger pilot | done | the four problems: 2 caught by the draft as written, 2 missed by the draft's "how" and caught by sharpened/new rows. The pilot ALSO found three live wger defects (R-762, R-763, R-764) | | D — the gap page | done | `onboarding/EXISTING-APPS-GAPS.md` from `scripts/onboarding_gaps.py`, read only | ## 1. Claims in the draft that were wrong | row | the draft said | what is true | |---|---|---| | 3.3 | "32 apps" gated | 33 templates carry `setup_gate: true` today (FIRST-ADMIN's 32 was 2026-09-29) | | 5.2 | "romm OOM at +76 s (decision 22)" | no "+76 s" exists in R-635, decision 22 or any audit; romm's OOM loop is R-635 (six hours after an update called success). Citation replaced | | 5.3 | "gate / review" | **no gate checks it**, and 8 templates differ today (R-758). immich's figure was right: `mem_limit` 4096M vs a 4224M sum, header naming a 256M DB that ran at 512M | | 6.2 | "35 of 53 update at night" | not reproducible from files; 38 of 53 carry a ladder today, and a ladder is not "updates at night" (marks can hold a step for a person) | | 8.3 | (implicit) the asset URL | the canonical template comment says `-logo.webp`; the controller loads `-logo.svg`/`.png` (R-761) | | 1.6 | how = "compose + a login on 9202" | **cannot show R-737**: the web login worked; only the phone app's route failed. Sharpened (list the env the settings READ; log in on every route) + new row 3.9 | | 1.4 | how = "an update on 9202" | a new app pinned at its newest tag has no update to make. Sharpened: install the PREVIOUS release, step INTO the pin | | 0.4 | how = "one packet capture or log read" | no packet-capture tool is in our kit; replaced by the entrypoint read (1.7) + the first-start log + the held connections | | 2.6 | why = "R-756 (refused with a folder present)" | R-756's cause is not known (possibly a 9202 venue artefact); R-442 (the inert "remove with data") added as the measured reason | | — duplicates of gates | 1.1, 2.1, 3.3 (probe flip), 4.2, 6.2, 8.1, 9.4 | each now names its gate: `image-pins`/`image-resolvable`, `volume-persistence`, `probe-measured`, `probe-matches-compose`, `test-record`(+`-move`), `copy-i18n`, `onboarding` | **Rows added:** 0.9 (no self-call at the public name, R-739), 1.7 (every entrypoint switch read and decided), 1.8 (debug off, R-482), 1.9 (`data_key`), 2.8 (an upload opens again through the front door, R-483), 3.9 (sign in as each client does — browser CSRF + the phone app's route, R-712/R-737), 8.5 (website app count). **Rows sharpened (how or why):** 0.4, 0.7, 1.4, 1.6, 2.3 (R-537/538), 2.6 (R-442), 3.1 (R-612), 3.2 (R-702), 3.4 (R-512), 3.6 (a second member), 3.8 (R-713), 4.1 (inspect the image), 4.3 (R-473, R-676), 4.4 (R-613), 4.5 (R-630), 5.1 (cgroup, not Docker's OOMKilled — R-528; R-703), 5.2 (R-635, R-514), 6.1 (R-624, R-738's product gap), 6.2/6.3 (R-742), 6.5 (R-743), 8.2 (R-515, R-498), 8.3 (R-761). ## 2. The pilot — would the checklist have caught this week's four wger problems? The record against the template as it was on 2026-09-29 (`d0e7e2e`): `wger-as-of-2026-09-29.md` — 27 of 60 rows open, 10 of them FAILS. The record as it is now: `app-catalog-felhom.eu/onboarding/wger.md` — 11 open, each a register row. | problem | the row that catches it | would the DRAFT's "how" have shown it? | after the review | |---|---|---|---| | **R-737** JWT key missing — the phone app's login 500 | 1.6, 3.9, 0.7 | **No.** "compose + a login on 9202" — the web login worked; only `/allauth/app/v1/auth/login` failed. 0.7 needed a real phone client | 1.6 lists the env the settings READ (`JWT_PRIVATE_KEY` is among 15 key-like names, C2); 3.9 calls the phone route with `curl` | | **R-738** no migration on update | 1.4 (+ 6.1) | **Only if an update was run.** On 2026-09-29 one existed (2.7 since 09-03) and the fixture's read-back caught it on 09-30; for a NEW app at its newest tag, the draft's how has nothing to run | 1.4: step from the previous release INTO the pin; 1.7: the entrypoint read names `DJANGO_PERFORM_MIGRATIONS` statically | | **R-752** a stranger locks everyone out | 3.6 | **Yes** — "N wrong passwords … who is locked" is exactly R-752's measured control | 3.6 says how to tell "everyone": the household's AND a second member's right password, and cites R-753 | | **R-755** development server | 1.5 (+ 1.7) | **Yes** — `ps` in the container shows `manage.py runserver` | 1.7 also finds it statically (`WGER_USE_GUNICORN`) | **Three more found by the new and sharpened rows on the current template (C8):** row 2.8 — a photo uploads (201) and never opens (404); row 1.7 — `DJANGO_DEBUG` unset, so `collectstatic` never runs and every CSS/JS file is 404 (R-762); row 3.4 — a stranger signs up after the setup and each anonymous visit makes a guest account (R-763); row 7.1 — mail goes to the console (R-764). None of these is in the draft's four; all were on the live template since it was written. ## 3. The gap page's headline (`onboarding/EXISTING-APPS-GAPS.md`, of 53) 0 Fit 52 · 1 Images/DB 53 · 2 Storage 38 · 3 Accounts 39 · 4 Health 49 · 5 Resources 24 · 6 Updates 26 · 7 Mail — (6 mapped) · 8 Text 52. Read from files only; "covered" means a file shows the signal named on the page, not that it was re-tested. What NO old app has recorded: the entrypoint switches, the production server, the secrets read (1.4–1.9), a restore round trip, a negative health control, lock-out (except the R-752 apps), a from-birth memory watch (except immich). ## 4. The live work, and teardown 9202 only, drill catalog (`app-catalog-drill` `e9f50b5`, wger identical to live). C0 repoint (saved `controller.yaml.pre-checklist1001`; control: the box's clone = drill `e9f50b5`, live `main` unchanged `6d72c09`); C1–C7 walk 1 (install, reads, logins, seed + photo, pause, memory, remove keeping data); C8/C8b walk 2 (stranger sign-up, guests, photo cause, static files, remove with data); C9 restore (clone = live `6d72c09`, standing apps healthy). **Teardown — machine:** wger removed twice through the product, both volumes gone, image deleted by the remove (decision 53), sampler/poller files removed from `/root`; the stack directory remains (the sync creates one per template). **Host:** nothing left (temp scripts removed per call). **Hub:** untouched (9202 is unenrolled). Secrets: the dashboard password and wger's generated password lived in a 0600 scratch directory, never printed; evidence scanned for both values and for token shapes — none. ## Files `A/` decoys (green run; red-proof by mutants) · `B/` upstream reads, the 2026-09-29 template copy, gates then and now · `C/` the 9202 walks · `tools/` `c_wger.py`, `c_wger2.py` · `wger-as-of-2026-09-29.md` the first-pass record.