Skip to content

test(v4): PR 3a of the 4.0 cut - migrate the suites to the post-fit aggregation containers - #863

Merged
igerber merged 3 commits into
mainfrom
feat/v4-pr3a-aggregate-test-migration
Oct 20, 2026
Merged

igerber merged 3 commits into
mainfrom
feat/v4-pr3a-aggregate-test-migration

Conversation

@igerber

@igerber igerber commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • 4.0 cut, PR 3a of three (3a tests-first, 3b mechanical removals, 3c the Imputation/TwoStage/ContinuousDiD removals + relay). feat(v4): PR 3 of the 4.0 cut - remove the fit-time aggregate=/balance_e= keywords #861 carried all three in one 1.5 MB diff that could not be reviewed; this is the tests-and-benchmarks piece, with no diff_diff/ change and no ledger row flips. The fit-time aggregate= / balance_e= keywords still work and still warn.
  • Test and benchmark sites read event-study / group surfaces from results.aggregate("event_study") / results.aggregate("group") (the container API that has existed since 3.9) instead of fit(aggregate=...) and the legacy event_study_effects / group_effects fields. Bootstrapped ImputationDiD / TwoStageDiD / ContinuousDiD sites stay on the fit-time route until PR 3c adds the post-fit relay (the post-fit route raises NotImplementedError on those fits today).
  • tests/test_aggregate_contract.py no longer uses the fit-time route as its numerical reference: every analytic surface is pinned at rtol=1e-12 and every bootstrapped CS / EfficientDiD replay at the replay-parity tolerances against tests/fixtures/v4_aggregate_oracles.json, captured by tests/_capture_v4_aggregate_oracles.py under both weight backends on a tree whose diff_diff/ is byte-identical to 8724fecd (origin/main). Every design arm the file exercises is captured; each event-study bundle carries ref_labels so reference-row membership is pinned by the oracle rather than read off the container under test (a tamper regression test covers this). The Test*Shim classes, fit-time fixtures and fail-closed pins stay until the removals land.
  • New tests/helpers/legacy_surface.py (es_dict, group_dict, legacy_standin) for consumer tests whose subject is the native (legacy-pickle) route.
  • Benchmarks (benchmarks/python/benchmark_callaway.py, benchmark_imputation.py, the speed_review scripts) call aggregate() post-fit inside the timed region and emit the same JSON row shape benchmarks/refresh_2026_07/run_refresh.py joins on.
  • docs/v4-design.md section 9 records the three-way split of cut-sequence item 3 (3a flips no rows, by design).

Methodology references (required if estimator / math changes)

  • Method name(s): N/A - no methodology changes (tests, benchmarks, fixture and spec note only)
  • Paper / source link(s): N/A
  • Any intentional deviations from the source (and why): None

