feat(alerts): confirmation-family reducer with discrete parity harness

Phase 1 slice 2 of docs/ALERT_ENGINE_EVOLUTION.md. ApplyDiscrete
characterizes the canonical lifecycle path's match-spec semantics for
connectivity / powered-state / discrete-state kinds: N consecutive
matching observations activate with StartTime at the first match, one
non-matching observation clears at this layer, severity follows the
spec while firing, and a disabled spec resolves. The incident sub-key
generalizes from metric name to state key.

reducer_parity_discrete_test.go diffs the reducer against
evaluateCanonicalLifecycleAlert after every step with fully simulated
time on both sides, including alert StartTime on first activations. The
harness again caught a real defect on first run — the confirmation
start-time understatement fixed in the previous commit. Recovery
confirmations and re-fire start restoration are documented as later
slices.
This commit is contained in:
rcourtman 2026-08-26 23:04:12 +01:00
parent 7c256cd04c
commit bb5fddf463
5 changed files with 569 additions and 17 deletions

View file

@ -34,5 +34,20 @@ poll (guest per-disk usage alerts were the remaining production caller;
user-visible as the stale-alert class, e.g. #1580). Fixed at the caller
with a focused regression test before the parity slice landed.
Remaining for later slices: offline/confirmation families, ack/snooze
lifecycle, intent-policy interaction, and a shadow-mode runtime feed.
Second slice (2026-08-26): the confirmation (discrete-state) family —
N-consecutive-match activation, single-observation recovery at the
evaluator layer, spec-carried severity — characterized against
`evaluateCanonicalLifecycleAlert`. The harness again surfaced a real
defect: the confirmation maps persist only counts, so
`lifecyclePreviousState` reconstructed pending runs dated at the current
observation and activation stamped StartTime at the final confirming poll,
understating outage start by the whole confirmation window (the manager
had already fixed this once for unified incidents via
`unifiedIncidentFirstSeen` but not for the generic lifecycle path). Fixed
with `lifecycleFirstMatched`, mirroring that precedent, before the parity
slice landed.
Remaining for later slices: recovery-confirmation gating
(`offlineRecoveryConfirmations`), re-fire-within-retention start
restoration, ack/snooze lifecycle, intent-policy interaction, and a
shadow-mode runtime feed.