feat(diagnostics): backfill power-scaling sensitivity across the stored fits (#381) - #442
Merged
Merged
Conversation
…ed fits Power-scaling sensitivity has been wired into every family pipeline since #408 and #416, but fits stored before those merged carry no psense artefacts. Their reports therefore showed no flags because none were measured, which a reader cannot distinguish from measured-clean — the distinction #381 names as its central meta-finding. Power-scaling is importance reweighting over the draws already in hand, not resampling, so the gap closes without refitting: a trace carrying log_prior and log_likelihood can be measured after the fact, and the numbers belong to exactly the posterior the published report was written from. - Extract psense_artifacts(trace, out, var_names) from run_psense so the fit path and the backfill share one implementation. run_psense delegates and keeps its context bookkeeping; the existing stale-removal and atomic-replace tests cover the refactor. - Add scripts/regenerate_psense.py. The reported-parameter set comes from each fit's own diagnostics.csv rather than being re-derived off the spec, so the backfill covers exactly what the report shows and stays correct for a model whose spec has changed since. Fits whose trace predates the log_prior wiring are reported as needing a refit, and the script exits non-zero while any remain, so a sweep cannot look clean while leaving estimands unmeasured. - Exclude hidden output-transaction staging directories from the target set: backfilling into one writes artefacts about to be discarded, or races a live fit. Coverage goes from 86 to 170 of 193 reporting fits. The remaining 23 need a refit. No report template change is needed — _priors.qmd already renders psense_summary.csv with a fallback. Findings recorded in notes/202607261700-psense-coverage-backfill.md. The ITT suite has no headline tau in the strong-prior/weak-likelihood class; five models (lf-005, lf-006, did-001, did-003, did-013) have their one randomised causal term in it, which is the measurement #382's proposed refits were missing. Refs #381, #382 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Drafted by an LLM-based AI tool (Claude Code/Opus 5).
What and why
Power-scaling prior sensitivity has been wired into every family pipeline since #408 and #416 —
run_psenseis called from 22 fit functions covering every family. What was missing was artefacts for fits stored before those merged: their reports showed no psense table, so a reader had no way to tell "measured, no concern" from "never measured". That distinction is #381's central meta-finding, and it mattered most for exactly the deliverables the issue names as most prior-dependent.The correction that made this cheap: power-scaling is importance reweighting over the draws already in hand, not resampling. A stored trace carrying
log_priorandlog_likelihoodcan be measured after the fact, so 84 fits were backfilled with no sampling at all — and the numbers belong to precisely the posterior each published report was written from, which is a stronger guarantee than a fresh fit would give.Coverage: 86 → 170 of 193 reporting fits.
Changes
diagnostics.psense_artifacts(trace, out, var_names)extracted fromrun_psense, so the fit path and the backfill share one implementation rather than growing a second that can drift.run_psensedelegates and keeps itscontext.tablesbookkeeping; the existing stale-removal and atomic-replace tests intests/statistical_models/test_diagnostics.pycover the refactor unchanged.scripts/regenerate_psense.py, following theregenerate_key_findings.pyidiom (all/ model id / fit dir,--output-dir, plus--dry-runand--force).diagnostics.csvrather than being re-derived off the spec. That file is the fit's record of which parameters it reported, so the backfill covers exactly what the report shows, and it stays correct for a model whose spec has been edited since the fit.log_priorwiring are reported as needing a refit, and the script exits non-zero while any remain — a sweep must not look clean while leaving estimands unmeasured..<id>-<config>.staging-XXXX) are excluded from the target set: backfilling into one writes artefacts about to be discarded, or races a live fit. Found by watching the first real sweep pick one up.No report template change is needed —
docs/models/_partials/_priors.qmdalready renderspsense_summary.csvandpsense.pngwith a "No psense summary." fallback, so the backfilled artefacts appear on the next render.What the measurement found
Across 1,647 parameters in 170 fits, 46 % carry some flag. That number should not be read as "46 % of the suite is unsound" — at n ≈ 54 a weakly-informative prior will often show sensitivity, and most flagged terms are nuisance or scale parameters whose prior dependence changes no reported claim. Restricting to the 155 headline causal terms across 77 models:
itttaulfb_grp_timehsbetadidtau_t2mechbeta_mechbxdeltaThe ITT suite — the primary deliverable — has no headline
tauin the serious class. Fifteen show potential prior–data conflict, which is unsurprising and informational at this sample size; none is prior-driven. That is a real result rather than an absence, and it was unmeasured for a third of the suite until now.Five models have their one randomised causal quantity flagged
potential strong prior / weak likelihood— the class where the prior does more work than the data:lrp-rli-lf-005andlrp-rli-lf-006atb_grp_time[1](the t2 contrast; phase coords are 0-based), both floor-limited outcomes where a thin likelihood is explicable; andlrp-rli-did-001,-003,-013attau_t2. These are precisely the refits #382 proposes — this pass supplies the measurement that motivates them instead of assuming it. Separately,lrp-rli-hs-001's slab scalehs_c2reads prior 1.12 against likelihood 0.028, exactly the horseshoe dependence #381 predicted.Recorded in
notes/202607261700-psense-coverage-backfill.md.Deliberately not in scope
23 fits cannot be repaired this way. Their traces predate the
log_prior/log_likelihoodwiring: themedfamily (18 of 19), all threerli-mm,rlm-jc-001, andrlm-mm-001(the one legitimate exemption #381 already records — a non-converged posterior, where a sensitivity diagnostic would mean nothing). The rest need a--config reportingrefit, after which the fit-time wiring emits psense with no further change.prior_pushforwardis not extended. #381 also asks for it across every estimand-reporting family, but it is not the same shape of job: it pushes a treatment term through the items-scale average marginal effect and is ITT-shaped by construction, taking the arm indicator and a trial count. Families whose estimand is a curve (mech), a ranking (hs), a latent change (lcsm) or a correlation (lcf) have no single "estimand scale" to push a prior onto — adding a call site there would produce a plausible-looking artefact that means nothing. It needs a per-family decision about what the estimand-scale check is, so it is written up rather than guessed. For context, the outcome-scale prior-predictive check is already near-universal (169 of 193); the estimand-scale one sits at 51 of 193.Verification
tests/test_regenerate_psense.py).ruff check src/ scripts/ tests/,npm run format:check,npm run spellcheckall clean. One allow-list addition (reweighting), inserted in convention order as a one-line diff.Refs #381, #382
🤖 Generated with Claude Code