Files
felhom.eu/documentation/tests/VALIDATION-update-slice3-2026-09-06.md
T
admin 417df06f35
gates / gates (push) Successful in 17s
slice 3 docs: the ruling, the shipped mechanism, and four rows closed
09-update-architecture.md gains the fourth dated operator ruling (2026-09-06,
Option 1) and its section 5 is rewritten from a proposed shape into the shipped
one: the pin, the stored definition, the render table, the four writers, the
startup ordering, and the trap this slice set for slice 2 - the live compose file
is now the frozen one, so a badge comparing against it would answer Naprakesz on
exactly the apps that are behind.

02-controller-module-map.md said 'copy compose + .felhom.yml'. That stopped being
true today, so it is corrected, and the two sections describing the old seam now
carry a banner saying they describe v0.234.0 and below - kept because every box
under v0.235.0 still behaves that way and because they are the measured account
of why it changed.

R-447, R-441, R-438 and R-455 closed and compressed into CLOSED-ITEMS; R-458
opened for the .felhom.yml asymmetry, with what would settle it by measurement.

Live evidence: two real catalog pushes travelling the real 15-minute cycle, both
reverted, the tree byte-identical afterwards. The restart that used to take 18.3
seconds and pull a new image now takes 0.1 seconds and pulls nothing.
2026-09-06 10:37:33 +02:00

8.2 KiB
Raw Blame History

VALIDATION — update arc slice 3, live on demo-hp (2026-09-06)

Controller 0.235.0, guest 9201 on demo-hp (Tier 0 — disposable). Evidence copied off the box at the end of the phase that produced it.

Method: endpoint level plus a REAL CATALOG PUSH travelling the REAL 15-minute cycle. There is no browser on DooPlex. Because v0.235.0 changes what the catalog SYNCER does, a hand-edited file on the box would have proved nothing — the same reasoning SPIKE-app-update-2026-09-01 §3 used, and the same app: bentopdf, deployed on demo-hp only, file-based, no database and no volume, so no data anywhere could be touched.

Two catalog commits, both reverted in the same session, and the catalog tree is byte-identical to 8220f8d afterwards: dc7e548 (non-image), 09b4ff5 (image), 1798ce6 + 17cc784 (the reverts).


0. Baseline, read before anything

ref     = ghcr.io/alam00000/bentopdf:v2.8.6
digest  = sha256:eaeea1e447205a79cb61d7efdc6966f37311dc1bc9c36a3a5c897bf79107c2c3
compose = image: ghcr.io/alam00000/bentopdf:v2.8.6   /   interval: 30s
controller before = 0.234.0

1. Adoption — all nine apps pinned, none guessed at

07:46:50 [INFO] [stacks] pin bentopdf: bentopdf=ghcr.io/alam00000/bentopdf:v2.8.6
07:46:50 [INFO] [stacks] pin bookstack: bookstack=lscr.io/linuxserver/bookstack:26.05.2, bookstack-db=mariadb:12.3
07:46:50 [INFO] [stacks] pin docmost: docmost=docmost/docmost:0.95.0, docmost-postgres=postgres:16-alpine, docmost-redis=redis:7-alpine
…
07:46:50 [INFO] [stacks] pin adoption: 9 pinned, 0 already pinned, 0 left unpinned
                         (0 not completely observed, 0 running something the template no longer offers)

applied-compose.yml appeared in every stack dir (bentopdf: 1109 bytes). Multi-service apps pinned per service — docmost and paperless-ngx carry three each.

2. Scenario A — the catalog still offers the pinned version: FIXES FLOW

Catalog commit dc7e548: healthcheck interval: 30s → 45s. No image touched. Left to travel the real cycle.

08:01:51 [INFO] [sync] Updated bentopdf/docker-compose.yml
08:01:51 [INFO] [sync] Periodic sync: Sablonok frissítve — frissítve: bentopdf

live compose : image: …bentopdf:v2.8.6      interval: 45s     ← the fix arrived
container    : ghcr.io/alam00000/bentopdf:v2.8.6  started=2026-09-06T03:30:20Z   ← untouched

This is the half the operator explicitly chose to keep, and it is intact.

3. Scenario B — the catalog moves: THE APP FREEZES

Catalog commit 09b4ff5: v2.8.6 → v2.8.5. Real cycle again.

08:20:29 [INFO] [sync] Updated bentopdf/docker-compose.yml
catalog cache: image: …bentopdf:v2.8.5      ← the catalog has moved
live compose : image: …bentopdf:v2.8.6      ← the app has NOT
pin          : bentopdf: ghcr.io/alam00000/bentopdf:v2.8.6

3b. And a restart afterwards leaves the version alone — the quantitative discriminator

POST /api/stacks/bentopdf/restart → {"ok":true,…}