Validation

  • Tests added/updated: tests/test_aggregate_contract.py (oracle conversion, 463 tests, run under DIFF_DIFF_BACKEND=python and rust), tests/helpers/legacy_surface.py, tests/fixtures/v4_aggregate_oracles.json + README.md, tests/_capture_v4_aggregate_oracles.py, and ~40 migrated suites (test_staggered, test_csdid_ported, test_methodology_callaway, test_efficient_did, test_imputation, test_two_stage, test_continuous_did, test_had, test_had_pretests, test_diagnostic_report, test_event_study_surface, test_honest_did, test_bootstrap_chunking, test_visualization, test_business_report, test_event_study_consumers, test_lpdid, the survey phase suites, ...). All touched suites pass against unchanged main source: 3,237 + 463 passed.
  • Backtest / simulation / notebook evidence (if applicable): benchmark_callaway.py and benchmark_imputation.py smoke-run on a 36-unit three-cohort panel; emitted event_study / group_effects rows keep the {event_time|group, att, se} shape.

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/20261020-v4-pr3a-aggregate-test-migration.md (### Internal)

Follow-ups

…ggregation containers

Spec section 9 item 3, split three ways (3a/3b/3c) so each piece fits the AI
reviewer's input cap. This is the tests-first piece: no library source change
and no ledger row flips; the deprecated fit-time aggregate=/balance_e= keywords
still work and still warn.

- Test and benchmark sites read event-study / group surfaces from
  results.aggregate("event_study") / ("group") instead of fit(aggregate=...)
  and the legacy event_study_effects / group_effects fields. Bootstrapped
  ImputationDiD / TwoStageDiD / ContinuousDiD sites stay on the fit-time route
  until PR 3c adds the post-fit relay (the post-fit route raises
  NotImplementedError there today).
- tests/test_aggregate_contract.py: fit-time-reference parity converted to
  committed per-backend numeric oracles (tests/fixtures/v4_aggregate_oracles.json,
  captured on the unmodified tree at 8724fec by
  tests/_capture_v4_aggregate_oracles.py); the Test*Shim classes, fit-time
  fixtures and fail-closed pins stay until the removals land.
- New tests/helpers/legacy_surface.py (es_dict, group_dict, legacy_standin)
  for consumer tests whose subject is the native (legacy-pickle) route.
- Benchmarks call aggregate() post-fit inside the timed region and emit the
  same JSON row shape the R refresh joins on.
- docs/v4-design.md section 9 records the split; changelog fragment under
  Internal.
@github-actions

Copy link
Copy Markdown

Overall assessment

✅ Looks good — no unmitigated P0/P1 findings. One nonblocking P2 test issue.

Executive summary

  • No library source, estimator equations, weighting, or variance implementations change.
  • The migration follows the Registry’s documented aggregation and bootstrap contracts.
  • One clustering assertion can pass without detecting any variance difference.
  • Static checks passed; runtime tests could not run because dependencies are unavailable.

Methodology

No defects identified. The migrated tests preserve R/Stata numerical comparisons and respect documented reference-row normalization, backend-specific bootstrap streams, and level-matched replicate inference. These conventions are covered by the existing Notes in docs/methodology/REGISTRY.md.

Code Quality

No additional findings.

Performance

No findings. Aggregations remain inside benchmark timing regions, including the separate bootstrap replays required by the post-fit API.

Maintainability

No findings. Shared helpers centralize the legacy-surface conversions.

Tech Debt

P3 — Existing bootstrap limitations remain tracked.

  • Location: TODO.md:L40-L41.
  • Impact: Bootstrapped ImputationDiD, TwoStageDiD, and ContinuousDiD tests appropriately retain their supported fit-time routes.
  • Concrete fix: None required for this PR; the relay work is already tracked.

Security

No findings in the changed code or fixtures.

Documentation/Tests

P2 — Clustering comparison succeeds because reference SEs are NaN.

  • Location: tests/test_aggregate_contract.py:L4217-L4219.
  • Impact: Both surfaces contain a reference row with a NaN SE. With its default equal_nan=False, np.array_equal() returns false even when every estimated SE matches. Consequently, the negated assertion passes if post-fit aggregation ignores cluster="cl", weakening the numerical parity check it replaces.
  • Concrete fix: Pass equal_nan=True or compare only non-reference rows. Preserve the former numerical coverage by adding the clustered TwoStageDiD arm to the captured event-study/group oracles.

Validation: all 48 changed Python files parsed successfully. Fixture checks found no label/covariance-shape issues or inference-consistency issues across 870 non-reference rows. Pytest execution was unavailable because pytest, NumPy, pandas, and SciPy are absent.

…ed oracle

Review follow-up (PR 3a, P2). The clustered TwoStageDiD contract test compared the
clustered and unclustered event-study SE arrays with a negated np.array_equal; both
carry a NaN reference-row SE, so array_equal was always False and the assertion
passed even if the post-fit aggregation ignored cluster=. The numerical coverage the
fit-time comparison used to give was also missing on this arm.

- tests/test_aggregate_contract.py: _twostage_clustered_panel() helper (mirrors
  _imputation_clustered_panel); test_cluster_arm_matches_oracle pins the
  event-study and group surfaces to the new TwoStageDiD clustered_all oracle arm at
  ANALYTIC_TOL, and the clustering-changes-variance check now compares the
  estimated (non-reference) rows only, asserting they are finite and not allclose.
- tests/_capture_v4_aggregate_oracles.py: capture the clustered_all arm.
- tests/fixtures/v4_aggregate_oracles.json: the one new arm; re-captured under both
  backends on a tree whose diff_diff/ is byte-identical to the recorded
  captured_at_head, every pre-existing analytic and bootstrap arm reproduced exactly
  (0 differences), numpy and rust analytic within rtol 1e-12 / atol 1e-13.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: af781b33f4daded82d5484ae0928e80232b91890


Overall assessment

✅ Looks good — the previous P2 finding is resolved. No unmitigated P0/P1 findings identified.

Executive summary

  • The clustering assertion now compares finite SEs on non-reference rows, removing the NaN-driven false pass.
  • Clustered TwoStageDiD event-study, group, and covariance results are pinned to captured oracles.
  • No library source or estimator equations change.
  • All 48 changed Python files parse successfully; fixture consistency and changelog checks passed.
  • Runtime tests could not run because pytest, NumPy, pandas, and SciPy are unavailable.

Methodology

No defects identified. The revised clustering coverage agrees with the Gardner GMM variance contract and documented reference-row conventions in docs/methodology/REGISTRY.md:L2265-L2289 and L2305, corroborated by the repository’s Gardner paper review.

Code Quality

No findings.

Performance

No new findings. The clustering regression adds a small deterministic fit without changing production execution.

Maintainability

No findings. The capture script and test use the same clustered-panel builder.

Tech Debt

P3 — Existing bootstrap limitations remain tracked.

  • Location: TODO.md:L40-L41.
  • Impact: Bootstrapped ImputationDiD, TwoStageDiD, and ContinuousDiD retain their supported fit-time aggregation routes.
  • Concrete fix: None required for this PR; relay work is already tracked.

Security

No findings in the changed code or fixtures.

Documentation/Tests

Previous P2 resolved — clustering comparison is now meaningful.

  • Locations: tests/test_aggregate_contract.py:L4214-L4233, tests/_capture_v4_aggregate_oracles.py:L291-L293, tests/fixtures/v4_aggregate_oracles.json:L1012-L1035.
  • Impact: Matching reference-row NaNs can no longer make the variance-difference assertion pass. Both clustered aggregation surfaces and the joint covariance also receive numerical regression coverage.
  • Concrete fix: Implemented; no further action required.

Validation found no label, covariance-shape, covariance-diagonal, or inference-consistency problems across 110 fixture bundles, including 876 non-reference rows and 32 covariance matrices. The changelog fragment check passed.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Oct 20, 2026
…py t.ppf drift)

