Files
felhom.eu/documentation/audits/PROBE-FIX-2026-09-22.md
T
admin a975cfde5b
gates / gates (push) Successful in 28s
probe fix, the gate, and the promotion train (R-618 closed, R-630..632 opened)
Part 1: tandoor/zipline/wger probes corrected in the catalog and red-proofed live on 9202 in both
directions - "Nem egeszseges" with the front door serving 200, then "Fut" after the real sync with
no redeploy. tandoor's failed edge re-walked: done at +41.1s where it was failed at +361.9s.

Part 2: fifteen proven versions on the live catalog, one commit per app; the guarded Update pressed
on four apps on demo-hp, all four done.

Opened: R-630 (paperless-ngx's probe has never run on any box - a silent absence, worse than the
wrong probe that was found in one night), R-631 (five templates no static rule can judge),
R-632 (28 of 53 templates never deployed by any drill). Closed: R-618.

Register 318 -> 321. No product code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-22 11:10:58 +02:00

15 KiB

THE MORNING AFTER — probe fix, gate, and the promotion train, 2026-09-22

Evidence: probe-fix-2026-09-22/. The night this follows: DRILL-update-night-2026-09-21.md.


Not done, or changed from the brief

Read this first. Everything else in this document is a claim; this section is where the claims are bounded.

  1. The brief said "the fourteen proven versions". FIFTEEN moved. The night's fourteen included nextcloud's MariaDB engine move; tandoor was re-walked today and became the fifteenth. Both are named below with why.

  2. I nearly dropped nextcloud's engine move on a wrong assumption, and the gate corrected me. I assumed check-engine-major.py would refuse mariadb:11.6 -> 12.3 and had written it up as "dropped, named". Running the gate against that exact commit instead of assuming it ALLOWED the move by name, citing R-469 (Slice 4 shipped; MARIADB_AUTO_UPGRADE=1 is already in that template, verified by reading it). The brief forbade PostgreSQL engine moves; this is MariaDB and it was proven end to end in 217.4 s. It moved. This is the second time in two days that running the instrument beat reasoning about it — see R-628.

  3. The first push of the fifteen moves FAILED CI, and I found it by checking rather than by being told. Job 877 on 15d7c2b read conclusion: failure: the CI runner has no PyYAML and the new gate answered INCONCLUSIVE, which the runner rightly refuses to call a pass. Fixed with a degraded line-reader mode and five more decoy cases; job 878 on 1ad1f34 is success. The moves themselves were never in doubt — the gate that shipped beside them was.

  4. bookstack's phase trace on demo-hp is INCOMPLETE and the reason is my own instrument. A 115-second probe run, meant only to list which apps were installed, pressed the Update as designed and then hit its timeout mid-update. Its phases are recorded to +21.6 s starting; the rest was read off the box afterwards (done, pin and installed both at 26.05.5). The second, full run then pressed Update on an already-current app and completed in 3.1 s, moving nothing. That second trace is real and is reported, but it is not a 26.05.2 -> 26.05.5 trace. Said here rather than presented as one.

  5. The brief asked me to "list templates the night's sweep could not judge". The honest answer needed three categories, not one — 20 with a verdict record, 4 deployed as props with no edge walked, 1 deployed only to measure its probe, and 28 never deployed at all. Filed as R-632 with the machine-readable list.

  6. Two things the brief could not have known, both found by the new gate and both left OPEN: paperless-ngx's health probe has never run on any box (R-630), and five templates cannot be judged by any static rule (R-631). Neither is fixed here: R-630's fix is product code or a container rename on live boxes, and the brief forbade product code.

One brief claim that turned out wrong: none. All three probe faults the brief named were verified against the files before any edit and all three were exactly as stated — tandoor's compose line 42 dials 127.0.0.1:80/accounts/login/, zipline's line 38 dials /api/healthcheck, wger's line 42 dials 127.0.0.1:8000.


Part 1 — the probes

1.1 The fix

One commit, app-catalog-felhom.eu@793c4fb. No image: line moved, so no catalog_since moved.

app was is the oracle that was already in the file
tandoor port 8080 port 80 wget --spider -q http://127.0.0.1:80/accounts/login/
zipline path /api/health path /api/healthcheck http.get('http://127.0.0.1:3000/api/healthcheck', …)
wger port 80 port 8000 wget --spider -q http://127.0.0.1:8000

1.2 Red-proofed live on 9202, through the product, in both directions

Deployed at the live (unfixed) pin first. Evidence probe-fix-2026-09-22/probe-before-observe.json, front-door-before.json, probe-after.json.