before v0.235.0 (spike §2, variant 1a) now
elapsed 18.3 s 0.1 s
image pulled? yes, v2.8.5 appeared in the local store no — docker images lists only v2.8.6
container recreated on the new image not even recreated, started=2026-09-06T03:30:20Z unchanged
digest changed sha256:eaeea1e4… — identical to the baseline
08:21:44 [INFO] [api] restart requested for stack: bentopdf
08:21:44 [INFO] [stacks] Stack bentopdf restarted successfully (took 0.1s)

The 18.3 s → 0.1 s split is worth more than the tags: the old path spent eighteen seconds because it was pulling a new image. There is nothing to pull now.

4. Scenario G — the badge still tells the truth

The trap this slice set for slice 2: the live compose file now names the OLD version, so a badge comparing against it would answer „Naprakész" on exactly the apps that are behind. Quoted from the live page while frozen:

<span class="tag tag-warn" title="Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.">Frissítés elérhető — 56 napja</span>

56 is arithmetic on a real value: bentopdf's catalog_since is 2026-07-12.

ASCII fragments, grep -oF, with positive and negative controls:

fragment /apps/bentopdf /stacks
Naprak 0 8 (the eight apps that are current)
napja 1 1
BentoPDF (positive control) 4 1
zzz-never-present (negative) 0 0
Nem-karbantartott-XYZ (negative) 0 0

The update button renders exactly once, unchanged.

5. Scenario D — the Update button still moves the version

POST /api/stacks/bentopdf/update → {"ok":true,…}, 16.7 s wall clock.

08:22:21 [INFO] [stacks] update bentopdf: pin advanced to the catalog's current definition (bentopdf=…:v2.8.5)
08:22:38 [INFO] [stacks] Stack bentopdf updated successfully (took 16.6s)

container : ghcr.io/alam00000/bentopdf:v2.8.5  started=2026-09-06T08:22:38Z
digest    : sha256:2d867aacb8ab5b196d00ee86944b1899d09d72df355384c5e15cf974737963a0
live      : image: …:v2.8.5      pin: …:v2.8.5      applied-compose: …:v2.8.5

The pin advanced at 08:22:21, seventeen seconds before the update completed — i.e. BEFORE the pull, which is the ordering the design requires. The digest matches the one the spike recorded for v2.8.5 (sha256:2d867aac…), independently.

6. A REAL GAP THIS RUN FOUND, fixed before Scenario B was run

Scenario A passing is what exposed it. The stored definition is written when the PIN is written, so the healthcheck fix delivered at 08:01:51 landed in the live compose file and not in applied-compose.yml. The first time the catalog then moved, the freeze rendered the pre-fix definition — reverting every fix delivered since, silently undoing the half of the ruling that says fixes keep flowing.

Observed directly at 08:20:29: the frozen live file came back with interval: 30s, not the 45s delivered nineteen minutes earlier.

Fixed the same session: the equal-images branch now refreshes the stored definition as it delivers a fix. The images cannot move in that branch by construction, so no version moves and no intent is rewritten. TestFixRefreshesTheStoredDefinition pins both halves. The 08:20:29 observation above was taken on the binary before that fix and is left in this document deliberately — it is the evidence for why the fix exists.

6b. The freeze holds in BOTH directions — and the refreshed store proves itself

The catalog was reverted to v2.8.6 (commits 1798ce6, 17cc784) and left to travel the cycle. The app was by then pinned to v2.8.5, so the older catalog is now the one that has "moved" relative to the pin:

08:35:29 [DEBUG] [sync] bentopdf/docker-compose.yml: hash match, skipped
catalog : image: …:v2.8.6   interval: 30s
live    : image: …:v2.8.5   interval: 45s     ← frozen, and BOTH fields come from the stored definition
container: ghcr.io/alam00000/bentopdf:v2.8.5

The pin is what the customer HAS, not what is newest — a downgrade in the catalog is refused exactly as an upgrade is. And the interval: 45s here is the §6 fix working: the applied definition written by the 08:22 update carried both the v2.8.5 image and the 45 s healthcheck, so the freeze reproduced the whole thing. hash match, skipped shows the content-compare still suppresses a pointless rewrite.

7. Teardown — back to the baseline, byte for byte

POST /api/stacks/bentopdf/update once more, against the reverted catalog:

container : ghcr.io/alam00000/bentopdf:v2.8.6
digest    : sha256:eaeea1e447205a79cb61d7efdc6966f37311dc1bc9c36a3a5c897bf79107c2c3   ← IDENTICAL to §0
live      : image: …:v2.8.6   interval: 30s      ← identical to §0
pin       : …:v2.8.6
status    : healthy

Fleet afterwards: 21 containers up, none unhealthy; /stacks renders „Naprakész" ×9 with napja at 0 and the negative control at 0. Controller 0.235.0, healthy.

Both catalog commits reverted; the catalog tree is byte-identical to 8220f8d. This run provisioned nothing — no guest, no storage, no hub record — so there is no teardown to report on any of the three layers.