Skip to content

Commit b3d55c2

Browse files
committed
Bump version to 3.11.1
First release cut through the changelog-fragment compiler: changelog_compile.py compiled the 7 pending changelog.d/ fragments (#807 replicate-weight DML survey designs, #808 infra sweep, plus the migrated #801/#803/#804/#805 entries and the fragment-system note) into the ## [3.11.1] - 2026-08-30 section, wrote the comparison link, and emptied the fragment store. Version synced across pyproject.toml, diff_diff/__init__.py, rust/Cargo.toml, llms-full.txt, and CITATION.cff (version + date-released). Release-build-check run 33322915869 passed on main before this bump.
1 parent 68ebc07 commit b3d55c2

13 files changed

Lines changed: 123 additions & 119 deletions

CHANGELOG.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,122 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
<!-- entries live in changelog.d/*.md; compiled at release by .claude/scripts/changelog_compile.py -->
1111

12+
## [3.11.1] - 2026-08-30
13+
14+
### Added
15+
- **DMLDiD replicate-weight survey designs**: `DMLDiD` now accepts
16+
replicate-weight `SurveyDesign`s (BRR / Fay / JK1 / JKn / SDR) on both
17+
lanes (panel and repeated cross sections), computing per-cell AND
18+
aggregate variances by IF-reweighting the augmented cross-fitted scores
19+
(`compute_replicate_if_variance`; nuisances are not re-estimated per
20+
replicate). Inference uses `df = rank(replicate matrix) - 1` with
21+
`min(df_survey, n_valid - 1)` capping; degenerate cells (zero or
22+
non-finite replicate variance) fail closed to NaN inference. Replicate +
23+
`cluster=` and replicate + `n_bootstrap > 0` are rejected with targeted
24+
errors (previously all replicate designs failed closed with a blanket
25+
`NotImplementedError`).
26+
- **Chang (2020) §4.2.2 RCS simulation-DGP replication** (`tests/test_methodology_dml_did.py`,
27+
DML PR-B2): the paper's own kernel-design repeated-cross-section DGP as
28+
maintainer validation fixtures for `DMLDiD(panel=False)` — a DGP-shape pin
29+
(distributions, all three innovation scales, the design's built-in confounded
30+
contrast → θ₀+1, and both correct-specification facts), seed-pinned recovery at
31+
both paper sample sizes with a discriminating comparison against the unadjusted
32+
contrast, and a slow Monte Carlo coverage lane. The §4.2 parameterizations are
33+
extracted into the paper review; the §4.2.1 ML design is documented as not
34+
replicable with the bundled unpenalized learners (narrowed TODO row) and the
35+
REGISTRY carries the replication-scope Note.
36+
37+
### Changed
38+
- **`n_bootstrap` type guards aligned onto `utils.validate_n_bootstrap`**
39+
for the estimators the M-081 sweep deliberately left out —
40+
`HeterogeneousAdoptionDiD`, `ChaisemartinDHaultfoeuille`, `TROP`,
41+
`SyntheticDiD` (jackknife lane included), plus the two HAD pretest
42+
helpers (`stute_test`, `stute_joint_pretest`): previously-accepted
43+
type-blind values now raise the shared message — `True` (silently ran as
44+
1 replicate on HAD/dCDH), floats like `2.5` (passed the `>= 2` floors),
45+
and bool/negative under SyntheticDiD's jackknife floor exemption. The
46+
estimator-specific floors are unchanged and keep their own messages for
47+
non-negative sub-floor integers (TROP/SDiD `n_bootstrap=1`, HAD `0`);
48+
NEGATIVE values now surface the shared validator's message instead of
49+
each estimator's former wording.
50+
- **`honest_did` inference-df resolution consolidated** onto the shared
51+
`aggregation.resolve_inference_df()` (three duplicated precedence blocks
52+
removed). Same precedence; `HonestDiDResults.df_survey` is now
53+
float-typed (`31.0` where it was `31`), and a fractional `df_inference`
54+
is preserved instead of truncated.
55+
56+
### Fixed
57+
- **Per-row event-study df provenance (M-092 completion)** for the four
58+
remaining holes — `EfficientDiD`, `ImputationDiD`, `ContinuousDiD`, and
59+
`HeterogeneousAdoptionDiD`: each results class gains a results-level
60+
`event_study_df` scalar (appended last; positional `__init__` indexes
61+
unchanged) threaded into the unified event-study container's per-row `df`
62+
column, which was all-NaN even on survey fits whose p-values were governed
63+
by a finite survey df. Finite on analytical survey fits (ImputationDiD:
64+
the final replicate-override df, level-matched on replicate replays, lead
65+
rows included; EfficientDiD: the post-overall snapshot; HAD: the
66+
unit-level design df); `None` — never the replicate-undefined `0`
67+
sentinel — on non-survey fits, on bootstrapped fits (percentile inference
68+
used no df, matching the shipped producer convention), and when no
69+
event-study surface was built. Inference values are unchanged everywhere.
70+
- **`ContinuousDiD` `survey_metadata` granularity unified across inference
71+
branches**: the bootstrap and degenerate no-post-cells arms now publish
72+
the same UNIT-level metadata as the analytical arm (the
73+
CS/EfficientDiD convention); previously they kept the obs-level resolve,
74+
so `sum_weights`/`effective_n`/`n_psu` — and `df_survey` on implicit-PSU
75+
designs — differed from the analytical arm by panel length on the same
76+
data. Metadata provenance only; estimates and inference unchanged.
77+
- **Documented (no behavior change)**: the event-study container's
78+
`df_survey` SCALAR — the fit's resolved scalar inference df — deliberately
79+
persists on bootstrapped fit-time and replayed surfaces (CS, DMLDiD,
80+
EfficientDiD identically) as the consumer channel HonestDiD's container
81+
branches read; the per-row `df` column is the inference-provenance channel
82+
that percentile bootstrap clears. Recorded as a REGISTRY Note with a
83+
cross-estimator parity pin.
84+
- **`survey_metadata` raw-scale provenance on the unit-level recompute**
85+
(CallawaySantAnna panel + repeated-cross-section lanes,
86+
`TripleDifference`/`StaggeredTripleDifference` staggered engine,
87+
`ContinuousDiD` analytical branch, `EfficientDiD`): the recompute passed
88+
the RESOLVED (mean-1 rescaled) weights as `compute_survey_metadata`'s
89+
raw weights, so `sum_weights`/`weight_range` reported the normalized
90+
scale instead of the user's original weight scale. They now report the
91+
raw scale, matching every other estimator (DMLDiD got the pattern in
92+
its survey PR). For previously-successful fits whose survey design does
93+
not alias a mutated role column, this is metadata-provenance only:
94+
estimates, SEs, p-values, CIs, `df_survey`, `n_strata`, `n_psu` are
95+
byte-identical, and `effective_n`/`design_effect` are scale-invariant
96+
(unchanged within floating-point round-off). Additionally,
97+
`ContinuousDiD`'s zero-dose-unit filter now re-resolves the survey
98+
design from pristine input rows: a design column aliasing a mutated
99+
role column (e.g. `weights` naming the dose column) previously
100+
zero-weighted every never-treated unit on filtered fits (failing with
101+
"No valid (g,t) cells"); such fits now estimate under the user's
102+
original weights, consistent with the unfiltered path.
103+
104+
### Internal
105+
- **Changelog fragments**: release notes are now authored as per-PR files
106+
under `changelog.d/` (see `changelog.d/README.md`) instead of editing
107+
`CHANGELOG.md`'s `## [Unreleased]` section, which stays pointer-only
108+
between releases (CI-enforced by `tests/test_changelog_fragments.py`) so
109+
concurrent PRs no longer conflict on the changelog. At release,
110+
`.claude/scripts/changelog_compile.py compile` merges the fragments into
111+
the new version section and deletes them. One ordering change relative to
112+
the old convention: within a category, compiled release sections list
113+
entries oldest-first (ascending fragment-filename order) rather than the
114+
newest-first order that prepending into Unreleased produced.
115+
- **Repo-process tooling** (infra sweep, part 2): a weekly `LWDiD Data
116+
Canary` CI lane that fails loudly when the SHA-pinned Prop 99 / Walmart
117+
loaders fall back to synthetic data (previously a visible-but-green test
118+
skip), then runs the replication tests it de-gates; a
119+
`tests/test_tracking_files.py` contract guard for TODO.md/DEFERRED.md
120+
(no deferred-work pointers in TODO rows, no ledger-lifecycle
121+
restatements beside `M-xxx` cross-links, documented table shapes with
122+
per-row column counts — which also surfaced and fixed two DEFERRED.md
123+
rows whose unescaped pipes broke the rendered tables and one row
124+
restating M-010's version target); and the `/push-pr-update`
125+
committed-range methodology scan restored via `premerge_scan.py --range`
126+
with the comparison ref passed as quoted data.
127+
12128
## [3.11.0] - 2026-08-29
13129

14130
### Added
@@ -5479,6 +5595,7 @@ for the full feature history leading to this release.
54795595
[2.1.2]: https://github.com/igerber/diff-diff/compare/v2.1.1...v2.1.2
54805596
[2.1.1]: https://github.com/igerber/diff-diff/compare/v2.1.0...v2.1.1
54815597
[2.1.0]: https://github.com/igerber/diff-diff/compare/v2.0.3...v2.1.0
5598+
[3.11.1]: https://github.com/igerber/diff-diff/compare/v3.11.0...v3.11.1
54825599
[3.11.0]: https://github.com/igerber/diff-diff/compare/v3.10.0...v3.11.0
54835600
[3.10.0]: https://github.com/igerber/diff-diff/compare/v3.9.1...v3.10.0
54845601
[3.9.1]: https://github.com/igerber/diff-diff/compare/v3.9.0...v3.9.1

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ authors:
77
family-names: Gerber
88
orcid: "https://orcid.org/0009-0009-3275-5591"
99
license: MIT
10-
version: "3.11.0"
11-
date-released: "2026-08-29"
10+
version: "3.11.1"
11+
date-released: "2026-08-30"
1212
doi: "10.5281/zenodo.19646175"
1313
url: "https://github.com/igerber/diff-diff"
1414
repository-code: "https://github.com/igerber/diff-diff"

changelog.d/20260829-event-study-df-provenance.md

Lines changed: 0 additions & 28 deletions
This file was deleted.

changelog.d/20260829-n-bootstrap-and-honest-did-df.md

Lines changed: 0 additions & 18 deletions
This file was deleted.

changelog.d/20260829-survey-metadata-raw-scale.md

Lines changed: 0 additions & 20 deletions
This file was deleted.

changelog.d/20260830-changelog-fragments.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

changelog.d/20260830-dml-replicate-weights.md

Lines changed: 0 additions & 12 deletions
This file was deleted.

changelog.d/20260830-dml-s42-fixtures.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

changelog.d/20260830-infra-process-sweep.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

diff_diff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ def __getattr__(name: str) -> _Any:
371371
raise AttributeError(f"module 'diff_diff' has no attribute {name!r}")
372372

373373

374-
__version__ = "3.11.0"
374+
__version__ = "3.11.1"
375375
__all__ = [
376376
# Estimators
377377
"DifferenceInDifferences",

0 commit comments

Comments
 (0)