EMRY-013: Anomaly processor (NaN/Inf + loss-spike z-score) #58

Merged
vxfemboy merged 2 commits from emry-013-anomaly into main 2026-06-25 02:52:07 +00:00
vxfemboy commented 2026-06-25 02:50:01 +00:00 (Migrated from github.com)

Closes EMRY-013 (#11). Branched off main.

What

crates/emry-engine/src/anomaly.rsAnomalyDetector watches one metric and emits AlertRecords:

  • Non-finite (NaN/Inf) → Critical alert immediately; the value is kept out of the window so it can't poison future z-scores. (This is the counterpart to EMA/Welford's 'reject, don't alert' — the detector is the single place that surfaces the bad value, closing the EMRY-002 'non-finite surfaces via Alert' contract.)
  • Loss spikeWarning when a finite value's rolling z-score exceeds the threshold (default 4).

Emits AlertRecord (not DerivedMetric), so it's a standalone type, not a Processor. Bus wiring (→ Event::Alert) is deferred to EMRY-015. Gentle brand-voice copy.

Verification

  • ./scripts/pre-commit-rust.sh green: clippy -D warnings, 34 engine tests, anomaly.rs 99% lines, total 95.60%
  • Tests: NaN/Inf critical alerts, spike warning, no-spike-within-threshold, min-samples gate, flat (zero-spread) window guard, batch events, wrong-metric ignore

Review-driven changes

  • z-score now uses sample std (n-1), consistent with Welford and the calibrated threshold (review conf 85).
  • Near-flat window guarded with std < f64::EPSILON (review conf 80).
  • Rewrote the flat-window test to use strictly identical values so it genuinely exercises the zero-std guard (review conf 80).
Closes EMRY-013 (#11). Branched off `main`. ## What `crates/emry-engine/src/anomaly.rs` — `AnomalyDetector` watches one metric and emits `AlertRecord`s: - **Non-finite** (NaN/Inf) → `Critical` alert immediately; the value is kept **out** of the window so it can't poison future z-scores. (This is the counterpart to EMA/Welford's 'reject, don't alert' — the detector is the single place that surfaces the bad value, closing the EMRY-002 'non-finite surfaces via Alert' contract.) - **Loss spike** → `Warning` when a finite value's rolling z-score exceeds the threshold (default 4). Emits `AlertRecord` (not `DerivedMetric`), so it's a standalone type, not a `Processor`. Bus wiring (→ `Event::Alert`) is deferred to EMRY-015. Gentle brand-voice copy. ## Verification - `./scripts/pre-commit-rust.sh` green: clippy `-D warnings`, 34 engine tests, anomaly.rs 99% lines, total 95.60% - Tests: NaN/Inf critical alerts, spike warning, no-spike-within-threshold, min-samples gate, flat (zero-spread) window guard, batch events, wrong-metric ignore ## Review-driven changes - z-score now uses **sample std (n-1)**, consistent with Welford and the calibrated threshold (review conf 85). - Near-flat window guarded with `std < f64::EPSILON` (review conf 80). - Rewrote the flat-window test to use strictly identical values so it genuinely exercises the zero-std guard (review conf 80).
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
femboy/emry!58
No description provided.