CI follow-up (PR 3a). The Python 3.9 leg failed seven analytic oracle checks, every one
on a survey arm's ci_lo at relative differences of 1e-12 to 3e-11 against ANALYTIC_TOL's
rtol 1e-12; the other 16 legs passed. That leg pins scipy 1.13.1, whose t.ppf differs
from 1.14+ by up to 3.4e-11 relative for df in the 100-130 range (the survey arms'
df), while t.sf (p-values) agrees to 1e-15. Reproduced: scipy 1.13.1 + numpy 2.0.2 on
Python 3.12 fails exactly those seven ids and passes 463/463 with this change.

- tests/test_aggregate_contract.py: CI_RTOL = 1e-9, documented beside ANALYTIC_TOL; the
  three oracle helpers compare ci_lo / ci_hi (and the sup-t band bounds) at
  max(rtol, CI_RTOL). att / se / t_stat / p_value / vcov keep the arithmetic tolerance.
- changelog fragment: states the CI-bound tolerance.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: d8a9191f64e37372a21f3df4fae42e156a49693d


Overall assessment

✅ Looks good — the previous clustering finding remains resolved. No unmitigated P0/P1 findings identified.

Executive summary

  • Clustered TwoStageDiD tests compare finite SEs on non-reference rows and retain numerical oracle coverage.
  • The new rtol=1e-9 tolerance applies only to interval bounds; estimates, SEs, statistics, p-values, and covariance retain their stricter tolerances.
  • Library source is unchanged from the oracle baseline.
  • All 48 changed Python files parse; fixture consistency and changelog checks pass.
  • Runtime tests could not run because pytest, NumPy, pandas, and SciPy are unavailable.

Methodology

No findings. The clustering assertions agree with the Gardner GMM variance contract and covariance conventions in docs/methodology/REGISTRY.md:L2265-L2289 and L2305, corroborated by docs/methodology/papers/gardner-2022-review.md.

The interval tolerance adjustment in tests/test_aggregate_contract.py:L59-L72 accommodates numerical variation without changing estimator methodology.

Code Quality

No findings. No new inline-inference or zero-SE fallback anti-patterns were found.

Performance

No new findings. Migrated benchmark aggregations remain inside their timed regions where both aggregation surfaces are measured.

Maintainability

No findings. The clustered-panel builder remains shared between oracle capture and regression tests.

Tech Debt

P3 — Existing bootstrap limitations remain tracked.

  • Location: TODO.md:L40-L41.
  • Impact: Bootstrapped ImputationDiD, TwoStageDiD, and ContinuousDiD tests retain supported fit-time aggregation routes pending the relay.
  • Concrete fix: None required in this PR; follow-up work is tracked.

Security

No findings in the changed code or fixtures.

Documentation/Tests

Previous P2 resolved — clustering comparison remains meaningful.

  • Location: tests/test_aggregate_contract.py:L4228-L4247.
  • Impact: Reference-row NaNs cannot make the variance-difference assertion pass; clustered event-study and group surfaces are checked against oracles.
  • Concrete fix: Implemented; no further action required.

Static validation found no label, inference-consistency, covariance-shape, symmetry, or diagonal problems across 110 fixture bundles, including 876 non-reference rows and 32 covariance matrices. The changelog fragment check and diff whitespace check passed. Runtime behavior remains unverified in this environment.

@igerber
igerber merged commit 13155a9 into main Oct 20, 2026
33 checks passed
@igerber
igerber deleted the feat/v4-pr3a-aggregate-test-migration branch October 20, 2026 22:36
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