Skip to content

fix(imputation): FE-span snap guard on the pretrends lead model#650

Open
igerber wants to merge 1 commit into
mainfrom
fix/imputation-lead-snap
Open

fix(imputation): FE-span snap guard on the pretrends lead model#650
igerber wants to merge 1 commit into
mainfrom
fix/imputation-lead-snap

Conversation

@igerber

@igerber igerber commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adopts snap_absorbed_regressors (the two-stage FE-span snap + LSMR confirmation the absorb= estimators use) on ImputationDiD._compute_lead_coefficients — the change the demean-modernization PR deliberately deferred (TODO row). Lead indicators are the most plausible FE-spanned regressors: when a lead's calendar period contains only its cohort's rows on the untreated sample (e.g., never-treated units unobserved there), the lead collapses to a calendar-time dummy in the span of the absorbed time FE.
  • Behavior: a spanned lead now snaps to exact zero → deterministic NaN coefficient for that horizon + a cause-specific warning naming lead[h], instead of relying on the raw rank check alone — which the documented truncated-MAP-iterate exposure can defeat in slow-convergence regimes (the junk direction then perturbs the identified lead coefficients; see the REGISTRY "Absorbed Fixed Effects" contract). Identified leads are untouched: the snap is a no-op there and the full imputation suites (incl. the R-parity anchors and the replicate/bit-equality grids) pass unmodified — 198 tests.
  • New TestLeadSnapAbsorbed: (a) a spanned-lead design (never-treated span ends before a cohort pre-period) locks NaN + the cause-specific warning + finite untouched siblings; (b) a balanced design locks the no-op (no warning, leads finite). REGISTRY ImputationDiD note documents the guard.

Methodology references (required if estimator / math changes)

  • Method name(s): Borusyak-Jaravel-Spiess (2024) pretrends Test 1 (Eq. 9 lead model) — identification-hygiene guard only; coefficients of identified leads are unchanged
  • Paper / source link(s): REGISTRY.md § ImputationDiD (new "pretrends lead FE-span guard" Note) + § Absorbed Fixed Effects (snap contract)
  • Any intentional deviations from the source (and why): None — an unidentified lead was never estimable; the guard makes its NaN deterministic and explained rather than convergence-dependent.

Validation

  • Tests added/updated: tests/test_imputation.py::TestLeadSnapAbsorbed (2 behavioral tests); tests/test_imputation.py + tests/test_methodology_imputation.py + tests/test_replicate_weight_expansion.py all pass (198).
  • Backtest / simulation / notebook evidence (if applicable): N/A

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

Generated with Claude Code

The Test-1 lead indicators + covariates now route through
snap_absorbed_regressors after the within-transform (the same two-stage
snap + LSMR confirmation the absorb= estimators use). A lead whose
calendar period contains only its cohort's rows on Omega_0 collapses to
a calendar-time dummy in the span of the absorbed time FE; it now snaps
to exact zero — deterministic NaN coefficient + cause-specific warning
naming lead[h] — instead of relying on the raw rank check alone, which
the documented truncated-MAP-iterate exposure can defeat in
slow-convergence regimes. Identified leads unchanged (full suites pass
unmodified); behavioral tests lock the spanned-NaN contract and the
no-op case. REGISTRY ImputationDiD note added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Overall Assessment

✅ Looks good. No unmitigated P0/P1 findings.

Executive Summary

  • Affected method: ImputationDiD BJS pretrends Test 1 / Eq. 9 lead model.
  • The FE-span snap guard is documented in REGISTRY.md and matches the shared absorbed-FE contract.
  • The implementation uses weighted pre-demean norms and passes the same survey weights into the snap and WLS path.
  • Downstream rank-deficient OLS already returns NaN coefficients/VCV cleanly for dropped or rank-0 designs.
  • I could not run the focused tests because pytest is not installed in this environment.

Methodology

  • Severity: P3 informational
    Impact: The PR changes pretrend lead handling, but the behavior is explicitly documented as a guard for FE-spanned, unidentified leads. It routes lead indicators and covariates through snap_absorbed_regressors after within-transform, consistent with the absorbed-FE registry contract.
    Concrete fix: None required. See docs/methodology/REGISTRY.md:L1566 and diff_diff/imputation.py:L2297-L2325.

Code Quality

No findings.

Performance

No findings. The added LSMR confirmation is behind the existing two-stage snap screen, so it is scoped to near-absorbed candidate regressors rather than every column unconditionally; see diff_diff/utils.py:L3241-L3267.

Maintainability

No findings.

Tech Debt

No findings. The relevant deferred TODO row has been removed after implementation; remaining TODO entries do not track this specific gap. See TODO.md:L45-L58.

Security

No findings.

Documentation/Tests

  • Severity: P3
    Impact: The spanned-lead test asserts effect and se are NaN, but it does not explicitly assert the full inference tuple (t_stat, p_value, conf_int) is NaN, despite the test comment describing full inference behavior. The implementation does use safe_inference, so this is a test-strength issue, not a correctness blocker.
    Concrete fix: In tests/test_imputation.py:L3103-L3105, also assert t_stat, p_value, and both CI endpoints are NaN, or reuse the existing assert_nan_inference helper.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-ci Triggers CI test workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant