From 1dad3c97fd031b3928c4e80445add06b9dc51005 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 21 Jul 2026 12:57:27 +0200 Subject: [PATCH] =?UTF-8?q?docs(report):=20STOP-1=20evidence=20=E2=80=94?= =?UTF-8?q?=20both=20legs=20passed;=20P1=20answered;=20new=20R-55=20findin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- REPORT.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/REPORT.md b/REPORT.md index 7decf48..44b75c3 100644 --- a/REPORT.md +++ b/REPORT.md @@ -128,13 +128,86 @@ promoted into `documentation/PROMPT-TEMPLATE.md` §10 (Part-5 rider). --- -## 6. Live validation +## 6. Live validation — STOP-1 RAN, both legs PASSED (operator-present) -**None claimed for R-51/R-52.** The three legs are destructive and operator-present. The available -method here is endpoint-level (no browser on DooPlex); these legs additionally need a container kill -and a guest reboot on the production demo box, so they are STOP-1, not a CC action. +Method: the exact endpoints the UI calls, driven from inside guest 9201 through traefik +(`--resolve felhom.demo-felhom.eu:443:127.0.0.1`, session cookie + CSRF token), plus container-level +truth from `docker ps -a` and the controller's own log. No browser (none on DooPlex). The session +cookie jar was deleted from the guest afterwards. -What IS verified without the box: the full green gate, all four design gates +**Deploy first, per the resolved ordering:** the operator saved the floor → `0.156.0`, the box +self-updated, and the R-23 datapoint came free — +`[selfupdate] Post-update startup: update successful (0.155.0 → 0.156.0)` then +`Current version 0.156.0 is up to date`: **one swap, no re-fire.** + +### Leg A — R-51, the dead primary (12:50:40 → 12:51:52 CEST) + +| Time | Event | +|---|---| +| 12:50:40 | `docker stop immich-server` → `exited | restart-policy=unless-stopped`; the three helpers stay `running` — the exact F4 shape | +| **12:50:53** | **stack state = `degraded`** (13 s). `immich-server -> exited | Exited (143)`, three helpers `running`. This read `running` for 18 hours on 2026-07-20 | +| 10:51:11Z | **exactly ONE** `Event pushed: app_start_failed (warn) — Telepített alkalmazás nem fut: Immich` — single-fire verified by `grep -c`, = 1 | +| — | Dashboard: banner *"Telepített alkalmazás nem fut: Immich (degraded)"* + link *Rendszermonitor*, and the new „Részlegesen leállt" label present in the rendered page | +| 12:51:37 | `docker start immich-server` | +| 12:51:52 | state back to `running`; **banner self-cleared** (state-based, as designed) | + +The `(degraded)` in the banner is the English-state wart predicted in §7 — visible now, pre-existing, +still a copy decision rather than a bug. + +### Leg B — R-52, the boot orphans (12:53:07 CEST reboot) + +Fixture: `docker stop` on **bookstack** (2 containers) and **calibre-web** (containers left in +place); **UI Stop** on **immich** via `POST /api/stacks/immich/stop` (`compose down` → 0 containers). +Then `pct reboot 9201`. + +``` +10:53:22Z [bootrecon] Boot reconciliation: 1 boot-orphaned app(s) found: [bookstack] — up to 2 attempt(s) +10:53:28Z [bootrecon] attempt 1/2: started "bookstack" (took 6.0s) +10:53:28Z [bootrecon] complete: 1 app(s) recovered in 1 attempt(s): [bookstack] +``` + +**Zero `app_start_failed`** since the reboot — a successful recovery inside the boot grace is silent, +which was the design claim. And the quiet path is observable on the healthy box too: the earlier +0.156.0 boot logged `Boot reconciliation: no boot-orphaned apps (nothing to start)`, so "nothing to +do" and "never ran" are distinguishable in production, not just in a test. + +### P1 — answered, and the F5 hypothesis CONFIRMED + +bookstack carries `restart=unless-stopped`. The Docker daemon came up at ~10:53:15Z. The container's +`StartedAt` is **`10:53:28.05Z` — the exact moment `bootrecon`'s `StartStack` returned.** Docker's +own restart policy did **not** resurrect it: a container stopped before shutdown is recorded +user-stopped and stays down across the reboot. Only R-52 brought it back. That is the F5 hypothesis +the audit could not test, settled with direct evidence and at zero extra cost. + +### The finding this leg produced — new R-55 (it is the opposite of what the leg set out to prove) + +Leg B was designed to show that a customer's deliberate Stop survives a reboot. **It does not — for +any drive-backed app.** immich, stopped from the UI seconds earlier, came back **running**: + +``` +10:53:18Z [gate] boot …: live bind confirmed — recreating drive-backed app immich (state=stopped) onto /mnt/felhom-drives/hdd_1 +10:53:18Z [stacks] Stopping stack: immich → Starting stack: immich +``` + +`internal/web/intermediary.go`'s boot bind gate selects on `cfg.Deployed && HDD_PATH` alone and runs +`StopStack` + `StartStack` on every match; the stack's `state` is passed in **for the log line only** +and never consulted. calibre-web went the same way; bookstack, not drive-backed, fell through to +R-52 — which is why the sweep found exactly one orphan. + +**R-52 is not implicated.** Its own gate behaved exactly as specified: immich, at zero containers, +was never a candidate. But two things follow and need an operator ruling rather than a drive-by fix +(the gate exists to fix a real bind-ordering bug and must keep doing that): + +1. A customer's Stop is silently undone on drive-backed apps at every reboot. +2. **R-52's practical scope on a real box is narrower than the brief assumed** — the gate reaches + every drive-backed app first, so the boot-orphan sweep only ever sees the remainder. + +Filed as **R-55**; likely shape is that the gate should recreate only apps that were RUNNING at +shutdown, i.e. the same running/stopped distinction R-52 already draws. + +## 6b. What was NOT claimed live + +**Nothing beyond the above.** What is verified without the box: the full green gate, all four design gates (`template_id_gate.py`, `emoji_gate.py`, `native_confirm_gate.py`, `offbox_rename_gate.py` — all OK), and the image build+push.