Commit Graph

874 Commits

Author SHA1 Message Date
rcourtman 6185cf3f88 Bound alert lifecycle replay with a durable projection watermark
Projection replay walked the entire alert event log on every boot, twice
(monitor start and the resource-store boundary), synchronously on the
serving path, and every replayed event queued a full incident-store JSON
rewrite. A 64k-event log made startup exceed the dev supervisor's
health-kill window, so the backend restarted forever and never served.

The event log now owns per-consumer projection watermarks in
alert_store_meta, WalkOldest takes an AfterID cursor, and the single
catch-up pass runs in the background at the canonical resource-store
boundary with periodic watermark checkpoints. The watermark only
advances when both the incident store and the canonical change recorder
are attached, so partial-surface passes repair without marking events
applied, and lowering the watermark forces a full repair replay.
Incident-store saves now coalesce: a burst of mutations queues one
whole-store serialization instead of one per event, live paths included.

Registers the alert-lifecycle-replay-startup-scalability coverage gap
and amends the alerts and monitoring subsystem contracts accordingly.
2026-08-31 21:57:58 +01:00
pulse-triage[bot] f69ce1daae Use Kubernetes node identity for host linking
Change-source: pulse-maintainer
2026-08-31 21:20:29 +01:00
rcourtman cf021bc9ce Surface typed helper container degradation 2026-08-31 15:53:50 +01:00
rcourtman d19398d82a Surface typed helper degradation in Agent Doctor 2026-08-31 13:06:28 +01:00
pulse-triage[bot] 8c63984696 Clear stale PBS backup-running state 2026-08-31 07:03:43 +01:00
Pulse Test 1d9e66b24c fix(mock): keep provider fixtures fresh on slow demo ticks
The public demo runs PULSE_MOCK_UPDATE_INTERVAL=60s, which stretches the
10-cohort supplemental rotation to 600s while the unified registry still
marks TrueNAS/VMware/availability sources stale after 120s. Every row those
sources own spent ~80% of each rotation flagged as a degraded stale source,
so the TrueNAS Storage tab read Healthy 0 / Attention 21 (and the vSphere
page all-Attention) despite healthy fixture data.

