Persistence re-sweep of all 58 (R-801 closed: CLEAN 40 / UNDETERMINED 18 / BROKEN 0), R-788 + R-803 (papra) closed, R-804..R-807 opened; STATUS "Before the first paying customer"; REPORT-persistence-sweep-2026-10-02; register 442
gates / gates (push) Successful in 30s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-02 16:54:26 +02:00
parent 5ed44ada6e
commit 93052884b2
90 changed files with 27056 additions and 48 deletions
+7 -2
View File
@@ -899,7 +899,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-785** | **[P3-LOW] SparkyFitness is pinned 11 releases and a major behind upstream (v0.17.3; upstream v1.7.3, v1.6.0 dated 2026-07-24).** READ 2026-10-01 (`audits/visitors-2026-10-01/C/bench/C1-previous-tag.txt`). **Needs:** an update walk 0.17 → 1.x through the ladder (bench + box), after R-784 is decided. | **OPEN — rank P3-LOW; owner: CC (after R-784)** |
| **R-786** | **[P3-LOW] SparkyFitness's onboarding record has six open rows** (`app-catalog-felhom.eu/onboarding/sparkyfitness.md`): 0.5 runtime internet (food search providers), 0.7 the phone app's sign-in route through traefik, 1.6 the env names the server reads, 1.7 the entrypoint read, 5.4 a second memory watch at another limit, 8.3 no logo/screenshots on felhom.eu (404). Everything else measured this session (bench + 9202). **Needs:** each row measured, or n/a with a reason. | **READY — rank P3-LOW; owner: CC (catalog)** |
| **R-787** | **[P2-MEDIUM] No catalog app's LICENCE has been checked against Felhom being a paid service; the first look (SparkyFitness, R-784) found a non-commercial-only licence.** The new-app checklist row 0.1 binds apps opened from 2026-10-01; the 53 exempt apps were never read. **Needs:** a read of every template's licence at its pinned tag (SPDX + any custom terms), one table, and the non-open ones to the operator. **UPDATE 2026-10-02 — READ:** all 58 templates (56 published + Grimmory + MeTube), every image, at the pinned tag — one table: `audits/licences-2026-10-02/TABLE.md` (method in `read-1.md`, `read-2.md`). OSI-approved: all but the rows below. Not OSI / limiting, each now its own row: Tandoor (Commons Clause, R-789), Emby (R-790), n8n (R-791), Plex (R-792), the EE/BUSL parts of Cal.com, Docmost, Outline and meilisearch (R-793), redis 7.4 in seven apps (R-794), recipe-importer has no licence file (R-795); SparkyFitness is R-784. Nothing was hidden or changed because of the read (the brief's rule). New apps carry their licence in record row 0.1. | **CLOSED 2026-10-02 — the read (decisions are R-784, R-789..R-795)** |
| **R-788** | **[P3-LOW] The volume-persistence gate reads an EMPTY declared volume as CLEAN — MeTube's `/state` passed while the app had written nothing there.** MEASURED 2026-10-02 on the bench (`audits/family-gate-2026-10-02/C-metube-bench/C2-volume-persistence-metube.txt`): "declared volume /state is EMPTY" → exit 0. The catalog's CLAUDE.md says an app that wrote nothing is UNDETERMINED (exit 2), never a pass. MeTube's real proof came from elsewhere (the history read back after a recreate and after a restore). **Needs:** the gate treats an empty declared volume as undecided, or the app gets an exerciser (MeTube: one `POST /add`); a decoy that is an app writing nothing. **UPDATE 2026-10-02:** part of the cause was R-801 (no port → no exercise at all); the catalog now also has `APP_EXERCISE` (MeTube: one `POST /add`). The empty-volume → CLEAN path itself is still to be checked. | **READY — rank P3-LOW; owner: CC** |
| **R-788** | **[P3-LOW] The volume-persistence gate reads an EMPTY declared volume as CLEAN — MeTube's `/state` passed while the app had written nothing there.** MEASURED 2026-10-02 on the bench (`audits/family-gate-2026-10-02/C-metube-bench/C2-volume-persistence-metube.txt`): "declared volume /state is EMPTY" → exit 0. The catalog's CLAUDE.md says an app that wrote nothing is UNDETERMINED (exit 2), never a pass. MeTube's real proof came from elsewhere (the history read back after a recreate and after a restore). **Needs:** the gate treats an empty declared volume as undecided, or the app gets an exerciser (MeTube: one `POST /add`); a decoy that is an app writing nothing. **UPDATE 2026-10-02:** part of the cause was R-801 (no port → no exercise at all); the catalog now also has `APP_EXERCISE` (MeTube: one `POST /add`). The empty-volume → CLEAN path itself is still to be checked. **DONE 2026-10-02:** an empty declared volume after the exercise is UNDETERMINED (red-proofed: `audits/persistence-sweep-2026-10-02/A/RP-R788-empty-volume.txt`); the gate calls the app's own upgrade-fixture seed when a volume stays empty. Bind mounts: R-805. | **CLOSED 2026-10-02 — catalog** |
| **R-789** | **[P2-MEDIUM] Tandoor's licence is AGPL-3.0 WITH the Commons Clause: it forbids selling "a product or service whose value derives, entirely or substantially, from the functionality of the Software" — fees for hosting or support included.** READ 2026-10-02 at 2.6.15 (`audits/licences-2026-10-02/TABLE.md`). Felhom charges for installing and caring for the household's apps; whether that value comes "substantially" from Tandoor is the question. **Needs (operator):** keep (the fee is for the box, not Tandoor), hide for new installs, or ask the authors. Nothing changed meanwhile. **RULED 2026-10-02 afternoon (`09` §3 decision 66):** treated like SparkyFitness — stays offered; the operator asks the authors for written permission; without it before the first paying customer Tandoor is hidden (`lifecycle: hidden`). STATUS "Before the first paying customer". | **WAITING-ON-OPERATOR — rank P2-MEDIUM; owner: operator (the request; trigger: first paying customer)** |
| **R-790** | **[P2-MEDIUM] Emby is proprietary: a personal, non-commercial, non-transferable licence.** READ 2026-10-02 (emby.media/terms.html; `audits/licences-2026-10-02/TABLE.md`). The household's private use fits; Felhom installs the official image from Emby (no copy distributed) as part of a paid service, a context the licence does not cover. Jellyfin (GPL) offers the same. **Needs (operator):** keep, hide, or ask Emby. **RULED 2026-10-02 afternoon (`09` §3 decision 66):** KEPT — the household is the licensee; Felhom installs the official, unchanged image. Part of the lawyer's review (R-802). | **CLOSED 2026-10-02 — operator ruling (kept)** |
| **R-791** | **[P3-LOW] n8n's Sustainable Use License allows own internal or personal use, and distribution only free of charge for non-commercial purposes.** READ 2026-10-02 at 2.42.1 (`audits/licences-2026-10-02/TABLE.md`). The household's own use is allowed; Felhom pulling the unchanged official image onto the household's box is arguably not distributing — a grey zone. **Needs (operator):** keep (recommended reading) or ask n8n. **RULED 2026-10-02 afternoon (`09` §3 decision 66):** KEPT — the household is the user; the official, unchanged image. Part of the lawyer's review (R-802). | **CLOSED 2026-10-02 — operator ruling (kept)** |
@@ -912,8 +912,13 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-798** | **[P3-LOW] Grimmory's template sets `SWAGGER_ENABLED=false`, which v3.5.0 does not read (it reads `API_DOCS_ENABLED`, default false).** READ 2026-10-02 (`audits/family-gate-2026-10-02/B/grimmory-reads/upstream-v3.5.0.txt`). Harmless today — the API docs are off by default. **Needs:** remove the dead line or set the right name, on the next Grimmory step. | **READY — rank P3-LOW; owner: CC** |
| **R-799** | **[P3-LOW] The MeTube fixture's `POST /add` leaves out `download_type`, which upstream's validator lists as required.** MEASURED 2026-10-02 (`audits/family-gate-2026-10-02/C-metube-bench/`): both 2026.09.28 and .29 answered 200 and downloaded anyway. Fragile if a later tag enforces it. **Needs:** add `download_type: video` to the fixture with the next MeTube step. | **READY — rank P3-LOW; owner: CC** |
| **R-800** | **[P2-MEDIUM] "Delete your data from the hard drive" keeps the app's files in the household's userdata folder — and neither the dialog nor the result says so.** MEASURED 2026-10-02. On demo-hp (live catalog, `audits/family-gate-2026-10-02/B4/grimmory-demo.txt`, `metube-demo.txt`): remove WITH data removed Grimmory's database volume, appdata and backups but KEPT the uploaded EPUB in `userdata/media/grimmory`; for MeTube the dialog offers no data checkbox at all and the result says "the app stored no data of its own on a drive" while the downloaded video stays in `userdata/media/metube`. On 9202 "keeping data" answers `hdd_paths_preserved: []` for the same reason (`audits/family-gate-2026-10-02/B/box/life.txt`): the remove reads `${HDD_PATH}` binds only. The model behind it may be right — userdata is the household's own files (a shared `media/` tree must never go with one app) — but then the dialog must say the files stay and where. **Needs (operator):** keep userdata on remove (and say so on the dialog), or delete an app's OWN userdata subfolder when the household ticks the box. **RULED 2026-10-02 afternoon (`09` §3 decision 67, option A):** the household's userdata files stay on every remove; the dialog and the result say so and name the folder. Recorded in `07` §6.5. Built in the next controller release. | **READY — rank P2-MEDIUM; owner: CC (the dialog and the result text)** |
| **R-801** | **[P2-MEDIUM] The volume-persistence gate never sent a request to ANY app: it read the routed port from label VALUES while `docker compose config --format json` puts the port in the label NAME.** MEASURED 2026-10-02 on the bench (`audits/family-gate-2026-10-02/B/volume-persistence-metube-exerciser-diag.txt`: `"ports": []`, `"exercise": []` for MeTube; the label shape read on Compose 2.26.1). So every verdict since the gate exists came only from what an app writes at start by itself; the GET exercise and the deep second pass never ran. **Fixed in the catalog** (`routed_ports()`, key=value; unit tests, red-proofed: `audits/family-gate-2026-10-02/B/RP-R801-routed-ports.txt`). **Still owed:** a full re-sweep of all 58 templates on the bench with the fixed gate — an app whose data path is written only on a request may now read differently. | **READY — rank P2-MEDIUM; owner: CC (the re-sweep)** |
| **R-801** | **[P2-MEDIUM] The volume-persistence gate never sent a request to ANY app: it read the routed port from label VALUES while `docker compose config --format json` puts the port in the label NAME.** MEASURED 2026-10-02 on the bench (`audits/family-gate-2026-10-02/B/volume-persistence-metube-exerciser-diag.txt`: `"ports": []`, `"exercise": []` for MeTube; the label shape read on Compose 2.26.1). So every verdict since the gate exists came only from what an app writes at start by itself; the GET exercise and the deep second pass never ran. **Fixed in the catalog** (`routed_ports()`, key=value; unit tests, red-proofed: `audits/family-gate-2026-10-02/B/RP-R801-routed-ports.txt`). **Still owed:** a full re-sweep of all 58 templates on the bench with the fixed gate — an app whose data path is written only on a request may now read differently. **DONE 2026-10-02:** the re-sweep of all 58 ran with the fixed gate (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): CLEAN 39 · UNDETERMINED 19 · BROKEN 0 (August: 38 · 11 · 4 of 53, every one of those decided by start-time writes alone — 0 of 53 probes had sent a request). Follow-ups R-805..R-807. | **CLOSED 2026-10-02 — catalog (the fixed gate + the re-sweep)** |
| **R-802** | **[P2-MEDIUM] A lawyer reviews the non-OSI licence list before the first paying customer.** Operator ruling 2026-10-02 (`09` §3 decision 66): Tandoor (R-789), SparkyFitness (R-784), Emby, n8n, Plex (kept — R-790..R-792), the EE/BUSL parts (R-793), redis 7.4 (R-794); the table is `audits/licences-2026-10-02/TABLE.md`. STATUS "Before the first paying customer". | **WAITING-ON-OPERATOR — rank P2-MEDIUM; owner: operator (trigger: first paying customer)** |
| **R-803** | **[P1-HIGH] papra cannot start under its own memory limit: a fresh install is OOM-killed in its migration and crash-loops.** MEASURED 2026-10-02 on the bench (`audits/persistence-sweep-2026-10-02/A/sweep/papra-oom-diagnosis.txt`, `audits/persistence-sweep-2026-10-02/A/sweep/batch-09-papra-rerun.txt`): `ghcr.io/papra-hq/papra:26.6.2-rootless` at the template's 256M → `OOMKilled=true` during `tsx src/scripts/migrate-up.script.ts`; with no limit it runs at ~416 MiB after 60 s, and its database lands correctly in `/app/app-data` (the R-156 mount is right). The 26.6.1 → 26.6.2 step was proven with harness v1 (no memory watch), which is how 256M stayed. **No box runs papra** (demo-hp, demo-felhom read 2026-10-02). **Needs:** a from-birth memory watch, then the limit raised with headroom, proven on the bench (install, seed, read back, persistence CLEAN). **FIXED 2026-10-02:** measured from birth on the bench (`audits/persistence-sweep-2026-10-02/A/papra/SUMMARY.md`): peak anon 405 MiB (cgroup peak 483 MiB), ~255 MiB idle; the limit is now 768M (`mem_request` 256M); at 768M healthy in 31 s, 0 kills, the seed read back, persistence CLEAN. No image move; no box ran papra. | **CLOSED 2026-10-02 — catalog (papra 768M)** |
| **R-804** | **[P3-LOW] plant-it's image is gone from Docker Hub: `msdeluise/plant-it:0.10.0` answers "pull access denied … repository does not exist".** MEASURED 2026-10-02 on the bench, logged in (`audits/persistence-sweep-2026-10-02/A/sweep/batch-*.txt`, plant-it). The app is already `lifecycle: abandoned` (not offered for new installs); a box that runs it could no longer reinstall or restore it from images. **No box runs it** (read 2026-10-02). **Needs (operator):** keep the abandoned template as it is, or hide it entirely. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** |
| **R-805** | **[P3-LOW] The volume-persistence gate judges an empty NAMED volume (R-788) but not an empty BIND mount.** MEASURED 2026-10-02 (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): Grimmory's `/app/data` bind held 0 files and read CLEAN (its state is in MariaDB); komga, paperless-ngx, radarr and sonarr also carry empty binds that are not judged. **Needs:** decide whether an empty bind after the exercise is UNDETERMINED like a named volume, with a decoy. | **READY — rank P3-LOW; owner: CC** |
| **R-806** | **[P3-LOW] The gate's GET exercise speaks plain http to an HTTPS backend (crafty-controller :8443), and gramps-web did not answer on :5000.** MEASURED 2026-10-02 (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): both reached data only through their fixture seed. **Needs:** read the `loadbalancer.server.scheme` label (upgrade_boxport already does); find why gramps-web's first GET got no answer. | **READY — rank P3-LOW; owner: CC** |
| **R-807** | **[P3-LOW] 13 apps stay UNDETERMINED because their seeds write only to the database, so their upload / media / cache / redis volumes stay empty.** MEASURED 2026-10-02 (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): claper, crafty-controller, dawarich (redis), docmost, gramps-web, immich (ML cache), outline (+redis), sparkyfitness, tandoor, vikunja, wger, wishlist, zipline — plus plex (no seed route: a plex.tv claim token) and wanderer (no fixture; unhealthy under the gate). None is BROKEN: nothing was written outside a preserved folder. **Needs:** per app, a seed that uploads one file (or the volume named n/a with a reason: a redis cache, an ML model cache). | **READY — rank P3-LOW; owner: CC** |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.