🔎 Whodunit
Step 1 / 7 · Point at the failing traces
replay · seed 778 · SigNoz v0.132.2 ← Back to overview

① Point at the failing traces

You have 61 failing traces and no idea why.

You point Whodunit at them. It auto-selects a case-control–matched healthy cohort and runs one deterministic pipeline — extract → mine → compile → verify → arm. No LLM anywhere.

whodunit — explain
# 61 failing checkout traces. what separates them from the healthy ones?
PS whodunit> whodunit explain --from-manifest corpus/out/manifest-778.json
runs the real seed-778 pipeline (replayed from the committed result)
13.1×lift
61 = 61verify match
0false culprits / 6
0LLM at runtime
HONEST
Interactive replay of a real recorded run (seed 778) against live SigNoz v0.132.2 — no live SigNoz required. Every number here is read from the committed run at docs/video/raw/explain-result.json. Nothing is fabricated for the demo; there is no LLM in the runtime.

② Extract + mine → the elimination board

One clickhouse_sql scan builds a per-trace boolean matrix (span predicates, parent→child edges, log features — joined by trace_id in the same store). FP-growth then enumerates the complete itemset lattice before any test runs, so the FDR control is valid. It shows its work: the obvious single-predicate answers are eliminated because each appears in both cohorts. Only the conjunction survives.

whodunit · verdict
DISCRIMINATOR — the culprit is WITH edge payment ⇒ redis-retry AND WITHOUT flag-service  (lift 13.1×)
candidate itemset lift95% CI badhealthyverdict
7,806 candidate itemsets enumerated · 36 features · 6 survivors cost meter: one scan, 163,464 rows, 1,843 ms

The six survivors are one discriminator in six equivalent encodings (edge vs span for the retry, three ways to name the missing flag-service) — all at the same 61 / 0; the highlighted winner is the one that compiles cleanest. Rows marked are near-misses struck out: adding a duration bucket to the winning pair changes nothing (same 61 / 0), so they are dominated and pruned. Single-predicate candidates never even reach here — each appears in both cohorts.

the same board, as the CLI actually printed it The Whodunit CLI elimination board

③ Compile → a trace-operator query you own

The crown jewel. The winning itemset is lowered into a valid SigNoz builder_trace_operator envelope — respecting engine constraints (operator direction, left-bias, trace-scoped NOT) that were recovered by probing the live v0.132.2 engine.

compiled expression

( A B ) && NOT C
returnSpansFrom = A  ·  count_distinct(trace_id)

leaf queries

Aservice.name = 'shop-payment'
Bname = 'redis-retry'
Cservice.name = 'shop-flag-service' AND name = 'GET /flags/evaluate'

⇒ is the direct (single-hop) descendant operator; the outcome operand is normalised left so a real Trace Explorer link returns the right spans. Absence is only expressible anchored to a positive operand — a bare NOT C returns zero and is refused.

④ Verify — mined vs the live engine

The synthesized query is not trusted — it is run against SigNoz as a scalar count_distinct(trace_id) and asserted equal to the miner's own local count. This is the differential receipt. It is the whole thesis in one row.

mined
61
SigNoz
61
verdict
MATCH
precision 1.00  ·  recall 1.00  ·  61 bad / 0 healthy
verified live · 46,805 rows scanned
the flat baseline returns precision 0.17 here — it cannot see the conjunction

⑤ Materialize — the artifact, not a paragraph

The deliverable is a Query Builder artifact you keep: a Trace Explorer permalink, a dashboard panel, and an armed v2alpha1 alert whose webhook fired end-to-end at t+182s.

↗ Open in a real SigNoz UI

This is the exact link Whodunit emitted (from docs/video/raw/permalink.txt). It resolves against a running SigNoz at localhost:8080 — the dev stack the run was recorded on — so it opens a real Trace Explorer, not a mock.

Trace-operator query executing in the SigNoz Query Builder
the compiled (A ⇒ B) && NOT C running live in the SigNoz Query Builder
FIRING · critical · caught at POST /whodunit (4,282 B)
webhook payload · docs/video/raw/webhook.log

Real captured webhook body from docs/video/raw/webhook.log — a v2alpha1 rule on the compiled discriminator delivered end-to-end.

The armed rule in SigNoz breaching both thresholds, and the delivered webhook body
the armed rule breaching WARN + CRIT thresholds → the delivered webhook

⑥ Determinism — same input, same verdict

There is no LLM and no randomness in the runtime. The same input plus seed always produces the identical verdict hash. Run it as many times as you like — it never drifts.

verdict hash · sha-256
95f8835759e2865ec90f17b45df7f1f74f9944484bad4f014e0f209826f91fb5
run #1 · hash stable

⑦ Benchmark — zero false culprits across six

Six scenarios run live against the stack, each scored against a machine-checkable ground-truth manifest, with a properly-implemented BubbleUp-style flat baseline for comparison. Whodunit nails the conjunction the baseline can't see, ties on single-feature faults (their home turf), and takes the honesty path — abstain / partial, never a false culprit — where a confident answer would be wrong.

scenarioground truthwhodunitrecallflat baselineoutcome
Zero false culprits across six — one conjunction no flat tool can express, an honest tie, two correct abstentions, one below-confidence partial · recall stays 1.00 on every expressible fault · the baseline never clears the 0.80 precision gate on the conjunction (6/6 by the scorecard)

The flagship replay above is seed 778 (61 bad traces). The benchmark aggregates six scenarios at their own seeds 101–106 — e.g. conditional_dep at seed 101 has 89 bad traces. Counts vary with seed; the invariants do not. Full table: benchmark/REPORT.md.

the benchmark as the harness printed it The Whodunit six-scenario benchmark output
step 1 of 7
← Overview