Velocity / Trend / Z-Score Aggregation Operators

The 9 velocity-family ops cover rate-of-change between adjacent events, behavioural cadence (inter-arrival statistics, burst-count peaks), value-trajectory (delta-from-prev, trend slope, trend residual), outlier flags (sigma-bound counts), value-flip counts, and entity-level z-scores. Together they answer "is this entity moving, drifting, spiking, or breaking from its own history?".

Op Required kwarg(s) Returns CPU tier Notes
bv.rate_of_change window f64 or null Tier 1 Two-event delta divided by Δt_ms.
bv.inter_arrival_stats window f64 or null Tier 1 v0 emits mean_ms; v0.1+ widens to {mean_ms, stddev_ms, cv}. Field-less.
bv.burst_count window, sub_window i64 Tier 1 Max events in any 1 of 64 sliding sub-window slots. Field-less.
bv.delta_from_prev f64 or null Tier 1 Lifetime-only; absolute jump (no Δt).
bv.trend window f64 or null Tier 1 OLS slope of (now_ms, field); smoother than rate_of_change.
bv.trend_residual window f64 or null Tier 1 Latest value minus its trend-line prediction; shares state with bv.trend.
bv.outlier_count window (sigma=3.0 default) i64 Tier 2 Welford-baseline + sigma-threshold count; the only Tier 2 velocity op (sqrt() per event).
bv.value_change_count window i64 Tier 1 Counts adjacent flips, not net distinct values.
bv.z_score baseline_window f64 or null Tier 1 Current event's deviation against the entity's running (mean, stddev).

All 9 are O(1) memory per entity — see the per-op page Complexity section for byte-level state shapes. Eight of nine are Tier 1; only bv.outlier_count is Tier 2 (one sqrt() per event for the threshold test). Per cost-class.md, the entire velocity family sits in the fast-update tier.

Key invariants

  • Server processing-time only. Every windowed dispatch in this family bins events by now_ms() per project_redis_shaped_no_event_time_ever. Producers cannot influence binning via payload fields; there is no event-time concept. Late events (Δt ≤ 0) are clamped to 0 (in inter_arrival_stats) or skipped (in rate_of_change).
  • window= is the canonical kwarg name for 7 of 9. bv.delta_from_prev is lifetime-only (no window= — it just diffs the most recent two values regardless of elapsed time). bv.z_score uses the SDK ergonomic name baseline_window= to make the "baseline-against-which-the-current-event-is-scored" intent explicit; the wire-form params field is still "window".
  • burst_count requires both window= AND sub_window=. sub_window partitions the outer window into bucketed slots; the helper rejects missing or malformed sub_window= at SDK call time and the server returns structured error aggregation_invalid_sub_window if it reaches register_validate.rs.
  • outlier_count defaults to sigma=3.0 — the classic three-sigma rule. Tighten to sigma=2.0 for ~5% tail; loosen to sigma=4.0 for ~0.006% tail. The op also warms up for MIN_BASELINE_N = 5 matching events before firing the outlier check, to avoid spurious early-stream increments.
  • Lifetime mode (window="forever") is allowed for all 9. Per crates/beava-core/src/register_validate.rs (~line 439–449) every velocity-family op is classified OpLifetimeBound::O1 — finite per-entity memory ceiling, register-time accepted in lifetime mode. For long-lifetime trend / z-score tracking, mind the FP-precision caveats noted on the per-op pages and prefer a fixed window= ≤ 1d on busy entities, or fall back to bv.ewma / bv.ew_zscore which carry a bounded-magnitude state by design.
  • Cold-start returns null (or 0 for the counters). rate_of_change, inter_arrival_stats, delta_from_prev, trend, trend_residual, z_score return null until enough events accumulate (typically n >= 2); burst_count, outlier_count, value_change_count return 0 and only ever increment.
  • Cold-entity eviction (@bv.event(cold_after=...)) drops the underlying state per V0-MEM-GOV-01; velocity ops rebuild fresh on the next post-eviction matching event.

When to use which

Note: per REQUIREMENTS.md, z_score is family AGG-Z-* — it lives here in velocity/ per RESEARCH §3 directory layout (entity-level statistics that pair naturally with the velocity / trend / outlier ops). bv.rate_of_change is the canonical Phase 9 velocity-family op per RESEARCH §5 — it lives here, not in decay/, because it computes a slope across two adjacent events rather than an exponentially-weighted statistic.

See also