tandoor zipline wger
BEFORE — controller state unhealthy unhealthy unhealthy
BEFORE — the app page, HU Nem egészséges Nem egészséges Nem egészséges
BEFORE — the app page, EN Not healthy Not healthy Not healthy
BEFORE — docker's own healthcheck healthy, healthy healthy, healthy healthy
BEFORE — the front door, followed 200 Login / Sign In 200 Zipline 200 wger Workout Manager
AFTER — controller state running running running
AFTER — the app page, HU / EN Fut / Running Fut / Running Fut / Running

The fix arrived through the real sync: POST /api/sync answered „Sablonok frissítve — frissítve: tandoor, wger, zipline", and all three read running at the next poll — no redeploy, no container restart.

The badge I quote is the HEALTH word, not the version badge. The version badge reads Naprakész / Up to date in both states and would have proved nothing; a first pass captured it and it was corrected. The scan strips <script> and <style> first (the same page carries those words inside a script block).

1.2b tandoor's edge, re-walked

Via the drill catalog (09 §6.5 — git.repo_url and rm -rf <data>/catalog-cache, R-615). Evidence probe-fix-2026-09-22/tandoor-rerun/.

2026-09-21, before the fix 2026-09-22, after
entered verifying +58.4 s +5.2 s
outcome failed at +361.9 s, app STOPPED done at +41.1 s
seed read back after UNUSABLE — No such container: tandoor found = True
installed_images rolled back to 2.6.13 2.6.15
containers none both running, restarts=0

Same app, same versions, same button. The only change is one port number. tandoor's verdict moves failed -> proven and it is on the live catalog.

Teardown, stated: controller.yaml restored, git.repo_url read back as the live catalog, <data>/data/catalog-cache re-cloned from the live remote and quoted (probe-fix-2026-09-22/teardown-catalog-pointer.txt), drill repo reset to the live main.

1.3 The gate

app-catalog-felhom.eu/scripts/check-probe-matches-compose.py, registered in catalog_gates.py as a --fast row, so it runs in the pre-push hook and in CI.

The oracle was already in every template. The probed service's own compose healthcheck.test dials the app on loopback. The gate resolves the probed service exactly as findProbeContainer does (healthprobe.go:297) and compares.

Two verdicts, and the narrower one is a measurement. A wrong PORT refuses for every check type — nothing listens, every dial is refused. A wrong PATH refuses only where the probe can fail on it: probeHTTP calls any response healthy for type: http, and for type: api with no expect block (healthprobe.go:253-262). So the path rule convicts zipline and merely warns about home-assistant, whose badge is right today and breaks the day someone adds an expect.

