Studio 5 — Develop the pathway
Studio 5
Commit to the research pathway your question and your licence actually support, and say what that pathway can and cannot establish.
Studio 5
This studio closes with Milestone 5: Your pathway, declared, a short chapter of its own after the lessons. What it asks you to produce. Research Contract v1: objective by target and reach by data strategy by warrant, with the pathway declared and its limits written before any result exists.
Studio 5 · Road map
Lesson 1 of this studio · Chapter 14
which group your data can honestly speak for
Chapter 14
Which group your observed data can honestly speak for, and where exactly the line falls past which a description of your sample is no longer allowed to become a claim about a wider population. You draw that line, and you write down the sentence on the far side of it that you refuse to say.
Chapter 14 · Key terms
Observational descriptive research
you sample and measure without assigning anyone a condition, then summarize what you found.
Undercoverage
people who belong in your target and never make the list.
Overcoverage
records on the list for people outside your target altogether.
Selection
any process that decides who lands in your data instead of chance alone.
Chapter 14 · Why this decision matters
“Which group did your procedure actually reach?”
Chapter 14 · Why this decision matters
Chapter 14 · The concept
Chapter 14 · The concept
Target population
everyone your question is about: every customer a streaming service will ever bill
Accessible population
the part of the target you could reach in principle, given time and cost
Sampling frame
the concrete list you actually draw from, such as the current subscriber list
Sample
the customers you actually survey, drawn from the frame and no wider
Chapter 14 · The concept

The four groups a description has to keep straight. The frame crosses the edge of your target population, which is where the three coverage errors live: people inside the target who never reach the list, records on the list that fall outside the target altogether, and one unit listed twice.
Chapter 14 · The concept
Selection
any process that decides who lands in your data instead of chance alone
Nonresponse
the units you drew who never gave you data
Chapter 14 · The concept
Chapter 14 · A worked example
Chapter 14 · A worked example
Chapter 14 · A worked example
among customers still subscribed, the billing-error rate is 2%
Chapter 14 · A worked example
import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
n = 50_000
# Everyone the service bills. Wrong charges are what we want to count.
wrong_charge = rng.random(n) < 0.060
# Customers hit by a wrong charge quit far more often — and quitters leave the
# subscriber list, which is the frame the survey draws from.
quit = rng.random(n) < np.where(wrong_charge, 0.70, 0.09)
target_rate = wrong_charge.mean()
frame_rate = wrong_charge[~quit].mean()
print(f"target population (everyone billed) : {target_rate*100:.1f}%")
print(f"sampling frame (current subscribers) : {frame_rate*100:.1f}%")
print(f"customers who left the frame : {quit.mean()*100:.0f}%")
print("\nthe survey can only ever see the second number. no sample size,")
print("and no random draw from that list, closes the gap to the first")Chapter 14 · A seeded simulation
import numpy as np
import matplotlib.pyplot as plt
SEED = 464
rng = np.random.default_rng(SEED)
population = np.clip(rng.normal(49, 17, size=100_000), 18, 90)
truth = population.mean()
random_means = [rng.choice(population, 500, replace=False).mean()
for _ in range(10)]
weights = np.exp(-(population - 25) ** 2 / (2 * 12 ** 2)) # younger = likelier
convenience = rng.choice(population, 500, replace=False,
p=weights / weights.sum())
fig, ax = plt.subplots(figsize=(7.6, 2.9))
ax.scatter(random_means, np.ones(10), s=60, color="#2a78d6", zorder=3)
ax.scatter([convenience.mean()], [0], s=60, color="#eb6834", zorder=3)
ax.axvline(truth, color="#333333", ls="--", lw=1.2)
ax.text(truth + .5, 1.55, f"true mean = {truth:.1f}", color="#333333",
fontsize=9)
ax.set_yticks([0, 1], ["Convenience\nchannel (n = 500)",
"Random samples\n(n = 500)"], fontsize=9)
ax.set_xlabel("Sample mean age (years)")
ax.set_ylim(-.7, 1.9)
plt.show()Chapter 14 · A seeded simulation

