docs(v0.215.0): CHANGELOG, CONTEXT decisions, README feature, REUSE entries
gates / gates (push) Successful in 10s

- CHANGELOG leads with the severity fix and the live warning-vs-warn proof.
- CONTEXT records the settled decisions so they are not re-litigated: Hiba is
  the label for predicted failure (no fourth word); sustain before count and
  why; the provenance of 64/55/60; phase 2 owns the new SMART attributes
  because they are a wire change under G-1; phase 1 state is one record per
  disk, not a series.
- README documents the 14-row ladder, the persisted state, the hourly cadence
  and the five message shapes.
- REUSE pins the severity wire contract on PushEvent — the defect's real home,
  so the next typo'd severity is caught at the table rather than in production
  — and records priorFor vs cardPriorFor, which differ by one observation and
  make the chip disagree with the email if mixed up.
This commit is contained in:
2026-08-14 08:34:31 +02:00
parent 34d83f5a02
commit 8144a70a72
4 changed files with 181 additions and 14 deletions
+58 -1
View File
@@ -7,7 +7,64 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-08-08 (v0.208.0 — R-254: the last two secrets leave the page source, and a gate against a fourth)
Last updated: 2026-08-14 (v0.215.0 — R-328..R-333: the disk alert that never sent)
> **2026-08-14 — v0.215.0 (R-328..R-333). Disk health, phase 1: the alert that reached nobody.**
>
> ### A severity string is a WIRE CONTRACT with the hub, not a label we choose.
>
> The hub accepts exactly `{info, warning, error, critical}` and **silently coerces anything else to
> `info`**, which `severityNotifies` then drops. `disk_health_degraded` shipped `"warn"` — one letter
> short of the contract — so **every Figyelmeztetés-level disk alert this product ever produced was
> emailed to nobody, on both legs.** Proven live side by side on 2026-08-14: `"warning"` →
> `notification_log` status **`sent`**; `"warn"` → stored `info`, **no row at all**.
> `app_start_failed` (`notifier.go` ~L546) carries the identical defect and was deliberately NOT
> changed here — it needs its own decision on whether it should notify (**R-329**).
>
> ### A drive's own PASSED verdict cannot fail on bad sectors. Do not build on it.
>
> Attributes 187/197/198 all carry `thresh: 0`; a normalized SMART value floors at 1 and can never drop
> to or below the threshold. The real drive (ST3000VX010, S/N Z6A07P2G) read `PASSED` at **352** pending
> sectors and **1001** reported-uncorrectable reads. Felhom already read the raw counters, which is the
> only reason it would have noticed at all. Evidence + fixtures:
> `felhom.eu/documentation/audits/DIAG-smart-passed-trap-2026-08-14.md`.
>
> **DECISIONS MADE, so they are not re-litigated:**
>
> - **Predicted failure is labelled „Hiba" — there is no fourth verdict word.** A fourth Hungarian word
> sharing a root with „Figyelmeztetés" would make the MORE severe state read as the milder one. Four
> labels, final: Rendben / Figyelmeztetés / Hiba / Nincs adat.
> - **Sustain is the primary rule; the count is the backstop.** Truth-table row 6 (unreadable sectors
> present again at the next check) sits ABOVE row 8 (count >= 64) because on the real drive sustain
> fires 12 Aug and the count not until 13 Aug. Row 8 exists only for a box powered off across the
> sustain window.
> - **The numbers and where they come from.** 64: the benign excursion peaked at 16 and cleared inside
> an hour; the terminal run passed 64 at 13 Aug 11:28 and never returned. **It is a judgement from ONE
> drive** — a static backstop, expected to be replaced by growth-rate detection in Phase 3. 55/60 °C:
> the operator's existing Prometheus bands on DooPlex, adopted unchanged so the two systems cannot
> disagree about the same drive. **These bands are SPINNING-DISK bands and are questionable for NVMe**
> — demo-hp's healthy Toshiba NVMe idles at **53 °C**, 2 °C below Figyelmeztetés (**R-333**).
> - **Phase 2 owns the new SMART attributes (187 Reported_Uncorrect, 199, 188).** They are a declared
> wire change, so under the G-1 gate the hub must model them in the same session. Putting them here
> would have turned a one-word severity fix into a three-repo change (**R-330**). Everything v0.215.0
> needs was already on the wire.
> - **Phase 1 state is one small record per disk, NOT a sample series.** `metrics.MetricsStore` is the
> right home for Phase 2/3 history; using it now would have put a schema migration on the critical
> path of the severity fix.
>
> **The trap this change nearly shipped, caught by a test and not by review:** the card and the check
> share one verdict function so the chip and the email can never disagree — but the check CONSUMES the
> prior and then overwrites it, so a card rendering afterwards read its own check's write and showed one
> level MORE severe than the alert. Fixed by `diskRecord.PriorSawUncorrectable`, which replays the prior
> that produced the stored verdict. The guarantee was previously asserted in a comment only.
>
> **Cadence is hourly, and it was MEASURED:** demo-hp `/disks` costs median 0.821s (min 0.805 / max
> 0.841, 10 calls, 3 physical rows) — 6x under the 5s bar. Open question deliberately NOT acted on: the
> agent runs bare `smartctl -a -j` with **no `-n standby`**, so an hourly poll would wake a spun-down
> HDD. demo-hp is all-flash so the measurement could not show it (**R-333**).
>
> **NOT live-validated:** the Fail-from-counters path has never fired on real hardware — only against
> the fixture's values in unit tests (**R-332**).
> **2026-08-08 — v0.208.0 (R-254). THE RULE, stated so it outlives this session:**
>