Skip to content

Add UserWarning emissions for 8 silent operations#256

Merged
igerber merged 6 commits into
mainfrom
silent-warning-audit
Apr 2, 2026
Merged

Add UserWarning emissions for 8 silent operations#256
igerber merged 6 commits into
mainfrom
silent-warning-audit

Conversation

@igerber

@igerber igerber commented Apr 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add UserWarning emissions for 8 previously silent operations across 5 estimators so users know what changed, why, and what to do differently
  • Warnings added: lstsq→pinv fallback, NaN y_tilde masking, always-treated survey note, consolidated (g,t) cell skip, missing treatment fill, Rust→Python fallback (4 sites), weight normalization, inf→0 never-treated conversion
  • Address AI review P1/P2/P3 findings: materialize NaN entries for skipped (g,t) cells per REGISTRY contract, reject observed treatment=NaN before TROP pivot, extract shared treatment validation helper, update return type annotations

Methodology references (required if estimator / math changes)

  • Method name(s): CallawaySantAnna, TwoStageDiD, TROP (local + global), SurveyDesign
  • Paper / source link(s): N/A — observability-only changes; no methodology modifications
  • Any intentional deviations from the source (and why): None — warnings surface existing behavior without changing estimates

Validation

  • Tests added/updated: tests/test_staggered.py, tests/test_two_stage.py, tests/test_trop.py, tests/test_survey.py (21 new tests total: positive/negative warning assertions, NaN materialization, treatment=NaN rejection)
  • Backtest / simulation / notebook evidence (if applicable): N/A

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

Generated with Claude Code

igerber and others added 2 commits April 2, 2026 14:03
Add warnings for: lstsq→pinv fallback, NaN y_tilde masking,
always-treated survey weight note, consolidated (g,t) cell skip,
missing treatment fill, Rust→Python fallback (4 sites),
weight normalization, and inf→0 never-treated conversion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P1-1: Materialize NaN entries for skipped (g,t) cells in vectorized
and covariate regression paths (REGISTRY contract line 350).

P1-2: Reject observed treatment=NaN before pivoting in TROP to
distinguish bad input from structural panel gaps.

P2-1: Consolidated skip warning now reports NaN cell reasons.

P2-2: Extract _validate_and_pivot_treatment shared helper to
trop_local.py, eliminating duplication between trop.py and
trop_global.py.

P2-3/P2-4: Add regression tests for treatment=NaN rejection and
NaN-materialization of skipped cells.

P3-1: Update return type annotations to Tuple[Dict, Dict, Dict].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

Overall Assessment

⚠️ Needs changes

Executive Summary

  • I did not find a source-paper or variance-formula regression in the TROP, SurveyDesign, or TwoStageDiD warning-only changes.
  • CallawaySantAnna still does not satisfy the Methodology Registry’s “missing (g,t) cells are NaN-materialized” contract across all estimator paths; the PR only fixes part of that surface.
  • TwoStageDiD only emits the new y_tilde masking warning for the default simple ATT path; aggregate="event_study" and aggregate="group" still perform the same masking silently.
  • The new tests mirror those gaps: skipped-cell materialization is only exercised for estimation_method="reg", and the y_tilde warning is only exercised for the default aggregation.
  • No security findings. No TODO.md entry mitigates the two P1 items.

Methodology

  • P1 [Newly identified] Impact: CallawaySantAnna still drops skipped (g,t) cells instead of materializing them as NaN outside the two panel-reg fast paths. The general panel path returns None on missing-period / empty-cell skips in diff_diff/staggered.py:677-721, the repeated-cross-section path does the same in diff_diff/staggered.py:2863-2894, and even the new vectorized survey branch still continues on zero effective survey mass before the NaN-materialization block in diff_diff/staggered.py:945-950 and diff_diff/staggered.py:1009-1019. fit() then drops those cells behind if att_gt is not None in diff_diff/staggered.py:1671-1681 and diff_diff/staggered.py:1761-1771. That still conflicts with the registry contract in docs/methodology/REGISTRY.md:350-351. Concrete fix: thread skip bookkeeping through _compute_att_gt_fast() and _compute_att_gt_rc(), convert all skip reasons (missing period, zero treated/control count, zero effective survey mass) into explicit NaN group_time_effects entries, and reuse the consolidated warning for those branches as well.

