Files
felhom.eu/documentation/audits/new-app-checklist-2026-10-01

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.