Skip to content

feat(v4): PR 1 of the 4.0 cut - new-estimator results-contract conformance - #857

Merged
igerber merged 1 commit into
mainfrom
docs/v4-pr1-conformance
Oct 18, 2026
Merged

igerber merged 1 commit into
mainfrom
docs/v4-pr1-conformance

Conversation

@igerber

@igerber igerber commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • PR 1 of the 4.0 cut (spec section 9 "4.0 cut sequence", item 1): brings the estimators born after 3.9 onto the 4.0 results contract before the removal waves start. Lands under the declared cut window; __version__ stays 3.13.0. No estimation, weighting, or variance code changes.
  • LWDiDResults.summary(alpha=None) / print_summary(alpha=None) join the family-wide summary-alpha guard (M-146, whose code_refs now include LWDiD): a value other than the fit-time alpha raises; the methods previously took no alpha at all.
  • LWDiDResults.to_dataframe(level=) / to_csv(level=) are reshaped onto the shared results schema (new behavior row M-153, introduced_in: 4.0, done): each level in {"simple", "group", "event_study"} returns aggregate(level).to_dataframe() plus the rolling/estimation_method/vcov_type columns; default "simple" (one headline row). The 3.10-3.13 bespoke frame is gone: term/cohort -> label, the trailing "Overall" row -> level="simple", n_treated -> n, n_obs/n_control -> to_dict() only, ci_lower/ci_upper -> conf_int_lower/conf_int_upper. Recorded as a Breaking Change in the fragment and the migration guide.
  • LWDiDResults.pvalue / .ci (3.10-born duplicates of p_value/conf_int, no reader anywhere in the repo) now emit a FutureWarning and are removed at 5.0 (new rows M-151/M-152, shimmed). diff_diff._deprecation.deprecated_field_property gains removed_in= so the warning and the generated docstring name the row's release (existing callers unchanged).
  • DurationDiDResults.aggregate("simple"): verbatim relay of the stored headline quintet (n = n_units, df NaN - inference is bootstrap-only; nothing recomputed).
  • RDDensityTestResult.to_dict(): JSON-serializable scalars with native casts (NaN inference passes through), the *_conventional block None unless report_all=True, bandwidths nested only when estimated.
  • Ledger/enforcement: rows M-151..M-153 (group lwdid-results-contract); id snapshot and ROW_COUNT_FLOOR 135; naming guard gains pvalue/ci tokens (ambiguous lanes) with allowlists for the independent same-named surfaces (RDPlot.__init__[ci], RandomizationResult.pvalue, SpecificationResult.pvalue, the lwdid-py mapping docstring). Spec sections 3.1/5/9/11, migration guide (appendix 113 rows, new orientation row, "Remaining 4.0 changes" + "Estimators added after 3.9" prose), REGISTRY Notes (LWDiD, DurationDiD, RDDensityTest), llms-full.txt, and docs/api/lwdid.rst (to_dataframe(level="group") snippet) updated.

Methodology references (required if estimator / math changes)

  • Method name(s): N/A - no methodology changes. Result views and serialization only; the DurationDiD simple relay copies stored inference (REGISTRY DurationDiD Note), the LWDiD frame reuses the existing aggregate() surfaces.
  • Paper / source link(s): N/A
  • Any intentional deviations from the source (and why): None. The cluster/variance semantics are untouched; the three REGISTRY **Note:** lines record API-contract decisions, not estimator deviations.

Validation

  • Tests added/updated: tests/test_lwdid.py (new TestV4ResultsContract: alpha guard incl. alpha=0.0, every to_dataframe level and the unknown-level error, to_csv(level=), joint-NaN relay on a PSM fit, alias warnings/docstring/read-only), tests/test_duration_did.py (simple relay bit-exact to the stored fields incl. NaN t_stat/df; n_bootstrap=0 variant; group now the unsupported-type probe), tests/test_rddensity.py (to_dict round trip, manual-h/report_all shapes, degraded-inference NaN round trip), tests/test_v4_rename_mechanical.py (removed_in in warning and docstring), tests/test_v4_matrix.py / tests/test_naming_guard.py (snapshot, tokens, allowlists). Gate simulation unchanged: 0 / 1 / 118 problems at 3.13.0 / 3.14.0 / 4.0.0 (109 ids).
  • Backtest / simulation / notebook evidence (if applicable): N/A

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

