Deliver alerts from first startup reports

Wire outbound alert callbacks before a monitor is published so immediately reconnecting agents cannot create active warnings in the constructor-to-Start gap. Add a pre-Start custom-sensor regression and bind the ordering in monitoring and agent lifecycle contracts.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot] 2026-08-29 16:18:40 +01:00
parent f29e7397b1
commit 3de3cd320d
4 changed files with 117 additions and 19 deletions

View file

@ -85,6 +85,10 @@ initial notification target into the notification manager. This adjacent
alerts/notifications wiring grants no agent enrollment, reporting, removal,
profile, update, probe-assignment, or command authority and must not mutate
host-agent state.
The same adjacent boundary installs external alert callbacks before the
monitor is exposed to report handlers. This guarantees that the first accepted
agent observation can reach canonical notification delivery without granting
the callback path enrollment, identity, profile, update, or command authority.
The shared `internal/api/ai_handlers.go` route may also reopen a dismissed
Patrol finding and mirror that state into the unified findings store. This is
AI finding-state management only; it grants no agent enrollment, report,
@ -6794,3 +6798,17 @@ agent report or mutate the live agent inventory to make a mock timeline
complete. `internal/api/alerting/alerts_test.go` and
`internal/mock/alert_incidents_test.go` pin the transport and fixture sides of
this separation.
### First agent reports cannot outrun alert delivery wiring
The monitoring constructor installs external alert callbacks before it returns
the monitor to API routing. An agent report accepted immediately after process
startup can therefore create a canonical alert and reach notification and push
delivery even if the long-running `Monitor.Start` goroutine has not yet begun.
`Start` may replace those single callback slots with runtime-specific WebSocket
context and add lifecycle projection replay, but it cannot be the first owner
of outbound alert wiring. This ordering changes no enrollment or token
authority; it only prevents the first accepted agent observation from losing
its alert consequence. `TestNewMonitorRoutesStartupCustomSensorWarningBeforeStart`
in `internal/monitoring/monitor_host_agents_test.go` pins the pre-`Start`
warning path.

View file

@ -38,6 +38,10 @@ monitor restart.
Monitor construction also applies the persisted grouping enabled flag, window,
and node/guest keys as one notification-manager policy, so restart behavior is
identical to a live alert-configuration save.
Monitor construction installs external alert callbacks before publishing the
monitor to API handlers. `Monitor.Start` adds runtime WebSocket context and
lifecycle replay, but an immediately reconnecting agent must not create a
canonical warning while notification delivery is still unwired.
Monitor construction enables the alerts-owned persistent event log once for
each tenant alert manager. This is bootstrap wiring only: monitoring does not
own event types, retention, query semantics, or lifecycle/notification truth,
@ -3433,6 +3437,20 @@ must not silently skip a supported destination. Monitoring broadcasts the
escalated alert after dispatch but does not reinterpret destination identity,
retry semantics, acknowledgement, or the critical-repeat cadence.
### External alert callbacks are ready before monitor publication
`monitoring.New` installs firing, resolution, AI, and escalation callback slots
before returning a monitor that API handlers can publish. The asynchronous
`Monitor.Start` loop adds lifecycle projection handling and rewires escalation
with its WebSocket hub, but it is not the first notification-delivery boundary.
This closes the startup interval in which an immediately reconnecting agent
could create and persist an active warning while the outbound callback was
still nil. The constructor-time callbacks use the already loaded alert and
destination configuration; runtime rewiring replaces single callback slots and
does not duplicate delivery. `TestNewMonitorRoutesStartupCustomSensorWarningBeforeStart`
in `internal/monitoring/monitor_host_agents_test.go` proves a first custom-sensor
warning reaches the external path before `Start` runs.
### Proxmox node unavailability is not credential evidence
The Proxmox client treats HTTP 595 from a node-scoped API path as a resource