hub v0.78.0 — R-97a: whole-guest backup events, operator-only

internal/quiesce had no route to the hub at all: three failed whole-guest backups
on 2026-07-27 produced zero events. Hub half of the fix.

whole_guest_backup_failed / _recovered are allowlisted with NO customerMessages
entry. Deliberately not backup_failed/backup_completed — those have customer
Hungarian templates AND sit in demo-felhom's live enabled_events, so reusing them
would email the customer that their backup failed while it is still retrying
behind the R-88 breaker.

The recovery joins recoveredPairedDownTypes because it is severity info and
severityNotifies drops info — otherwise the operator hears it break and never
hears it heal. Its customer leg is pairing-gated and can never fire.

Operator cooldown gains a per-tier dimension from the event details, so one tier
cannot mask another for an hour. Narrow: empty suffix unless a tier is sent, so
no existing event type changes.
This commit is contained in:
2026-07-27 16:59:05 +02:00
parent 65409aecd1
commit 331193b898
6 changed files with 179 additions and 2 deletions
@@ -0,0 +1,18 @@
package api
import "testing"
// R-97a — the allowlist entry is the delivery chain's first link.
//
// The recorded gotcha: an event type missing from allowedEventTypes makes POST /event return 400 and
// the event vanishes. `internal/quiesce` had no route to the hub at all, so three failed whole-guest
// backups on 2026-07-27 produced zero events. This pins the entry so a cleanup cannot quietly
// re-break the chain.
func TestWholeGuestBackupEventTypesAreAllowlisted(t *testing.T) {
for _, et := range []string{"whole_guest_backup_failed", "whole_guest_backup_recovered"} {
if !allowedEventTypes[et] {
t.Fatalf("%s must be in allowedEventTypes, or POST /event 400s and the whole-guest "+
"backup tier goes silent again", et)
}
}
}