Repository navigation
feat(v4): item 9b of the 4.0 cut - retire the 5.0-window shims at 4.0 + placebo wrappers take the calendar-time contract (M-016, M-050..M-058, M-148, M-149, M-151, M-152 removed; M-156, M-157) - #885
Conversation
… and give the placebo wrappers the calendar-time contract (M-016, M-050..M-058, M-148, M-149, M-151, M-152 removed; M-156, M-157 done) Shim retirement (user decision 2026-11-01: no 4.x debt). The 14 ledger rows scheduled for a 5.0 removal flip to `removed` at 4.0. None of their FutureWarning shims had reached a release (they landed on main after the v3.13.0 tag), so the released 3.x names break at 4.0 without a warned release - stated plainly in the migration guide, the ledger notes and the spec amendments. The overall_* / overall_att_* FutureWarning properties are deleted from the nine results classes and the DMLDiDResults re-declaration (AttributeError now; the canonical att/se/t_stat/p_value/conf_int are the only vocabulary; the __setstate__ legacy-key migration stays so 3.x pickles load); EventStudyResults.period_effects, _PeriodEffectView and the leaf-local stars helper are gone from results_base.py (to_dataframe() is the route); SyntheticControl(inner_max_iter=, inner_min_decrease=) are removed (TypeError; positional slots after them shift up two - the DeprecationWarning DID ship in 3.13.0, so deprecated_in is corrected to 3.13.0); LWDiDResults.pvalue / .ci are gone. diff_diff/_deprecation.py is reduced to the pickle-migration (LEGACY_OVERALL_QUINTET, migrate_renamed_state_keys, migrate_event_study_reference_sentinel) and required-arg (NOT_SUPPLIED, require_arg) helpers; deprecated_field_property, resolve_renamed_kwarg, warn_deprecated_kwarg and deprecated_kwarg_message are deleted with their last consumers. With the descriptors gone, assigning a legacy name to a results instance creates a shadow attribute instead of raising (non-slotted dataclasses) - the naming guard's consumer sweep is the in-repo defense. Placebo wrappers (TODO.md 2(c)-ii row closed; rows M-156 / M-157). run_placebo_test / run_all_placebo_tests take time= as the calendar period column only (spec section 8 rule 1) and require post_periods for every test_type; the 0/1 post dummy the permutation and leave-one-out tests need is derived as data[time].isin(post_periods) by the new diagnostics._resolve_placebo_post (the placebo_timing_test / placebo_group_test idiom), on a copy under the collision-guarded _placebo_post column. A missing time column, missing/empty post_periods, NaN in time, post_periods values absent from the column, or a pre-existing _placebo_post column raise ValueError - on the suite wrapper before its try/except blocks. Through 3.x the wrappers forwarded time itself as the dummy, so on any multi-period calendar column DifferenceInDifferences.fit rejected it and run_all_placebo_tests silently returned the two tests as error dicts (tutorial 04's placebo cell printed ERROR lines; the key-only test assertions hid it). The practitioner snippet, llms-practitioner.txt and llms-full.txt stop advising time='post'; REGISTRY carries the wrapper Note; tests/test_diagnostics.py gains the calendar-contract suite (validation, no-mutation, bitwise parity with the direct callees, real results from run_all on a six-period panel) as the rows' test_ref. Bookkeeping: ledger header + 16 rows (M-156/M-157 added; ROW_COUNT_FLOOR 139, id range (156, 157)); spec ladder, sections 5/8/11, the phase-5 roster and item 9b with dated amendments; migration guide (orientation rows, the Results-fields section, M-016/M-148/M-149/M-151/M-152 appendix Fix cells, two new renames-post rows, the positional-shift paragraph, 115 rows in total); REGISTRY / REPORTING-adjacent prose, docs/api/results.rst and diagnostics.rst, doc-deps notes, the two guides; the unreleased changelog fragments that promised a 5.0 removal reworded; API autosummary stubs drop the retired properties (Sphinx -W green, no further drift); the naming guard's DMLDiDResults.overall_att and _deprecation placebo_effects entries deleted, the two wrappers moved into the rule-1 calendar inventory, and the retired token families' remaining in-repo hits allowlisted; the capture / benchmark getattr fallbacks read the canonical names; tutorial 17 re-executed on the Rust backend (only the practitioner-snippet cell moved).
✅ Looks goodNo unmitigated P0 or P1 findings identified in the supplied diff. Executive summary
Methodology
Code QualityNo findings. Shared validation precedes the suite’s exception handlers, and temporary post indicators are added to copies of the input. PerformanceNo findings. The additional validation and data copy introduce no evident material regression relative to repeated estimator fits. MaintainabilityNo findings. Deleted helpers have no remaining imports in the scanned library, tests, or benchmarks. Tech DebtNo findings. The removed TODO row corresponds to the implemented wrapper redesign. Required pickle-migration helpers remain intact. SecurityNo findings in the changed code or notebook content. Documentation/TestsNo findings. The PR includes the required changelog fragment, migration guidance, removal checks, and wrapper tests covering callee parity, datetime labels, validation, and input immutability ( Static parsing passed for 366 Python files. Runtime test results reported in the PR were not independently verified because the required dependencies are unavailable. |
Summary
removedwithdeprecated_in: null: none of theirFutureWarningshims had reached a release (they landed on main after the v3.13.0 tag), so the released 3.x namesoverall_att/overall_se/overall_t_stat/overall_p_value/overall_conf_int(nine results classes +DMLDiDResults; all nineContinuousDiDResultsspellings),EventStudyResults.period_effectsandLWDiDResults.pvalue/.cibreak at 4.0 without a warned release - stated plainly in the migration guide, the ledger notes and the spec amendments. Reading one is anAttributeError; the canonical quintetatt/se/t_stat/p_value/conf_intis the only vocabulary; the__setstate__legacy-key migration stays so 3.x pickles load. [M-148] [M-149] (SyntheticControl(inner_max_iter=, inner_min_decrease=)) flip toremovedwithdeprecated_in: 3.13.0- thatDeprecationWarningdid ship in 3.13.0 - so passing either is aTypeErrorandstandardize/alpha/seed/v_cv_t0move two positional slots left.diff_diff/_deprecation.pyis reduced to the pickle-migration (LEGACY_OVERALL_QUINTET,migrate_renamed_state_keys,migrate_event_study_reference_sentinel) and required-arg (NOT_SUPPLIED,require_arg) helpers;deprecated_field_property,resolve_renamed_kwarg,warn_deprecated_kwarg,deprecated_kwarg_messageare deleted with their last consumers. With the descriptors gone, assigning a legacy name to a results instance creates a shadow attribute instead of raising (non-slotted dataclasses) - recorded in the tests and the migration guide; the naming guard's consumer sweep is the in-repo defense.run_placebo_test/run_all_placebo_teststaketime=as the calendar period column only (spec section 8 rule 1) and requirepost_periodsfor everytest_type; the 0/1 post dummy for the permutation and leave-one-out tests is derived asdata[time].isin(post_periods)by the new private_resolve_placebo_post(theplacebo_timing_test/placebo_group_testidiom), on a copy under the collision-guarded_placebo_postcolumn. A missingtimecolumn, missing/emptypost_periods, NaN intime,post_periodsvalues absent from the column, or a pre-existing_placebo_postcolumn raiseValueError- on the suite wrapper BEFORE itstry/exceptblocks. This also fixes a live bug: through 3.x the wrappers forwardedtimeitself as the dummy, so on any multi-period calendar columnDifferenceInDifferences.fitrejected it andrun_all_placebo_testssilently reported the two tests as error dicts (tutorial 04's placebo cell printedERRORlines; the key-only test assertions hid it). Calls that passed the 0/1 indicator astime=withpost_periods=[1]return identical numbers; calls that omittedpost_periodsmust add it. The practitioner snippet,llms-practitioner.txtandllms-full.txtstop advisingtime='post'; the two wrappers join the naming guard's rule-1 calendar inventory.ROW_COUNT_FLOOR139, new id range(156, 157), docstrings); spec ladder, sections 5 / 8 / 11, the phase-5 roster and a new cut-sequence item 9b, all as dated amendments; migration guide (orientation rows, the Results-fields section, the M-016 / M-148 / M-149 / M-151 / M-152 appendix Fix cells, two newrenames-postrows, the positional-shift paragraph; 115 rows in total); REGISTRY (field-naming paragraph, M-016 / LWDiD / SyntheticControl notes, a new PlaceboTests- **Note:**for the wrapper contract),docs/api/results.rstanddiagnostics.rst,docs/doc-deps.yamlnotes, both guides; the five unreleased changelog fragments that promised a 5.0 removal reworded; API autosummary stubs drop the retired properties; the naming guard'sDMLDiDResults.overall_attsurface entry and_deprecation.pyplacebo_effectsconsumer entry deleted and the retired token families' remaining in-repo hits allowlisted (body-localoverall_attnames, the kept__setstate__key maps, methodology history); the capture / benchmarkgetattrfallbacks read canonical names; tutorial 17 re-executed on the Rust backend (only the stored practitioner-snippet cell moved - no numbers).Methodology references (required if estimator / math changes)
time.isin(post_periods)rule the timing/group callees already apply; the callee arithmetic is untouched.docs/methodology/REGISTRY.mdPlaceboTests section (BDM 2004, https://doi.org/10.1162/003355304772839588);docs/v4-design.mdsection 8 rule 1 (time= calendar column,post= 0/1 indicator), section 5 (results contract).post_periods(no midpoint default) while the directplacebo_timing_test/placebo_group_testcallees keep their second-half-of-periods fallback - recorded as a REGISTRY- **Note:**(M-156 / M-157). The pre-existing permutation-testt_stat = original_att / seline (REGISTRY "permutation inference is notsafe_inference" Note) is unchanged.Validation
tests/test_diagnostics.py(newTestPlaceboWrapperCalendarContract:post_periodsrequired for every test type and on the suite wrapper - raised, not an error dict; misspelledtimecolumn; absentpost_periodsvalues; NaN intime; pre-existing_placebo_postcolumn; input not mutated; bitwise parity of the wrapper with the directpermutation_test/leave_one_out_testcallees;run_all_placebo_testsreturns a realPlaceboTestResultsfor every key on a six-period panel - the regression test for the bug);tests/test_v4_storage_flip.py,tests/test_result_aliases.py,tests/test_balance_interop_contract.py,tests/test_v4_merge_mpd.py,tests/test_lwdid.py,tests/test_methodology_synthetic_control.py(legacy names asserted ABSENT -getattr_staticNone, not a field,hasattrFalse;SyntheticControlrejects the removed params at__init__andset_params,get_paramsno longer lists them);tests/test_v4_rename_mechanical.py(tests of the four deleted helpers removed);tests/test_v4_rename_semantic.py,tests/test_methodology_placebo.py(callers passpost_periods);tests/test_v4_matrix.py(row floor / id snapshot / docstrings);tests/test_naming_guard.py(allowlist moves). Green locally: the 4.0 gate suites (test_v4_matrix,test_naming_guard,test_tracking_files,test_changelog_fragments,test_guides,test_docs_ia,test_doc_deps_integrity,test_v4_diagnostic_family_docs,test_base_estimator,test_practitioner) plus every per-estimator suite whose results class changed (test_staggered,test_sun_abraham,test_imputation,test_two_stage,test_stacked_did,test_efficient_did,test_wooldridge,test_chaisemartin_dhaultfoeuille,test_continuous_did,test_dml_did,test_twfe_event_study_decoupling); a simulated__version__ = 4.0.0bump keepstest_v4_matrix+test_naming_guardgreen (reverted). Sphinx-Wbuild green with no autosummary drift beyond the deliberate stub edits. Python 3.9compileallof the touched test files green.pytest --nbmakegreen on04_parallel_trends(its placebo cell now yields fivePlaceboTestResults, noERRORlines - the notebook has no committed outputs, item 10 re-executes it) and17_brand_awareness_survey; 17 re-executed on the Rust backend with the worktree kernel - the cell-level diff touches only the stored practitioner-snippet text (plus the ipykernel path prefix, normalized back); no number moved.Security / privacy
Changelog
changelog.d/20261101-v4-pr9b-shim-retirement-placebo-time.md)