Changelog

  • changelog.d/ fragment added (or N/A - no user-visible change): Yes - changelog.d/20261018-v4-pr1-conformance.md

…mance

Brings the estimators born after 3.9 onto the 4.0 results contract (spec
sections 5 and 6) before the removal waves start.

- LWDiDResults.summary()/print_summary() take alpha=None under the
  family-wide M-146 guard (previously no alpha at all).
- LWDiDResults.to_dataframe(level=) / to_csv(level=) are aggregate-backed
  on the shared schemas (+ rolling/estimation_method/vcov_type), default
  "simple"; the 3.10 bespoke frame is gone (ledger behavior row M-153).
- LWDiDResults.pvalue / .ci alias properties deprecated (FutureWarning,
  removed 5.0; ledger rows M-151/M-152) via deprecated_field_property,
  which gains removed_in= so warning and docstring name the right release.
- DurationDiDResults.aggregate("simple") relays the stored quintet (df NaN).
- RDDensityTestResult.to_dict() added.
- Ledger: rows M-151..M-153 (group lwdid-results-contract), M-146 code_refs;
  id snapshot/floor 135; naming guard tokens pvalue/ci (ambiguous lanes) +
  allowlists; spec sections 3.1/5/9/11, migration guide, REGISTRY Notes,
  llms-full, lwdid.rst updated; tests for every surface.
@github-actions

Copy link
Copy Markdown

Overall assessment

✅ Looks good — no actionable P0, P1, or P2 findings identified in the supplied diff.

Executive summary

  • Changes affect LWDiD, DurationDiD, and RDDensityTest result views and serialization.
  • Stored estimates and inference are relayed without changing estimation, weighting, variance, or comparison groups.
  • Registry Notes, migration guidance, deprecation ledger, and changelog fragment agree with the implementation.
  • Dependency-free checks passed. Runtime tests could not run because pytest, NumPy, pandas, and SciPy are unavailable.

Methodology

No findings. Cross-checked the affected semantics against docs/methodology/REGISTRY.md and the repository’s Lee–Wooldridge, Deaner–Ku, and Cattaneo–Jansson–Ma paper/source reviews.

  • DurationDiD preserves the stored headline inference and bootstrap-appropriate NaN degrees of freedom: diff_diff/duration_did_results.py:L558-L576.
  • LWDiD exports reuse existing aggregation surfaces, preserving their estimands and inference: diff_diff/lwdid_results.py:L430-L435.
  • RDDensity serialization preserves None and NaN values without recomputing inference: diff_diff/rddensity.py:L774-L832.

The documented contract choices require no corrective action.

Code Quality

No findings. level reaches CSV exports, alpha reaches summary validation, and the deprecation helper preserves existing callers’ default removal version.

Performance

No findings. Added work is proportional to the exported result size; estimation paths remain unchanged.

Maintainability

No findings. Shared schemas and the existing deprecation helper avoid duplicated contracts.

Tech Debt

No new untracked limitation identified. Existing estimator limitations remain outside this diff.

Security

No secrets or newly introduced unsafe behavior identified.

Documentation/Tests

No findings requiring changes. The behavior changes include the required changelog fragment.

Validation passed for Python syntax, changelog conventions, ledger/migration parity, new naming-token coverage, and isolated deprecation/serialization behavior. The numerical and pandas-backed pytest suites remain unverified in this environment.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Oct 18, 2026
@igerber
igerber merged commit 5d110f3 into main Oct 18, 2026
39 of 40 checks passed
@igerber
igerber deleted the docs/v4-pr1-conformance branch October 18, 2026 17:53
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