slice 3 docs: the ruling, the shipped mechanism, and four rows closed
gates / gates (push) Successful in 17s
gates / gates (push) Successful in 17s
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.
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# 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:
|
||||
|
||||
```html
|
||||
<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.
|
||||
Reference in New Issue
Block a user