docs: R-182 closed, R-90 closed on measurement, R-86 unblocked, ep0 record corrected
gates / gates (push) Successful in 7s
gates / gates (push) Successful in 7s
R-182 CLOSED (controller v0.194.0 + hub v0.90.0/.1), proven live on demo-hp. The hub's notification_log for the run reads: two per-app failures RECORDED, one digest SENT naming both, and the customer channel SKIPPED with operator_only. Against the measured previous behaviour — two failures, one email naming one app, one leaving no trace anywhere. Scenario D proved itself on an event I had not planned: disk_critical alarmed on two filesystems, the second was collapsed by the cooldown, and that collapse is now visible WITH ITS KEY. Yesterday it would have left nothing at all. A gap the spec did not anticipate is recorded with its fix: the per-app event also fires from the periodic sweep, outside any run, so making it record-only would have created a NEW silence. The sweep emits a digest too, with no run_id, so it stays under the ordinary hourly cooldown. ep0: MEASURED on the box — 7757 MB (8 GB), 4 vCPU, and the 4 GiB swapfile SURVIVED the resize and is active (checked, because a resize is a stop/start). The 40 GB local disk is UNCHANGED, so no disk figure was touched anywhere. Five documents corrected — three of which the task's list did not name, found by searching. Two audit/evidence documents ANNOTATED, body untouched: they record what was true when written and that is their value. R-90 CLOSED. R-86 unblocked and re-ranked, stated honestly: 8 GB is comfortable, not unbounded — the original OOM was a 14.46 GB restore — so the restore-test cadence should still be paced, just not by fear of the endpoint. target-selection.md's "D-d did not name ep0 either way" is deliberately left standing. It is the operator's question, not CC's. STATUS.md 127 -> 83 lines, items rather than sentences.
This commit is contained in:
@@ -574,6 +574,30 @@ because that was a different filesystem. On a merged box it can. Decision **B2**
|
||||
It **refuses per app and never deletes**: nothing on this filesystem is generational, so pruning could
|
||||
only destroy a different app's only local copy.
|
||||
|
||||
**WHAT IS RECORDED, WHAT IS E-MAILED, AND HOW OFTEN (controller v0.194.0 + hub v0.90.x, R-182).**
|
||||
The two are deliberately different mechanisms, because conflating them is how seven failures went
|
||||
missing on 2026-08-03 without leaving a trace.
|
||||
|
||||
| | Record | Notification |
|
||||
|---|---|---|
|
||||
| what | `recovery_unit_capture_failed`, one per failed app | `backup_run_failures`, one per RUN |
|
||||
| when | every time, unconditionally | at the end of a run, **only if something failed** |
|
||||
| gated by | nothing — not cooldowns, preferences or delivery | the hub's operator cooldown |
|
||||
| where it lands | the events table **and** `notification_log` (status `recorded`) | the operator's inbox |
|
||||
|
||||
- **A clean run e-mails nothing.** Silence means the run finished and found nothing wrong — and that
|
||||
is only safe because the hub's daily deadline check raises `expected_backup_missed` from the box's
|
||||
REPORT freshness, independent of any mail the box sends. That check is load-bearing for this
|
||||
design; weakening it re-opens a silent-failure path.
|
||||
- **A suppressed operator notification leaves a `suppressed` row** naming the key that suppressed it.
|
||||
Deciding not to tell someone is itself an event worth recording.
|
||||
- **Deliberate skips are not failures** and never appear in the digest — a disconnected or
|
||||
decommissioned drive has its own alert, and a nightly digest about an unplugged drive is one the
|
||||
operator stops reading.
|
||||
- **Cadence:** a nightly run gives at most one mail a day. A manual run always reports, even within
|
||||
the hour, because someone pressing the button is actively trying to get a backup. The periodic
|
||||
capture sweep is capped by the ordinary hourly cooldown.
|
||||
|
||||
**THE CONTRACT, stated as what the code provides (controller v0.193.0, R-181).** The reserve is a
|
||||
**per-app, per-run ADMISSION decision, not a capture check.** It is taken once for an app, immediately
|
||||
before that app's FIRST write of the run, and it covers **all three write legs — the database dump, the
|
||||
|
||||
Reference in New Issue
Block a user