Files
felhom.eu/documentation/audits/catalog-currency-2026-09-30.md
T
admin 42bf40bd2c
gates / gates (push) Successful in 29s
More apps update themselves at night (30+2 -> 35+3); the same-name security-fix gap measured (R-740, decision for the operator)
- Twelve steps published on both venues (catalog): first ladders for calibre-web, gitea, wger,
  crafty-controller, uptime-kuma, zipline (two steps); within-major emby, ghost, home-assistant,
  outline, rallly. immich's step v3.0.3 -> v3.2.2 re-proven at 768M (Part D).
- Part C: the night leg skips a digest-only change AND the catalog never records a same-tag re-test,
  so a same-name upstream fix reaches no box. Row R-740; the decision in STATUS; `09` decision 30
  carries a dated note (the decision itself unchanged).
- Rows: 369 -> 377. Opened R-735..R-742; closed R-735, R-738, R-742; narrowed R-462, R-624, R-446,
  R-440, R-734, R-732. The currency audit gains §1b (and corrects its 31+1 to 30+2).

Evidence: documentation/audits/more-night-apps-2026-09-30/. Report: REPORT-more-night-apps-2026-09-30.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-30 20:27:28 +02:00

32 KiB
Raw Blame History

Catalog currency — measured 2026-09-30

Catalog app-catalog-felhom.eu main = 64461979250b (clean tree). Controller source read at felhom-controller main = d48da6c. Read-only. Measured from DooPlex against the public registries with anonymous tokens. No box was touched. Nothing was pulled. Nothing was committed.

Method: the 2026-09-21 method (UPDATE-ARC-STATE-2026-09-21.md §3, update-arc-2026-09-21/00-drift.py), adapted in catalog-currency-2026-09-30/00-currency.py. What changed is written at the top of that script.

1. Headline numbers

53 apps · 79 image: lines · 67 distinct pins. 53 lines are exact releases, 24 are floating lines (12 distinct), 1 is Felhom's own image, 1 could not be measured (plant-it: the repository is gone).

question apps
behind upstream inside the same major (any image) 25 of 53
behind upstream across a major (any image) 19 of 53
behind in either way 37 of 53
— inside the major, exact pins only 24
— across a major, exact pins only 8 (gitea, gokapi, gramps-web, homepage, jellyfin, nextcloud, paperless-ngx, sparkyfitness)
— across a major, only because a floating engine line has a newer major 14 (postgres → 18, redis → 8, mariadb → 13.0, postgis → 18, claper 2.5 → 3.0)

Can update itself at night today:

number apps what it counts
(a) a box at the pin just before the catalog's head gets a step tonight 28, plus 1 conditional (nextcloud) the controller's own rule, catalog side (§4)
(b) the catalog's ladder has ≥1 proven entry 29 a looser count
never, today 24 no update_ladder: at all → skip reason no_test_record

(a) and (b) differ by one app only. Every entry in all 29 ladders is proven, and no entry has needs_person. The one difference is nextcloud: its newest step has files_may_change, so the leg takes it only when a fresh whole copy of the app is on the box.

What (a) does NOT say. It says the catalog offers a step. It does not say how many boxes are at a pin that matches. Nobody measured the fleet's pins today. A box installed from today's catalog is at the head and has nothing to do. And a night step only goes to the catalog's head, which is itself behind upstream for 37 apps.

1a. After the same day's moves (added by the session, 2026-09-30 afternoon)

The tables below are the MORNING measurement (catalog 6446197). The session then moved five apps on both venues: rallly, outline and sparkyfitness to PostgreSQL 18 (their first ladder steps), bookstack 26.05.5 → 26.09.1 and kimai apache-2.57.0 → 2.67.0 (+ the Part F apps named in pg-last-six-2026-09-30/F/). Re-running 04-analyse.py on the afternoon catalog with the same registry data: night (a) 31 + nextcloud conditional; (b) 32 carry a proven step; 21 have no ladder. The "behind upstream" counts are not re-measured; bookstack and kimai are now at their newest release inside the major.

1b. After the evening's moves (added 2026-09-30 evening, more-night-apps-2026-09-30/)

