Analyze the experiment precompute result-consistency canary across prod-US and prod-EU, deep-dive any issues, and produce an actionable report. Sweeps the canary's Prometheus health gauges in both regions, and when anything is unhealthy pulls the structured divergence/failure logs from Loki to reconstruct exactly which (team, experiment, metric) went wrong, by how much, and which class of divergence it is (stability vs correctness, and for correctness whether exposure counts or only values differ). Mechanism-level root cause needs ClickHouse and is out of scope — the skill hands off with precise drill-down steps. Use when the user asks to check / analyze / verify the experiment precompute canary, investigate a canary divergence or alert, or confirm precomputed experiment results are consistent in production. All data comes through the Grafana MCP (Prometheus + Loki) — no payload decryption, no ClickHouse.
76
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Tell the user, with evidence, whether experiment precomputed results are consistent in production — in both prod-US and prod-EU — and when they aren't, hand them an actionable report: which metric diverged, by how much, which divergence class it is, and the next step. Classify only what the data supports (stability vs correctness; for correctness, whether counts or only values diverged) — do not assert a ClickHouse-level mechanism as fact. Read-only investigation; never mutate anything.
Experiment results are served from precomputed ClickHouse cache tables. A class of multi-node read-your-writes
bugs (one fixed in PR #62854: a precomputed read not seeing its own writes; also cache content drifting from
the events table) is only observable in production. The canary guards against it: a daily Temporal workflow
(experiment-precompute-canary) samples experiment metrics on precompute-enabled teams and runs each metric
three times — twice forced through the precomputed path (runs a, b), once forced through a direct
events scan (run c) — then compares:
0.1%); mean/ratio
sums get the loose tolerance because they join live event values; their exposure counts stay strict.
Retention resolves both of its numbers from metric events, so both get the loose tolerance.2%).
Since PR #87880 (2026-08-25), sum deviations also need to clear an absolute per-variant floor
(MIN_CORRECTNESS_SUM_DELTA = 100, raw metric units) — sub-floor gaps are excluded from the reported
deviation and the gauges entirely, so sparse metrics (tens of conversions on millions of exposures) no
longer page on a handful of events' worth of expected live-vs-frozen drift. Exposure counts are never
floored.Tolerances in code: STRICT_TOLERANCE = 0.001, LOOSE_TOLERANCE = 0.02, low-volume floor
MIN_EXPOSURES_PER_VARIANT = 100, correctness sum floor MIN_CORRECTNESS_SUM_DELTA = 100.0.
Source of truth: products/experiments/backend/temporal/canary_logic.py
(read it if anything here seems stale).
Outcome taxonomy (per metric): pass | divergence | path_flip | error | skipped.
This skill needs the Grafana MCP connected for both regions:
grafana → prod-US (mcp__grafana__* tools)grafana-eu → prod-EU (mcp__grafana-eu__* tools)Region is pinned per server; there is no per-query region arg. The server you call IS the region. If the
mcp__grafana__* / mcp__grafana-eu__* tools aren't available this session, the setup or a restart is
needed — see tools/infra-scripts/mcp/README.md for the per-user Grafana MCP setup. Do not
fabricate results if a region's tools are missing; report that region as "not checked" and say why.
If only one region's MCP is set up, analyze that one and clearly flag the other as uncovered.
Pushed via pushgateway under job experiment_precompute_canary. If a bare name returns nothing, add
{job="experiment_precompute_canary"} or browse metrics named experiment_precompute_canary*.
| Metric | Meaning | Healthy |
|---|---|---|
experiment_precompute_canary_outcomes | one series per outcome label (last run) | mostly pass (+ some skipped); divergence == 0 |
experiment_precompute_canary_max_deviation | check="stability" and check="correctness" | stability ≈ 0; correctness small (≤ a few %) |
experiment_precompute_canary_last_run_timestamp | unix ts of last completed run | recent — time() - <this> small; stale (>~48h) means the canary itself is broken |
The canary emits these log events (the message string is the structured event name; rich context is in fields):
experiment_precompute_canary_divergence (error) — the money log. Fields: team_id, experiment_id,
metric_uuid, metric_type, stability_deviation, correctness_deviation, runs (list of
{label, query_id, is_precomputed, variants} — per-variant sum + number_of_samples for runs a/b/c).experiment_precompute_canary_run_failed (warning) — a run threw; has team_id, experiment_id,
metric_uuid, run_label, query_id, and a traceback (exc_info). Temporal retries the whole triple
after this; only exhausted retries surface as an error outcome, so expect several lines per bad metric.experiment_precompute_canary_run_finished (info) — per-run summary with the outcome counts + triggered_manually.experiment_precompute_canary_sampled (info) — what was sampled (target/experiment counts, unfilled quotas).The query_ids are experiment-canary-<id>-<a|b|c> — they're the client_query_ids for ClickHouse
system.query_log if a human wants to go deeper (out of scope here).
For each region (US via mcp__grafana__*, EU via mcp__grafana-eu__*), query the three metrics. First
discover the exact Prometheus query tool the MCP exposes (don't assume its name/signature). Record per region:
outcome)time() - experiment_precompute_canary_last_run_timestamp)Derive a per-region verdict:
divergence, deviations within tolerance.last_run_timestamp old / metric missing → the canary or its schedule is broken (this is its
own incident; check the Temporal schedule experiment-precompute-canary-schedule exists and isn't paused).divergence, or elevated error/path_flip → go to step 2.Use the same region's Grafana MCP to query Loki:
experiment_precompute_canary_divergence (and _run_failed) lines over a window covering the last
run(s) — default last 48h, widen if needed.runs for a/b/c.If outcomes shows divergence but no matching log line is found, widen the time range; if still nothing, say
so — don't invent the offending metric.
divergence with both runs precomputed (a,b is_precomputed=true): the real thing. Use which deviation
is over tolerance to say which check failed:
number_of_samples from runs, b vs c (these are exposure counts; they are in the log):
sum differs → same-size exposure sets, different values. Do not call this
an exposure undercount or "events not ingested" — the matching counts contradict that. (Equal counts
don't prove the two sets contain the same users; only ClickHouse can confirm that.) Candidate drivers
are only visible in ClickHouse (e.g. a stale first_exposure_time shifting each user's metric
window, or winsorization/outlier sensitivity amplifying a few heavy users). Name these as candidates to
verify, not conclusions; and note that on a winsorized mean/ratio metric a multi-percent deviation can
come from a handful of users, so the magnitude may be concentrated, not broad.
High severity either way.path_flip: a forced-precomputed run reported is_precomputed=false — it fell back to the direct scan
(lazy-computation executor wait-timeout). Known, separately-tracked, lower severity; users see result jumps.
Not a correctness bug in the cache.error: two causes, distinguishable in the logs. Either the three runs returned different variant
sets (verdict detail "variant set mismatch between runs", no _run_failed line), or a run kept failing
through Temporal's retries — query timeout, OOM, ClickHouse errors — leaving _run_failed tracebacks.
Monitoring signal; flag if persistent across runs.skipped: low-volume (<100 exposures/variant), no exposures yet, or metric edited/removed mid-run.
Benign, but a high skip rate means thin coverage — note it.Output a single markdown report. Keep it tight and actionable — lead with the verdict, not the raw numbers.
# Experiment precompute canary — <date/time UTC>
## Verdict
- prod-US: <Healthy | Unhealthy | Stale | Not checked> — <one line>
- prod-EU: <Healthy | Unhealthy | Stale | Not checked> — <one line>
## Health (last run, per region)
| region | last run | pass | skipped | divergence | path_flip | error | max stability dev | max correctness dev |
| ------ | -------- | ---- | ------- | ---------- | --------- | ----- | ----------------- | ------------------- |
| US | ... | ... | ... | ... | ... | ... | ... | ... |
| EU | ... | ... | ... | ... | ... | ... | ... | ... |
## Issues (only if any)
For each: region, team_id, experiment_id, metric_uuid (type), outcome, which check failed + deviation,
the a/b/c per-variant numbers, the query_ids, and a one-line root-cause classification.
## Recommended actions
- Concrete next steps, most urgent first. Examples:
- stability divergence (a≠b) → the read-your-writes / PR #62854 class; verify lazy-computation INSERTs still
set insert_distributed_sync=1; page the Experiments team. Provide the query_ids for system.query_log forensics.
- correctness divergence (b≠c) → do NOT anchor on insert_distributed_sync (a≈b means writes are visible). If
counts match (value-only), the next step is a ClickHouse drill-down on the query_ids comparing the exposure
sets and first_exposure_time (b vs c) — not a row-count hunt. Page Experiments with the count-vs-value finding.
- path_flip only → lower priority; note executor wait-timeout, link to its tracking.
- stale / missing metrics → the canary or its schedule is down; check the Temporal schedule.
- all healthy → state it plainly, note sample size (pass+skipped) and that both regions were covered.system.query_log if they choose.)canary_logic.py has changed (different tolerances, metric names, or log events), trust the code and
note the drift in your report.b0fece0
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.