Files
pulse/pkg/reporting
rcourtman a21e0ef8f8 Stop reports and resource history calling a moved alert recovered (#2607)
Follows #2591. A node alert that moves to its linked Pulse agent closes
with a `moved_to_agent` resolution (#2580). Two readers still presented
that close as a recovery.

**Performance reports.** The node, VM and container enrichers copied
recently resolved alerts into `reporting.AlertInfo` without the
resolution, so a report generated in the five-minute window after a
handover read HEALTHY, "All systems operating normally", counted the
alert as "triggered and resolved", and sent the AI narrator `resolved:
true`.

- Report rows now carry the alert's resource and resolution
(`reportActiveAlertInfo` / `reportResolvedAlertInfo`).
- `pkg/reporting` has one rule (`unresolvedAlerts`): an alert that
closed without recovering stays live unless the report also shows its
successor's reading for that metric, meaning an open alert or a recovery
since the handover. That way the condition is counted once, and fleets
match handovers across all their resources.
- The single-resource PDF card reads WARNING (or CRITICAL at the alert's
level) with "Alert moved to <agent> - check the agent for the current
reading". The active list names the agent, the alerts table shows
"Moved" with the engine's summary as a note, and recommendations say to
check the agent.
- Fleets count the alert as active and name the agent. Their all-clear
pattern and "No fleet-wide action required" no longer appear while any
alert is active.
- The narrator payload carries `resolved: false`, the summary and
`successor_alert_listed`, with prompt rules for both.

**Resource history.** `alert_resolved` changes were labelled by kind
alone ("Alert resolved", green) with that label prefixed to the
headline. `getResourceChangePresentation` now reads the
`alert_resolution` metadata: the change reads "Alert moved" in the
neutral blue of the history badge, and its summary is the whole headline
in the drawer, `ResourceChangeSummary` and Patrol's assessment handoff.

**Verification**
- A private mock stack running this branch, with a local-only overlay
that closes a real pve1 memory alert through `clearAlertWithResolution`.
Within the window, `/api/admin/reports/generate?resourceType=node`
rendered CRITICAL with the handover message and a "Moved" row, while the
ordinary CPU recovery stayed green.
- In the browser, a full drawer (Availability checks, Fleet view) showed
the producer-made rows as "Alert moved" next to "Alert resolved" at 1280
and 375. A Proxmox node's own drawer uses the `table-row` presentation,
which fetches no change history (see below).
- Tests in registered proof files: `pkg/reporting/alert_state_test.go`
(verdict, successor timing, fleet pool, PDF text and width),
`internal/api/contract_test.go`, `internal/ai/report_narrator_test.go`,
`resourceChangePresentation.test.ts`,
`ResourceDetailDrawer.history.test.tsx` and
`patrolInvestigationContextModel.test.ts`. Each failed with the fix
reverted.
- Second opinion: two read-only review rounds. The temporal match,
fleet-wide pool, narrator dedupe signal, fleet all-clear fallbacks and
contract wording came from them.

**Not fixed here (separate changes)**
- Reports requested as `agent` carry no alert rows at all. That is how
the Reports page addresses every Proxmox node in v6, so such a report
reads HEALTHY even with active critical alerts. This change makes the
shared rule correct for when those reports attach the machine's alerts.
- Node and guest alert lifecycle changes are recorded under legacy alert
resource IDs, which canonical history queries never match. Facet kind
counts still group moved closes under "Alert resolved".
2026-10-07 09:10:57 +01:00
..
2026-03-18 16:06:30 +00:00
2026-03-18 16:06:30 +00:00