Skip to content

feat(v4): PR 4a of the 4.0 cut - remove StaggeredTripleDifference/SDDD and QDiD/QDiDResults, flip M-085 - #866

Merged
igerber merged 1 commit into
mainfrom
feat/v4-pr4a-std-qdid-removals
Oct 23, 2026
Merged

igerber merged 1 commit into
mainfrom
feat/v4-pr4a-std-qdid-removals

Conversation

@igerber

@igerber igerber commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • 4.0 cut, PR 4a. Cut-sequence item 4 (the three merges) is split three ways in docs/v4-design.md (user decision 2026-10-21): CI executes the tutorials and five of them construct MultiPeriodDiD, so 4b rebuilds those notebooks on TWFE(event_study=True) and 4c removes the MultiPeriodDiD family afterwards. This PR takes the two notebook-safe removals: the StaggeredTripleDifference class, its module diff_diff/staggered_triple_diff.py and the SDDD alias (M-013, M-064), and the QDiD class and QDiDResults alias (M-015, M-061). Importing any of the four names raises ImportError. The staggered DDD estimator is TripleDifference().fit(..., unit=, time=, first_treat=, partition=) (the same engine, diff_diff/_staggered_triple_diff_engine.py, pinned against the committed oracle in tests/test_v4_merge_ddd.py); the quantile-DiD comparison estimator is ChangesInChanges(method="qdid"). R's compact control_group spellings died with the class; _validate_all_params reads params["method"] directly; the engine's dead cluster= accepted-then-warned branch goes with the class.
  • M-085 -> done: in the 2x2x2 design TripleDifference.fit(time=) raises ValueError naming post= (through 3.x the M-031 shim reinterpreted it with a FutureWarning); supplying both keeps the "pass only post=" error. M-031 stays shimmed (both names still bind on fit) with warning: null and a note clause.
  • Ledger/spec: M-013/M-015 -> removed, M-061/M-064/M-085 -> done with test_refs, M-080 drops the deleted module from code_refs; the spec's cross-row rule gains its alias clause (alias rows at done keep old_target as the historical record), item 4 is split 4a/4b/4c with 4a marked shipped and the 4a-before-4b-before-4c ordering added. docs/doc-deps.yaml re-primes the staggered_triple_diff group on the results module. Non-terminal rows with removed_in: "4.0": 11 -> 7; the full 4.0.0 due gate: 34 -> 29 rows (38 diagnostics); 0 due at 3.13.0.
  • Tests: the three StaggeredTripleDifference suites (test_staggered_triple_diff, test_methodology_staggered_triple_diff, test_survey_staggered_ddd) are ported to TripleDifference's staggered mode (keyword fits; the R golden's compact control_group values mapped), so the engine keeps its behavior, methodology, survey and R-parity coverage. test_v4_merge_ddd / test_v4_merge_cic drop the old-vs-new parity lanes for Test*Removal pins and the M-085 raise pin; the capture scripts are marked historical and make_2x2 lives in the CiC merge test. Mixed suites move to the surviving surfaces.
  • Docs: API index/homepage rows and the two autosummary stubs removed; the staggered.rst/changes_in_changes.rst sections dropped; REGISTRY's TripleDifference notes updated for M-085 and the removal, with the ## StaggeredTripleDifference and ### QDiD sections kept as the engine's methodology record (archive framing lands with M-090); llms*.txt fold the staggered fit signature and the qdid method into the surviving entries and the Ortiz-Villavicencio citation moves onto the TripleDifference catalog lines; paper review, survey docs, tracking files, migration guide and .claude/memory.md reworded. benchmarks/python/benchmark_triple_diff.py passes post= in 2x2x2 mode.

