feat(alerts): alerts that arrive together go out as one email (#5465)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m9s
CI / unit (push) Successful in 1m26s
CI / publish (push) Successful in 1m13s

#5465 "One email per burst of alerts". When a switch reboots, every host's
agent-offline rule fires at once and each account got one email per rule.
Alert email now waits 30 s from the first alert: each recipient then gets one
message listing every alert in the window, oldest first, in their own zone
("[Steward] 3 alerts: 2 firing, 1 resolved"). One alert alone keeps its
usual email. Webhooks still post per alert, at once. Every alert's event
records the email's outcome. A burst held when Steward stops is not sent;
the alerts stay on the record and on the Alerts page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 08:29:52 -04:00
co-authored by Claude Opus 5.5
parent 48a0de5600
commit 997aa50939
6 changed files with 264 additions and 20 deletions
+15
View File
@@ -247,6 +247,21 @@ def alert_email(cfg: dict, alert: dict, to: Recipient) -> tuple[str, str, str]:
return facts["subject"], text, html
def burst_email(cfg: dict, alerts: list[dict], to: Recipient) -> tuple[str, str, str]:
"""(subject, text, html) for several alerts that arrived together
(core.alert_burst, #5465): how many and what they came to in the subject,
then each alert's headline, rule and time, oldest first, in the
recipient's zone."""
from collections import Counter
facts = [alert_facts(a, to.tz, to.show_utc) for a in alerts]
counts = Counter(f["state"] for f in facts)
said = ", ".join(f"{n} {state.lower()}" for state, n in counts.most_common())
base = (cfg.get("base_url") or "").rstrip("/")
text, html = _render("burst", alerts=facts, said=said,
link=f"{base}/alerts/" if base else "")
return f"[Steward] {len(facts)} alerts: {said}", text, html
def report_email(cfg: dict, data: dict, to: Recipient) -> tuple[str, str, str]:
"""(subject, text, html) for the weekly report built by
reports.build_report_data, its dates in the recipient's zone."""