Files
felhom.eu/REPORT-persistence-sweep-2026-10-02.md
T

15 KiB

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.