docs(report): hub v0.70.1 shipped — ghost Delete reachable; operator live leg pending

This commit is contained in:
2026-07-22 09:26:14 +02:00
parent 068bce673c
commit 1613bc7120
+70 -39
View File
@@ -1,49 +1,80 @@
# REPORT — Agent-only publish train 0.93.0 executed (operational run, docs-only in this repo)
# REPORT — hub v0.70.1: the ghost customer's Delete button must exist
**Date:** 2026-07-22 · **Repo:** `felhom.eu` (runbook record + §A5 discharge; no code, no version
bump, no hub deploy) · **Full record:** `documentation/pilot/RUNBOOK-publish-agent-0.93-2026-07-22.md`
**Date:** 2026-07-22 · **Repo:** `felhom.eu` (hub only) · **XS render-gate fix, DEPLOYED** ·
**STOP honoured: the ghost delete itself is NOT run — awaiting the operator's click.**
## What happened
## The defect (fourth inert seam — a template gate this time)
felhom-agent **v0.93.0** (hyphen-free escrow wordlist, source-only since 2026-07-21 at `a452dc33`)
was **built, published, manifest-vouched and deployed to both fleet boxes** by CC, with one
password-gated STOP → Viktor (the Day-0 manifest save, verified by CC in the hub DB **before** any
box moved — publish-train rule 1).
v0.70.0's ghost-delete was fully implemented server-side (`handleCustomerDelete`/`Preview` accept
`cfg == nil`) and fully unreachable: the Danger-zone card — and the `customerDeleteOpen/Submit`
script — sat inside the `{{if .HasConfig}}` block that also wraps the RESET card
(`customer_unified.html` old ~L736L945). A ghost rendered no Danger zone at all. Handler tests
POST directly and proved nothing about reachability. Confirmed live on `demo-vm-felhom` pre-fix.
- **Artifact:** sha256 `a68b2ff73200622ea9e23aed71a0a8ff885808626cab9e9d554a5a5df5ced28f`
(13 817 690 bytes), sha-chain identical: built binary → Gitea package (PUT 201) → independent
anonymous GET.
- **Manifest:** agent 0.92.1→**0.93.0** + new sha. Golden (0.153.0), floor (**0.156.0**) and
wrapper sha **byte-identical before/after**. **Deviation, ruled:** MinAgent moved 0.92.1→0.93.0
at the operator save (spec said do-not-touch); flagged before deploy, Viktor ruled leave it —
the held-floor window was transient (minutes) and harmless.
- **Deploys** (felhom-pve first, then demo-hp over G1 break-glass — secret file→file, shredded):
both boxes clean-restart **5/5** — active/NRestarts=0, capabilities **68/68 degraded=0**,
ReassertGuestBinds touched only enrolled drives, guestnet sweeps quiet (0 ERROR/WARN), hub
`hosts.agent_version` = **0.93.0** for both (06:56:52Z / 06:57:52Z). `.bak-0.92.1` retained on
both; escape hatch not needed.
- **Green gate** on the exact HEAD: build/vet/test green; the ex-flaky escrow package **10/10**
fresh runs (`-count=10`) — that flake was this defect, so the clean run is itself evidence.
## The change
## Changes in this repo
- **`configs.go` `handleCustomerUnified`**: new `Deletable` flag = the **exact negation** of the
preview's 404 predicate (`cfg == nil && no hosts && residue empty`). Hosts were already fetched
for the Host tab, so only the residue count is a new read — and it runs solely on the ghost
shape. Lookup error → log + `Deletable=false` (fail toward hiding a destructive control).
- **Template**: gate split — RESET card + its JS stay `{{if .HasConfig}}`; Danger zone + delete
dialog + delete JS gate on `{{if .Deletable}}`; Block/Unblock forms get an inner
`{{if .HasConfig}}`; ghost shape gets a one-sentence intro prefix (the dialog explains the rest).
1. **`documentation/pilot/RUNBOOK-publish-agent-0.93-2026-07-22.md`** — the executed record
(timestamps, hashes, per-box evidence, the MinAgent deviation + ruling, observations).
2. **`documentation/runbooks/RUNBOOK-onboarding-draft-v4.md`** §A item 5 — the "agent ≥ 0.93.0
box-side before the first real escrow ceremony" gate marked **DISCHARGED 2026-07-22**, citing
the runbook record. (The spec pointed at a ROADMAP "wordlist row" — **none exists**; the gate's
real home is §A5, so the annotation went there. Recorded as a deviation, not improvised into a
new ROADMAP row.)
## Tests (`customer_ghost_delete_render_test.go`) + red-proofs
## Fleet after this train
Render tests against the real template set, real store on `t.TempDir()`, asserting the delete
form's `action` attribute and the `customerDeleteOpen(` call site (not free-text):
Both boxes agent **0.93.0**; Day-0 manifest agent 0.93.0 / MinAgent 0.93.0 / golden 0.153.0 /
floor 0.156.0. `peti-felhom` untouched by design — Friday's reinstall pulls the vouched manifest
and lands on 0.93.0.
- **A — ghost with residue** (reports only, no config, 0 hosts): delete form + opener + script
present; RESET form ABSENT; Block/Unblock ABSENT. **PASS**
- **B — configured customer**: every affordance of today present (RESET, Block, delete machinery);
plus the blocked→Unblock branch (fixture note: `SaveCustomerConfig` never writes status —
blocking goes through `SetCustomerConfigStatus`, same as the /block handler). **PASS**
- **C — nothing left**: page-level **404**, mirroring the preview's semantics. There is **no
renderable `Deletable=false` state** with real store reads — `customer != nil` requires a report
row (`GetCustomer` selects from `reports`), and a report row makes `residue.Total() ≥ 1`. The
only false-branch shape short of that is a residue-lookup error (fail-safe, compile-trivial).
## Observations (not acted on)
**Red-proof 1 (RUN):** Danger-zone gate reverted to `{{if .HasConfig}}` → test A **FAILED** on all
three delete-affordance assertions ("ghost page must render the delete form … dead UI"). Restored →
green.
**Red-proof 2 (RUN):** RESET card gated on `Deletable` (the lookalike) → the RESET-absent assertion
**FAILED** ("RESET card must NOT render for a ghost (found action=/configs/ghost/reset)").
Restored → green. `git diff` clean of scaffolding (0 REDPROOF markers).
- demo-hp's 1TB NVMe is now **enrolled** (`/mnt/nvme-1tb`, reasserted into 9201) — TASK-H's
"unenrolled/NTFS" note is outdated; `operations/nodes.md` may want a refresh.
- demo-hp still key-less (break-glass worked cleanly); tailnet key-expiry toggle still open.
- Host journals are CEST; all runbook evidence stamps note the offset.
Green gate: `go build ./... && go vet ./... && go test ./...`**green** (web suite 12.0 s).
## Ship (GitOps)
| Step | Evidence |
|---|---|
| Feature commit | `f4c2c41` (code + template + tests + CHANGELOG + ROADMAP + CONTEXT + CLAUDE.md) |
| Image | `felhom-hub:0.70.1` built + pushed, ID `f1741b46bfbb` |
| Manifest bump | **separate chore commit** `068bce6` (operator preference honoured) |
| Sync | ArgoCD hard-refresh + patch-sync → **Synced/Healthy**, rollout complete |
| Live verify | pod image `felhom-hub:0.70.1`, imageID digest `sha256:f1741b46bfbb…` (= the built image), clean startup log, `/login` 200. Footer is on password-gated pages — the visual check lands on the operator's next login (endpoint-level method stated per convention) |
## AWAITING OPERATOR — the live leg is yours
Open `demo-vm-felhom` → Edit tab (Danger zone now renders with the ghost prefix) → **Delete
customer…** → three acks + typed id → confirm. Expected: customer gone from the Customers list,
journal legs `residue=ok customer_delete=ok` with `skipped_no_config` on the Hetzner/descriptor
legs, staleness emails stop. That click doubles as the v0.70.0 + v0.70.1 live validation; the
ROADMAP R-25b row flips its "PENDING" note once done.
## Docs also in this train
- `hub/CHANGELOG.md` v0.70.1 (inert-seam framing, template/handler lines cited).
- `CLAUDE.md`: **the seam-wiring rule now covers template gates** — any conditional UI affordance
ships with a render test per branch.
- ROADMAP: R-25b annotated (v0.70.1 render fix, operator live leg PENDING); **R-17 → CLOSED**
(operator console action: `u629193-sub1` deleted, no sub-accounts remain on PBS-storage-1,
2026-07-22).
## Observations (recorded, not acted on)
- Pre-existing page-reachability quirk: a customer with host rows but neither config nor reports
404s on the unified page (configs.go `cfg == nil && customer == nil`) while the delete preview
would 200. No such state exists in practice (hosts imply enrollment implies reports); noted only.
- The Create Config button for ghosts is untouched (re-adopting a ghost stays a legitimate path).