hub v0.81.0 — E-2: backup_target_absent gets its own signal (ships first)

An event type the hub does not allowlist makes POST /event return 400 and the
event vanishes (R-97a). The controller cannot emit backup_target_absent until
this is live, so the hub half ships first.

E-2 Phase 0 established that an absent backup target has NO prompt signal today.
The controller's drive-gate path stops apps and logs a WARN but emits nothing:
NotifyStorageDisconnected is defined and never called anywhere (verified against
the gitignored-cmd/ trap with a positive control). A drive that is ONLY a backup
target has no apps to stop, so it is entirely silent. The sole signal is the
tier's own failure at its next due cycle -- up to ~24h on the daily local tier,
which is the R-100 shape: a real fault visible only after a deadline elapses.

Added to BOTH registers, because each half fails differently:
  allowedEventTypes  -- without it the event is lost at the door;
  customerMessages   -- without it the event IS delivered but in the controller's
                        raw operator English, and nothing looks broken.

backup_target_absent is deliberately NOT folded into storage_disconnected: that
says "a drive went away and some apps may have stopped"; this says "the thing
that makes your backup survive a disk failure is gone".

Hungarian copy names the consequence, not just the fact. backup_target_restored
is the paired recovery at info severity -- severityNotifies NOT widened.

Three tests pin the pair and the copy's substance. All red-proofed with the
mutation VERIFIED to have landed first: the initial attempt silently no-op'd
(gofmt had realigned the map) and the test "passed" -- a false proof that would
have been reported as evidence.

Green gate: build + vet + test rc=0, run separately from this commit.
This commit is contained in:
2026-07-29 07:55:18 +02:00
parent b5a73e050b
commit 1257014c2b
5 changed files with 124 additions and 10 deletions
+37
View File
@@ -1,3 +1,40 @@
## v0.81.0 — E-2: the absent backup target gets its own signal (2026-07-29)
**Hub half of E-2, and it ships FIRST by necessity:** an event type the hub does not allowlist makes
`POST /event` return 400 and the event vanishes (the R-97a failure). The controller cannot emit
`backup_target_absent` until this is live.
E-2's Phase 0 established that an absent backup target has **no prompt signal today**. The drive-gate
path stops apps and logs a WARN but emits nothing — `NotifyStorageDisconnected` is defined and never
called anywhere in the controller (verified against the gitignored-`cmd/` trap with a positive
control). A drive that is *only* a backup target has no apps to stop, so it is entirely silent. The
sole signal is the tier's own failure at its next due cycle, i.e. **up to ~24 h** on the daily local
tier — the R-100 shape, where a real fault is visible only after a deadline elapses.
Added to BOTH registers, because each half fails differently and the second failure is the quiet one:
- `allowedEventTypes` (`internal/api/handler.go`) — without it the event is lost at the door;
- `customerMessages` (`internal/notify/templates.go`) — without it the event IS delivered, but the
customer receives the controller's raw operator English instead of Hungarian, and nothing looks
broken.
`backup_target_absent` is deliberately **not** folded into `storage_disconnected`. That one says "a
drive went away and some apps may have stopped"; this one says "the thing that makes your backup
survive a disk failure is gone" — a different customer action and a different operator urgency.
Hungarian copy states the CONSEQUENCE, not just the fact:
> „A rendszermentés meghajtója nem érhető el — amíg vissza nem csatlakoztatod, a teljes rendszermentés nem készül el."
`backup_target_restored` is the paired recovery (`info` severity — the existing recovery pattern;
`severityNotifies` is untouched and NOT widened).
**Tests + red-proofs.** `api.TestBackupTargetEventTypesAreAllowlisted` and
`notify.TestBackupTargetCustomerMessagesArePresent` pin the pair; a third test pins that the copy
names what is at risk and what happens, so a future shortening to a bare „Meghajtó hiányzik." cannot
pass. All three red-proofed with the mutation VERIFIED to have landed first — the initial attempt
silently no-op'd (gofmt had realigned the map to three spaces) and the test "passed", which would
have been a false proof.
## v0.80.0 — R-100: staleness counts from the last SUCCESS (2026-07-28)
`OffsiteChecker.isStale` counted from `last_run`, which the controller writes **unconditionally** at the