STL seasonal anomaly detector (complement to DBSCAN, orthogonal signal)
New per-country anomaly detector using STL (Seasonal-Trend decomposition via Loess; Cleveland 1990) that learns each country's own weekly rhythm and flags days that break it. ORTHOGONAL to DBSCAN at /v1/anomaly/dbscan/{cc} — DBSCAN catches shape-anomalous days against a 45-day rolling cloud; STL catches days that deviate from THIS country's own seasonal pattern. Egypt may always have high anomaly_rate on Fridays — DBSCAN sees that as normal, STL flags non-Friday spikes as anomalous. Implementation in scripts/build-stl-seasonal-anomaly.py: 90-day per-country daily anomaly_rate time series, statsmodels.tsa.seasonal.STL(period=7, robust=True), residual = observed - (trend + seasonal), z-score within country, flag if |z|>2.0. Cron daily at 04:45 UTC. Sidecar at /opt/voidly-ai/models/stl_seasonal_anomaly_v1.json, time-series at /opt/voidly-ai/data/stl_seasonal_anomaly.parquet. First run: 43 of 211 countries had ≥60 days of data for viable fit. Today's top |z|: BY +4.38 (Belarus, only flagged today). Last-7-day STL set (BY, NG, QA, SA) has ZERO overlap with today's DBSCAN flagged set (AU, BR, CA, DE, EG, ES, FR, GB, IN, IQ, JP, KR, MA, MX, NL, SG, US, ZA) — 4 unique STL signals + 18 unique DBSCAN signals, exactly the orthogonal complement promised. Honest caveats: STL is descriptive not causal (high z != censorship — could be holiday, fiber cut, measurement-coverage artifact), sensitive to data sparsity (168 of 211 countries skipped for <60d data), period=7 assumes weekly seasonality (constant censors get a small seasonal component but still get residuals).