Code Quality

  • P1 [Newly identified] Impact: the new TwoStageDiD warning for masked non-finite y_tilde is only wired into _stage2_static() in diff_diff/two_stage.py:1052-1065. The event-study and group paths still zero the same rows silently in diff_diff/two_stage.py:1133-1137 and diff_diff/two_stage.py:1352-1355. That leaves aggregate="event_study" and aggregate="group" with the same silent operation the PR is supposed to surface. Concrete fix: emit the warning once in a shared pre-Stage-2 location (preferred), or factor the masking into a common helper used by all three Stage-2 paths, with bootstrap refits able to suppress duplicate warnings.

Performance

  • No findings in the diff-scoped changes.

Maintainability

  • No separate findings beyond the propagation gaps above.

Tech Debt

  • No TODO.md entry covers either P1 item, so neither is mitigated under the review rubric.

Security

  • No findings in the diff-scoped changes.

Documentation/Tests

  • P3 Impact: the new positive skip-warning test is effectively inert. It filters for "cell(s) skipped" in tests/test_staggered.py:4044-4063, but the implementation warns "could not be estimated (NaN)" in diff_diff/staggered.py:1835-1839. The materialization test also only covers the estimation_method="reg" fast path in tests/test_staggered.py:4083-4112, and the new TwoStageDiD warning test only covers the default simple ATT path in tests/test_two_stage.py:1237-1249. Concrete fix: assert against the real warning text/category, add skipped-cell coverage for ipw/dr and panel=False, and add aggregate="event_study" / aggregate="group" warning cases for TwoStageDiD.

Path to Approval

  1. Extend CallawaySantAnna skip materialization to the general panel and repeated-cross-section branches, and include zero-effective-survey-mass skips in the same bookkeeping.
  2. Move TwoStageDiD’s y_tilde masking warning to a shared path so simple, event-study, and group aggregation all surface it consistently without bootstrap warning spam.
  3. Update tests to cover the non-reg Callaway paths, the non-simple TwoStage paths, and the actual consolidated skip-warning message.

- Revert NaN materialization for skipped (g,t) cells (broke
  test_non_consecutive_time_periods and test_some_units_treated_first_period)
- Fix inf/int64 TypeError on pandas >=2.0 in test_item8 (cast to float)
- Extract _mask_nan_ytilde helper in TwoStageDiD for consistent y_tilde
  warning across static, event_study, and group Stage 2 paths
- Remove redundant _aggregate_simple NaN warning (upstream warnings suffice)
- Fix test string mismatch ("cell(s) skipped" → "could not be estimated")
- Add event_study/group aggregation warning tests for TwoStageDiD
- Add TODO.md entry for NaN materialization as future tech debt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber

igerber commented Apr 2, 2026

Copy link
Copy Markdown
Owner Author

/ai-review

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: bf9874d7c784479d4e7f8d77ed1d3595e4274cd4


Overall Assessment

⚠️ Needs changes

Executive Summary

  • Prior re-review item on TwoStageDiD appears resolved: the new shared y_tilde masking helper is now used by simple, event-study, and group Stage 2 paths in diff_diff/two_stage.py:L1090-L1418, with matching tests in tests/test_two_stage.py:L1237-L1325.
  • I did not find a source-paper, weighting, or variance-formula regression in the TROP, SurveyDesign, or TwoStageDiD warning additions.
  • CallawaySantAnna still does not satisfy the Methodology Registry contract that non-estimable (g,t) cells be materialized as NaN. The general panel path, repeated-cross-section path, and vectorized survey zero-mass branch still drop those cells entirely.
  • The new CallawaySantAnna consolidated skip warning only covers the fast-path skip bookkeeping, while _aggregate_simple() no longer emits the old fallback NaN-exclusion warning, so the affected IPW/DR/panel=False cases remain silent.
  • The new skip-warning test is non-assertive and only exercises the reg path, so it would not catch the remaining CallawaySantAnna gap.
  • I could not execute tests in this sandbox: pytest is not installed and the default python lacks project deps like numpy.

Methodology

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • No findings.

Tech Debt

  • P3 Impact: The new tracking entry in TODO.md:L55 is useful, but under this review rubric it does not mitigate the P1 above because the live implementation still diverges from the registry contract. Concrete fix: keep the TODO as follow-up tracking after the estimator behavior is fixed; it is not a substitute for the fix.

Security

  • No findings.

Documentation/Tests

  • P3 Impact: The new skip-warning test in tests/test_staggered.py:L4020-L4065 only checks the warning text if a warning already exists, so it passes even when no warning is emitted. It also exercises only the sparse-panel reg path, not the still-broken IPW/DR or panel=False branches. Concrete fix: make the sparse-panel case assert the warning unconditionally, then add one IPW or DR reproduction and one panel=False reproduction that both assert warning emission and presence of a NaN-valued (g,t) entry in results.group_time_effects.
  • Execution note: static review only; I could not run the modified tests in this environment.

Path to Approval

  1. Extend CallawaySantAnna skip handling so _compute_att_gt_fast(), _compute_att_gt_rc(), and the vectorized survey zero-mass branch all return structured skip reasons that become explicit NaN group_time_effects[(g,t)] entries.
  2. Make the consolidated skipped-cell warning cover those general/RCS/survey branches as well, or restore a fallback warning only after NaN materialization is guaranteed.
  3. Strengthen tests/test_staggered.py:L4020-L4065 with unconditional assertions and add IPW/DR plus panel=False coverage that verifies both warning behavior and NaN materialization.

Update REGISTRY.md to document actual behavior: non-estimable (g,t)
cells are omitted from group_time_effects (not NaN-materialized).
NaN materialization tracked as future work in TODO.md.

Strengthen skip-warning test: unconditional assertion, deterministic
test data with both succeeding and failing cohorts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber

igerber commented Apr 2, 2026

Copy link
Copy Markdown
Owner Author

/ai-review

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 0df5d33992578213d6348dff316cc2291c7db1e3


Overall Assessment

⚠️ Needs changes

Static review only. I could not run the tests in this sandbox because /usr/bin/python is missing numpy, pandas, and pytest.

Executive Summary