Red-proofs, four: on the unfixed tree (d4392e2a10f4 — the hash the brief names, and the repo's HEAD when I started) it refuses all three; on the fixed tree it passes; a clean app given a wrong port is refused; an api+expect probe given a wrong path is refused. Each of the three real faults is re-introduced by the test rather than read off the tree, because reading them off the tree would have gone green for the wrong reason the moment R-618 was fixed.

Decoys, both ways, ten in total (suite now 56 cases): the port moved in a YAML comment; Traefik's loadbalancer.server.port; a published ports: mapping; a NON-probed sidecar's own healthcheck; a path mismatch on a probe that cannot fail on it; and five more with PyYAML shadowed out — the mode CI actually runs — where the three faults must still be refused.

--root=<dir> exists so the suite judges its own clone. Without it every case would read identical bytes and pass for nothing: the constant-for-measurement shape this repo's suite already warns about.

Run over all 53 — six WARNINGS, none a conviction and none a pass:

app why no verdict
paperless-ngx no container name matches the stack name — the probe has never run (R-630)
vikunja the probed service has no compose healthcheck — no oracle
crafty-controller, mealie, uptime-kuma the healthcheck runs through a python/script helper — no loopback URL to read
home-assistant path mismatch on a probe that cannot fail on it — right today, fragile (R-631)

1.4 What the night's sweep could not judge

28 of 53 templates have never been deployed by any drill (R-632). Full list in the register and in probe-fix-2026-09-22/not-judged.json. Also: 4 were deployed as props in the bad-days legs with no edge walked (bentopdf, glance, uptime-kuma, wishlist), and 1 only to measure its probe (wger).


Part 2 — the promotion train

The moves

Fifteen commits, one per app, catalog_since: "2026-09-22" on each. Every gate run before each commit; nothing forced, nothing dropped. check-image-resolvable.py confirms all 18 unique pins still exist upstream.

app move commit
actualbudget 26.7.0 -> 26.9.0 2060032
audiobookshelf 2.35.1 -> 2.36.1 6525b8e
bookstack 26.05.2 -> 26.05.5 ac4828f
docmost 0.95.0 -> 0.96.0 6d8cd87
grafana 13.1.0 -> 13.2.2 068f445
home-assistant 2026.7.2 -> 2026.9.3 4405f12
mealie v3.20.1 -> v3.27.0 008348b
n8n 2.31.3 -> 2.40.5 4132e35
navidrome 0.63.2 -> 0.64.0 7708a04
papra 26.6.1 -> 26.6.2 e96887e
privatebin 2.0.5 -> 2.0.6 f547f16
romm 5.0.0 -> 5.3.0 15f9ebf
tandoor 2.6.13 -> 2.6.15 c3807c7
vikunja 2.3.0 -> 2.6.0 22f598b
nextcloud ENGINE mariadb:11.6 -> mariadb:12.3 39374d5

The guarded Update, pressed on both demo boxes

demo-hp — https://192.168.0.138

app badge before (HU / EN) phases pin after installed after front-door state
bookstack Naprakész / Up to date safety-dump +0.0s → pulling +1.1s → starting +2.1s → done +3.1s lscr.io/linuxserver/bookstack:26.05.5 lscr.io/linuxserver/bookstack:26.05.5 running
docmost Frissítés elérhető — ma / Update available — today safety-dump +0.0s → pulling +1.0s → starting +79.0s → verifying +91.3s → done +106.6s docmost/docmost:0.96.0 docmost/docmost:0.96.0 running
privatebin Frissítés elérhető — ma / Update available — today safety-dump +0.0s → pulling +1.1s → starting +4.1s → verifying +5.2s → done +15.4s privatebin/pdo:2.0.6 privatebin/pdo:2.0.6 running
romm Frissítés elérhető — ma / Update available — today safety-dump +0.0s → pulling +1.1s → starting +13.3s → verifying +40.0s → done +74.8s rommapp/romm:5.3.0 rommapp/romm:5.3.0 running

Page after the update, as the household reads it:

  • bookstack — HU: ← Alkalmazások BookStack Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Egyszerű wiki platform - polcok, könyvek, fejezetek és oldalak ~150M RAM productivity Pi k EN: ← Apps BookStack Running Up to date Open ↗ Log Export Settings A simple wiki platform - shelves, books, chapters and pages ~150M RAM productivity Runs on Pi Move to anoth
  • docmost — HU: *← Alkalmazások Docmost Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Modern wiki és dokumentáció platform Notion-szerű szerkesztővel ~200M RAM productivity Csak * EN: ← Apps Docmost Running Up to date Open ↗ Log Export Settings A modern wiki and documentation platform with a Notion-like editor ~200M RAM productivity x86 only Move to an
  • privatebin — HU: ← Alkalmazások PrivateBin Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Titkosított szöveg megosztás - a szerver nem látja a tartalmat ~30M RAM security Pi kompa EN: ← Apps PrivateBin Running Up to date Open ↗ Log Export Settings Encrypted text sharing - the server never sees the content ~30M RAM security Runs on Pi Move to another st
  • romm — HU: *← Alkalmazások RomM Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Retró játékgyűjtemény kezelő, böngésző és lejátszó ~300M RAM media HDD szükséges Csak x86 Hova * EN: *← Apps RomM Running Up to date Open ↗ Log Export Settings Retro game collection manager, browser and player ~300M RAM media Needs a hard drive x86 only Where do I put my *

demo-felhom — https://192.168.0.149

Not installed on this box, so not pressed: bookstack, docmost, privatebin, romm.

app badge before (HU / EN) phases pin after installed after front-door state

Page after the update, as the household reads it:

The second box, honestly: the four moved apps were pressed on one demo box, not two, because they exist on only one. Both boxes are on catalog 1ad1f34.


What this leaves open

row what
R-630 (new, P2) paperless-ngx's probe has never run on any box. Not fixed: both candidate fixes are product code or rename a running container on live boxes. Not measured, and said so: what verifying does for a stack with no probe target.
R-631 (new, P3) five templates no static rule can judge; home-assistant is right only because its check type cannot fail.
R-632 (new, P3) 28 of 53 templates have never been deployed by any drill. This is the rotation's queue.
R-618 CLOSED — three probes fixed, red-proofed live both ways, gate shipped with ten decoys, tandoor re-walked failed -> proven.
R-462 widened: the night's 14 proven / 3 failed / 4 inconclusive becomes 15 proven / 2 failed / 4 inconclusive, and all fifteen are now on the live catalog.

unproven.py --summary did not move: 55 claims, 20 walked, 35 not walked — the same as 2026-08-22. Nothing here changed a claim's status; it changed the evidence behind one row of the capability map.

Fences

Peti's box is parked and was not touched. It receives a badge if it ever syncs and nothing else. Nothing ran on DooPlex beyond ordinary pushes, nothing on ep0. felhom-controller, felhom-agent and the hub were read only — no product code was written. No golden, no bake, no vouch, no --no-verify, no branch. local-lvm untouched, no prune, tester-1 never reset.

Repository state

repo head CI
app-catalog-felhom.eu 1ad1f34 job 878 success (job 877 on 15d7c2b was failure — see "Not done" §3)
felhom.eu this commit all 16 gates green locally