Methodology references (required if estimator / math changes)

  • Method name(s): Ortiz-Villavicencio & Sant'Anna (2025) staggered triple difference; Athey & Imbens (2006) quantile DiD comparison estimator. No estimator mathematics changes: both engines are untouched and the surviving surfaces reproduce the committed pre-merge oracles (tests/test_v4_merge_ddd.py::ORACLE_STAGGERED, tests/test_v4_merge_cic.py::ORACLES) and the R golden (tests/test_methodology_staggered_triple_diff.py, run locally with the gitignored CSVs present).
  • Paper / source link(s): docs/methodology/REGISTRY.md TripleDifference, StaggeredTripleDifference (retained as the engine's record) and QDiD sections; docs/methodology/papers/ortiz-villavicencio-santanna-2025-review.md.
  • Any intentional deviations from the source (and why): None

Validation

  • Tests added/updated: tests/test_v4_merge_ddd.py (140 python / 123 rust), tests/test_v4_merge_cic.py (53), the three ported staggered-DDD suites (51 / 79 / 39, both backends), tests/test_aggregate_contract.py (459, both backends), tests/test_staggered_aggregation.py, tests/test_survey_metadata_raw_scale.py, tests/test_event_study_surface.py, tests/test_anticipation_policy.py, tests/test_v4_inference_policy.py, tests/test_balance_interop_contract.py, tests/test_profile_panel.py, tests/test_base_estimator.py, tests/test_naming_guard.py, tests/test_practitioner.py, tests/test_changes_in_changes.py, tests/test_changes_in_changes_parity.py, tests/test_methodology_changes_in_changes.py, tests/test_v4_rename_semantic.py, tests/test_v4_matrix.py (301), plus test_business_report, test_linalg, test_staggered docstring updates. Consumer suites (test_triple_diff, test_methodology_triple_diff, test_power, test_event_study_consumers, test_visualization, test_diagnostic_report, test_honest_did, test_pretrends, test_result_aliases, test_results_serialization, test_t27_cic_distributional_effects_drift), the doc gates (test_docs_ia, test_doc_deps_integrity, test_doc_snippets, test_guides, test_tracking_files, test_changelog_fragments) and test_aliases/test_v4_rename_mechanical pass. Python 3.9 leg pins (scipy 1.13.1 / numpy 2.0.2): the two merge files and the methodology suite pass (272). mypy diff_diff clean; Sphinx -W clean; py_compile under 3.9 clean; the benchmark script runs on a generated DDD CSV.
  • Backtest / simulation / notebook evidence (if applicable): N/A (no notebook changes; tutorials 08/16 mention the removed class in markdown only and are owned by 4b).

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/20261021-v4-pr4a-std-qdid-removals.md (### Removed, ### Breaking Changes, ### Internal)

Follow-ups

  • 4b: tutorials 02/04/05/07/23 (+ tests/test_t23_spillover_tva_drift.py) on TWFE(event_study=True), plus the StaggeredTripleDifference markdown mentions in 08/16.
  • 4c: the MultiPeriodDiD family (M-010/M-011/M-012/M-060, M-007, M-016, M-083, power registry, benchmark arm), after the TWFE event-study path is decoupled from MultiPeriodDiDResults.

…D and QDiD/QDiDResults, flip M-085

Cut-sequence item 4 is split three ways (4a/4b/4c, user decision): CI executes the
tutorials and five of them construct MultiPeriodDiD, so the MultiPeriodDiD family
waits for the notebook rebuild (4b) and lands in 4c. This PR takes the two
notebook-safe removals and the M-085 raise.

Removed: the StaggeredTripleDifference class and its module
(diff_diff/staggered_triple_diff.py), the SDDD alias, the QDiD class and the
QDiDResults alias. The staggered DDD estimator is
TripleDifference().fit(..., unit=, time=, first_treat=, partition=) (same engine,
pinned against the committed oracle); the quantile-DiD comparison estimator is
ChangesInChanges(method="qdid"). _validate_all_params reads params["method"]
directly now that the only method-less constructor is gone; the engine's dead
cluster= accepted-then-warned branch goes with the class.

M-085: in the 2x2x2 design TripleDifference.fit(time=) raises ValueError naming
post= instead of the 3.x M-031 FutureWarning reinterpretation; both-supplied keeps
its "pass only post=" error. resolve_renamed_kwarg is no longer imported there.

Ledger: M-013/M-015 -> removed, M-061/M-064/M-085 -> done (test_refs added),
M-031 keeps shimmed with warning null, M-080 drops the deleted module. Spec: item 4
split with 4a marked shipped, 4a-before-4b-before-4c ordering, the cross-row
rule's alias clause. doc-deps: the staggered_triple_diff group is re-primed on the
results module. Removed_in-4.0 non-terminal rows 11 -> 7; the full 4.0.0 due gate
34 -> 29 rows.

Tests: the three StaggeredTripleDifference suites are ported to TripleDifference's
staggered mode (keyword fits, R-golden compact control_group values mapped); the
DDD/CiC merge gates drop their old-vs-new parity lanes for removal pins; the two
capture scripts are marked historical; mixed suites (aggregate contract, staggered
aggregation, survey metadata, event-study surface, anticipation/inference
rosters, balance interop, profile panel, base estimator, practitioner, CiC
suites, rename semantic) move to the surviving surfaces.

Docs: API index/homepage rows and the two autosummary stubs removed, staggered.rst
and changes_in_changes.rst sections dropped, REGISTRY TripleDifference notes and
the StaggeredTripleDifference/QDiD section banners updated (archive framing stays
with M-090), guides (llms*.txt) fold the staggered signature and the qdid method
into the surviving entries, catalog citations moved onto TripleDifference, paper
review/survey docs/tracking files reworded, migration guide and memory notes
updated. Benchmark benchmark_triple_diff.py passes post= in 2x2x2 mode.
@github-actions

Copy link
Copy Markdown

Overall assessment: ✅ Looks good

No unmitigated P0 or P1 findings identified.

Executive summary

  • Estimation, weighting, variance, and inference formulas remain unchanged.
  • API removals and the 2×2×2 time= rejection match the documented migration contract.
  • No unexpected executable callers of removed names were found; historical oracle scripts are explicitly marked.
  • Static checks passed. Statistical tests could not run because this environment lacks pytest, NumPy, SciPy, and pandas.

Methodology

No new defects found. Checked the affected DDD and CiC/QDiD paths against docs/methodology/REGISTRY.md, docstrings, and repository paper reviews for Ortiz-Villavicencio–Sant’Anna (2025) and Athey–Imbens (2006).

  • P3 — Informational: documented conventions remain intact.
    Impact: Existing paper/R differences, including QDiD’s finite-sample formula, remain explicitly documented. The new time= rejection agrees with the Registry.
    Concrete fix: None required.
    Locations: docs/methodology/REGISTRY.md:L4070-L4086, docs/methodology/REGISTRY.md:L5852-L5862, diff_diff/triple_diff.py:L861-L878.

Code Quality

No findings. Export removal and constructor/fit validation remain consistent. The deleted engine warning was unreachable through the surviving surface, which rejects cluster= before delegation.

Performance

No findings. No new computational work or allocation-heavy paths introduced.

Maintainability

No findings. Methodology records, dependency mappings, migration documentation, and removal tests follow the surviving APIs.

Tech Debt

  • P3 — Informational: R validation coverage remains tracked.
    Impact: Staggered-DDD R comparisons still skip when generated CSV fixtures are unavailable. Committed oracle checks remain in place.
    Concrete fix: No action required for approval; fixture publication and additional parity coverage are tracked under PR Add Staggered Triple Difference estimator #245.
    Location: DEFERRED.md:L60-L62.

Security

No findings. No added secrets, external execution paths, or sensitive-data handling identified.

Documentation/Tests

No new findings. The required changelog fragment covers removals and breaking behavior.

Verification completed: Python syntax parsing, static export/import checks, oracle-generator equivalence, and six isolated dispatch/argument checks. Full estimator and regression-suite results were not independently verified in this environment.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Oct 21, 2026
@igerber
igerber merged commit 6ae1d36 into main Oct 23, 2026
64 of 72 checks passed
@igerber
igerber deleted the feat/v4-pr4a-std-qdid-removals branch October 23, 2026 10:00
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