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.