Run before the first fit. Minutes of work that flag most failures before any sample is drawn.
The decision table. Find the symptom, run the cheap check, take the pivot.
| # | Symptom | Likely cause | Cheap check | Action |
|---|---|---|---|---|
| 1 | Divergences / r-hat > 1.01 | Model fighting the data (misspecification, conflict-laden priors) — not merely a tuning problem | Where do divergent draws sit? Does the conflict survive target_accept 0.95? | Triage parameters (learning, pair plots) before re-tuning; fix the structure the conflict points at |
| 2 | One channel's HDI ≫ its estimate | Weak/flat spend (low CoV) — intercept-confounded | CoV < ~0.3? | Report the interval, or buy an experiment; no model rescues a flat series |
| 3 | Two channels' HDIs huge and their spends correlated | Collinearity — the combined effect is identified, the split isn't | Spend corr > ~0.9 / VIF > 10 | Group them, or lift-test one channel to anchor the split |
| 4 | PPC fails (shape / autocorrelation) | Missing time structure: trend break, seasonality, carryover misspecification | Plot residuals over time and by season | Richer trend / seasonality / adstock — then re-check |
| 5 | PPC passes | — it certifies fit, not attribution | (none — that's the point) | Never read a green PPC as causal validation |
| 6 | Confounding robustness value (RV) low on a channel | Estimate would be nullified by a mild confounder | Does a candidate confounder that strong exist? | Find/keep the proxy control; calibrate |
| 7 | RV high but a plausible confounder is known | RV measures required strength, not existence | corr(spend, proxy), corr(proxy, KPI) | Control the proxy regardless; RV is no green light |
| 8 | Parameter prior-dominated (contraction ≈ 0, overlap ≈ 1) | The data carries no information (flat, collinear, short window) | CoV, spend correlations, window length | Experiment, or own the prior in the writeup |
| 9 | Negative contraction + big shift | Prior–data conflict — often confounding demanding an absurd effect | corr(spend, demand proxy) | Close the back-door; if it persists, calibrate |
| 10 | Estimate piled on the sign constraint (β ≈ 0 under a positive prior) | True effect negative (cannibalization) or mediated-away | Cross-product / holdout correlations; mediator inventory | Allow the sign / model the cross-effect or mediation explicitly |
| 11 | Spend max/median ≫ ~8 | Data-entry spike or one-off burst → normalization trap | The 30-second max/median screen | Verify, winsorize/cap, percentile-normalize |
| 12 | KPI level break | Regime change a constant-coefficient MMM can't absorb | Plot the KPI; ask the business what happened | Dummy / piecewise trend; never let media absorb a break |
| 13 | Holiday-shaped residuals | Seasonality misspecification | Residuals by week-of-year | Holiday terms / higher Fourier order |
| 14 | A ROAS that would make the channel the best investment the company owns | Demand confounding — a mirage by construction | corr(spend, demand proxy); benchmark sanity | Treat as confounded until an experiment says otherwise |
| 15 | Naive corr(spend, KPI) ≫ any plausible ROAS | Spend chases demand — the dashboard mirage | The mirage table | The proxy control is mandatory; calibrate the channel |
Ordered escalation — exhaust each rung before climbing to the next.
Convergence checks the sampler, never the attribution. Green diagnostics — clean r-hat, zero divergences, a passing posterior predictive check — validate the computation, not the causal claims. Identification comes from design: declared causal roles, pre-specified structure, and experiments.