Methodology

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • P3 Impact: The new comments now misdescribe runtime behavior. fit() says general/RCS paths “already materialize NaN cells” and _aggregate_simple() says upstream “already informed the user,” but the general/RCS paths still drop None cells and _aggregate_simple() no longer emits the removed warning (diff_diff/staggered.py#L1797, diff_diff/staggered_aggregation.py#L109). Concrete fix: update those comments after the skipped-cell contract is made consistent.

Tech Debt

  • P3 Impact: The new TODO entry is useful tracking, but it does not mitigate the live CallawaySantAnna correctness/methodology gap because this rubric does not allow TODO tracking to downgrade a P1 (TODO.md#L55). Concrete fix: keep the TODO only for any residual follow-up after the runtime behavior and registry wording are aligned.

Security

  • No findings.

Documentation/Tests

  • P3 Impact: The new skip-warning coverage is too narrow. tests/test_staggered.py#L4020 only exercises estimation_method="reg" on a plain panel dataset, which hits the vectorized fast path. It does not cover panel=False, survey-weighted reg (which routes through the general path), or zero-effective-survey-mass cases, so the still-silent branches remain unguarded. Concrete fix: add one panel=False reproduction, one survey-weighted reg reproduction that routes through _compute_att_gt_fast(), and one zero-mass survey case. Each test should assert the chosen contract for skipped cells and the warning behavior.

Path to Approval

  1. Make CallawaySantAnna skipped-cell handling consistent across _compute_att_gt_fast(), _compute_att_gt_rc(), and the vectorized survey zero-mass branch: either preserve non-estimable (g,t) cells as NaN entries everywhere or intentionally omit them everywhere with a warning from every affected branch.
  2. Fix the methodology documentation so it matches the implementation and the review rubric: either restore the NaN-materialization contract in the registry, or convert the omission behavior into a labeled **Note (deviation from R):** and remove the contradictory TODO entry.
  3. Expand tests/test_staggered.py with panel=False, survey-weighted reg, and zero-effective-mass cases that assert the exact skipped-cell contract and warning semantics.

Extend consolidated skip warning to all CS estimation paths (general
DR/IPW and repeated cross-section), not just vectorized/covariate reg.

Update REGISTRY.md with labeled **Note:** documenting omission behavior
per reviewer rubric requirements.

Fix misleading comment in _aggregate_simple about upstream warnings.

Add DR-path and panel=False skip warning tests. Filter expected skip
warnings in power tests that use not_yet_treated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber

igerber commented Apr 2, 2026

Copy link
Copy Markdown
Owner Author

/ai-review

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: c339fd619eed21d9544bbc5f42be40676403324b


Overall Assessment

⚠️ Needs changes

Static review only. I could not run the tests in this sandbox because numpy, pandas, and pytest are not installed.

Executive Summary

  • The broad prior CallawaySantAnna re-review issue is mostly resolved: general-panel and repeated-cross-section skipped cells now feed the fit-time consolidated warning, and the registry text is now explicitly labeled as a Note.
  • One P1 remains in the same estimator: the survey-weighted zero-effective-mass branches still bypass that new warning contract in three touched staggered.py paths.
  • The new warning additions in TwoStageDiD, TROP, and SurveyDesign do not show new inference, variance, or identification defects in the changed code.
  • TODO.md now contradicts the updated registry text, and the new skip-warning tests still miss the unresolved survey-weighted branch.

Methodology

  • P1 Impact: The skipped-cell warning contract is still only partially fixed for survey-weighted CallawaySantAnna. The new consolidated warning in diff_diff/staggered.py:1802 only reports cells counted through _skip_info / _n_skipped_other, but three zero-effective-mass survey branches in the touched file still bypass that path: _compute_att_gt_fast() returns np.nan without registering a skip at diff_diff/staggered.py:733 and diff_diff/staggered.py:735, the vectorized survey path silently continues at diff_diff/staggered.py:949, and _compute_att_gt_rc() does the same NaN-without-warning pattern at diff_diff/staggered.py:2894 and diff_diff/staggered.py:2896. That leaves silent behavior in exactly the pattern this PR is auditing, and it overstates the new registry note that says fit() emits the consolidated warning across estimation paths at docs/methodology/REGISTRY.md:350. Under the checklist’s pattern-consistency rule, this is still a P1. Concrete fix: choose one contract for zero-effective-mass survey cells and apply it uniformly in all three branches: either treat them as skipped cells and feed them into the consolidated warning/omission path, or preserve them as NaN everywhere with an explicit warning and matching registry text.

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • No findings.

Tech Debt

  • P3 Impact: TODO.md still tracks “materialize NaN entries ... per REGISTRY.md line 350” at TODO.md:55, but line 350 now documents the opposite omission contract at docs/methodology/REGISTRY.md:350. Even if deferred work remains, this entry now points future cleanup at the wrong target. Concrete fix: rewrite the TODO to track the actual remaining inconsistency (the survey zero-effective-mass branches), or remove it once the final contract is settled.

Security

  • No findings.

Documentation/Tests

  • P3 Impact: The new skip-warning tests exercise a balanced vectorized reg case, a non-survey DR case, and panel=False at tests/test_staggered.py:4052, tests/test_staggered.py:4095, and tests/test_staggered.py:4120, but they still do not hit any survey-weighted zero-effective-mass branch. That leaves the remaining partial-fix bug unguarded. Concrete fix: add one survey-weighted panel reg test that drives _compute_all_att_gt_vectorized() into zero treated/control mass, and one survey-weighted non-vectorized case (panel=False or a forced general path) that drives _compute_att_gt_fast() / _compute_att_gt_rc() the same way, then assert the chosen warning and group_time_effects contract.

Path to Approval

  1. Make zero-effective-mass survey cells follow one consistent CallawaySantAnna contract in diff_diff/staggered.py:733, diff_diff/staggered.py:949, and diff_diff/staggered.py:2894, and ensure diff_diff/staggered.py:1802 reports them.
  2. Update docs/methodology/REGISTRY.md:350 and TODO.md:55 so both describe the same final contract.
  3. Add survey-weighted regression tests in tests/test_staggered.py covering both the vectorized and non-vectorized zero-mass paths.

Make survey zero-effective-mass branches consistent with skip contract:
- _compute_att_gt_fast: return None (not np.nan) for zero-mass cells
- vectorized survey path: track zero-mass skips in skipped_empty_cell
- _compute_att_gt_rc: return None (not np.nan) for zero-mass cells

All three now feed into the consolidated skip warning.

Update TODO.md to describe actual remaining work (downstream consumer
updates for optional NaN materialization) instead of contradicting
the REGISTRY.md omission contract.

Add survey-weighted zero-mass skip warning test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber

igerber commented Apr 2, 2026

Copy link
Copy Markdown
Owner Author

/ai-review

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 7b290d29ea6d91770a780cb06e0d285872ddd84e


Overall Assessment

✅ Looks good

Static review only. I could not run the test suite in this environment because pytest is not installed.

Executive Summary

  • The prior CallawaySantAnna re-review P1 appears resolved: the survey zero-effective-mass branches now omit the affected (g,t) cells and flow into the consolidated skip-warning contract in the fit path, and the registry/TODO now describe the same omission behavior. See diff_diff/staggered.py:733, diff_diff/staggered.py:949, diff_diff/staggered.py:2894, diff_diff/staggered.py:1803, docs/methodology/REGISTRY.md:350, and TODO.md:55.
  • I did not find new P0/P1 methodology defects in estimator math, weighting, variance/SE, identification checks, or defaults across CallawaySantAnna, TwoStageDiD, TROP, and SurveyDesign.
  • TROP’s new observed-treatment=NaN rejection is consistent with the documented D-matrix contract for an absorbing treatment indicator and does not appear to change the estimator’s numerical method. See diff_diff/trop_local.py:30, diff_diff/trop.py:521, and diff_diff/trop_global.py:566.
  • [Newly identified] TROP still has duplicated internal treatment-pivot logic in bootstrap/refit helpers instead of fully reusing the new shared validator. I view this as P3 maintainability, not a blocker, because public fit() already validates/warns before those paths run.
  • Test coverage improved substantially, but the repaired non-vectorized survey CallawaySantAnna branches still are not hit directly by the new warning tests.

Methodology

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • P3 Impact: [Newly identified] the new TROP helper centralizes treatment validation for the public fit paths, but internal bootstrap/refit helpers still rebuild D with raw pivot(...).fillna(0) logic in multiple places. That leaves warning/validation semantics duplicated and easier to let drift in future changes, even though I do not see a current correctness bug because outer fit() already validates first. See diff_diff/trop_local.py:30, diff_diff/trop_local.py:1257, diff_diff/trop_global.py:934, diff_diff/trop_global.py:1163, and diff_diff/trop_global.py:1252. Concrete fix: route those internal builders through the shared helper, or split out a shared _pivot_treatment_matrix(..., warn=False) utility so fit and refit paths share identical semantics.

Tech Debt

  • P3 Impact: The remaining CallawaySantAnna omission-vs-NaN downstream-consumer follow-up is now explicitly tracked and aligned between the registry and TODO, so it is properly deferred rather than silently inconsistent. See TODO.md:55 and docs/methodology/REGISTRY.md:350. Concrete fix: none required in this PR.

Security

  • No findings.

Documentation/Tests

  • P3 Impact: The new survey zero-mass warning test covers the vectorized panel reg path only; it does not directly exercise the repaired non-vectorized survey branches that caused the prior re-review finding. That leaves weaker regression protection on the exact branches that were previously broken. See tests/test_staggered.py:4133, diff_diff/staggered.py:733, and diff_diff/staggered.py:2894. Concrete fix: add one survey-weighted panel case that forces the general path (for example via covariates or non-reg estimation) and one panel=False survey case, both asserting the consolidated skip warning.

@igerber igerber merged commit 006568b into main Apr 2, 2026
14 checks passed
@igerber igerber deleted the silent-warning-audit branch April 2, 2026 21:15
igerber added a commit that referenced this pull request Apr 2, 2026
Version bump covering silent warning audit (#256), replicate weight
expansion (#253), and imputation pre-period coefficients (#252).
Adds Phase 8 survey maturity roadmap (SDR, FPC expansion, lonely PSU,
CV, weight trimming, compatibility matrix).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant