v0.3.2: reversible SetConfig step in --selftest=task (slice-4 pre-check)

Append a reversible SetConfig write+revert to runSelftestTask: read
GuestConfig, write a `description` marker, verify it landed, restore the
original (or delete if absent), verify the restore. Handles PVE's dual-mode
SetConfig return (empty UPID = synchronous; UPID = WaitTask+assert OK).

Live self-gate PASSED on demo-felhom / guest 9999. Findings:
- LXC `description` write is synchronous (empty UPID) — dual-mode modeling
  confirmed; empty string is success, not an error.
- PVE appends a trailing newline to `description` on read; slice-4 reconcile
  must normalize description comparisons (hence normDesc helper).

First live exercise of the VM.Config.* privilege cluster. Standing operator
token rotated during the run; new secret stored out-of-band, not in the repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 21:13:04 +02:00
parent 237452c8c6
commit 605ce25f58
4 changed files with 233 additions and 22 deletions
+32
View File
@@ -3,6 +3,38 @@
All notable changes to **felhom-agent** are recorded here. Update on every code
change that gets pushed.
## v0.3.2 — SetConfig selftest extension (slice-4 pre-check) (2026-06-08)
The gate before slice 4: prove `SetConfig` works live under the scoped token before
reconcile is built on it. **Self-gated live run PASSED** on `demo-felhom`/guest 9999.
### Added
- **Reversible `SetConfig` step appended to `--selftest=task`** (`cmd/felhom-agent/main.go`,
`selftestSetConfig`): read `GuestConfig` → write a `description` marker
(`felhom-selftest <RFC3339>`) → verify it landed → restore the original value (or
`delete` the key if it was absent) → verify the restore. Handles PVE's dual-mode
`SetConfig` return per the `mutate.go` contract: empty UPID = synchronous success
(printed `synchronous`); non-empty UPID = `WaitTask` + assert `exitstatus=OK`.
The existing snapshot → rollback → delete-snapshot steps are unchanged. First live
exercise of the **`VM.Config.*`** privilege cluster.
- **`normDesc` / `extraString` helpers** — `extraString` decodes a string-valued key
from `GuestConfig.Extra` (raw JSON); `normDesc` strips the trailing newline PVE
appends to `description` on read, so a written value round-trips equal.
### Finding (live)
- The LXC `description` write returned **synchronous (empty UPID)** — PVE applied it
inline, no task. The agent's dual-mode `SetConfig` modeling is correct: the
empty-string path is real and must not be treated as an error.
- PVE **appends a trailing `\n` to `description`** on read (stored URL-encoded as
`%0A`). A naive exact-match reconcile would see perpetual drift — slice-4 reconcile
must normalize `description` comparisons (hence `normDesc`).
### Ops
- Standing operator token (`felhom-agent@pve!agent`, privsep) **rotated** during this
run (the prior secret was not retrievable); role + both user/token ACL rows
re-confirmed at `/`. New secret stored out-of-band, **not persisted to the repo**.
Guest 9999 left pristine (stopped, no `description`, no leftover snapshot). Version → 0.3.2.
## Docs + live validation — no version bump (2026-06-08)
### Changed