Ten random sample means cluster around the true mean age; the single convenience-channel mean sits far below it.
Chapter 14
Where the tool failed
You ask a chatbot: “My survey of current subscribers shows a 2% billing-error rate on a large panel. Can I report the service’s billing-error rate as 2%?” It answers with full confidence: “Yes. With a sample that large, your estimate is precise and reliable.” This is wrong in two named ways at once. It commits a silent scope change, quietly upgrading “current subscribers” to “the service’s customers,” and it leans on the fallacy that a big sample cures a tilted one.
Chapter 14 · An AI failure case
Chapter 14
This stays yours
Three decisions stay yours alone. First, which population your project is really about: the AI does not know your question’s intent. Second, which frame you can honestly reach, and therefore the coverage gap you must disclose. Third, the boundary line itself: the exact sentence where your description stops and the population claim you refuse to make. You may ask an AI to attack that line, never to draw it for you.
Chapter 14 · Your move
Work it in the companion notebook with Chapter 14 open beside it. Log every delegation in your AI Research Ledger.
Lesson 2 of this studio · Chapter 15
whether your comparison has earned the word because
Chapter 15
Whether your observational comparison has earned the word because or must stop at associated with. You settle it by naming the one confounder the comparison turns on and writing the identification argument that closes the back door, or by admitting in writing that you have none.
Chapter 15 · Why this decision matters
Chapter 15 · Why this decision matters
Chapter 15 · The concept
The conditions you compare were set by the world rather than by you, so a causal reading has to be argued rather than assumed (Blair et al. 2023).
Counterfactual
the outcome that would have happened under the choice that was not made
Confounder
a third factor that pushes on both who gets the treatment and the outcome
Chapter 15 · The concept
Chapter 15 · The concept
Selection on observables
identifying an effect by adjusting for a set of measured confounders sufficient to block every back-door path
Natural experiment
a situation where a chance-like force outside anyone’s control decided who got treated, making assignment as-if random
Chapter 15 · The concept
Chapter 15 · The concept
Chapter 15 · The concept
Chapter 15 · A worked example
Chapter 15 · A worked example
Chapter 15 · A worked example
Chapter 15 · A worked example
import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
n = 10_000
# Diet drives BOTH the microbe and leanness. The microbe itself does nothing.
high_fiber = rng.random(n) < 0.5
microbe = rng.random(n) < np.where(high_fiber, 0.75, 0.25)
leanness = 20 - 3.0 * high_fiber + rng.normal(0, 1.5, size=n) # lower = leaner
df = pd.DataFrame({"high_fiber": high_fiber, "microbe": microbe,
"leanness": leanness})
naive = df[df.microbe].leanness.mean() - df[~df.microbe].leanness.mean()
within = (df.groupby("high_fiber")
.apply(lambda g: g[g.microbe].leanness.mean()
- g[~g.microbe].leanness.mean(), include_groups=False))
print(f"true microbe effect : {0.0:+.2f}")
print(f"naive carrier-vs-noncarrier gap : {naive:+.2f}")
print(f"within high-fiber mice : {within[True]:+.2f}")
print(f"within low-fiber mice : {within[False]:+.2f}")
print(f"average of the two within-diet gaps: {within.mean():+.2f}")
print("\nthe naive gap is the DIET gap wearing the microbe's name;")
print("ten thousand mice make it precise, not true")Chapter 15
Where the tool failed
You paste your mouse comparison into a chatbot and ask, “Is this a natural experiment, and can I say the microbe causes leanness?” It answers with a confident, well-formatted yes: your setup “resembles a natural experiment,” the difference “can be read causally,” the write-up “looks rigorous.” This is two named failures at once: plausible-but-wrong-method, attaching a design label to a comparison whose key assumption plainly fails, and silent scope change, upgrading associated with to because while sounding like it settled the question. Fluency is not evidence. You catch it by refusing the label and asking the one question the model cannot answer for you: how was treatment actually assigned? No lottery, no cutoff, no outside force decided which mice carried the microbe. They sorted themselves by diet and behavior, so there is no as-if-random assignment, the back door stays open, and because is not earned. Say associated with, and log why.
Chapter 15
This stays yours
Three decisions stay yours alone. Whether your design identifies a causal effect is a judgment about how treatment was really assigned, and only you know that. Which confounder most threatens your comparison turns on the mechanism, which a model cannot see in your data. And whether your finding earns because or must stop at associated with is the whole skill of this chapter. A tool will happily call your comparison a “natural experiment” because you typed the words. Earning because is your signature, not the model’s.
Chapter 15 · Your move
Work it in the companion notebook with Chapter 15 open beside it. Log every delegation in your AI Research Ledger.
Lesson 3 of this studio · Chapter 16
whether the number your randomized study reports is a property the world already has, or an effect you would go out and cause
Chapter 16
A coin flip does not decide what kind of question you asked. You decide whether your randomized study is measuring a property the world already has or testing an intervention you would deploy, and you name the artifacts your measurement still has to guard against before the number means anything.
Chapter 16 · Why this decision matters
“You randomized,” they say, “so you wrote causes. Show me the question first.”
Chapter 16 · Why this decision matters
Chapter 16 · The concept
Chapter 16 · The concept
A latent characteristic: how much a search interface’s position pulls clicks, apart from how good each result is (Joachims et al. 2005).
Latent characteristic
a real property you cannot read off directly, so a design has to reveal it
Controlled stimulus
a version of a prompt, item, or setting that you fix on purpose and assign by chance
Experiment as a measurement instrument
using randomly assigned controlled stimuli to expose a latent characteristic and report it as a plain description
Chapter 16 · The concept
Chapter 16 · The concept
A demand effect: users who notice they are in a study click more carefully than they would at home (Orne 1962).
Demand effect
when the people being measured guess what you are looking for and drift toward it
Instrument effect
when the measuring device or wording moves the reading on its own
Construct validity
the degree to which your number estimates the concept you meant rather than something adjacent
Chapter 16 · A worked example
Chapter 16 · A worked example
Chapter 16 · A worked example
The redesign renders every slot identically and logs impressions server-side.
Chapter 16 · A worked example
import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
n_sessions = 12_000
# The probe track is identical every time; only its slot is dealt by chance.
slot = rng.integers(1, 7, size=n_sessions)
attention = {1: 0.150, 2: 0.104, 3: 0.081, 4: 0.066, 5: 0.055, 6: 0.043}
played = rng.random(n_sessions) < np.array([attention[s] for s in slot])
by_slot = (pd.DataFrame({"slot": slot, "played": played})
.groupby("slot")["played"].agg(sessions="size", play_rate="mean"))
by_slot["play_rate"] = (by_slot.play_rate * 100).round(1)
print(by_slot.to_string())
r1, r5 = by_slot.play_rate[1], by_slot.play_rate[5]
print(f"\nslot 1 vs slot 5 : {r1/r5:.1f}x, at identical audio and artwork")
print("descriptive, for these sessions. it does not say what promoting a")
print("DIFFERENT track to slot 1 would do")Chapter 16
Where the tool failed
You paste your attention-curve result into an AI tool and ask it to write the findings sentence. It returns something fluent: “Moving tracks to the top slot causes a 3x lift in plays, so the team should promote its best matches to slot 1.” It reads like a win, but it is a silent scope change: you asked how attention is distributed, and the tool quietly answered a causal, deployable question about what promoting would do. You catch it by laying its sentence beside yours, word for word. Yours asked how much position pulls; its answer asked what to do. The tells are “causes,” “should,” and the imagined “lift” from an intervention you never ran. Reject it, and rewrite the claim bounded to the units you observed.
Chapter 16
This stays yours
Three calls stay yours. Whether your inquiry is descriptive or causal, because the kind lives in your question’s words and no tool can read your intent. Whether the design actually measures your construct rather than something adjacent, which is a judgment about meaning, not about code. And which artifacts you are willing to stake your name on having ruled out. An AI partner can draft, locate, and attack. You declare and defend.
Chapter 16 · Your move
Work it in the companion notebook with Chapter 16 open beside it. Log every delegation in your AI Research Ledger.
Lesson 4 of this studio · Chapter 17
whether you are forecasting unseen cases, and if you are, what score you would accept as honest
Chapter 17
Decide whether your question is truly a forecast about cases nobody has seen yet, and if it is, sign a four-part contract before you fit anything: target, baseline, split, metric, plus a leakage check. That contract is what lets you defend a modest honest score and say out loud where your forecast stops working.
Chapter 17 · Key terms
Model-selection bias
that flattery, the gap between a winner’s score on the data that crowned it and its honest score on data it has never met (Varma & Simon 2006).
Data leakage
information reaching your model, or your choice of model, that would not be available at the real forecast moment (Kaufman et al. 2011).
Chapter 17 · Why this decision matters
Chapter 17 · Why this decision matters
Ninety-four percent compared to WHAT? Most weeks this lake is fine, so if I just say ‘no bloom’ every single week I am already right about eighty-five percent of the time.
Chapter 17 · The concept
Prediction
a best guess about a case whose outcome you cannot see yet
Reach
the cases a claim is meant to cover
Generalization
reach from your sample to a broader population
Chapter 17 · The concept
bloom_next_week.Chapter 17 · The concept
Chapter 17 · The concept
Chapter 17 · The concept
Chapter 17 · A worked example
bloom_next_week, one if the lake exceeds the advisory threshold.Chapter 17 · A worked example
Chapter 17 · A worked example
chlorophyll_a, the pigment reading. The score leaps to 0.97.Chapter 17 · A worked example
temp and rain are known a week early. chl is measured during the bloom.import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
# 120 weekly samples across three summers, 40 weeks each, six bloom weeks per
# summer (15%). The calendar order is the design: it decides the three roles.
summer = np.repeat([1, 2, 3], 40)
bloom = np.zeros(120, dtype=bool)
for s in (1, 2, 3):
weeks = np.where(summer == s)[0]
bloom[rng.choice(weeks, 6, replace=False)] = True
temp = 22 + 3.0 * bloom + rng.normal(0, 2.0, size=120) # known a week early
rain = 20 + 4.0 * bloom + rng.normal(0, 6.0, size=120) # known a week early
chl = 5 + 30 * bloom + rng.normal(0, 8.0, size=120) # measured DURING it
train, locked = summer == 1, summer == 3
z = lambda v: (v - v[train].mean()) / v[train].std()
acc = lambda pred: float((pred == bloom[locked]).mean())
baseline = acc(np.zeros(locked.sum(), dtype=bool)) # always "no bloom"
score = 0.55 * z(temp) + 0.35 * z(rain)
honest = acc(score[locked] > np.quantile(score[train], 0.85))
leaky = acc(chl[locked] > np.quantile(chl[train], 0.85))
print(f"baseline, always say 'no bloom' : {baseline:.2f}")
print(f"honest features, locked summer : {honest:.2f}")
print(f"with chlorophyll_a added : {leaky:.2f}")
print("\nthe third number is not skill. chlorophyll is measured during the")
print("bloom you claim to forecast, so it cannot exist at prediction time")Chapter 17 · A seeded simulation
import numpy as np
SEED = 464
degrees = np.arange(1, 13)
def world(rng, n):
x = rng.uniform(-3, 3, n)
return x, np.sin(1.5 * x) + rng.normal(0, .35, n)
def rmse(coefs, data):
x, y = data
return np.sqrt(np.mean((np.polyval(coefs, x) - y) ** 2))
# Left panel: one world, the three roles.
rng = np.random.default_rng(SEED)
training, selection, final = (world(rng, 40) for _ in range(3))
fits = [np.polyfit(training[0], training[1], d) for d in degrees]
sel_err = np.array([rmse(c, selection) for c in fits])
chosen = int(np.argmin(sel_err)) # chosen on SELECTION, never final
print("chosen degree:", degrees[chosen])
print("selection score that chose it:", round(float(sel_err[chosen]), 3))
print("its one final-holdout score: ", round(rmse(fits[chosen], final), 3))
# Right panel: optimism in expectation. Restart the seed so this study
# reproduces on its own.
rng = np.random.default_rng(SEED)
gaps = []
for _ in range(500):
tr, sel, big = world(rng, 40), world(rng, 40), world(rng, 10_000)
f = [np.polyfit(tr[0], tr[1], d) for d in degrees]
se = np.array([rmse(c, sel) for c in f])
pick = int(np.argmin(se))
gaps.append(rmse(f[pick], big) - se[pick]) # true error minus its crown
gaps = np.asarray(gaps)
print("mean optimism:", round(float(gaps.mean()), 3), "RMSE")
print("median:", round(float(np.median(gaps)), 3))
print("winner truly worse than its crowning score in",
round(100 * float((gaps > 0).mean()), 1), "% of worlds")
print("largest single gap:", round(float(gaps.max()), 2))Chapter 17 · A seeded simulation

