8e26cdc3a5
Pilot-blockers bundle shipped + live-validated: agent v0.73.0 (F2 mount-role fallback), controller v0.102.0 (async restore family, F4 re-adjudicated as a UX bug not a restore bug), catalog healthcheck sweep. All three accepted live on the demo node. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
298 lines
20 KiB
Markdown
298 lines
20 KiB
Markdown
# CAMPAIGN — "No-mercy" unattended full-system test (2026-07-06)
|
||
|
||
> Unattended pilot-readiness stress + regression campaign on the demo node
|
||
> (felhom-pve / guest 9201), run overnight while the operator slept. **No production code
|
||
> changed.** Throwaway harness + evidence bundle live on DooPlex
|
||
> (`~/campaign-nomercy-2026-07-05/`), never committed. This document is the one sanctioned
|
||
> artifact.
|
||
>
|
||
> **Headline:** the platform's resilience and safety came out **strong** — every fault-injection
|
||
> and crash-recovery scenario passed, the destroy-and-restore DR path is proven sound, the soak ran
|
||
> 2.5 h with zero invariant breaks, and every safety gate held. The raw FAIL tally (25) is
|
||
> **inflated and misleading**: ~10 FAILs are collateral of a *single* real app finding
|
||
> (vaultwarden crash-loop), ~8 are the product **correctly refusing** an operation that the harness
|
||
> then mis-scored, and the rest are harness-calibration bugs (incl. the one that made the P9 finale
|
||
> read FAIL even though the restore itself works). The genuine product findings are a short list,
|
||
> all MEDIUM-or-below, plus one HIGH-interest app-config bug.
|
||
|
||
## Run header
|
||
|
||
| Item | Value |
|
||
|---|---|
|
||
| Date run | 2026-07-06, ~07:40–12:13 CEST (P0 supervised 07:40; P1–P10 detached 08:09–12:13) |
|
||
| Host | felhom-pve (192.168.0.162), node demo-felhom |
|
||
| Guest | 9201 (demo-felhom), LAN 192.168.0.127 |
|
||
| Controller / Agent / Hub | **0.100.0 / 0.72.0 / 0.35.0** (== baselines; git heads af7ea0b / 26664d6 / 61f4898) |
|
||
| Auth mode | DISABLED by deployed config (no dashboard password → CSRF exempt); T-AUTH → XFAIL |
|
||
| Runner | on DooPlex (survives the host reboots/hard-reset); detached `setsid nohup`; journal-resumable; each test in an isolated subshell |
|
||
| RNG seed (soak) | 20260706 |
|
||
| Restore points | R0 `ct/9201/2026-07-06T05:40:07Z` (verify ok), R0.5 (P2), **R1 `ct/9201/2026-07-06T06:47:23Z` (verify ok)** |
|
||
|
||
## End state (operator's first-coffee summary)
|
||
|
||
- **Guest 9201 was destroyed and restored** during the finale. The automated restore hit a harness
|
||
bug (below); **CC recovered it manually from R1** — 9201 is **running**, controller **healthy**,
|
||
all three USB drives **re-bound**, and the full app stack is back. State is as of **R1**
|
||
(2026-07-06 06:47 UTC); anything written to guest volumes during P7/P8 is expected-lost (by DR
|
||
design — restore returns to snapshot state).
|
||
- **Only unhealthy container: `vaultwarden`** (crash-looping — see Finding F1). Everything else is
|
||
healthy: nextcloud, navidrome, gitea, filebrowser, cloudflared, paperless, mealie, calcom,
|
||
rallly, sparkyfitness, uptime-kuma, actualbudget, felhom-controller.
|
||
- **Notifications restored** to the original 11 events (email nagyfenyvesi.viktor@gmail.com,
|
||
cooldown 6 h) and verified. They were muted for the run and P10 could not un-mute (controller was
|
||
down mid-finale); CC restored them manually.
|
||
- **No chaos residue:** 0 leftover auto-revert timers, no disk fillers, no stray iptables DROP
|
||
rules, USB drives re-authorized (device letters re-enumerated sdb↔sdd after the unplug tests —
|
||
cosmetic; drives mount by stable name).
|
||
- **Password status:** unchanged (auth remained disabled per the operator's kickoff choice).
|
||
|
||
## Result tally (raw, then triaged)
|
||
|
||
| Status | Raw count |
|
||
|---|---|
|
||
| PASS | 24 |
|
||
| FAIL | 25 |
|
||
| XFAIL | 2 |
|
||
| SKIP | 4 |
|
||
|
||
**After triage the 25 FAILs decompose as:** ~10 *collateral* of Finding F1 (a single crash-looping
|
||
app failing health-embedded assertions), ~8 *harness-calibration* mis-scores (the product behaved
|
||
correctly), 3–4 *genuine findings*, and the P9/P10 pair caused by one harness bug (DR restore is
|
||
sound). Details below; the matrix marks each FAIL's class.
|
||
|
||
---
|
||
|
||
## Genuine findings (product — evidence packages, hypotheses labelled)
|
||
|
||
### F1 — `vaultwarden` crash-loops: app-email shim sets `SMTP_FROM` without `SMTP_HOST` — **HIGH interest**
|
||
- **Symptom:** vaultwarden container in `Restarting` throughout. Container log (verbatim):
|
||
*"Error loading config: Both SMTP_HOST and SMTP_FROM need to be set for email support without USE_SENDMAIL"*.
|
||
- **Repro:** deploy vaultwarden with `app_email.enabled=true` in settings (the demo's state). The
|
||
mailrelay/app-email integration injects `SMTP_FROM=…@felhom.eu` into the compose env but not
|
||
`SMTP_HOST` → vaultwarden refuses to start and crash-loops.
|
||
- **Blast radius (why the FAIL count looks bad):** vaultwarden is the highest-stakes app and is
|
||
embedded in many assertions, so its crash *collateral-failed*: T-DEPLOY-SET, T-CANARY-PLANT,
|
||
T-LIFECYCLE, T-REMOVE-REDEPLOY, T-BAK-FULL, T-RST-VW/TIER2 (skipped — no vw canary), T-FLT-DOCKERD,
|
||
and all four T-RBT-* reboot tests (their `assert_stack` waits on vaultwarden reaching health).
|
||
- **Hypothesis (labelled):** the app-email SMTP shim (`internal/mailrelay`, catalog vaultwarden
|
||
template) should inject **both** `SMTP_HOST` (the relay endpoint) and `SMTP_FROM`, or vaultwarden's
|
||
template should gate SMTP on both being present. Compare with other SMTP apps — likely affects any
|
||
app whose image hard-requires `SMTP_HOST` when `SMTP_FROM` is set. Cross-check against
|
||
`SPIKE-smtp-app-relay-2026-06-28.md`.
|
||
- **Not fixed** (report-only). This is the single most valuable finding of the run.
|
||
|
||
### F2 — `decommission (anyway)` on a user-data drive: agent returns HTTP 403 — **MEDIUM, verify**
|
||
- **Symptom:** `POST /api/storage/decommission {mode:"anyway"}` → controller 502 wrapping
|
||
`agentapi: POST /disks/decommission: HTTP 403`.
|
||
- **Question to resolve:** is the agent's 403 **correct gating** (decommission is operator-signed /
|
||
role-protected even for user-data) or a **real gap** (the customer-facing "decommission anyway"
|
||
flow can't complete)? The storage-authz redesign makes system/backup operator-only, but this was a
|
||
**user-data** drive, so a 403 here may be an over-broad gate. Evidence:
|
||
`evidence/T-STG-DECOM-ANYWAY/decom_anyway.txt`.
|
||
- **Note:** `T-STG-DECOM-MIGRATE` (409) is *not* this — it was a correct backup-in-progress refusal
|
||
(see harness note H3).
|
||
|
||
### F3 — Gitea-outage: catalog sync does not recover cleanly after connectivity is restored — **MEDIUM, verify**
|
||
- **Symptom:** with the Gitea IP blocked from the guest, `POST /api/sync` failed (expected/graceful,
|
||
controller stayed up — good); **after** unblocking, the follow-up sync did not return 2xx within
|
||
the test window. Evidence: `evidence/T-FLT-GITEA-OUT/sync_after.txt`.
|
||
- **Hypothesis (labelled):** either a sync backoff/cooldown longer than the test's post-unblock
|
||
wait, or a cached failure state not cleared on recovery. Contrast with **T-FLT-HUB-OUT which
|
||
PASSED** (hub report resumed after unblock). Worth confirming whether catalog sync self-heals on a
|
||
longer horizon.
|
||
|
||
### F4 — DB restore returned HTTP 000 — **LOW, likely transient/collateral**
|
||
- `T-RST-DB` (paperless DB restore) recorded HTTP 000 (connection failed) at that instant — most
|
||
likely a transient controller unavailability during the restore suite rather than a defect.
|
||
Re-run in the targeted P1–P3 re-run (below) to confirm.
|
||
|
||
---
|
||
|
||
## Strong positive results (real signal — do not lose these under the FAIL count)
|
||
|
||
### Resilience / fault recovery — **6/6 PASS**, with measured recovery times
|
||
| Scenario | Result | Time-to-recover |
|
||
|---|---|---|
|
||
| Controller `kill -9` (real SIGKILL of the process, mid-backup) | PASS — restart policy recovered, API 200 | ~ (container-up) |
|
||
| Guest hard `pct stop` mid-backup (power-loss) | PASS — journal resume, canaries intact | 44 s |
|
||
| Agent `kill -9` on host | PASS — systemd restart, clean binds | 6 s |
|
||
| App crash-loop (DB dependency knocked over) | PASS — detected + alert + recovered on repair | 45 s |
|
||
| Cloudflared down → external down → recover | PASS — external tunnel returned | (self-heal) |
|
||
| Hub outage (iptables block, auto-revert) | PASS — controller kept serving, report resumed | — |
|
||
| dockerd restart reconverge | 320 s | — |
|
||
|
||
### Host reboot storms — **recovery WORKED** (the T-RBT FAILs are collateral of F1)
|
||
The `T-RBT-*` tests are marked FAIL only because `assert_stack` waits on vaultwarden (F1) reaching
|
||
health. The **host + controller + apps actually recovered every time**, proven by the recorded
|
||
times: **clean reboot 126 s, reboot mid-backup 69 s, reboot mid-migration 58 s, and a true
|
||
`sysrq-b` HARD power-loss 103 s.** Filesystems came back clean; the demo node survived a hard reset
|
||
and self-restored in under two minutes.
|
||
|
||
### Safety gates — **all held**
|
||
- `T-STG-SEC-FORMAT` PASS: type-to-confirm enforced, non-candidate refused, and the **`/dev/sda`
|
||
root-disk fence correctly refused** (the `FENCE`/FAIL journal line is that self-test *tripping the
|
||
fence on purpose* — it is proof the fence works, not a failure).
|
||
- `T-SEC-PROTECTED` PASS: traefik/cloudflared/felhom-controller/filebrowser refuse stop/remove and
|
||
stay Up.
|
||
- `T-BAK-SINGLEFLIGHT` PASS: second backup correctly 409'd.
|
||
- `T-RST-NEG` PASS: bad restore ids/stacks refused cleanly with **zero writes** under target paths.
|
||
- Backups never touched any namespace/group but `ct/9201` (`T-BAK-PBS` PASS).
|
||
|
||
### Stability & DR
|
||
- **Soak: 76 ops over ~2.5 h, 0 invariant breaks** (seed 20260706) — controller stayed < 5 s
|
||
responsive throughout, no unexpected 5xx, no state-model divergence, no RSS leak signal.
|
||
- **CPU soak: 20 min stress-ng, worst page latency 172 ms** (< 5 s target).
|
||
- **DR destroy-and-restore is proven sound.** The PBS whole-guest restore mechanism, transparent
|
||
decryption, 3-volume layout, drive re-bind, and full app recovery all work — demonstrated by the
|
||
successful manual recovery from R1. The finale's automated FAIL was purely the harness bug H1.
|
||
|
||
---
|
||
|
||
## Harness bugs found (fix before any re-run — some already fixed in the workspace copy)
|
||
|
||
| # | Bug | Effect | Status |
|
||
|---|---|---|---|
|
||
| H1 | P9 built the `pct restore` volid from the **epoch** backup-time, not the ISO-8601 form → `unable to parse PBS volume name`. Destroy succeeded, restore couldn't parse, node left down. | Caused T-P9-DESTROY-RESTORE FAIL and, downstream, T-P10-NOTIF-RESTORE FAIL (controller down during P10). **DR mechanism itself is fine.** | **FIXED** (epoch→ISO in the snapshot pointer) |
|
||
| H2 | `assert_stack` / dockerd / reboot assertions hard-depend on a single app (vaultwarden) reaching health. | Turned F1 into ~10 collateral FAILs. | Fix: assert controller+agent+binds+tunnel; treat per-app health as informational. |
|
||
| H3 | Storage lifecycle tests don't serialize around backups. | MIGRATE-ALL/APP + DECOM-MIGRATE got **correct** 409 "backup in progress" but were scored FAIL. | Fix: wait for `/api/backup/status` idle before storage ops. |
|
||
| H4 | Enrollment test didn't handle the already-enrolled/mounted precondition. | T-STG-ENROLL scored FAIL on a **correct** 409 needs-confirmation. | Fix: pre-clean or expect the confirmation branch. |
|
||
| H5 | Deploy-values builder sent `DOMAIN=<random secret>` (DOMAIN is auto-filled by the controller). | Latent — did **not** cause the vw crash (F1 did), but would misconfigure DOMAIN. | Fix: never send DOMAIN. |
|
||
| H6 | `T-PAGE-ALL` treated `/debug` as must-be-200. | FAIL on `/debug`→404, which is **by-design** when debug mode is off (12/12 real pages 200). | Reclassify XFAIL. |
|
||
| H7 | `T-RES-MEMGUARD` expected the 384 MB mem-request guard to fire, but the guest had ample free RAM (avail 10.2 GB) so it correctly didn't. | FAIL on absence of a refusal that wasn't warranted. | Fix: deploy enough to actually cross the reserve, or assert the accounting only. |
|
||
| H8 | Journal notes with embedded newlines split a TSV row (one stray blank line). | Cosmetic. | Fix: strip newlines in `journal_set` notes. |
|
||
|
||
**Recommended follow-up:** with H1–H5 fixed and F1 worked around (set `SMTP_HOST` or disable
|
||
app-email for the restore centrepiece), **re-run P1–P3 targeted** to get clean, real backup/restore
|
||
(F3-volume-tar / C2 Tier-2) coverage on a healthy app set — this is the one piece of pilot-critical
|
||
coverage the vaultwarden crash denied us tonight.
|
||
|
||
---
|
||
|
||
## Test matrix (id | class | result | note)
|
||
|
||
| Test | Verdict | Note |
|
||
|---|---|---|
|
||
| T-P0-INVENTORY / VERSIONS / R0 / NOTIF / SMOKE | **PASS** | preflight clean; R0 verified ok |
|
||
| T-PAGE-ALL | FAIL → **harness (H6)** | only `/debug`→404 (by-design); 12/12 real pages 200 |
|
||
| T-DEPLOY-SET | FAIL → **collateral F1** | 6/7 apps healthy; vaultwarden crash-loop |
|
||
| T-CANARY-PLANT / T-LIFECYCLE / T-REMOVE-REDEPLOY | FAIL → **collateral F1** | vaultwarden-dependent |
|
||
| T-SYNC-IDEMPOTENT | **PASS** | zero writes on re-sync |
|
||
| T-SEC-PROTECTED | **PASS** | protected stacks refuse + stay Up |
|
||
| T-AUTH-1 / T-AUTH-2 | **XFAIL** | auth disabled by deployed config (known) |
|
||
| T-BAK-API / T-BAK-SINGLEFLIGHT / T-BAK-PBS | **PASS** | API truthful; single-flight 409; group-scoped |
|
||
| T-BAK-FULL | FAIL → **collateral F1** | vaultwarden volume tar absent (app never ran) |
|
||
| T-BAK-OFFBOX | SKIP | needs provisioned SFTP key (unattended) |
|
||
| T-RST-NEG | **PASS** | bad restores refused, zero writes |
|
||
| T-RST-DB | FAIL → **F4 (LOW)** | HTTP 000 (likely transient) |
|
||
| T-RST-VW / T-RST-TIER2 | SKIP → **collateral F1** | no vaultwarden canary (app crashed) |
|
||
| T-STG-SEC-FORMAT | **PASS** | type-to-confirm + `/dev/sda` fence + mkfs-never-ran |
|
||
| T-STG-RECOMMISSION | **PASS** | drive recommissioned, apps recoverable |
|
||
| T-STG-ENROLL | FAIL → **harness (H4)** | correct 409 needs-confirmation (device mounted) |
|
||
| T-STG-MIGRATE-ALL / -APP / -NEG, T-STG-DECOM-MIGRATE | FAIL → **harness (H3)** | correct 409 "backup in progress" |
|
||
| T-STG-DECOM-ANYWAY | FAIL → **F2 (MED)** | agent `POST /disks/decommission` 403 — verify |
|
||
| T-STG-UNPLUG / T-STG-FULL | FAIL → **mixed** | partial; re-run after H3 |
|
||
| T-STG-NET | SKIP | needs provisioned NFS/SMB export |
|
||
| T-FLT-KILL9 / POWERLOSS-GUEST / AGENT / CRASHLOOP / CF / HUB-OUT | **PASS** | full fault recovery (times above) |
|
||
| T-FLT-DOCKERD | FAIL → **collateral F1** | reconverge saw vw=restarting |
|
||
| T-FLT-GITEA-OUT | FAIL → **F3 (MED)** | sync didn't recover in-window post-unblock |
|
||
| T-RBT-CLEAN / MIDBACKUP / MIDMIGRATION / HARD | FAIL → **collateral F1 / H2** | host recovered 58–126 s (hard reset 103 s) |
|
||
| T-RES-CPU | **PASS** | 20 min soak, latency 172 ms |
|
||
| T-RES-MEMGUARD | FAIL → **harness (H7)** | ample RAM; guard correctly didn't fire |
|
||
| T-SOAK-LOOP | **PASS** | 76 ops, 0 invariant breaks |
|
||
| T-P9-DESTROY-RESTORE | FAIL → **harness (H1)** | volid format; **DR proven via manual recovery** |
|
||
| T-P10-NOTIF-RESTORE | FAIL → **consequence of H1** | controller down during P10; **restored manually** |
|
||
| T-P10-CLEANUP / ENDSTATE / REPORT | **PASS** | no residue; end state captured |
|
||
|
||
## What this campaign does NOT prove
|
||
- **Offsite escrow restore with the operator-held recovery code R** — cannot be unattended.
|
||
- **True-CGNAT WG tunnel behaviour** — no CGNAT vantage from the LAN runner.
|
||
- **Clean backup/restore of the highest-stakes app** — denied tonight by F1; recover via the
|
||
targeted P1–P3 re-run.
|
||
- **Non-technical-user UX** — no human-in-the-loop signal.
|
||
- Any assertion reading a best-effort endpoint (alert/monitoring HTML scrape, hub-report timestamp)
|
||
is corroborating, not authoritative.
|
||
|
||
## Not changed
|
||
No production code, versions, or configuration were changed. The single change is this document.
|
||
The demo node was destroyed and restored (authorized) and left **running and healthy** at R1 state,
|
||
with notifications restored. Evidence bundle retained on DooPlex, never committed; no secrets in
|
||
this report.
|
||
|
||
---
|
||
|
||
# Findings triage addendum (2026-07-06)
|
||
|
||
Follow-up implementation task on F1–F3. F1/F3 fixed + shipped; F2 diagnosed to a verdict (agent
|
||
untouched — a fix is a separate follow-up). Shipped: catalog `d86e256`, controller **v0.101.0**
|
||
(`fc28033` sync deadline, `c997d79` agent-refusal surfacing).
|
||
|
||
## F1 — CORRECTED root cause (the original hypothesis above was wrong)
|
||
|
||
The report's F1 hypothesis — *"the shim sets `SMTP_FROM` without `SMTP_HOST`"* — is **incorrect**:
|
||
with app-email off (the default) the shim injects **nothing**. Validated against live source +
|
||
the pinned image. Real cause: the template **always** defines `SMTP_HOST=${SMTP_HOST:-}` and
|
||
`SMTP_FROM=${SMTP_FROM:-}`; vaultwarden treats a **defined-but-empty** env var as *set*, and with
|
||
its `_enable_smtp` defaulting **true** its validation (`smtp_host.is_some() == smtp_from.is_empty()`
|
||
→ `true == true`) errors out and the process exits. Every fresh email-off deploy shipped a dead
|
||
password manager.
|
||
|
||
**Empirical proof** on `vaultwarden/server:1.33.2-alpine` (evidence:
|
||
DooPlex `evidence/PROBE-F1/`): P1 `SMTP_HOST=`/`SMTP_FROM=` (defined-empty) → **exit 12**, exact
|
||
campaign error; P2 `+_ENABLE_SMTP=false` → **boots**; P3 `_ENABLE_SMTP=true`+host+from → **boots**.
|
||
|
||
**Fix** (catalog): gate the SMTP group with `_ENABLE_SMTP` — compose default `false` (validation
|
||
skipped, clean boot), flipped `"true"` by the app-email injection via `smtp_mapping.extra`.
|
||
**Live-validated via the real UI pipeline** (v0.101.0 on 9201): fresh email-off deploy → healthy,
|
||
`_ENABLE_SMTP=false`, restarts=0; app-email ON → healthy with `SMTP_HOST=felhom-controller` +
|
||
`SMTP_FROM=vaultwarden@felhom.eu`; OFF → healthy. The other 5 smtp-mapped templates are
|
||
campaign-boot-proven tolerant (no edits).
|
||
|
||
## F2 — VERDICT: **real finding** (agent role-resolution inconsistency; fail-safe direction)
|
||
|
||
Campaign-time agent journal (persistent journald survived the reboots), the authoritative evidence:
|
||
```
|
||
Jul 06 08:36:03 felhom-agent WARN "local-api: protected — decommission refused by role"
|
||
vmid=9201 where=/mnt/teszt_enroll role=system
|
||
```
|
||
`where` + `role` give the verdict directly (no reproduction needed — the drive was never mutated).
|
||
|
||
**Mechanism** (`felhom-agent internal/localapi/disks.go:1020` `roleForMountPath`): it loops the
|
||
agent's `storage.Observe()` **storage targets** for one whose `MountPath == where`, and **fail-safe
|
||
returns `RoleSystem` when none matches** (`:1031`). `/mnt/teszt_enroll` is a real user-data USB
|
||
drive but is **not a PVE storage** (`pvesm status` lists only felhom-usb / felhom-flash — both
|
||
`content backup` — plus the PBS stores; no `teszt_enroll`). So no target matches → `system` →
|
||
decommission refused.
|
||
|
||
**Why it's a real finding, not a harness artifact:** the agent's own `/disks` inventory
|
||
**already classifies teszt_enroll as `role: user-data`** (durable_id `uuid:f2236136…`), yet the
|
||
decommission/eject gate independently resolves it to `system`. Two role-resolution paths disagree:
|
||
the disk-inventory path knows it's user-data; the gate (keyed on PVE storage targets only) doesn't
|
||
see a bind-mounted user-data drive that isn't its own PVE `dir` storage. Any such drive hits this,
|
||
so a legitimate customer "decommission anyway" can be blocked.
|
||
|
||
**Severity: MEDIUM, fail-safe direction.** The default is *over-refusal* (refuse rather than wrongly
|
||
unmount) — it never permits a wrong decommission, only blocks a right one. Not a security hole.
|
||
|
||
**Suggested fix (deferred — agent follow-up task, NOT done here):** have `roleForMountPath` fall
|
||
back to the durable-id / DiskInfo role (or `RoleForRawDevice`) when no storage target matches the
|
||
mount, instead of blanket-defaulting to `system` — so an enrolled user-data drive is decommissionable
|
||
regardless of whether it is a standalone PVE storage. Pair with a red-proof on a bind-mount-only
|
||
user-data drive.
|
||
|
||
## F3 — FIXED (controller v0.101.0)
|
||
|
||
Real half confirmed: `internal/sync/sync.go runGitInDir` ran `cmd.Run()` with no context, so a hung
|
||
remote parked the sync goroutine, held `syncing=true`, and refused every later sync until a
|
||
controller restart. Fixed with a per-command `exec.CommandContext` deadline (`gitCmdTimeout = 120s`);
|
||
the deadline error names the timeout + masked args. Debounce unchanged. Tests + red-proof shipped;
|
||
live sync smoke `POST /api/sync` → 2xx post-deploy.
|
||
|
||
## Harness note (unchanged — for the record)
|
||
The P9 finale FAIL in the main report was a harness volid-format bug (epoch vs ISO-8601), fixed in
|
||
the DooPlex workspace copy; the DR restore mechanism itself is sound (9201 recovered from R1). The
|
||
other harness mis-scores (H2–H8) stand as written.
|
||
|
||
---
|
||
**F2 UPDATE (2026-07-06): CLOSED** — `felhom-agent` v0.73.0 mount-role fallback; live-proven eject +
|
||
decommission on `/mnt/teszt_enroll` (both were 403). See `RERUN-p1p3-2026-07-06.md` closure appendix.
|