Causal Inference 10: The Closed Loop

The capstone. Every rung of the ladder, welded into one operating loop and run three full cycles on the world where this series began — unobserved_confounding, the history whose dashboard fooled the naive read in notebook 00 and whose back-door no control variable could fully close in notebook 01. Per cycle: fit on everything known so far → prioritize with the EIG × EVOI grid → design the test window → measure (the readout is generated from the sealed truth plus sampling noise at the design's SE — an honest experiment, never an oracle) → calibrate via the likelihood route, and go around again. The narrator grades every cycle against the key; the analyst inside the loop never sees it. This page mirrors nbs/causal/causal_10_the_closed_loop.ipynb.

31.5% → 17.6%
mean |attribution error| vs the sealed key, falling monotonically across three calibrated refits
−923
true KPI value of round 0's "optimal" reallocation vs simply keeping the historical plan — confidently wrong
+788
of that destroyed value recovered by round 3's plan — 68% of the way from the round-0 plan to the true optimum
350 → 237
EVPI (KPI units) — the value of what's left to learn shrinks as the loop learns: self-limiting

1 — The sealed benchmarks

Before the loop starts, the narrator computes three numbers the analyst never sees: the true media value of the historical allocation (14,885 KPI units), the true value of the best possible allocation, grid-searched on the true response curves (15,122), and the gap between them — 238 KPI units, the most any reallocation of the same total budget could win. That headroom is the yardstick every "optimal" plan below is graded against.

2 — The loop

Three cycles. Each round's test is a 10-week go-dark on the grid's top untested channel, in the next free window — staggered, clear of the previous test's cool-down (rungs 5 & 8 discipline, compressed). Readout SE: 0.09 — rung-5-grade precision.

round priority ranking test window (weeks) readout (±0.09)
0Display · TV · Social · SearchDisplay go-dark96–1060.67
1TV · Display · Social · SearchTV go-dark110–1200.86
2Display · Search · Social · TVSearch go-dark (top untested)124–1340.85
3Display · Social · TV · Search— final grading round

Measured — the notebook's loop log. Note round 2: Display tops the ranking again (its readout is a year old in loop time and the posterior still leans on it), but the one-test-per-channel-per-year rule sends the budget to the top untested channel, Search. Prioritization is a policy, not a reflex.

from mmm_framework.planning.priority import compute_experiment_priorities
from mmm_framework.planning.budget import optimize_budget

# every round, on the current calibrated fit:
grid, portfolio = compute_experiment_priorities(mmm, max_draws=120, random_seed=42)
plan = optimize_budget(mmm, total_budget=annual_media_budget, random_seed=42)
# portfolio["evpi"] is the loop's fuel gauge; the narrator grades `plan`
# against the sealed true response curves the analyst never sees

3 — Unsealing: does the posterior converge on the truth?

The analyst saw posteriors tighten. The narrator can say something stronger: they tightened toward the right answers. Mean |relative attribution error| against the sealed key falls every round — 31.5% → 25.5% → 20.9% → 17.6% — as each go-dark readout enters the model as an in-graph likelihood term (rung 6's route) and the posterior re-negotiates the channels it could never separate from the confounder observationally.

Measurednbs/artifacts/causal_10_closed_loop.json. Three experiments walk the posterior onto the key: error falls monotonically and lands at a bit over half its starting level. Not zero — three go-darks measured three window responses, and the residual lives in the parts of the curves no test has touched yet.

4 — Unsealing: do the decisions get better?

Parameters are means, not ends. Each round the analyst also asked the model for its optimal reallocation of the same total budget. The narrator evaluates every one of those plans on the true response curves — including the plan the round-0 model would have shipped.

Measured — the round-0 trap: reallocating on the confounded model would have shipped a plan worse than doing nothing — true value 13,962 vs the historical plan's 14,885, i.e. −923 KPI units, because the "optimal" plan moves budget toward the over-credited demand chasers. Three experiments later, round 3's plan is worth 14,750: +788 recovered, within 0.9% of the status quo and 68% of the way from the round-0 plan to the true optimum (15,122). Note the shape of the repair: the first readout (Display) barely moves the plan (−923 → −928); the TV and Search readouts do the heavy lifting. The loop's first job wasn't to squeeze out the 238-unit headroom — it was to stop a confident 900-unit mistake.

5 — Unsealing: is the loop self-limiting?

A healthy measurement program spends less on learning as it learns. EVPI — the total remaining value of perfect information, rung 9's ceiling — is the loop's own fuel gauge: 350 → 308 → 222 → 237 KPI units across the three cycles. When EVPI dips below the cost of the next test, the rational program pauses — until decay (rung 9) re-inflates it.

Measured — the value of what's left to learn. The wobble between rounds 2 and 3 (222 → 237) is Monte-Carlo noise on a simulated quantity plus a decision problem that reshapes with each refit; the notebook's asserts gate the trend, not strict monotonicity.

6 — The ladder, climbed

Eleven notebooks ago, Veranda Home's dashboard said everything works and the naive read doubled Social's value. Here is what each rung contributed to the loop that just ran:

rung contribution to the loop
0001knowing why the baseline is biased — and that no control fully fixes it
02estimand discipline; trust checks; "I don't know" as a model output
0304structure for what observational adjustment can't reach — funnels, latent factors
05readouts worth believing: calibrated estimators, honest SEs
06the likelihood route — experiments that commit, filed under the right estimand
07portfolios: composition, off-panel windows, tension checks
08tests chosen by anchored power and cost — including the ones not to run
09the program: EIG/EVOI, the EVPI ceiling, decay, the calendar
10the loop itself — error ↓, EVPI ↓, decisions ↑, all graded against a sealed key

7 — Three habits are the whole method

  1. Name the estimand. Every number is an answer to a counterfactual question; say which one.
  2. Buy information deliberately. Observational data is cheap and biased; experiments are costly and clean; the exchange rate is EVOI.
  3. Grade yourself whenever the truth is available — in synthetic worlds always, in production whenever an experiment lands. A measurement system that never checks itself against ground truth isn't measuring; it's narrating.

Where the loop re-enters: evidence decays, the world drifts, and next quarter's EVPI creeps back up. The loop doesn't end — that's the point. For the platform's automated version of this cycle — the T₀–T₅ measurement loop with experiment registries, calibration tracking and report generation — see the platform overview and the Experimental Measurement Lifecycle notebook series in the repository.

Check your understanding

Commit to an answer before opening each one.

The round-0 model had converged — green r-hat, clean sampling. How does a converged model ship a budget plan worth −923 KPI vs doing nothing?

Because convergence certifies the arithmetic of the model you wrote, not the causal validity of what it learned. On the unobserved_confounding history, the latent demand wave makes the chaser channels look great, so the posterior is confidently wrong — and an optimizer amplifies exactly what the model is most wrong about, moving real dollars toward the over-credited channels. The narrator can price the damage only because this world has a sealed key. In production nobody hands you that number — which is why the loop grades its decisions every time an experiment lands, and why "reallocate confidently on an uncalibrated observational fit" is the single most expensive move in this series.

EVPI falls 350 → 308 → 222, then ticks back up to 237 in round 3. Did the loop stop being self-limiting?

No. EVPI is an estimate — a Monte-Carlo average over simulated perfect readouts (120 draws per round here) — and each refit also reshapes the decision problem it's computed on, so small non-monotonic wobbles are expected. The notebook's asserts encode exactly this honesty: they gate that EVPI ends well below where it started, not that every step falls. The operational signal is the level: the value of everything still unknown is down by roughly a third after three tests, and when it drops below the cost of the next test, the program pauses until decay re-inflates it.

Why does the notebook generate readouts as "sealed truth + noise at the design's SE" instead of just handing the model the true value?

Because the claim being graded is about the procedure, not about one lucky number. An oracle readout would make calibration look better than any real experiment can — real go-darks arrive with sampling error, and each one can mislead within its SE (the loop's readouts carry ±0.09). Showing error fall 31.5% → 17.6% under honest noise is the demonstration that matters: the likelihood route weighs each readout by its stated precision (rung 6), so noisy-but-honest evidence still walks the posterior toward the key. It's the same reason rung 5 obsessed over estimator calibration — garbage SEs would poison exactly this loop.

💡 Run it yourself

This page mirrors nbs/causal/causal_10_the_closed_loop.ipynb — notebook 10 of 11 in the Causal Inference in Practice series (brand: Veranda Home; synthetic worlds with sealed answer keys; every claim graded by an assert). All numbers from nbs/artifacts/causal_10_closed_loop.json, produced by nbs/causal/causal_10_the_closed_loop.ipynb. Previous: 09 · The Measurement Program. Series complete — back to where it began: 00 · The Ladder of Evidence, the naive read this loop just un-fooled. Source: github.com/redam94/mmm-framework.