Rebase provider-backed fixtures every tick once the rotation would outlive
the 120s freshness budget (at slow ticks that is cheaper per wall-clock
second than the default 2s config's once-per-20s cadence), and derive mock
supplemental stale thresholds from the actual refresh cadence the same way
PVE/PBS/PMG thresholds already derive from their polling intervals. Split
the supplemental uptime step so per-tick refreshes do not advance PBS
uptime by the rotation-scaled step.
2026-08-30 22:10:49 +01:00
Pulse Test 16a1574a18 Add multi-location service delivery evidence 2026-08-30 21:44:32 +01:00
pulse-triage[bot] e094a55b45 Integrate typed helper container summaries 2026-08-30 21:29:11 +01:00
Pulse Test c0b190bfa1 Add application response contracts to availability checks 2026-08-30 17:38:59 +01:00
Richard Courtman 34849268e5 Enforce collector credential scope allowlists 2026-08-30 17:18:38 +01:00
Pulse Test 712f1a3bfe Add availability history and fleet view 2026-08-30 15:38:34 +01:00
Pulse Test 5b2e377843 Expose secure agent runtime posture 2026-08-30 00:38:44 +01:00
Pulse Test 6405c47064 Clear Docker host removal block on fresh-token re-enroll
Removing a Docker host blocks its reports, but the report path only ever
consulted the in-memory block and offered no way for a legitimate
re-enroll to clear it: reinstalling the agent with a newly generated
token kept being rejected until a server restart happened to wipe the
in-memory map, which read as the Docker tab staying missing until the
Pulse server was rebooted. The restart also silently lifted deliberate
blocks, because the persisted entry was never consulted on report.

Docker reports now follow the host-agent rule from #1581: the block
check consults both the in-memory map and the persisted store, and a
token minted after the removal is explicit re-enroll intent that clears
the block from both. A still-running old agent presenting its
pre-removal token stays blocked, including across restarts.

Refs #1728
2026-08-30 00:07:53 +01:00
Pulse Test a966264bb1 Contain agent command authority 2026-08-29 22:12:41 +01:00
pulse-triage[bot] 8c65d716f4 Expose honest Proxmox update evidence 2026-08-29 20:51:19 +01:00
Pulse Test 6db3cc8616 fix(alerts): correlate linked outage signals 2026-08-29 18:19:00 +01:00
Pulse Test a506cdde8b Separate notification server errors from rejections 2026-08-29 16:57:44 +01:00
pulse-triage[bot] 3de3cd320d 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
2026-08-29 16:20:20 +01:00
pulse-triage[bot] f29e7397b1 Complete schema 14 privacy disclosure
Name every outbound alert-quality field in both operator-facing privacy documents, lock the current schema value in verification, and align the alert-manager adoption test with the telemetry contract.

Change-source: pulse-maintainer
2026-08-29 15:23:28 +01:00
Pulse Test 0df9747bd7 Add privacy-safe alert quality telemetry 2026-08-29 14:00:28 +01:00
pulse-triage[bot] 75a8e67347 Stabilize rolling metric history snapshots 2026-08-28 23:23:43 +01:00
pulse-triage[bot] afaf128950 Disambiguate same-name Proxmox agent links
Use unique provider-observed node interface addresses to restore automatic
host-agent linking when standalone sites share a short hostname but use
distinct FQDN endpoints. Keep reused addresses ambiguous and fail closed.

Refs #1753

Contract-Neutral: narrows internal agent-to-node identity reconciliation without changing API or extension shapes
2026-08-28 18:07:10 +01:00
pulse-triage[bot] 9e820355b6 Fix Unraid sentinel empty slot reporting 2026-08-28 14:20:43 +01:00
Pulse Test 9ce4b7e20f Harden alert durability and watchdog isolation 2026-08-28 10:14:02 +01:00
pulse-triage[bot] 7e3c624900 Keep agent removal token revocation atomic
Serialize monitoring-owned host, Docker, and Kubernetes token cleanup with API-token persistence. Restore the complete live inventory when a reduced inventory cannot commit so restart never silently reverses an apparent revocation, and govern the boundary with forced-failure proof.

Change-source: pulse-maintainer
2026-08-28 07:55:17 +01:00
pulse-triage[bot] f5f854afa2 Complete Unraid monitoring contract 2026-08-28 01:32:29 +01:00
pulse-triage[bot] cb27b8eb24 Fix Unraid empty slot health reporting 2026-08-28 01:23:12 +01:00
Pulse Test 413288d049 Keep offline mock hosts on the host alert lifecycle 2026-08-28 00:07:17 +01:00
rcourtman 1878245cdb Make alert incident timelines complete 2026-08-27 21:42:25 +01:00
pulse-triage[bot] fec18f343f Preserve fresh rolling-window metric authority
Keep in-memory observations authoritative over duplicate persisted history, add direct provider and cache reset coverage, and register the rolling-window proof files with canonical governance.\n\nChange-source: pulse-maintainer
2026-08-27 21:22:12 +01:00
rcourtman 4b9abd4eca Add rolling-window metric alert evaluation 2026-08-27 21:11:44 +01:00
rcourtman b0c1eca4cd Add predictive storage capacity alerts 2026-08-27 20:41:08 +01:00
rcourtman e0baecc418 Make alert escalations destination-specific and repeatable 2026-08-27 19:51:12 +01:00
rcourtman 3d4c43162a feat(alerts): add recurring scoped maintenance 2026-08-27 18:20:03 +01:00
rcourtman ff507a6e44 feat(alerts): add per-alert snooze 2026-08-27 17:20:32 +01:00
rcourtman f4e1e47e74 feat(alerts): add external dead-man monitoring 2026-08-27 16:27:46 +01:00
rcourtman f6773f262a fix(alerts): make active restart state durable
Contract-Neutral: monitor construction only moves alerts-owned durable-store initialization before alert workers; monitoring and host-agent behavior are unchanged
2026-08-27 15:21:18 +01:00
rcourtman b6babd0b4b fix(alerts): make incident timelines lifecycle-driven
Contract-Neutral: monitor startup wiring changes only alert lifecycle projection behavior; agent-lifecycle deletion and reenrollment contracts are unchanged
2026-08-27 14:45:36 +01:00
rcourtman 73499d7161 feat(alerts): migrate persisted alert identities 2026-08-27 12:04:49 +01:00
pulse-triage[bot] 4f91232cc6 fix(proxmox): stabilize storage inventory refreshes
Sort backup-server and datastore rows deterministically so provider refresh order cannot reset expanded detail state. Exclude optical devices from PVE, legacy agent, and canonical physical-disk projections.

Fixes #1785
Fixes #1786

Contract-Neutral: Corrects refresh ordering and non-disk filtering without changing public APIs or subsystem contracts.
Change-source: pulse-maintainer
2026-08-27 11:05:14 +01:00
pulse-triage[bot] 0fd9171ff7 Prevent host identity forks during install handoff
Allow one retiring-agent health window after a trusted replacement install token is minted, while preserving clone-safe forks for ambiguous, conflicting, or longer-lived identities. Retire superseded token bindings and document the remaining migration gap for identities already forked.

Change-source: pulse-maintainer
2026-08-27 08:47:42 +01:00
rcourtman bfc1a81867 feat(alerts): shadow-mode reducer feed with always-on divergence telemetry
Phase 1 capstone of docs/ALERT_ENGINE_EVOLUTION.md. The deterministic
reducer now runs continuously inside the live manager against the same
production observations: the canonical lifecycle path (connectivity,
powered-state, discrete-state kinds) via a deferred hook that replays
each evaluation — including the resolved intent context — through the
reducer; the poll-driven recovery paths (PBS/PMG/storage, node,
connection-degraded); and manual acknowledge/unacknowledge/clear. The
feed seeds from active canonical alerts at enable so restarts do not
read as mass divergence.

Every state disagreement is counted (Manager.ShadowDivergences) and
recorded in the alert event log as a shadow_divergence event with both
engines' states, rate-limited to one report per key per ten minutes.
After each divergence the reducer resyncs to the manager, so one
divergence yields one event — including divergences caused by manager
mutations the feed does not observe. Appends never block evaluation and
a disabled feed is a nil-check no-op.

This converts the parity harnesses' test-time guarantee into an
always-on invariant; the production divergence rate becomes the
go/no-go evidence for each Phase 2 family cutover. The full
activation/ack/recovery/re-fire cycle runs divergence-free in tests.
2026-08-26 23:49:12 +01:00
pulse-triage[bot] 18bf3e1a8d Complete alert event log governance proof
Bind monitor-side event-log bootstrap to its canonical boundary and exercise the new event infrastructure through the registry-recognized alerts, API, monitoring, and agent-lifecycle proof files.

Change-source: pulse-maintainer
2026-08-26 21:32:10 +01:00
rcourtman 9572a113ae feat(alerts): append-only alert event log with suppression reasons
Adds internal/alerts/eventlog: a SQLite-backed, additive event log that
records lifecycle transitions (resolved, acknowledged, unacknowledged,
escalated, flapping detected) and notification decisions (dispatched,
deferred by quiet hours, suppressed — with the mechanism that held
them). Appends never block alert evaluation: a full buffer drops the
event and counts the drop; a store that fails to open degrades to
recording nothing. 90-day retention, hourly prune.

The manager emits at the existing funnels only — dispatchAlert and the
safe-call resolve/ack/escalate seams — so no lifecycle behavior
changes. Lifecycle "fired" is deliberately not recorded yet: the
active-alert store funnel also runs on persisted restore, so firing
waits for the explicit activation seam in a later phase. The monitoring
bootstrap enables the log per manager; ephemeral managers and tests
record nothing unless they opt in.

GET /api/alerts/events (monitoring:read) reads the log with
alertIdentifier/type/since/limit filters, newest first.

Phase 0 of docs/ALERT_ENGINE_EVOLUTION.md (coverage gap
alert-engine-suppression-observability).
2026-08-26 21:15:37 +01:00
pulse-triage[bot] 8fb7b37641 Stabilize release regression tests
Contract-Neutral: Freezes mock sampler cadence only inside the existing cache regression and updates release-preflight assertions to recognize current conditional runner routing and shard-degradation behavior; runtime and public contracts are unchanged.

Change-source: pulse-maintainer
2026-08-26 20:22:20 +01:00
rcourtman 9e1b8bfa1a Dedupe policy postures, AI-safe summaries, and self-alias ids in the stream
Third increment of the resource-payload-static-metadata gap, the
snapshot-side statics. Measurement first: the mock estate ships only 5
distinct non-default policy postures across 723 resources and 56
distinct templated AI-safe summary strings across the same rows
(~150KB of inline duplication per snapshot), and every canonical alias
list carries the row's own wire id verbatim.

Non-default policies and AI-safe summaries now dedupe through the same
content-addressed catalog contract as capabilities: state-level
policyCatalog and aiSafeSummaryCatalog hold the distinct blobs,
resources carry policyRef / aiSafeSummaryRef, and ingestion expands
refs back to the inline shape (default posture synthesis unchanged for
ref-less rows). The broadcast conversion returns one catalogs struct so
the next deduped field is a loop entry, and catalog changes ride the
same delta frame as the refs that need them. Alias lists stop shipping
the row's own wire id — every identity consumer unions resource.id
beside the alias spread — while the primary id stays so aliases remain
the complete live vocabulary a host is reachable by, per the
established slimming invariant.

Verified against a 1857-resource estate: zero inline policies or
summaries on the wire (743 refs resolving into 1KB + 7KB catalogs),
zero alias lists carrying the wire id, catalog-expanded postures and
summaries reaching the store byte-equivalently, and the estate
rendering clean on desktop and the 36-row phone window.

Remaining gap scope: canonicalIdentity alias/superseded history is
genuine per-resource identity data (~600KB per snapshot) with no safe
derivation shortcut, and the proxmox-facet/platformData mirror
duplication (~200KB) is deliberate canonical-merge fabric; both stay
recorded under resource-payload-static-metadata for a future owner.
2026-08-26 18:46:12 +01:00
rcourtman cba1e2abeb Reconcile delivery health and Proxmox Assistant controls 2026-08-26 18:33:41 +01:00
pulse-triage[bot] 75b6655d6e Restore Proxmox node network details
Refs #1716

Change-source: pulse-maintainer
2026-08-26 15:17:12 +01:00
rcourtman 5d46e7d0e7 Complete mock drawer history coverage
Contract-Neutral: restores existing metricsTarget identity and complete mock history fallback contracts without changing their public shape
2026-08-26 12:11:10 +01:00
rcourtman 8f7d5c2e98 Fix API-only Proxmox node history
Change-source: pulse-maintainer
2026-08-26 00:27:58 +01:00