preview_alert_rule
The preview→save gateway: validates and normalizes a candidate rule spec and, when valid, backtests how often it WOULD have fired over the last N days (default 7). READ: never persists anything, never throws for an invalid spec.
Call this before save_alert_rule to calibrate. IMPORTANT: check backtest.dataCoverage first, before reading totalWouldFire — dataCoverage.status = NO_MATCHING_DATA means the filter matched zero rows over the whole window (likely a typo'd field or wrong value in the filter), NOT a calibrated threshold; fix the filter, don't touch the threshold. Only when status = EVALUATED (rows were matched) does totalWouldFire being 0 suggest the threshold may be too high — if it fires every window, too low. Fix any entries in problems[] before saving — save_alert_rule re-runs this exact validation and will reject the same way.
Two modes — supply EITHER the structured measurement fields OR fromQuerySql (a raw QuerySQL SELECT parsed into a measurement draft, e.g. for "alert on this query"); when fromQuerySql is set the structured fields are ignored.
Static condition: comparator + warningThreshold (+ optional criticalThreshold escalation). Anomaly condition: zScoreThreshold + direction instead of comparator/warningThreshold; groupBy must be empty. Anomaly backtest is not yet supported — backtest is null for those.
Returns normalizedSpec (best-effort echo of the compiled spec), problems[] (empty when valid), backtest (would-fire counts, per-series observed values, and dataCoverage — rowsMatched/firstEventAt/lastEventAt/status over the backtest window — only when valid), seasonality (a 0-1 daily-periodicity score of the backtest's primary series plus a suggestedMode of ANOMALY/THRESHOLD/UNKNOWN — a data-driven nudge on baseline vs fixed-threshold rules; null when there was no backtest), and warnings[] (calibration hints, NEVER a reason to withhold saving — unlike problems[], a non-empty warnings[] still saves fine). warnings[] currently carries one code, FIELD_NEVER_OBSERVED: a filter/groupBy field that querysql couldn't resolve to a known column (so it silently reads from the JSON catch-all) and that has never appeared in this customer's recent telemetry. FIELD_NEVER_OBSERVED together with dataCoverage.status = NO_MATCHING_DATA is a strong signal of a typo'd field name — fix the spelling and re-preview rather than loosening the threshold.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| fn | No | Catalog measure function: count, error_rate, p95, error_burn_rate, ... | |
| arg | No | Optional field the measure operates on, e.g. duration_ms | |
| unit | No | Explicit display unit for the measure, e.g. BYTES or DURATION_MS — set it when the metric name doesn't self-describe its unit (OTel names like jvm.memory.used or http.server.request.duration carry no unit suffix); omit to let the server infer the unit from the metric name or measure function | |
| filter | No | Optional QuerySQL boolean filter, e.g. service = 'my-svc' | |
| params | No | Optional named measure params, e.g. {"budget":"0.001"} | |
| source | No | Telemetry source: LOGS, SPANS, METRICS | |
| groupBy | No | Optional fields to group the series by | |
| direction | No | Anomaly direction: HIGH or LOW | |
| comparator | No | Threshold comparator: GT, GTE, LT, LTE (static rules) | |
| metricName | No | Metric name (required only when source is METRICS) | |
| metricType | No | Metric type: GAUGE, SUM, HISTOGRAM, ... (only when source is METRICS) | |
| fromQuerySql | No | Raw QuerySQL SELECT to derive the spec from, instead of the structured fields | |
| lookbackDays | No | Days of history to backtest (default 7, clamped to [1, maxBacktestDays]) | |
| windowMinutes | No | Rolling window length in minutes; values below the configured minimum (5) are clamped up | |
| zScoreThreshold | No | Anomaly z-score threshold (> 0) — supply instead of comparator/warningThreshold | |
| warningThreshold | No | Warning-tier threshold (static rules) | |
| criticalThreshold | No | Optional critical-tier threshold (escalation) | |
| anomalyConsecutiveWindows | No | Consecutive anomalous windows required (>= 1) | |
| warningConsecutiveWindows | No | Consecutive breaching windows for the warning tier (default 1) | |
| criticalConsecutiveWindows | No | Consecutive breaching windows for the critical tier (default 1) |