Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
4.1 KiB
REPORT — agent v0.81.0: NAS verify-before-commit (retry=0 + classifier + detached verify job) — DEPLOYED
Date: 2026-07-11 · Class: implementation (agent half of the cross-repo "NAS
verify-before-commit + page redesign" task). Baseline v0.80.0 (300f06722b) → v0.81.0
(added9d), live on felhom-pve. Pairs with controller v0.113.0 + host-install v1.13.0.
Full task-wide record (red-proof outcomes, live A–E transcripts, teardown):
felhom-controller/REPORT.md. Evidence base: SPIKE-nas-verify-2026-07-11 (b57f6c1).
What shipped (agent)
retry=0in the production NFS option string (netmount.go mountOptions, Q4-vi): a dead-NAS on-demand access fails clean in ~3.8 s (ENODEV) instead of a 91 s wedge; verify failures classify asNo route to hostinstead of a diagnostic-free systemd timeout. SMB unchanged (mount.cifs rejectsretry=). Installed units are NOT rewritten (none existed on the demo anyway).internal/storage/netverify.go—ClassifyNetVerifyFailure(journalTail, tcpReachable): pure, first-match table on the Q4 VERBATIM substrings →unreachable | nfs_export | smb_auth | smb_share | timeout | mount_failed;nfs_exportdeliberately merges not-found/not-permitted (NFSv4 identical strings). String-based BY DESIGN — every mount failure exits rc=32.- Detached verify job (
localapi/netverifyjob.go, formatjob shape but IN-MEMORY single slot — a restart empties the slot;GET /netstorage/verify-statusphasenoneis the CONTROLLER's rollback signal, Scenario F): add = decode → role-gate → sync fast-fail (full spec validation- 2 s TCP pre-probe; unreachable = HTTP 502
{code:unreachable}, NOTHING installed) → SMB creds (0600) → EnsureNetworkMount → detached verify: trigger read through the automount, success judged from /proc/mounts ONLY (§8: EACCES on a 0700 export is a GOOD mount), failure = journal classification + auto-rollback (RemoveNetworkMount + creds). Single-flight: second add = 409.
- 2 s TCP pre-probe; unreachable = HTTP 502
- Unprivileged journal read (
journalctl -u <unit> -n 20 -o cat, NO sudo, NO new sudoers grant): needs thesystemd-journalgroup (host-install v1.13.0; existing hosts:usermod -aG systemd-journal felhom-agent). Unavailable ⇒ degrade tomount_failed+ hint, still rolled back. - New exports:
storage.NetworkMountedAt,storage.NetworkEndpointReachable. REUSE.md updated (helpers + the in-memory-vs-persisted job pattern row + the four Server net-verify seams).
Tests + red-proofs (agent side; outcomes verbatim in felhom-controller/REPORT.md)
A1 retry=0 (revert → fail) · A2 classifier table incl. merged nfs_export + empty-journal
degradation (exit-code mutant → every non-generic row fails) · A3 rollback effects via fakes
(drop rollback → removed=[] + creds linger) · A4 §8 truth table (readability mutant → both rows
fail) · A5 pre-probe zero-install (skip probe → 200-with-install) · A6 single-flight + no-job shape
(drop check → 409 lost). Green gate go build && go vet && go test ./... PASS (known
TestGenerateRecoveryCode wordlist flake hit once, clean on re-run).
Deployed + verified (felhom-pve)
.bak-0.80.0 kept → install -m0755 → usermod -aG systemd-journal felhom-agent →
systemctl restart felhom-agent. Verified: felhom-agent 0.81.0; id felhom-agent →
…,999(systemd-journal); clean start journal (enrolled drive re-bound, local-api listening,
desired-state gen 10). Live proof the journal path works: Scenario A's failure detail carried the
REAL mount.nfs4 … reason given by server: No such file or directory lines read unprivileged.
NOT published: 0.81.0 is deployed on the demo only — no Gitea publish, Day-0 manifest untouched (the publish train is the operator's separate decision); Peti's box untouched.
Observations
- Pre-existing (2026-07-08, unrelated):
lanresolver: cannot list provisioned guests: permission denied—/var/lib/felhom-agent/guestsis root-0700 under the non-root agent. RemoveNetworkMountleaves the empty mountpoint dir + anot-found failedresidual unit listing (untilreset-failed/reboot) — cosmetic, documented in the task report.