hub v0.71.0: paired recovery mails (F11), prefs seeding at claim + empty-email no-clobber (F12), priority headers + operator test leg (F14-light)
This commit is contained in:
@@ -1,5 +1,43 @@
|
||||
# Felhom Hub — Changelog
|
||||
|
||||
## v0.71.0 — paired recovery mails, prefs seeding at claim, priority headers, operator test leg (2026-07-22)
|
||||
|
||||
Origin: `documentation/audits/AUDIT-power-outage-recovery-2026-07-22.md` F11 (recovery is silent),
|
||||
F12 (prefs row optional → customer never notified), F14-light (delivered ≠ noticed). Live proof of
|
||||
the gap: the demo customer got „A szerver nem elérhető!" at 15:29 and was never told it recovered.
|
||||
|
||||
- **Paired recovery notifications (F11)** — `dispatcher.go` `processRecovery`, an explicit
|
||||
eventType branch in `ProcessEvent` BEFORE the severity gate (`severityNotifies` and the checkers'
|
||||
`emitTransition` severities are byte-untouched; `*_recovered` stays `info`). Operator always gets
|
||||
both edges (existing 1 h per-type cooldown); the customer gets recovery **iff the customer was
|
||||
mailed the paired stale/down** — pairing evidence is `store.LastCustomerSentAt` over
|
||||
`notification_log` (customer channel, status=sent, `node_recovered→{node_stale,node_down}`,
|
||||
`host_recovered→{host_stale,host_down}`), ties resolve to no-mail (flap-safe). `enabled_events`
|
||||
is deliberately NOT consulted for recovery. Suppressions log at INFO with the reason.
|
||||
`FormatOperatorEmail` renders ✅ for `*_recovered`; `customerMessages` gains `host_recovered`.
|
||||
- **Prefs seeding at claim (F12)** — `claim.Engine.MarkClaimed` seeds `customer_notifications`
|
||||
from the registered `customer_configs.email` on the unclaimed→claimed transition via new
|
||||
`store.SeedNotificationPrefs` (INSERT OR IGNORE — never touches an existing row; empty email =
|
||||
no-op; a seed failure never fails the claim). Default set (critical-only, Viktor may adjust):
|
||||
node_down, backup_failed, disk_critical, host_disk_critical, storage_fill_critical,
|
||||
offbox_repo_orphaned.
|
||||
- **Empty-email no-clobber guard (F12)** — `handleSavePreferences`: a push with an empty email
|
||||
preserves a stored non-empty address (events + cooldown still apply); a non-empty push updates
|
||||
everything. Phase-0a fact: controller 0.160.0 guards both push legs itself
|
||||
(`cmd/controller/main.go:821` startup skips empty email; `web/handlers.go:1532` refuses
|
||||
empty-with-events), so the clobber was latent — this is the hub-side belt for older/rogue boxes.
|
||||
- **Priority headers (F14-light)** — `sendEmailFn`/`sendEmail` gain a `headers` param; Resend
|
||||
payload carries `"headers"` only when non-empty. `priorityHeaders(severity)`: error/critical →
|
||||
`X-Priority: 1` + `Importance: high`; warning/info → none. Mechanism probed live pre-implementation
|
||||
(Resend accepted, HTTP 200, mail id `34d3f7f3…`).
|
||||
- **Operator test leg** — the `test` event now also mails the operator (`✅ <id>: teszt / operator
|
||||
channel OK`, priority headers forced) — one click proves customer channel + operator channel +
|
||||
header rendering. Fixed a latent nil-deref found here: `sendTestEmail` dereferenced
|
||||
`prefs.Email` while `GetNotificationPrefs` returns `(nil, nil)` for a customer with no row — a
|
||||
test event for such a customer (e.g. demo-hp) panicked the dispatcher goroutine.
|
||||
- Tests: 17 new (449 → 466) across store/notify/claim/api; 4 red-proofs run + reverted (pairing
|
||||
removed, upsert-seed, guard removed, unconditional headers) — see `REPORT.md`.
|
||||
|
||||
## v0.70.1 — the ghost customer's Delete button must exist (2026-07-22)
|
||||
|
||||
**The fourth inert-seam defect: v0.70.0's ghost-delete path was fully implemented and fully
|
||||
|
||||
Reference in New Issue
Block a user