Aurora 05 · The Unified Workflow
The payoff. Five chapters ago, Aurora Coffee Co. asked a simple question — where
should next quarter's budget go? — and the dashboard answered with correlations.
This finale runs the whole causal pipeline in one pass and ends where every analysis
should: a decision. Two plans, same budget. The dashboard's plan funds
the demand-chasing mirages; the experiment-anchored causal plan funds the brand engines.
Graded against Aurora's sealed ground truth, the gap between them is
≈ +$11.9M of revenue per year. This page mirrors
nbs/aurora/05_unified_workflow.ipynb — every headline number is the notebook's
measured output.
🧭 Before you start
Prerequisites: Workshop 00–05 (Bayesian fluency assumed) — or equivalent. · Time: ~35–45 min reading + fit time if running
You will learn:
- How the whole pipeline composes in one pass — data → causal spec → fit → experiment calibration → extensions → decision — with every chapter of the series as a rung.
- How four geo-lift results in the likelihood let the model recover Aurora's true ROAS ranking and levels, flipping the dashboard plan into its mirror image (≈ +$11.9M/yr against the sealed truth).
- Why the final step is deciding on probabilities, not points — act when the chance of being directionally right is high, experiment when it isn't.
Run it:
nbs/aurora/05_unified_workflow.ipynb on GitHub.
The pipeline, recapped
Each rung of the workflow is one chapter of the series. Nothing here is new — the point of the finale is that the pieces compose into one pass from raw data to a plan a CFO can sign.
- Look before you model. Chapter 00 showed the trap: Search has the highest spend–sales correlation (0.85) and the lowest true ROAS (0.66). Correlation rankings are the wrong input to a budget.
- State the causal model. Chapter 01 drew the DAG — unobserved demand drives both spend and sales — declared variable roles, and showed that adjustment alone cannot close the gap (the demand proxy is noisy; see also stress 03).
- Fit and read honestly. Chapter 02 fit the base MMM and read ROAS, marginal ROAS, and what-ifs with credible intervals — uncertainty as a first-class output, not a footnote.
- Calibrate with experiments. Where observational data cannot identify the answer, randomized geo-lift evidence is folded into the likelihood. Aurora ran an incrementality program covering all four channels; this notebook anchors the model to it.
- Extend where structure demands it. Chapter 03 used NestedMMM to show TV and Display work through awareness (mediation), and MultivariateMMM to test the Cold Brew cannibalization hypothesis — and found it negligible (ψ ≈ 0): the apparent dip was a shared demand wave, so the products must be planned together for that reason.
- Decide and ship. Turn corrected ROAS into a reallocation, value it, and put it in a board-ready report (Chapter 04) with the causal assumptions printed on the table.
💡 The same workflow, under fire
This is the friendly version — Aurora's world is hostile but known. The stress series runs the identical recovery workflow against a world that breaks everything at once, and measures every step: Stress 05 · The Gauntlet (Search ROAS 5.59 → 0.65 against a truth of 0.66). The scorecard for all sixteen pressure-test worlds lives at Pressure Testing.
Anchored to the experiments, the model lands on the truth
The notebook builds the final model in one cell: the base panel with the demand control,
yearly seasonality, a linear trend — and four ExperimentMeasurement objects,
one ROAS geo-lift result per channel, each entering the likelihood as
\( \hat{R}_c^{\text{exp}} \sim \mathcal{N}\!\big(\mathrm{ROAS}_c(\theta),\; 0.08^2\big) \).
The experiments don't overwrite the model; they discipline it where the observational
data is silent.
experiments = [ExperimentMeasurement(ch, window, value=float(aurora.true_roas[ch]),
se=0.08, estimand=ExperimentEstimand.ROAS)
for ch in CHANNELS]
mmm = BayesianMMM(panel, cfg, TrendConfig(type=TrendType.LINEAR), experiments=experiments)
results = mmm.fit(draws=500, tune=500, chains=2, cores=1, random_seed=0)
The result doesn't just recover the ranking (Display > TV > Search > Social) — it lands on the true ROAS values themselves. Every channel hugs the 45° line:
Measured — the notebook's recovery table, replotted: true ROAS (x) vs experiment-anchored model ROAS (y). TV 2.14 → 2.07, Search 0.66 → 0.63, Social 0.49 → 0.45, Display 2.11 → 2.08. Compare the unanchored base model in Chapter 02, where confounding inflates Search.
Two planners walk in
Same data, same budget, two scoring rules. The dashboard planner ranks channels by spend–sales correlation and moves money toward the top two — Search (r = 0.85) and Social (r = 0.69), the two channels whose spend chases demand hardest. The causal planner ranks by experiment-anchored ROAS and moves the same money toward TV (2.07) and Display (2.08). The rule is identical in both cases: shift a pool of 25% of the annual budget ($3,829k) from the bottom two channels to the top two, half the pool each.
Measured — side-by-side allocations from the notebook's plan cells: current annual spend per channel and what each plan does to it ($000s). The two plans are near mirror images: the dashboard funds precisely the channels the causal model defunds. The dashboard plan cuts Display — the quiet channel with a true ROAS of 2.11 — to $939k.
The punchline: value both plans against the truth
Because Aurora is synthetic, we can grade both plans against the sealed answer key: value each budget shift at the channels' true ROAS, \( \Delta R \;=\; \sum_c \Delta s_c \cdot \mathrm{ROAS}_c^{\text{true}} \). The notebook prints the verdict:
Shift $3,829k of budget (vs leaving it where it is):
Dashboard plan (fund ['Search', 'Social']): $-5,942k revenue
Causal plan (fund ['TV', 'Display']): $+5,942k revenue
→ Cost of chasing correlation instead of causal ROAS: ~$11,884k/yr (illustrative).
Measured — the notebook's annual-revenue-impact chart. The dashboard plan doesn't just underperform; it destroys $5.9M/yr by moving money from ROAS-2.1 channels into ROAS-0.5–0.7 channels. The causal plan earns the mirror-image +$5.9M/yr. Gap: $11,884k/yr ≈ +$11.9M/yr on the same budget.
⚠️ Read the dollar figure honestly
The notebook is explicit: this is a deliberately stylized, first-order comparison — it values the average dollar and holds saturation fixed, and a real planner wouldn't swing the budget fully inverted. The figure is an illustrative upper bound on the direction and order of magnitude, not a forecast. The lesson it carries, though, is solid and verified: chasing correlation moves money the wrong way, and the gap between the two directions is the price of getting it wrong. For saturation-aware optimization done properly, see the budget-optimization workflow.
Reallocation under uncertainty: decisions as probabilities
A Bayesian workflow never hands the CFO a bare point estimate. The plan's value inherits uncertainty from the ROAS posteriors — here disciplined by the geo-lift experiments' standard error (se = 0.08 per channel). Propagating that uncertainty through the same plan-valuation arithmetic turns "the causal plan is worth more" from an assertion into a probability statement:
Illustrative recreation (seeded) — Monte Carlo propagation of the experiments' se = 0.08 through the notebook's plan-valuation formula (4,000 draws, seeded). With experiment-grade precision on every channel, P(causal plan beats the dashboard plan) — and P(the dashboard plan loses money) —. The notebook itself reports the point values; the distributions here show why the decision is safe even before the point estimate is.
Definition: a decision-grade answer
Not "Search ROAS is 0.66" but "the probability that moving the next dollar from Search to Display earns more than it costs is ≈ 1." Decisions are made on probabilities of being directionally right, sized by what's at stake. When posteriors are wide (as in the unanchored model of Chapter 02), the right decision may be "run the experiment first" — which is exactly how Aurora's incrementality program earns its keep here.
Move the budget yourself
The notebook fixes the shifted pool at 25% of budget. Drag the slider to shift more or less, and watch the expected annual gain under each plan. This toy adds one realistic wrinkle the notebook's first-order arithmetic omits: saturating response curves (calibrated so each channel's average ROAS at current spend matches Aurora's truth), so pushing ever more money into TV/Display earns diminishing returns.
Illustrative — computed live in your browser on toy saturating response curves, not a model output. Note what saturation does to the notebook's first-order ±$5.9M: at the 25% shift the causal plan's gain flattens to roughly +$2.0M/yr (TV/Display can only absorb so much), while the dashboard plan's loss stays near −$5.2M/yr — moving money out of high-ROAS channels hurts at nearly full first-order strength. The direction never flips; the magnitude is curve-dependent. The slider caps at 35% because beyond that the dashboard plan would drive Display's spend below zero — even the toy refuses.
The closing doctrine
Why should anyone believe this model? Not because it converged, and not because it fits — eight silent failures sampled cleanly. It earns trust two ways. First, it was graded against a sealed answer key: every claim on these six pages — the mirage, the brand engines, the cannibalization verdict, the recovery — is checked against Aurora's known ground truth, the same discipline the stress harness applies to sixteen hostile worlds. Second, it folds in experiments where observational data cannot identify the answer: no amount of adjustment de-confounds demand-chasing spend (Chapter 01, stress 03) — but one geo-lift result per channel, entered into the likelihood, pins the model to randomized evidence and the truth follows.
The Aurora story, in one line:
A dashboard sees correlations and would have funded a mirage.
mmm_framework sees a causal system — confounded spend,
a brand-awareness pathway, products riding a shared demand wave — anchors it to
randomized experiments, quantifies the
uncertainty, and turns it into a defensible
reallocation worth real money.
| Chapter | What it added |
|---|---|
| 00 · The Trap | The hook: naive correlation crowns Search; true ROAS says it's a mirage. |
| 01 · Causality | Made assumptions explicit; proved adjustment isn't enough; anchored to experiments. |
| 02 · The Base MMM | ROAS & marginal ROAS with credible intervals; honest diagnostics. |
| 03 · Extended Models | TV/Display are brand engines (mediation); the cannibalization claim tested negligible (ψ ≈ 0) — a shared demand wave. |
| 04 · Reporting | A board-ready deck with the causal assumptions on the table. |
| 05 · Workflow (this page) | One pipeline → a reallocation the CFO can sign: ≈ +$11.9M/yr vs the dashboard. |
What to take away
- The pipeline composes: data → causal spec → fit → experiment calibration → extensions where structure demands → decision. One pass, every chapter a rung.
- Experiment anchoring works: with four geo-lift results in the likelihood, the model recovers the true ROAS ranking and levels (worst miss 0.07 on a scale of 2.1).
- The plans are mirror images: the dashboard funds Search/Social (r = 0.85, 0.69; true ROAS 0.66, 0.49); the causal plan funds TV/Display (true ROAS 2.14, 2.11). Same budget, opposite directions.
- The gap is the headline: ≈ +$11.9M/yr ($11,884k, measured) — an illustrative first-order upper bound, but the direction is verified against ground truth.
- Decide on probabilities, not points: propagate posterior uncertainty into the plan's value; act when the probability of being directionally right is high, experiment when it isn't.
💡 Run it yourself
This page mirrors nbs/aurora/05_unified_workflow.ipynb (committed with baked
outputs), generated by nbs/builders/build_aurora_notebooks.py on the shared
synthetic world in nbs/builders/aurora.py. The series guide is
nbs/README_aurora.md; the final board report it ships lands at
nbs/artifacts/aurora_final_plan.html. Source:
github.com/redam94/mmm-framework.