Live demo

16 series in. 4 alerts out.

A consumer subscription business, 60 days of daily cohorts, 16 series across four acquisition channels. On day 41 one channel started sending a different kind of user. Blended month-3 retention, the number in the board deck, never moved. Find the channel and find the region of the curve.

Every number on this page is computed in your browser right now, by the same CUSUM and Bayesian changepoint code that runs in production. Nothing here is a screenshot.

The comparison that matters

Against the rule most teams actually ship

The standard configuration is a percentage or sigma threshold against a trailing mean, evaluated per metric. Run it over the exact same 16 series and count what lands in the channel.

Naive rule
60
alerts sent · alert if |Δ| > 2.5σ vs 7-day mean
Of those, false
32
fired before anything was wrong
This engine
4
alerts sent, all correct
Noise reduction
93%
fewer messages, same catch

The false-alarm count is not a judgement call. The incident was injected at a known index, so every naive alert before that index is wrong by construction.

What the engine found

Detection run

Detection run4 alerts16 series tested · 4 fired · 0 suppressed by BH at FDR=0.05
MetricTrendBaselineNowChangeConfidenceMethod
Hours to second session
channel=paid_social
29.02256.574+94.9%99.9%CUSUM
Week-2 retention
channel=paid_social
41.1%25.8%−37.3%99.9%CUSUM
Sessions per active user
channel=paid_social
4.7443.144−33.7%99.9%CUSUM
Day-7 activation rate
channel=paid_social
56.0%39.8%−29.0%99.9%CUSUM

Show your work

Hours to second session · channel=paid_social

Baseline
29.022
After changepoint
56.574
Change
+94.9%
Confidence
99.9%
CUSUM (Page 1954)
The series60 daysdashed line = baseline mean · red = detected changepoint
23.91842.94861.978changepointJun 29Aug 27
The CUSUM accumulatorsmall deviations stack slowly; a real shift stacks fast
h = 0.254 (decision boundary)0.700

Baseline mean μ = 0.0039 and σ = 0.0635, both computed from the first two thirds of the day-over-day percentage changes. The slack k = 0.0318 is half a sigma, and the decision boundary h = 0.2542 is four. The accumulator runs on percentage changes rather than raw levels so a healthy growing series cannot drift across the boundary on its own.

What the naive rule did on this same series

alert if |Δ| > 2.5σ vs 7-day mean fired 5 times across this one metric, including 1 before anything was actually wrong. The statistical pipeline sent one message, on the day the regime actually changed.

What lands in Slack

One message, with the work already done

R
Retention CliffAPP#eng-alerts
🔴 Hours to second session jumped +94.9%channel=paid_social
Baseline
29.022
Now
56.574
Confidence
99.9%
Started
Aug 9
Channel
paid_social
Cohorts
signup date ≥ Aug 9
Changed
creative set 14 + new LP
Users
38,410
Likely cause · Every signal that moved belongs to paid_social cohorts starting Aug 9. The shape of the break is diagnostic: day-7 activation and week-2 retention fell together, while sessions per active user dropped and time to second session nearly doubled. That is a first-session problem, not a long-run value problem, and it points at what the user landed on rather than at the product they eventually got to. Blended month-3 retention is unchanged because these cohorts are 18 days old, so month-3 is not observable for them yet.
  1. 1.Diff the paid_social landing page and creative set against the Aug 8 version.
  2. 2.Compare first-session event streams for cohorts on either side of Aug 9.
  3. 3.Check whether the new creative changed the promise the landing page pays off.
AcknowledgeFalse positiveCUSUM · q < 0.0001 · BH-corrected

The written cause is generated only after the statistics confirm the change. The model never decides whether something is an anomaly — it explains one that has already been established. Getting that order backwards is how these products hallucinate.