Correction first: the numbers above were counted before the immich step of the late afternoon; that step carries files_may_change (R-734), so at 4c552cf — the evening session's start — 04-analyse.py reads night (a) 30 + 2 conditional (nextcloud, immich); (b) 32, not 31 + 1. The evening published first ladders for calibre-web, gitea, wger, crafty-controller, uptime-kuma and zipline, and within-major steps for emby, ghost, home-assistant, outline, rallly (and zipline's second). Re-run of 04-analyse.py on the evening catalog, same registry data (more-night-apps-2026-09-30/count/): night (a) 35 + 3 conditional (nextcloud, immich, calibre-web); (b) 38 carry a proven step; 15 have no ladder. The "behind upstream" counts are not re-measured; each of the eleven moved apps is now at the newest release inside its major (re-checked the same evening, more-night-apps-2026-09-30/00-upstream-recheck.txt).

2. What could not be measured — stated, not guessed

  1. plant-it (msdeluise/plant-it:0.10.0): Docker Hub gives 401 on the tag list, and its catalog API gives 404 for the repository. Same finding as 2026-09-21. The app is lifecycle: abandoned.
  2. recipe-importer: Felhom's own image (gitea.dooplex.hu), not upstream. Not compared.
  3. Upload dates: not measured for any image. ghcr's anonymous API has no timestamp. Not needed for this question, so not attempted.
  4. Whether the newest tag is a GA release. The filter is by tag name only (rc, beta, alpha, dev, nightly, …). Two results need a person to check: mariadb:13.0 (a two-part line tag exists; GA not checked) and gitea/gitea:28.0.0 (pushed to Docker Hub 2026-09-30 00:15 UTC, the same day; a version renumbering, not checked).
  5. Tag shape switches. The rule compares only tags of the same shape. A control pass (09-shape-switch-check.txt) looked for higher versions under ANY shape for every pin called "up to date". It found three real switches, applied by hand with their tag lists (10-shape-switch-detail.txt):
    • gramps-web v25.6.0: upstream dropped the v. It is at 26.9.1. Calendar versions, so 25→26 is counted as across a major.
    • jellyfin 10.11.11: upstream 12 uses two-part tags. It is at 12.1 (12.0 had rc1..rc7).
    • kimai apache-2.57.0: upstream stopped the apache- tags. Plain 2.67.0 exists. Whether it is the same Apache variant is NOT checked. Two more hits were noise: sonarr (5.14-2.0.0…-ls5, a develop-style tag) and tandoor (a dependabot-… branch tag). The control ran only on pins called up to date, and not on floating lines. A switch hiding behind a pin already called "behind" would change only its "newest" value.
  6. Floating pins — did the tag move? 18 of 24 floating lines resolve to exactly the digest the ladder recorded when the step was tested. None has moved since its test. 6 cannot be compared, because the app has no ladder: outline (postgres 16, redis 7), rallly (postgres 16), sparkyfitness (postgres 15), zipline (postgres 16), wger (2.6).
  7. Majors for 0.x and calendar versions. "Major" is the first number of the tag, as written. For v0.x apps a minor step can be breaking. For calendar versions (bookstack 26.05, home-assistant 2026.9, actualbudget 26.9, papra 26.6) the first number is a year. This table does not interpret it.
  8. ghcr rate limit. immich-server and immich-machine-learning got HTTP 429 on the first run. They were measured on a slower retry (02-retry-429-run.txt). Nothing is left at 429.

3. Per image

