feat: dedicated practitioner_next_steps() handler for ChangesInChanges/QDiD#692
Conversation
…s/QDiD
Replace the _handle_generic fallback for ChangesInChangesResults (shared
by CiC and QDiD via the QDiDResults alias) with a branch-aware handler
keyed on the estimator field and covariate status:
- Distributional identification guidance: Step 2 swaps the generic
parallel-trends-variant text for the Athey-Imbens assumptions
(monotone outcome model, time-invariance of U, support; QDiD's
stronger four-cell restriction), and Step 3 frames
check_parallel_trends() as a necessary-but-insufficient means screen.
- Branch-specific diagnostics guidance: eq. 17 interior range
(unconditional CiC only), conditional-envelope support (covariate CiC
only), the p. 447 CiC-over-QDiD recommendation with the footnote-21
monotonicity caveat (QDiD), two-pre-period distributional placebo,
and sup-t uniform-band reading of the QTE profile.
- Specification-preserving refit snippets (_cic_fit_snippet /
_did_anchor_snippet): panel=True + unit= and the fit's actual
covariate list are mirrored; the with/without-covariates comparison
is labeled unconditional-by-design; inference settings are
normalized to defaults and the snippet says so.
- The mean-DiD anchor splits on covariate status: the p. 447
population-equivalence claim stays unconditional-only; covariate
QDiD gets a descriptive covariate-adjusted anchor.
- Bootstrap-health warnings (_cic_bootstrap_warnings): inference
disabled at n_bootstrap=0; replicate-failure share above the 5%
fit-time materiality threshold. Per-instance display names via
_estimator_display ("ChangesInChanges (CiC)" vs "QDiD").
Guidance-only change - no estimation or inference behavior touched.
Closes the TODO practitioner-handler row (#682). 30 new tests across
TestCiCHandler and TestCiCHandlerSpecificationPropagation, including a
panel+covariates combined fixture and semantic snippet assertions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W75ea3yFYQbjVhB2evhRiZ
PR Review: feat dedicated practitioner_next_steps() handler for ChangesInChanges/QDiDOverall AssessmentExecutive Summary
MethodologyFinding 1Severity: P1 Code QualityFinding 2Severity: P3 PerformanceNo findings. MaintainabilityNo findings. Tech DebtNo findings. The removed TODO row is appropriate because the handler is implemented in this PR. SecurityNo findings. Documentation/TestsFinding 3Severity: P3 Path To Approval
|
…eed comment - P1: CiC Step 3 no longer frames check_parallel_trends() on means as a necessary screen - under a nonlinear outcome model, group mean trends need not be parallel in a valid CiC design, so a mean-trend break is NOT by itself evidence against CiC; the means check is now a labeled descriptive anchor and the two-pre-period distributional placebo is pointed to as the relevant falsification. QDiD keeps a meaningful means screen (its additive quantile model moves cell means additively; population mean equivalence with DiD). Lock tests both ways. - P3: _cic_bootstrap_warnings accepts numpy scalars and warns on n_bootstrap=1 (cannot clear the >= 2 valid-replicate SE gate). - P3: snippet comment no longer calls seed=42 a default (seed default is None; 42 is illustrative). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W75ea3yFYQbjVhB2evhRiZ
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyNo findings. The new CiC Step 3 correctly says mean parallel trends are not required and a mean-trend break is not by itself evidence against CiC ( QDiD guidance is also scoped appropriately: stronger all-four-cells unobservable restriction, p. 447 CiC-over-QDiD recommendation, unconditional-only population mean equivalence, and covariate QDiD as descriptive mean-DiD anchoring only ( Code QualityNo findings. The dedicated handler dispatch and per-instance display name are narrowly scoped and defensive for mock/missing PerformanceNo findings. The change is guidance-only. Added tests include covariate CiC/QDiD fits with MaintainabilityNo findings. The handler keeps branch-specific text in one place and adds targeted tests for dispatch, branch scope, snippets, and prior-review locks ( Tech DebtNo findings. Removing the TODO row for the dedicated practitioner handler is appropriate because the handler is implemented in this PR ( SecurityNo findings. No secrets or risky external inputs were introduced. Documentation/TestsNo PR findings. Validation note: I attempted |
Summary
_handle_genericfallback forChangesInChangesResults(shared by CiC and QDiD via theQDiDResultsalias) with a dedicated handler branching on theestimatorfield and covariate status - the generic fallback recommended mean parallel-trends checks and HonestDiD, neither of which fits a distributional 2x2 estimator with no event-study effects.check_parallel_trends()as a necessary-but-insufficient means screen._cic_fit_snippet/_did_anchor_snippet:panel=True+unit=(bootstrap scheme) and the actual covariate list are mirrored; inference settings are normalized to defaults and the snippet says so._cic_bootstrap_warnings): inference disabled atn_bootstrap=0; replicate-failure share above the 5% fit-time materiality threshold (mirrorswarn_bootstrap_failure_rate). Per-instance display names for the shared results class ("ChangesInChanges (CiC)" vs "QDiD").[Unreleased]bullet,docs/doc-deps.yamlpractitioner note extended.Methodology references (required if estimator / math changes)
Validation
tests/test_practitioner.py- 30 new tests (TestCiCHandler,TestCiCHandlerSpecificationPropagation): dispatch + per-instance display names, branch-content locks (interior-range/envelope/prefer-CiC step scoping), bare-mock unknown-kind default lock, snippetast.parsesweeps plus semantic assertions that snippets mirrorcovariates/panel/unit(incl. a panel+covariates combined fixture), bootstrap-health warning paths (n_bootstrap=0, 50% failure, 2% below-threshold negative), NaN-ATT warning,completed_stepsfiltering. Fixture fits verified warning-free underwarnings.simplefilter('error').Security / privacy
Generated with Claude Code