Left: training error keeps falling while selection error turns up after degree six, where the model is chosen; a diamond marks its one final-holdout score. Right: across 500 worlds, the winner’s true error exceeds its crowning score on average, with a long right tail.
Chapter 17 · A seeded simulation
Chapter 17
Where the tool failed
You paste your feature list and ask an AI partner which features are safe to use. It answers with confidence: “chlorophyll_a is your strongest predictor, keep it.” The reasoning sounds airtight, because chlorophyll correlates almost perfectly with blooms. That near-perfect correlation is exactly the tell. This is the plausible-but-wrong-method failure: the tool ranked the feature on how well it fits the past, not on whether it could exist in time for a forecast. You catch it with one question statistics alone cannot answer. When is chlorophyll measured? During the bloom, so no forecast made a week earlier could have it. Drop it, watch the score fall back to 0.88, and keep the honest model.
Chapter 17
This stays yours
Four calls stay yours. The target: what you forecast and why it matters. The baseline: the honest rule the model must beat. The timing of every feature: whether a value would truly exist at the forecast moment, which only you, who knows your data’s timeline, can settle. And the verdict on whether your project should predict at all. A tool that has never seen your timeline cannot make these calls for you.
Chapter 17 · Your move
Work it in the companion notebook with Chapter 17 open beside it. Log every delegation in your AI Research Ledger.
Lesson 5 of this studio · Chapter 18
whether your difference in outcomes has earned the word “because”, and whose effect that difference actually describes
Chapter 18
Decide whether the way treatment was assigned in your study lets you read a plain difference in outcomes as a cause, and then name exactly which quantity that difference estimates and whose effect it is. Nobody else can confirm how you sorted your units, so no tool gets to declare that your design proves cause.
Chapter 18 · Key terms
Attrition
a unit’s outcome going missing after assignment.
Noncompliance
assigned units that never take the treatment, like patients who changed numbers and never got a text.
Spillover
treatment reaching the control arm, like a texted patient reminding an untreated friend.
Chapter 18 · Why this decision matters
“Better after treatment” is not “the treatment worked.” Show me that chance, not the kind of patient, decided who got treated, and tell me exactly who your number speaks for.
Chapter 18 · Why this decision matters
Chapter 18 · The concept
Causal question
a question about what would change if you intervened rather than what merely goes together
Potential outcome Y(1)
what that unit would show with the treatment
Potential outcome Y(0)
what the same unit would show without it
Chapter 18 · The concept
Chapter 18 · The concept
Chapter 18 · The concept
Chapter 18 · A worked example
Chapter 18 · A worked example
Chapter 18 · A worked example
Chapter 18 · A worked example
import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
n_arm = 400
arms = {}
for arm, refills, dropout_if_disorganized in (("reminder", 284, 0.16),
("control", 248, 0.38)):
organized = rng.random(n_arm) < 0.5 # a trait you never observe
priority = organized + rng.normal(0, 0.6, size=n_arm)
refill = np.zeros(n_arm, dtype=bool)
refill[np.argsort(-priority)[:refills]] = True
# attrition the TREATMENT changes: reminders keep disorganized patients in
dropped = rng.random(n_arm) < np.where(organized, 0.04,
dropout_if_disorganized)
arms[arm] = {"refill": refill, "seen": ~dropped, "organized": organized}
r, c = arms["reminder"], arms["control"]
print(f"reminder arm, everyone randomized : {r['refill'].mean()*100:.0f}%")
print(f"control arm, everyone randomized : {c['refill'].mean()*100:.0f}%")
print(f"gap : "
f"{(r['refill'].mean() - c['refill'].mean())*100:+.0f} pp")
cc = r["refill"][r["seen"]].mean() - c["refill"][c["seen"]].mean()
print(f"\ngap among those still measurable : {cc*100:+.0f} pp")
print(f"retention : {r['seen'].mean()*100:.0f}% vs "
f"{c['seen'].mean()*100:.0f}%")
print(f"organized share of those seen : "
f"{r['organized'][r['seen']].mean()*100:.0f}% vs "
f"{c['organized'][c['seen']].mean()*100:.0f}%")
print("\nthe last line is the problem: the two arms no longer describe the")
print("same kind of patient, so the second gap is not an effect on anyone")Chapter 18 · A seeded simulation
import numpy as np
import matplotlib.pyplot as plt
SEED = 464
rng = np.random.default_rng(SEED)
n, tau = 200, 5.0 # true effect: +5 points
y0 = np.clip(rng.normal(70, 12, n), 20, 95) # refill rate, no reminder
y1 = y0 + tau # exactly +5 for every patient
estimates = []
for _ in range(2000):
treated = rng.permutation(n) < n // 2 # a fresh coin-flip assignment
estimates.append(y1[treated].mean() - y0[~treated].mean())
estimates = np.array(estimates)
fig, ax = plt.subplots(figsize=(7.6, 3.2))
ax.hist(estimates, bins=40, color="#2a78d6", edgecolor="white", lw=.4)
ax.axvline(tau, color="#333333", ls="--", lw=1.2)
ax.text(.02, .92, f"true effect = {tau:.1f} pp", color="#333333",
fontsize=9, transform=ax.transAxes)
ax.text(.02, .82, f"mean of estimates = {estimates.mean():.1f} pp",
color="#2a78d6", fontsize=9, transform=ax.transAxes)
ax.set_xlabel("Estimated effect of the reminder (percentage points)")
ax.set_ylabel("Number of re-randomizations")
plt.show()Chapter 18 · A seeded simulation

