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
+31
View File
@@ -1,5 +1,36 @@
# Felhom Hub — Changelog
## v0.78.0 — R-97a: the whole-guest backup tier gets a voice (operator-only) (2026-07-27)
`internal/quiesce` had no route to the hub at all. On 2026-07-27 three failed whole-guest backups and
twelve app-stack stop/starts produced **zero** events. This is the hub half of the fix.
**Two new operator-tier event types**, allowlisted with **no `customerMessages` entry**:
`whole_guest_backup_failed` (error) and `whole_guest_backup_recovered` (info).
**Deliberately NOT `backup_failed`/`backup_completed`.** Both of those carry customer-facing Hungarian
templates AND sit in demo-felhom's live `enabled_events` — reusing them would have emailed the
**customer** „A biztonsági mentés sikertelen" while the backup was still retrying behind the R-88
breaker. A customer can take no action on a failed whole-guest backup. Same pattern as R-85's
restore-test types: in the allowlist so the chain works, out of `customerMessages` so the dispatcher
*structurally cannot* route them to a customer.
**The recovery rides the F11 pairing branch.** `whole_guest_backup_recovered` is severity `info`, and
`severityNotifies` drops `info` — routing it normally would store the event and never mail it, so the
operator would be told a tier broke and never told it healed. Adding it to `recoveredPairedDownTypes`
puts it on the recovery branch, which runs BEFORE the severity gate. Its customer leg needs no special
handling: that leg is pairing-gated on a customer-channel "sent" row for the down type, and the down
type can never produce one.
**Per-tier operator cooldown** (`cooldownTierSuffix`). The operator cooldown was keyed
`customerID + ":" + eventType` — correct for an event describing ONE thing, wrong for one describing
ONE TIER when a box has two: `felhom-pbs` failing at 09:00 would swallow `local` failing at 09:20 for
the rest of the hour. The key now appends the `tier` from the event details **when present**, so no
existing event type's cooldown behaviour changes. Widening it for everything would turn one hourly
`app_start_failed` into one per app — a flood, not a fix.
Tests +8. `build`/`vet`/`test` rc=0, 17 packages ok.
## v0.77.0 — R-85 Part 2: a restore-test result becomes a SIGNAL (2026-07-26)
Until now a failed restore-test was a `[WARN]` line in the ingest handler and nothing else — no