expr still queries the old name, silently going blank or never firing.
scripts/verify/ catches this in CI, on every push, rather than relying
on a human noticing during a live incident.
check_alert_coverage.py
docker/observability/alerts/seo-alerts.yml,
alertmanager.yml, the coverage-map table in alerts/README.md, and
docs/runbooks/:
- both YAML files parse, and every alert has
severity/summary/description/runbook - every
runbook:annotation resolves to a real file - every PromQL
expris paren/brace-balanced - every
by (...)/without (...)label is actually declared on a metric the expression references - every metric in
core/observability/metrics.pyhas a coverage-map row — alerted, or explicitly marked not-alerted with a reason - every runbook file is reachable from an alert or the runbooks index
check_dashboard_metrics.py
expr against the same metric catalog
— the exact class of drift a unit test can’t catch, since it’s about
config-referencing-config, not application logic.
Run both together
verify-observability job on every push — see
Observability.