hub v0.79.0 — R-97c: make the operator-only claim true

v0.78.0 asserted in a comment that a type with no customerMessages entry cannot
reach a customer. It can: templates.go falls back to the raw message when the
entry is missing, and the only customer gate is prefs.EnabledEvents — pure
configuration. A customer with whole_guest_backup_failed enabled would have been
emailed raw English operator text about a backup they cannot act on. The new test
proves it against the v0.78.0 shape.

operatorOnlyEvents is now an explicit register checked before prefs, logging a
skipped/operator_only row so the skip is visible. NOT implemented as 'missing
customerMessages blocks delivery' — several types rely on that fallback on
purpose. The handler comment now names the real mechanism.
This commit is contained in:
2026-07-27 17:54:47 +02:00
parent 9ea5675950
commit 2c0e43e0d0
5 changed files with 228 additions and 4 deletions
+8 -3
View File
@@ -1562,9 +1562,14 @@ var allowedEventTypes = map[string]bool{
// DELIBERATELY NOT `backup_failed`/`backup_completed`. Those two carry customerMessages entries
// AND sit in demo-felhom's live enabled_events, so reusing them would email the CUSTOMER, in
// Hungarian, that their backup failed — while it is still retrying behind the R-88 breaker. A
// customer can take no action on a failed whole-guest backup. These follow the R-85 pattern
// instead: allowlisted, with NO customerMessages entry, so the dispatcher structurally cannot
// route them to a customer. Do NOT add customerMessages entries without a copy review.
// customer can take no action on a failed whole-guest backup.
//
// OPERATOR-ONLY IS ENFORCED BY `notify.operatorOnlyEvents`, NOT by the absence of a
// customerMessages entry. v0.78.0 claimed the latter and was WRONG (corrected in v0.79.0/R-97c):
// `FormatCustomerEmail` treats a missing entry as a fallback to the raw message, and the only
// customer gate is `prefs.EnabledEvents` — configuration, which a customer or a future code path
// can change. The register is checked before customer dispatch and logs a `skipped/operator_only`
// row. Adding a type here does NOT make it operator-only; add it to that register too.
"whole_guest_backup_failed": true,
"whole_guest_backup_recovered": true,