Files
felhom.eu/REPORT.md
T

81 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# REPORT — hub v0.70.1: the ghost customer's Delete button must exist
**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.**
## The defect (fourth inert seam — a template gate this time)
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.
## The change
- **`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).
## Tests (`customer_ghost_delete_render_test.go`) + red-proofs
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):
- **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).
**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).
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).