"Floating (line)" = the tag names a version LINE, not a release: fewer than three numbers in its first version run (12.3, 16-alpine, 16-3.5-alpine, immich's 16-vectorchord…). For a floating line, "newest same major" is the newest line of the same shape in that major. "Newest at all" is the newest line of that shape.

app service pinned newest same major newest at all behind in major behind across major note
actualbudget actualbudget 26.9.0 26.9.0 26.9.0 no no
adventurelog adventurelog v0.13.0 v0.13.0 v0.13.0 no no
adventurelog adventurelog-postgres 16-3.5-alpine 16-3.5-alpine 18-3.6-alpine no yes floating (line); digest = ladder's tested digest
adventurelog adventurelog-frontend v0.13.0 v0.13.0 v0.13.0 no no
audiobookshelf audiobookshelf 2.36.1 2.37.1 2.37.1 yes no
bentopdf bentopdf v2.8.6 v2.8.8 v2.8.8 yes no
bookstack bookstack 26.05.5 26.09.1 26.09.1 yes no
bookstack bookstack-db 12.3 12.3 13.0 no yes floating (line); digest = ladder's tested digest
calcom calcom v6.2.0 v6.2.0 v6.2.0 no no
calcom calcom-postgres 18-alpine 18-alpine 18-alpine no no floating (line); digest = ladder's tested digest
calibre-web calibre-web v4.0.6 v4.0.8 v4.0.8 yes no
claper claper 2.5 2.5 3.0 no yes floating (line); digest = ladder's tested digest
claper claper-postgres 17-alpine 17-alpine 18-alpine no yes floating (line); digest = ladder's tested digest
code-server code-server 4.129.0 4.139.1 4.139.1 yes no
crafty-controller crafty-controller 4.10.7 4.11.0 4.11.0 yes no
docmost docmost 0.96.0 0.96.0 0.96.0 no no
docmost docmost-postgres 18-alpine 18-alpine 18-alpine no no floating (line); digest = ladder's tested digest
docmost docmost-redis 7-alpine 7-alpine 8-alpine no yes floating (line); digest = ladder's tested digest
emby emby 4.11.0.3 4.11.0.4 4.11.0.4 yes no
ghost ghost 6.65.0-alpine 6.67.0-alpine 6.67.0-alpine yes no
gitea gitea 1.27.0 1.27.3 28.0.0 yes yes
glance glance v0.8.5 v0.8.6 v0.8.6 yes no
gokapi gokapi v1.9.6 v1.9.6 v2.2.4 no yes
grafana grafana 13.2.2 13.2.3 13.2.3 yes no
gramps-web gramps-web v25.6.0 v25.6.0 26.9.1 no yes SHAPE SWITCH: upstream dropped the v prefix in 2026 (26.x.y); calendar versions, so the 25→26 step is a new year, counted as across
home-assistant home-assistant 2026.9.3 2026.9.4 2026.9.4 yes no
homebox homebox 0.26.2 0.26.2 0.26.2 no no
homepage homepage v1.13.2 v1.13.2 v2.4.0 no yes
immich immich-server v3.2.2 v3.2.4 v3.2.4 yes no measured on retry after ghcr 429
immich immich-machine-learning v3.2.2 v3.2.4 v3.2.4 yes no measured on retry after ghcr 429
immich immich-postgres 16-vectorchord0.4.3-pgvectors0.2.0 16-vectorchord0.4.3-pgvectors0.3.0 17-vectorchord0.4.3-pgvectors0.3.0 yes yes floating (line); digest = ladder's tested digest
immich immich-redis 7-alpine 7-alpine 8-alpine no yes floating (line); digest = ladder's tested digest
jellyfin jellyfin 10.11.11 10.11.11 12.1 no yes SHAPE SWITCH: upstream 12.x publishes two-part tags (12.1); 12.0 went through rc1..rc7
kimai kimai apache-2.57.0 2.67.0 2.67.0 yes no SHAPE SWITCH: upstream stopped publishing apache- tags after 2.57.0; plain 2.67.0 exists (whether it is the same apache variant is NOT checked)
kimai kimai-db 11.8 11.8 13.0 no yes floating (line); digest = ladder's tested digest
komga komga 1.27.1 1.28.0 1.28.0 yes no
mealie mealie v3.28.0 v3.28.0 v3.28.0 no no
n8n n8n 2.41.2 2.42.1 2.42.1 yes no
navidrome navidrome 0.64.1 0.64.2 0.64.2 yes no
nextcloud nextcloud 34.0.4-apache 34.0.4-apache 35.0.1-apache no yes
nextcloud nextcloud-db 12.3 12.3 13.0 no yes floating (line); digest = ladder's tested digest
nextcloud nextcloud-redis 7-alpine 7-alpine 8-alpine no yes floating (line); digest = ladder's tested digest
onlyoffice onlyoffice 9.4.0 9.4.0 9.4.0 no no
opengist opengist 1.15 1.15 1.15 no no floating (line); digest = ladder's tested digest
outline outline 1.9.1 1.10.1 1.10.1 yes no
outline outline-postgres 16-alpine 16-alpine 18-alpine no yes floating (line); no ladder digest to compare
outline outline-redis 7-alpine 7-alpine 8-alpine no yes floating (line); no ladder digest to compare
paperless-ngx paperless-webserver 2.20.15 2.20.15 3.2.1 no yes
paperless-ngx paperless-postgres 18-alpine 18-alpine 18-alpine no no floating (line); digest = ladder's tested digest
paperless-ngx paperless-redis 7-alpine 7-alpine 8-alpine no yes floating (line); digest = ladder's tested digest
papra papra 26.6.2-rootless 26.6.2-rootless 26.6.2-rootless no no
plant-it plant-it 0.10.0 ? ? ? ? NOT MEASURED: HTTPError: 401 Client Error: Unauthorized for url: https://r
plex plex 1.41.4.9463-630c9f557 1.43.4.10903-e5521bd8c 1.43.4.10903-e5521bd8c yes no
privatebin privatebin 2.0.6 2.0.6 2.0.6 no no
radarr radarr 6.4.4 6.4.4 6.4.4 no no
rallly rallly 4.11.1 4.15.3 4.15.3 yes no
rallly rallly-postgres 16-alpine 16-alpine 18-alpine no yes floating (line); no ladder digest to compare
recipe-importer recipe-importer v0.9.11 — — n/a n/a Felhom's own image, not upstream
romm romm 5.3.1 5.3.1 5.3.1 no no
romm romm-db 11.8 11.8 13.0 no yes floating (line); digest = ladder's tested digest
romm romm-redis 7-alpine 7-alpine 8-alpine no yes floating (line); digest = ladder's tested digest
seerr seerr 2.7.3 2.7.3 2.7.3 no no
sonarr sonarr 4.0.20 4.0.20 4.0.20 no no
sparkyfitness sparkyfitness-db 15-alpine 15-alpine 18-alpine no yes floating (line); no ladder digest to compare
sparkyfitness sparkyfitness-server v0.17.3 v0.17.3 v1.7.3 no yes
sparkyfitness sparkyfitness-frontend v0.17.3 v0.17.3 v1.7.3 no yes
tandoor tandoor 2.6.15 2.6.15 2.6.15 no no
tandoor tandoor-postgres 17-alpine 17-alpine 18-alpine no yes floating (line); digest = ladder's tested digest
termix termix 2.8.0 2.8.0 2.8.0 no no
uptime-kuma uptime-kuma 2.4.0 2.5.5 2.5.5 yes no
vaultwarden vaultwarden 1.36.0-alpine 1.37.3-alpine 1.37.3-alpine yes no
vikunja vikunja 2.6.0 2.6.0 2.6.0 no no
wanderer wanderer v0.20.0 v0.21.0 v0.21.0 yes no
wanderer wanderer-db v0.20.0 v0.21.0 v0.21.0 yes no
wanderer wanderer-search v1.36.0 v1.54.2 v1.54.2 yes no
wger wger 2.6 2.7 2.7 yes no floating (line); no ladder digest to compare
wishlist wishlist v0.67.1 v0.67.1 v0.67.1 no no
zipline zipline 4.6.1 4.8.0 4.8.0 yes no
zipline zipline-postgres 16-alpine 16-alpine 18-alpine no yes floating (line); no ladder digest to compare

4. The night rule, from the code

Source: felhom-controller/controller/internal/stacks/unattended.go at d48da6c, function legCandidate (L392–463). RunUpdateLeg (L221) → runUpdateLeg (L232–347) calls it for every deployed, non-protected app (L276–281) and presses StartGuardedUpdate only when it returns no reason (L311–321). The ladder reader is ladder.go (LoadLadder L83, sameRefs L118, nextLadderStep L152, which makes the same choice, L163).

In order, an app is skipped when:

code line skip condition
L397–407 held a hold on the app
L400 stopped_by_household the household stopped it
L408–416 current / ahead / order_unknown CatalogOrder is not Behind
L417–419 unpinned app.yaml has no pinned_images
L422–424 no_test_record the template has no update_ladder:
L426–432 — picks the NEWEST entry whose from equals the box's pin, every service, exactly
L433–435 no_test_record no match, and the pin is the head's to (behind only on a digest no step records)
L436–438 older_than_ladder no match at all: the box is older than the ladder
L441 not_proven the entry's verdict is not proven
L444 needs_person marks.needs_person is set and not blank
L447 failed_before the same step was undone or held on this ladder (R-680)
L450–458 files_may_change_no_whole_copy marks.files_may_change and no fresh whole copy on the box

After that, StartGuardedUpdate can still refuse for box reasons: no_backup, engine_no_test, memory, disk, busy… (UpdatePreflight, update.go L368 on; the checks at L431–470).

Definition used for (a). For each app, take a box whose pin is the from of the ladder's newest entry (one step behind the head). Apply the catalog-side rows above (L422–458) to the entry the code would pick. Count "yes" when nothing skips it, and "conditional" when only files_may_change stands in the way. The box-side rows (hold, stop, failed_before, whole copy, refusals) are not in this count, because they depend on the box.

Three things this rule means, read from the code:

  • A box older than the ladder is never pressed (L436–438). For 21 apps the ladder's first entry is a backfill of the 2026-09-21/22 move (backfilled). A box installed before that move matches it and can climb. A box older than that from cannot.
  • A digest-only change is never pressed at night. A box at the head tag with an older digest is "behind" by digestBehind (updateorder.go L95), but no entry has that from, so L433–435 skips it.
  • The 11 backfilled head entries are harness v1 (no memory watch). The box does not read harness_version, only verdict, so it presses them like any other: actualbudget, audiobookshelf, bookstack, grafana, home-assistant, papra, privatebin, radarr, sonarr, termix, vikunja.

5. Per app — the ladder

"Head = compose" is the gate's rule 3 (check-test-record.py, run today: 53 read, 29 with a ladder, 0 convicted, 03-check-test-record.txt). "Box one step behind, tonight" is number (a).

app entries proven of which backfilled needs_person files_may_change engine conversion head = compose box one step behind, tonight catalog_since
actualbudget 1 1 1 0 0 0 yes yes 2026-09-22
adventurelog 1 1 0 0 0 0 yes yes 2026-09-27
audiobookshelf 1 1 1 0 0 0 yes yes 2026-09-22
bentopdf 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-12
bookstack 1 1 1 0 0 0 yes yes 2026-09-22
calcom 1 1 0 0 0 1 yes yes 2026-09-28
calibre-web 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-12
claper 1 1 0 0 0 1 yes yes 2026-09-28
code-server 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
crafty-controller 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-06-26
docmost 2 2 1 0 0 1 yes yes 2026-09-25
emby 2 2 1 0 0 0 yes yes 2026-09-23
ghost 2 2 1 0 0 0 yes yes 2026-09-23
gitea 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
glance 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-09-21
gokapi 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
grafana 1 1 1 0 0 0 yes yes 2026-09-22
gramps-web 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
home-assistant 1 1 1 0 0 0 yes yes 2026-09-22
homebox 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-19
homepage 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
immich 2 2 1 0 0 0 yes yes 2026-09-23
jellyfin 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
kimai 1 1 0 0 0 0 yes yes 2026-09-23
komga 1 1 0 0 0 0 yes yes 2026-09-23
mealie 2 2 1 0 0 0 yes yes 2026-09-25
n8n 3 3 1 0 0 0 yes yes 2026-09-25
navidrome 2 2 1 0 0 0 yes yes 2026-09-23
nextcloud 2 2 1 0 1 0 yes conditional: files_may_change (needs a fresh whole copy) 2026-09-23
onlyoffice 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
opengist 1 1 0 0 0 0 yes yes 2026-09-23
outline 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
paperless-ngx 1 1 0 0 0 1 yes yes 2026-09-27
papra 1 1 1 0 0 0 yes yes 2026-09-22
plant-it 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-21
plex 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-02-15
privatebin 1 1 1 0 0 0 yes yes 2026-09-22
radarr 1 1 1 0 0 0 yes yes 2026-09-22
rallly 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
recipe-importer 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-12
romm 3 3 1 0 0 0 yes yes 2026-09-23
seerr 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
sonarr 1 1 1 0 0 0 yes yes 2026-09-22
sparkyfitness 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
tandoor 2 2 1 0 0 1 yes yes 2026-09-27
termix 1 1 1 0 0 0 yes yes 2026-09-22
uptime-kuma 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-09-21
vaultwarden 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18
vikunja 1 1 1 0 0 0 yes yes 2026-09-22
wanderer 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-21
wger 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-19
wishlist 1 1 0 0 0 0 yes yes 2026-09-23
zipline 0 0 0 0 0 0 — no ladder: never (no_test_record) 2026-07-18

6. Plan for R-462 — widen the upgrade harness

Order. First: what a household loses if an update goes wrong. Then: how far behind. T1 = a database engine service, or household files the backup marks mandatory. T2 = household data in the app's own volume (SQLite or files). T3 = little or no household data.

Cost, per app. The only measured numbers are in R-462: a proven edge takes 6.4–305 s (median 72 s) plus the 600 s soak, a failing edge ~556 s, and a new fixture dominates everything else (the database legs were costed at 21–34 CC-hours for 15 services, 09 §6.4). So: fixture exists = one bench run plus one box run. Fixture needed = write a seed and a read-back through the app's own interface, with a negative control, first. No route = the harness can only answer inconclusive.

6.1 Do next — in this order

  1. immich (T1, fixture exists). Server and ML v3.2.2 → v3.2.4, and a newer pgvectors line inside PostgreSQL 16. Cheapest high-value run: the fixture exists and the step is inside the major.
  2. bookstack (T1, fixture exists). 26.05.5 → 26.09.1. The fixture proves the database half only (R-460).
  3. kimai (T1, fixture exists). 2.57.0 → 2.67.0, but the tag shape changed (apache- is gone). A person must first confirm which plain tag is the Apache image. Then one run.
  4. calibre-web (T1, fixture needed). v4.0.6 → v4.0.8, household books marked mandatory, and no ladder at all. The first new fixture to write.
  5. gitea (T2, fixture exists but fails at the web installer, R-624). 1.27.0 → 1.27.3. Cheap: the installer-form POST was never written. It unblocks gitea's first ladder entry.
  6. The T2 apps with a fixture and a step inside the major — one run each: audiobookshelf, emby, ghost, grafana, home-assistant, komga, n8n, navidrome. This is the bulk and costs machine time only.
  7. T2 apps that need a new fixture and are behind inside the major: wanderer (3 images, incl. meilisearch v1.36 → v1.54), uptime-kuma, crafty-controller, wger.

6.2 Majors — human work by the 2026-09-02 ruling, not harness bulk

nextcloud 34 → 35, paperless-ngx 2 → 3, gokapi 1 → 2, homepage 1 → 2, sparkyfitness 0.17 → 1.7, gitea 1 → 28 (check first), jellyfin 10 → 12, gramps-web v25 → 26. Engine lines: redis 7 → 8 (6 apps), mariadb → 13.0 (bookstack, kimai, nextcloud, romm; GA not checked), PostgreSQL for the R-463 remainder (adventurelog/postgis, immich, outline, rallly, sparkyfitness on 15, zipline).

6.3 The honest ceiling — CORRECTED the same day

The morning version of this section named 8 apps that can only ever be inconclusive. Three of them were wrong, measured on the bench the same afternoon: outline has a front-door first-run route (POST /api/installation.create, refused once a workspace exists), rallly signs up through its own API with the six-digit e-mail code read from its own table in place of a mailbox, and zipline 4's first-run route is POST /api/setup (the old fixture called two other paths). outline and rallly moved on both venues the same day; zipline's fixture now tries /api/setup first.

What remains in the class: vaultwarden (closed sign-up by design, R-624) and code-server (a browser IDE); plex (a plex.tv claim token), homepage and onlyoffice (no household data) are no-route by nature. Plus plant-it (image gone) and recipe-importer (our own image; a fixture is needed). So the most the harness can prove is 47 of 53.

6.4 The whole ranking

rank app data at risk ladder entries images behind in major across major fixture
1 outline T1: DB engine: outline-postgres 0 1 2 no route fixture written 2026-09-30 (installation.create); moved to PG 18 the same day
2 rallly T1: DB engine: rallly-postgres 0 1 1 no route fixture written 2026-09-30 (sign-up + its own e-mail code); moved to PG 18 the same day
3 zipline T1: DB engine: zipline-postgres 0 1 1 no route first-run route POST /api/setup measured 2026-09-30; fixture fixed; PG stays 16
4 calibre-web T1: household files (backup class mandatory) 0 1 0 fixture needed
5 sparkyfitness T1: DB engine: sparkyfitness-db 0 0 3 fixture needed
6 immich T1: DB engine: immich-postgres; household files (backup class mandatory) 2 3 2 fixture exists
7 bookstack T1: DB engine: bookstack-db 1 1 1 fixture exists
8 kimai T1: DB engine: kimai-db 1 1 1 fixture exists
9 nextcloud T1: DB engine: nextcloud-db; household files (backup class mandatory) 2 0 3 fixture exists
10 claper T1: DB engine: claper-postgres 1 0 2 fixture exists
11 paperless-ngx T1: DB engine: paperless-postgres; household files (backup class mandatory) 1 0 2 fixture exists
12 romm T1: DB engine: romm-db 3 0 2 fixture exists
13 adventurelog T1: DB engine: adventurelog-postgres 1 0 1 fixture exists
14 docmost T1: DB engine: docmost-postgres 2 0 1 fixture exists
15 tandoor T1: DB engine: tandoor-postgres 2 0 1 fixture exists
16 calcom T1: DB engine: calcom-postgres 1 0 0 fixture exists
17 wanderer T2: household data in the app's own volume (SQLite / files) 0 3 0 fixture needed
18 gitea T2: household data in the app's own volume (SQLite / files) 0 1 1 fixture exists, fails at the web installer (R-624, fixable)
19 code-server T2: household data in the app's own volume (SQLite / files) 0 1 0 no route: its front door is a browser IDE behind one password
20 crafty-controller T2: household data in the app's own volume (SQLite / files) 0 1 0 fixture needed
21 plex T2: household data in the app's own volume (SQLite / files) 0 1 0 no route: the first-run claim needs a token minted at plex.tv by a real Plex acc
22 uptime-kuma T2: household data in the app's own volume (SQLite / files) 0 1 0 fixture needed
23 vaultwarden T2: household data in the app's own volume (SQLite / files) 0 1 0 no route by design: closed sign-up (R-624)
24 wger T2: household data in the app's own volume (SQLite / files) 0 1 0 fixture needed
25 gokapi T2: household data in the app's own volume (SQLite / files) 0 0 1 fixture needed
26 gramps-web T2: household data in the app's own volume (SQLite / files) 0 0 1 fixture exists
27 jellyfin T2: household data in the app's own volume (SQLite / files) 0 0 1 fixture exists
28 homebox T2: household data in the app's own volume (SQLite / files) 0 0 0 fixture exists
29 plant-it T2: household data in the app's own volume (SQLite / files) 0 0 0 fixture needed
30 recipe-importer T2: household data in the app's own volume (SQLite / files) 0 0 0 fixture needed
31 seerr T2: household data in the app's own volume (SQLite / files) 0 0 0 fixture needed
32 audiobookshelf T2: household data in the app's own volume (SQLite / files) 1 1 0 fixture exists
33 emby T2: household data in the app's own volume (SQLite / files) 2 1 0 fixture exists
34 ghost T2: household data in the app's own volume (SQLite / files) 2 1 0 fixture exists
35 grafana T2: household data in the app's own volume (SQLite / files) 1 1 0 fixture exists
36 home-assistant T2: household data in the app's own volume (SQLite / files) 1 1 0 fixture exists
37 komga T2: household data in the app's own volume (SQLite / files) 1 1 0 fixture exists
38 n8n T2: household data in the app's own volume (SQLite / files) 3 1 0 fixture exists
39 navidrome T2: household data in the app's own volume (SQLite / files) 2 1 0 fixture exists
40 actualbudget T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
41 mealie T2: household data in the app's own volume (SQLite / files) 2 0 0 fixture exists
42 opengist T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
43 papra T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
44 privatebin T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
45 radarr T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
46 sonarr T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
47 termix T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
48 vikunja T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
49 wishlist T2: household data in the app's own volume (SQLite / files) 1 0 0 fixture exists
50 bentopdf T3: little or no household data (stateless or config only) 0 1 0 fixture needed
51 glance T3: little or no household data (stateless or config only) 0 1 0 fixture needed
52 homepage T3: little or no household data (stateless or config only) 0 0 1 no route: a dashboard rendered from config files in the template
53 onlyoffice T3: little or no household data (stateless or config only) 0 0 0 no route: a stateless document server: it holds no household data of its own, so

7. Files

In catalog-currency-2026-09-30/:

file what
00-currency.py the measurement (registries, read-only)
01-currency-raw.json, 01-currency-run.txt its raw output
02-retry-429.py, 02-retry-429-run.txt the slower retry for the two ghcr 429 rows
03-check-test-record.txt the catalog's own ladder gate, run today
04-analyse.py, 04-analyse-run.txt tables and counts (offline; the night rule is in its header)
05-summary.json the counts and app lists
06-, 07-, 08-*.md the three tables above
09-shape-switch-check.txt, 10-shape-switch-detail.txt the shape-switch control

Findings that should become register rows (this session did not edit the register — read-only brief): the three tag-shape switches (gramps-web, jellyfin, kimai), which the badge and any shape-based tool read as "up to date"; and the gitea 28.0.0 / mariadb 13.0 tags, which need a GA check before anyone plans on them.