test(nomercy): Phase 4 complete — 4.4 mountpoint-gate, 4.6 concurrency; 4.7 deferred

4.4 drive yank: belt skips ensure (manager.go:143), no rootfs write, clean
reconnect recovery. 4.6 concurrency: eject-in-use warned via impact, concurrent
backup+enroll no corruption. 4.7 capstone deferred (operator-gated, needs golden
volid+passphrase; compensating coverage via 1.2/3.3/4.1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PsnU2ASocYrvzqE82YDYW
This commit is contained in:
2026-06-23 20:16:38 +02:00
parent cd6715d592
commit 2c1441f5dd
@@ -333,6 +333,41 @@ Pre-host-reboot capture: `sdb1`=`da9e7089`(usb), `sdc1`=`81a26531`(flash); `onbo
one tick. Not a gate failure (the gate decision is logged correctly throughout).
- Restored usb via re-register → intent→enrolled, bound at stable (now `sdc1` post-swap), both drives back.
### 4.4 — drive yank mid-flight → mountpoint-gate — ✅ PASS (exact-endpoint, no host reboot)
- **Method:** `systemctl stop felhom-agent` (freeze binds), lazy-unmounted the flash bind on the host
(yank), then `POST /api/stacks/navidrome/restart` (HDD_PATH=flash, now unmounted) to run the deploy belt.
- The belt logged **`userdata belt: drive /mnt/felhom-drives/felhom-flash not mounted — skipping ensure
(held by drive gate)`** (`internal/stacks/manager.go:143`) and wrote **nothing** — the yanked path
`/mnt/felhom-drives/felhom-flash` stayed **empty** (now resolves to `pve-root[/mnt/felhom-drives]`, i.e.
the parent/rootfs, but no app data or placeholder was created there). The controller refused to MkdirAll
onto the rootfs. Wrong outcome (app data written to rootfs / placeholder created) ruled out.
- **Recovery:** `systemctl start felhom-agent` → flash re-bound at stable (`sdb1[/felhom-data]`), navidrome
stayed healthy throughout, both intents enrolled, no leftover placeholder. Reconnect recovered cleanly.
### 4.6 — concurrency — ✅ PASS (exact-endpoint)
- **(b) eject-in-use is WARNED:** `impact?where=…felhom-flash` → `apps:["Navidrome"]` — the in-use app is
named before any eject (the type-to-confirm warning surface), so eject-in-use is warned, never silent.
- **(a) concurrent backup + deregister + re-register** (raced against each other): `remove`→302,
`backup`→ok, `register`→ok. Post-race the registry is **consistent** — exactly one flash + one usb entry
(no duplicates, no corruption); both drives bound (usb `sdc1`, flash `sdb1`); navidrome + radarr healthy.
Wrong outcomes (concurrent corruption / silent app breakage) ruled out.
### 4.7 — CAPSTONE (destroy + re-provision 9201) — ⏸️ DEFERRED (operator-gated; not run)
- Explicitly operator-gated and requires the **golden volid + retrieval passphrase**, which were **not
provided** for this autonomous run. Destroying the live demo guest is the campaign's most destructive
step; per the "confirm before irreversible ops" guardrail I did **not** run it without an operator
go-ahead. **Compensating coverage:** the exact original-bug scenario (stable registration with no
placeholder, drives live, sys_drive separate, no banner) is already proven clean via **1.2** (register →
stable, no placeholder), **3.3** (full-server restart recovery), and **4.1/4.2** (host-reboot + real
sdb↔sdc swap recovery). *To close 4.7:* a supervised run with the operator supplying the golden volid +
passphrase, then re-enroll via "Regisztrálás" and assert the clean end-state.
**Phase 4 verdict:** durability holds — host-reboot drive re-bind by UUID survives a **real /dev letter
swap** (4.2), the intent gate skips ejected drives (4.3), the mountpoint-gate blocks rootfs writes on a
drive yank (4.4), and concurrency doesn't corrupt (4.6). Two findings: **F3** (`onboot:0` → guest doesn't
auto-start after host reboot — the highest-impact finding) and **F4** (stable-but-present double-bind). The
capstone (4.7) is deferred pending operator credentials.
## Phase 5 — restore to clean baseline — ⏳ PENDING
---