The histogram of 2,000 re-randomized estimates centers on the true 5-point effect.
Chapter 18 · What attrition does to that promise
import numpy as np
import matplotlib.pyplot as plt
SEED = 464
rng = np.random.default_rng(SEED)
N, reps, tau = 2000, 2000, 5.0
health = rng.normal(size=N)
y0 = 60 + 10 * health + rng.normal(0, 5, size=N) # refill rate, no reminder
y1 = y0 + tau # ... with the reminder
r0 = health > -0.8 # still measurable under control
r1 = health > -0.2 # ... under the reminder: the sickest drop out
contrasts = []
for _ in range(reps):
z = rng.permutation(N) < N // 2 # an honest coin flip
y = np.where(z, y1, y0)
retained = np.where(z, r1, r0)
contrasts.append(y[z & retained].mean() - y[(~z) & retained].mean())
contrasts = np.asarray(contrasts)
print("true effect for everyone enrolled:", np.mean(y1 - y0))
print("average complete-case contrast: ", contrasts.mean().round(2))Chapter 18 · What attrition does to that promise

The complete-case contrasts pile up near 8 points, far from the true 5-point effect.
Chapter 18
Where the tool failed
You paste your trial into the tool and it reports, with full confidence, “the reminder raised on-time refills by 12 points, and the effect is significant.” The code runs without a single error. Here is the trap. A third of the reminder arm, the patients whose blood pressure was worst, stopped answering follow-up and left the study, so the tool computed the gap among the patients who stayed and labeled it “the effect of the reminder.” That is a silent scope change. The number is a complete-case contrast: it is not the effect for everyone you enrolled, and dropping the sickest patients from one arm pushed it up.
Chapter 18 · An AI failure case
Chapter 18
This stays yours
Three calls never leave your hands. You decide whether random assignment actually happened in your study, because a tool cannot see how you sorted the arms. You decide which quantity your number estimates and for whom, the effect for everyone enrolled, or whether attrition has left you without a causal number at all until you state an assumption and test how much it matters. And you decide whether the effect is large enough to act on, and whether it is even ethical to randomize real patients and withhold something from the control arm. You own the final sentence, its boundary, and its uncertainty.
Chapter 18 · Your move
Work it in the companion notebook with Chapter 18 open beside it. Log every delegation in your AI Research Ledger.
Lesson 6 of this studio · Chapter 19
whether the extra moving piece in your design earns its place, or just adds an error you cannot account for
Chapter 19
When your answer has to be stitched together from several measurements or stages, decide whether the combined design is still one aligned thing you can diagnose before you run it. Stacking careful pieces does not automatically produce a careful result, and deciding to cut a piece you cannot characterize is as much a design decision as adding one.
Chapter 19 · Why this decision matters
A precise result built on one uncharacterized step is a precise wrong answer.
Chapter 19 · Why this decision matters
Chapter 19 · The concept
Blair, Cooper, Coppock and Humphreys named the four (Blair et al. 2019); RDSS develops them (Blair et al. 2023).
Chapter 19 · The concept
Complex, or hybrid, design
a research design whose four MIDA parts have more than one moving piece: several measurements, stages, or sub-questions stitched into a single answer
Alignment
all four parts point at the same single quantity
Error propagation
each measured input’s uncertainty flows into the final answer, sometimes amplified
Chapter 19 · The concept
Chapter 19 · The concept
Chapter 19 · The concept
Bias
a systematic tilt that more data does not shrink
Variance
run-to-run wobble that more data does shrink
Chapter 19 · A worked example
share = its revenue / total market revenue.Chapter 19 · A worked example
Chapter 19 · A worked example
Chapter 19 · A worked example
uncovered = 22.0 is real sales the survey never reaches.import numpy as np, pandas as pd
SEED = 464
rng = np.random.default_rng(SEED)
months = 12
chains = {"chain A": 38.5, "chain B": 29.5, "chain C": 21.0, "chain D": 13.5}
uncovered = 22.0 # independents + online delivery: real sales, never surveyed
avg = {c: rng.normal(rev, rev * 0.09, size=months).mean()
for c, rev in chains.items()}
surveyed_total = sum(avg.values())
def index(total):
return sum((rev / total * 100) ** 2 for rev in avg.values())
print(pd.DataFrame({"avg monthly revenue ($m)":
pd.Series(avg).round(1)}).to_string())
print(f"\nindex using the SURVEYED total : {index(surveyed_total):,.0f}")
print(f"index using the true total : {index(surveyed_total + uncovered):,.0f}")
print("\naveraging twelve months bought precision, not correctness. the")
print("denominator is missing sales, so every share, and the index, tilts up")Chapter 19
Where the tool failed
You paste your revenue table into an AI tool and ask for the concentration index. It returns, with total confidence, index = 2,862 ± 18. The number looks plausible for a market with a few big chains, the interval is tiny, and the code ran without an error. It is wrong. The tool propagated only the month-to-month revenue variance, the part you already crushed by averaging twelve months, and never modeled the systematic bias from a denominator that leaves out every seller your survey does not reach. That tight ± 18 describes wobble it could see and stays silent about the tilt it could not.
Chapter 19 · An AI failure case
Chapter 19
This stays yours
Three calls stay yours. You decide what single quantity your inquiry names (which market, over what geography and time window), whether each sub-design actually targets that same quantity so the parts align, and whether to trust a combined number your diagnosis says a systematic error dominates. A tool can propagate the arithmetic, but it cannot decide that a precise number is a correct one. That judgment is the whole point of the chapter, and it is yours.
Chapter 19 · Your move
Work it in the companion notebook with Chapter 19 open beside it. Log every delegation in your AI Research Ledger.
Studio 5 closes here
What the lessons handed you becomes one artifact you can defend.
Milestone 5
The artifact
What this milestone produces. Research Contract v1: objective by target and reach by data strategy by warrant, with the pathway declared and its limits written before any result exists.
Milestone 5 · Check before you start
Milestone 5 · In the studio
Milestone 5 · Every studio, these four
Ethics, permissions, and data exposure
A change of pathway can change your permission status; recheck rather than assume.
Evidence, provenance, and reproducibility
Name the prior work that used this pathway on a comparable question.
AI activity, verification, and human decisions
Ask an assistant to argue for a different pathway, then answer the argument yourself.
Uncertainty, claim boundary, and revision history
Different pathways carry different sources of uncertainty; name yours here.
Milestone 5
How the record works
Your milestone artifact is a dated, numbered version with the reason for the version attached. When later evidence changes it, you write the next version rather than editing the last one, because the sequence of changes is itself part of your research record.
AI is your arm and your research assistant, not your brain.
AI can review AI, and a second model is a real auditor of the first. The last decision is always human.

EDR|AI · Studio 5 — Develop the pathway