93052884b2
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
149 lines
15 KiB
Markdown
149 lines
15 KiB
Markdown
# REPORT — every app checked again with the fixed persistence check; the remove dialog tells the truth; licence rulings; v0.288.0 + golden (2026-10-02, afternoon)
|
|
|
|
Architecture read: `07-backup-architecture.md` §6 (tiers) and §6.5 (kept data — now with decision 67), `09` §3 decisions
|
|
36, 65, 66, 67. Evidence root: `documentation/audits/persistence-sweep-2026-10-02/`.
|
|
|
|
| Part | State | One line |
|
|
|---|---|---|
|
|
| **A** — the re-sweep (R-801) | **DONE** | R-788 rule + the app's own fixture seed in the gate; all 58 on the bench; **0 BROKEN**; papra's start-up defect found and fixed (R-803). |
|
|
| **B** — the remove dialog (R-800) | **DONE** | v0.288.0: `userdata_kept` in the dialog data and both results; the sentence in both languages; red-proofed; live on 9202. |
|
|
| **C** — release, floor, golden | **DONE** | Floor 0.288.0 (min_agent 0.131.0), both demo boxes in 20 s; golden 0.288.0 baked, round-tripped, vouched; currency gate OK. |
|
|
| Rulings | **RECORDED** | `09` §3 66 (licences) and 67 (userdata stays); `07` §6.5; STATUS "Before the first paying customer". |
|
|
|
|
**Sweep headline (58 templates):** before (2026-08-02, 53 apps, + the new apps' records) CLEAN 38 · UNDETERMINED 11 ·
|
|
BROKEN 4 — **every one of those decided by start-time writes alone** → now **CLEAN 40 · UNDETERMINED 18 · BROKEN 0 ·
|
|
INCONCLUSIVE 0** (39 · 19 in the sweep; papra CLEAN after its fix). The August BROKEN four (gramps-web, papra, privatebin,
|
|
wishlist) were fixed in Campaign 10; none is BROKEN now. **No data-loss finding: no app writes outside a preserved folder.**
|
|
|
|
## Claims in the brief, checked
|
|
|
|
- **"Every stored verdict came only from start-time writes"** — RIGHT for the August sweep: 0 of 53 probes had sent a
|
|
request (`ports: []`, `exercise: []` in every `probe.json`). Not quite for today's morning runs of MeTube and
|
|
Grimmory, which ran after the R-801 fix.
|
|
- **"The upgrade fixtures can serve as exercisers"** — PARTLY. 51 of 58 apps have one; the gate called it for 16 apps;
|
|
it decided 2 (metube, privatebin). 13 seeded fine and stayed UNDETERMINED, because the seeds write only to the
|
|
database — upload / media / cache / redis volumes stay empty (R-807). plex's seed cannot run (a plex.tv claim token).
|
|
- **"Userdata is never touched by a remove"** — RIGHT, and it was already true before this session: the remove reads
|
|
`${HDD_PATH}` binds only, pinned since R-442 (`TestRemoveStack_R442_UserdataConventionFromPerAppPath`). What was
|
|
wrong was only that nothing SAID so. Measured again on 9202 (the video stayed).
|
|
- **The R-788 decoy as written ("an app that writes nothing → exit 2, red on the old rule")** — the old rule already gave
|
|
exit 2 for an app that writes nothing. The real gap was an app that writes SOMEWHERE while a declared volume stays
|
|
empty (old: CLEAN). The decoy was built for that shape and seen red (`A/RP-R788-empty-volume.txt`).
|
|
|
|
## Part A — the sweep
|
|
|
|
Gate changes (catalog): an empty declared volume after the exercise is UNDETERMINED (R-788, red-proofed; three old tests
|
|
pinned the old rule and changed with it); when a volume stays empty the gate calls the app's own upgrade-fixture seed
|
|
(one call through `upgrade_fixtures` / `upgrade_boxport`, as `upgrade-test.py` does — it replaces the morning's
|
|
MeTube-only exerciser). Bench 9401 on demo-hp, 200G, swap 0, Docker Hub logged in, 8 batches; full table with reasons:
|
|
`A/sweep/TABLE.md`; verdict lines in the catalog: `audits/persistence-sweep-2026-10-02/verdicts.txt`.
|
|
|
|
| app | before | now | what made it write | notes |
|
|
|---|---|---|---|---|
|
|
| actualbudget | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| adventurelog | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| audiobookshelf | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| bentopdf | UNDETERMINED (08-02, start writes) | UNDETERMINED | deep pass | nothing was written to any mount and nothing data-classified in any writable layer — the app produced no data |
|
|
| bookstack | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| calcom | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| calibre-web | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | calibre-web: /app/calibre-web-automated/empty_library — database file(s) touched but byte-identical to the ima |
|
|
| claper | UNDETERMINED (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Claper: seed OK — claper: see | claper: declared volume /app/priv/static/uploads is EMPTY after the exercise — the app was not shown to write |
|
|
| code-server | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| crafty-controller | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Crafty: seed OK — crafty: POS | crafty-controller: declared volume /crafty/servers is EMPTY after the exercise — the app was not shown to writ |
|
|
| dawarich | CLEAN (10-01, start writes) | UNDETERMINED | fixture seed OK — `fixture Dawarich: seed OK — dawarich: | dawarich-redis: declared volume /data is EMPTY after the exercise — the app was not shown to write where the t |
|
|
| docmost | UNDETERMINED (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Docmost: seed OK — docmost: / | docmost: declared volume /app/data/storage is EMPTY after the exercise — the app was not shown to write where |
|
|
| emby | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| ghost | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| gitea | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| glance | UNDETERMINED (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| gokapi | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| grafana | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| gramps-web | BROKEN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture GrampsWeb: seed OK — gramps-w | gramps-web: declared volume /app/secret is EMPTY after the exercise — the app was not shown to write where the |
|
|
| grimmory | CLEAN (10-02, after R-801) | CLEAN | GET exercise (or start) | grimmory: this container's mounts are all empty while it created entries in ['/', '/app', '/etc'] — benign whe |
|
|
| home-assistant | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| homebox | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| homepage | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| immich | UNDETERMINED (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Immich: seed OK — immich: see | immich-machine-learning: declared volume /cache is EMPTY after the exercise — the app was not shown to write w |
|
|
| jellyfin | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| karakeep | CLEAN (10-01, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| kimai | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| komga | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| mealie | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| metube | CLEAN (10-02, after R-801) | CLEAN | fixture seed OK — `fixture MeTube: seed OK — metube: the | - |
|
|
| n8n | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| navidrome | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| nextcloud | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| onlyoffice | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | onlyoffice: writable-layer writes at /var/www/onlyoffice/documentserver/sdkjs-plugins/{07FD8DFA-DFE0-4089-AL24 |
|
|
| opengist | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| outline | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Outline: seed OK — outline: s | outline: declared volume /var/lib/outline/data is EMPTY after the exercise — the app was not shown to write wh |
|
|
| paperless-ngx | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| papra | BROKEN (08-02, start writes) | CLEAN after the fix (768M); UNDETERMINED at 256M (OOM) | nothing answered HTTP | R-803: OOM-killed at 256M; fixed |
|
|
| plant-it | UNDETERMINED (08-02, start writes) | UNDETERMINED | start only (no routed port, no HTTP exercise) | no containers created (compose up rc=18: se/plant-it, repository does not exist or may require 'docker login': |
|
|
| plex | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed FAILED — `fixture NoRoute: seed returned nothin | plex: declared volume /transcode is EMPTY after the exercise — the app was not shown to write where the templa |
|
|
| privatebin | BROKEN (08-02, start writes) | CLEAN | fixture seed OK — `fixture PrivateBin: seed OK — private | - |
|
|
| radarr | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | radarr: writable-layer writes at /media (path suggests state, no database signature — judgement needed): ['mov |
|
|
| radicale | CLEAN (10-01, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| rallly | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| recipe-importer | UNDETERMINED (08-02, start writes) | UNDETERMINED | deep pass | NOTHING this app wrote landed in ANY folder the template preserves: all 1 mount(s) across 1 container(s) are e |
|
|
| romm | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| seerr | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| sonarr | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | sonarr: writable-layer writes at /media (path suggests state, no database signature — judgement needed): ['tv' |
|
|
| sparkyfitness | UNDETERMINED (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Sparkyfitness: seed OK — spar | sparkyfitness-server: declared volume /app/SparkyFitnessServer/backup is EMPTY after the exercise — the app wa |
|
|
| tandoor | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Django: seed OK — tandoor: se | tandoor: declared volume /opt/recipes/mediafiles is EMPTY after the exercise — the app was not shown to write |
|
|
| termix | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| uptime-kuma | UNDETERMINED (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| vaultwarden | CLEAN (08-02, start writes) | CLEAN | GET exercise (or start) | - |
|
|
| vikunja | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Vikunja: seed OK — vikunja: c | vikunja: declared volume /app/vikunja/files is EMPTY after the exercise — the app was not shown to write where |
|
|
| wanderer | UNDETERMINED (08-02, start writes) | UNDETERMINED | GET exercise (or start) | wanderer: unhealthy; wanderer: declared volume /app/uploads is EMPTY after the exercise — the app was not show |
|
|
| wger | CLEAN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Wger: seed OK — wger: POST /a | wger: declared volume /home/wger/media is EMPTY after the exercise — the app was not shown to write where the |
|
|
| wishlist | BROKEN (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Wishlist: seed OK — wishlist: | wishlist: declared volume /usr/src/app/uploads is EMPTY after the exercise — the app was not shown to write wh |
|
|
| zipline | UNDETERMINED (08-02, start writes) | UNDETERMINED | fixture seed OK — `fixture Zipline: seed OK — zipline: / | zipline: declared volume /zipline/uploads is EMPTY after the exercise — the app was not shown to write where t |
|
|
|
|
**REFUSED (BROKEN): none** — so no P1 data-loss row, no stop, no box to name.
|
|
|
|
**UNDETERMINED, why:** 13 apps — a second volume (uploads / media / cache / redis / backup) stays empty after a
|
|
successful seed that writes only the database (claper, crafty-controller, dawarich, docmost, gramps-web, immich, outline,
|
|
sparkyfitness, tandoor, vikunja, wger, wishlist, zipline — R-807). plex — no seed route (a plex.tv claim token).
|
|
bentopdf — stateless by design (no volume; a browser-side PDF tool). recipe-importer — stateless converter (its /data is
|
|
never written). wanderer — unhealthy under the gate, no fixture. plant-it — its image is gone from Docker Hub (R-804).
|
|
|
|
**Found and fixed on the way: papra (R-803, P1).** At its 256M limit every fresh install was OOM-killed in its
|
|
migration and crash-looped (its 26.6.2 step was proven by harness v1, without a memory watch). Measured from birth: peak
|
|
anon 405 MiB, cgroup peak 483 MiB, ~255 MiB idle. Now 768M (`mem_request` 256M): healthy in 31 s, 0 kills, the seed
|
|
read back, persistence CLEAN (`A/papra/`). No box ran papra.
|
|
|
|
`EXISTING-APPS-GAPS.md` regenerated from the new verdicts (storage 38 → 36 of 53); records 2.1 updated (radicale,
|
|
karakeep, grimmory, metube CLEAN; dawarich done with the redis volume named; sparkyfitness and wger open).
|
|
|
|
## Part B — the remove dialog (controller v0.288.0)
|
|
|
|
`GET /api/stacks/{name}/hdd-data`, `POST …/remove`, `DELETE /api/stacks/{name}` carry `userdata_kept` (always a
|
|
list). Both dialogs and both results: „A fájljaid ezekben a mappákban megmaradnak — a fájlböngészőben látod őket:" /
|
|
"Your files in these folders stay — you see them in the file browser:" + the folders. The false "no data on a drive" note
|
|
is gone when userdata exists. Tests + red-proof (two mutants, `B/RP-R800-mutants.txt`); parity fixtures regenerated —
|
|
additions only, 0 deletions, 109 pages. **Live on 9202** (`B/r800-9202.txt`, endpoint-level, no browser): MeTube from the
|
|
live catalog, one download; the dialog's data names the folder; the page carries the line in hu and en (with a negative
|
|
control); remove with data → 409 on 9202 (no host agent, R-442 — the with-data result is pinned by the unit test);
|
|
remove keeping data → the result names the folder; the video still there.
|
|
|
|
## Part C — release
|
|
|
|
Controller v0.288.0 (`580b656`), image built from the pushed tree. Floor 0.288.0 + min_agent 0.131.0 → demo-hp and
|
|
demo-felhom on 0.288.0 in 20 s (`C/floor.txt`). Golden 0.288.0: sha `436fdfa1…`, round trip equal, vouched (golden 0.288.0
|
|
/ agent 0.138.0 / min_agent 0.131.0), R-120 refused 0.287.0 after; `golden_currency_gate.py`: "OK — the newest released
|
|
controller has a golden" (`documentation/tests/golden-0.288.0-2026-10-02/`).
|
|
|
|
## Rows
|
|
|
|
Register **436 → 442**. Closed: R-788, R-790, R-791, R-792, R-795, R-801, R-803. Updated: R-789 (Tandoor — trigger),
|
|
R-800 (ruled A, built). Opened: R-802 (lawyer review), R-804 (plant-it image gone), R-805 (empty binds not judged),
|
|
R-806 (https backends / gramps-web first GET), R-807 (seeds that write only the database).
|
|
|
|
## Teardown
|
|
|
|
- **Bench 9401:** built and destroyed twice; `pct list` 9201 + 9202 only; nvme-scratch back to its baseline (+120 KiB).
|
|
Docker Hub credentials shredded on DooPlex, demo-hp and the bench; `docker logout` before the destroy.
|
|
- **9202:** MeTube removed (keeping data — with data refused by R-442), the harness's own test folder removed by hand;
|
|
controller 0.288.0; live catalog.
|
|
- **Hub:** the floor and the artifact manifest changed on purpose; nothing else. Tester-2: not touched.
|