fix(mcp): get_anomaly shows Steward's own restarts beside every anomaly, and a router's containers (#5862)

All 16 Traefik 4xx anomalies on 2026-10-15 came back with nothing
around them, though 15 were Steward's own restarts. A router has no
host, and every events source was read by host.

- Core: each break of more than 90 s in Steward's once-a-minute health
  sample is a "steward" event beside every anomaly, placed or not.
- Plugins: an optional mcp_resource_events(db, source, resource, since,
  until) for a resource with no host. Traefik's gives the container
  events of the swarm service behind a router, on any host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 15:58:28 -04:00
co-authored by Claude Opus 5.5
parent a24b9da69d
commit 91bb199d14
8 changed files with 139 additions and 10 deletions
+5 -2
View File
@@ -158,9 +158,12 @@ async def get_anomaly(anomaly_id: str, occurrences: int = 5) -> dict:
- `occurrences[]`: `started_at`, `ended_at`, `held_s`, `peak_level`,
and `usual` for that hour as Steward has learned it now (`usual_hold`,
`usual_peak`, `flag_above`), plus `around`: alert transitions, Ansible
runs, other anomalies on the host, and what plugins saw (container
runs, other anomalies on the host, what plugins saw (container
starts, deaths and OOM kills; maintenance jobs; reboots and agent
stops), from 30 minutes before to 30 minutes after; and
stops; for a Traefik router, its swarm service's containers on any
host), and kind "steward": a stretch Steward itself wasn't recording
(a restart or deploy), around which any reading can be wrong. From 30
minutes before to 30 minutes after; and
`anomalies_elsewhere`, how many anomalies on other hosts overlapped it.
Many at once points at something shared (network, storage, Steward).
- `series`: the newest shown occurrence with time either side (as long