Skip to content

docs(v4): PR 4c-ii-b of the 4.0 cut - methodology/benchmark prose, tracking rows and the oracle fixture's mpd_twin prune - #871

Closed
igerber wants to merge 1 commit into
feat/v4-pr4c-ii-remove-mpdfrom
feat/v4-pr4c-ii-b-docs-benchmarks
Closed

igerber wants to merge 1 commit into
feat/v4-pr4c-ii-remove-mpdfrom
feat/v4-pr4c-ii-b-docs-benchmarks

Conversation

@igerber

@igerber igerber commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • 4.0 cut, PR 4c-ii-b: the docs/benchmarks half of the MultiPeriodDiD removal, stacked on PR 4c-ii-a (feat(v4): PR 4c-ii-a of the 4.0 cut - remove MultiPeriodDiD, MultiPeriodDiDResults, PeriodEffect and EventStudy #870; this PR's base is that branch and retargets to main once it lands). Item 4c-ii was split because the single diff exceeded the AI review input budget; nothing here is needed by a CI gate or the REGISTRY deviation rule in the same diff as the source, which is why it could be deferred.
  • Methodology / tracking prose: docs/methodology/REGISTRY.md (the remaining historical mentions outside the archive note and the two Notes that shipped in feat(v4): PR 4c-ii-a of the 4.0 cut - remove MultiPeriodDiD, MultiPeriodDiDResults, PeriodEffect and EventStudy #870), REPORTING.md, survey-theory.md, variance-conventions.md (one prose line; the generated table is unchanged from feat(v4): PR 4c-ii-a of the 4.0 cut - remove MultiPeriodDiD, MultiPeriodDiDResults, PeriodEffect and EventStudy #870), METHODOLOGY_REVIEW.md, docs/benchmarks.rst (prose names the live TWFE event-study arm; the committed 2026-07 refresh tables stay byte-identical with a provenance sentence), docs/survey-roadmap.md, docs/performance-plan.md, CONTRIBUTING.md, the repo debugging-notes file, the Rust rank-deficiency comment. TODO rows 33/35/38/95/99 reworded (the 4c-ii-a rows 89 and the two new rows are unchanged); three DEFERRED rows that governed the removed class are retired.
  • Benchmarks (user decision 2026-10-25: relabel + local parity run only): the live event-study arm is the NEW benchmarks/python/benchmark_twfe_event_study.py (TwoWayFixedEffects(cluster=unit).fit(..., event_study=True, spec="within"), same CLI and output keys as the frozen script; period_effects converted to reference-relative event_time as before; the headline is the post-period average with the former container's exact arithmetic). benchmarks/run_benchmarks.py calls it. benchmark_multiperiod.py and benchmarks/refresh_2026_07/ stay frozen (header comment): that harness runs the script inside pinned 3.5.3/3.7.0 venvs that predate event_study=True. speed_review/bench_brand_awareness_survey.py relabels to spec="pooled" (the 3.x call passed unit= to a class that read it only for Conley, so the measured design was the pooled one); benchmark_honest.py drops the fake per-period record type; the two R scripts update comments only.
  • Oracle fixture: tests/fixtures/v4_twfe_event_study_oracles.json drops its mpd_twin records and the mpd_ctor/mpd_fit echo keys (every twfe record and the provenance block byte-identical; pruned through the capture script's own dump_fixture serializer); tests/helpers/twfe_es_oracle_lanes.py loses the MPD twin fields; tests/_capture_v4_merge_mpd_oracles.py is TWFE-only and documents that a re-run is a re-baseline (the independent twin is gone); tests/fixtures/README.md records the per-lane capture SHAs (68e383b; d45d531 for the two clustered hc2_bm lanes).

Methodology references (required if estimator / math changes)

  • Method name(s): N/A - prose, tracking files and benchmark scripts only; no diff_diff/ change.
  • Paper / source link(s): N/A
  • Any intentional deviations from the source (and why): None

Validation

  • Tests added/updated: no test logic changes; the oracle fixture prune and the lane-metadata cleanup are pinned by tests/test_twfe_event_study_decoupling.py (31 passed on python and rust). Gates on this branch: naming guard (docs lane), tracking files, variance-conventions table gate, changelog fragments, docs IA, doc snippets, guides, doc-deps, the review-script section map, the ledger matrix: 1354 passed.
  • Backtest / simulation / notebook evidence (if applicable): local R parity run of the new arm (run_benchmarks.py --estimator multiperiod --scale small, R + fixest 0.14.2): Python vs R headline ATT differ by 2e-12, SE relative difference 0.0%, per-period effects correlation 1.000 (max att diff 3e-11), compare_estimates PASS; the p-value/CI differ as before because R uses nobs - k while the surface uses its shared post-row df (not gated).

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/20261025-v4-pr4c-ii-b-docs-benchmarks.md (### Internal)

Follow-ups

@github-actions

Copy link
Copy Markdown

Overall assessment: ✅ Looks good

No unmitigated P0 or P1 findings identified in the supplied diff.

Executive summary

  • The live benchmark preserves the documented unit-FE event-study design and covariance-aware post-period average.
  • The survey benchmark explicitly selects the appropriate pooled design.
  • All 13 retained TWFE fixture records and their provenance are unchanged.
  • Python syntax and changelog-fragment checks pass. Numerical tests could not run because NumPy is unavailable.

Methodology

No undocumented methodology mismatch found. Reviewed simultaneous-adoption TWFE event studies against the Registry’s Freyaldenhoven/Wooldridge specification, and HonestDiD input requirements against the local Rambachan–Roth source review.

The new average uses (1'V1/m^2), including cross-period covariance, and delegates inference to safe_inference() (benchmarks/python/benchmark_twfe_event_study.py:L138-L166).

  • Severity: P3 — Documented inference convention.
    Impact: Python and R headline p-values/CIs can differ through their degrees-of-freedom conventions. The benchmark documents this, and the Registry explicitly records the residual-df convention (docs/methodology/REGISTRY.md:L593-L615).
    Concrete fix: None required.

Code Quality

No findings. The new averaging path handles empty post-period selections, missing covariance, NaN effects, and invalid variance without introducing inline t-statistic computation.

Performance

No findings. The benchmark continues to time the estimator fit; summary extraction remains outside the timed block.

Maintainability

No findings. The live benchmark is clearly distinguished from the frozen scripts used by pinned historical environments.

Tech Debt

  • Severity: P3 — Independent R regression fixture remains tracked.
    Impact: The retained self-capture detects regressions but cannot establish correctness against an external implementation. This limitation is explicitly tracked in TODO.md:L97.
    Concrete fix: No approval action required; the tracked follow-up specifies committed fixest coefficients and covariance blocks.

Security

No findings in the changed code or data.

Documentation/Tests

No actionable findings. The changelog fragment follows the repository convention, and the fixture documentation explains capture provenance and re-baselining.

Validation completed: six changed Python files parsed successfully; changelog checks passed; reconstructing the prior fixture from the supplied diff confirmed unchanged TWFE records and provenance. Numerical tests and the reported R parity run were not independently rerun.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Oct 26, 2026
…acking rows and the oracle fixture's mpd_twin prune

The docs/benchmarks half of the MultiPeriodDiD removal (split from PR
4c-ii-a because the single diff exceeded the AI review input budget).

REGISTRY / REPORTING / survey-theory / METHODOLOGY_REVIEW and the remaining
user-doc prose (benchmarks.rst, survey-roadmap, performance-plan,
CONTRIBUTING, the repo debugging notes, the rust rank-deficiency comment)
stop describing the removed class as live. TODO rows 33/35/38/95/99 reworded;
three DEFERRED rows that governed the class retired.

Benchmarks: new live arm benchmarks/python/benchmark_twfe_event_study.py
(spec="within", cluster=unit; parity vs R fixest::feols(... | unit) verified
locally - ATT 2e-12, SE 0.0%, period-effect correlation 1.000);
benchmark_multiperiod.py and benchmarks/refresh_2026_07 stay frozen for the
pinned 3.x-wheel harness (header comment says so); speed_review and
benchmark_honest drop the removed names.

Oracle fixture: the mpd_twin records and the lane metadata's MPD twin fields
are pruned (TWFE records byte-identical); the capture script is TWFE-only
and documents the per-lane capture SHAs; README section updated.
@igerber
igerber force-pushed the feat/v4-pr4c-ii-b-docs-benchmarks branch from ffe8f7c to 3ddb83e Compare October 26, 2026 09:53
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 3ddb83eaf991954deaea9e120104acc761fb5fd0


Overall assessment: ✅ Looks good

The prior review remains supported. No unmitigated P0 or P1 findings identified in this diff.

Executive summary

  • The live benchmark preserves the simultaneous-adoption unit-FE design and covariance-aware post-period average.
  • The survey benchmark explicitly preserves its former pooled specification.
  • All 13 retained TWFE oracle records and provenance are unchanged; all lane definitions match their fixture echoes.
  • Six changed Python files parse successfully, and changelog validation passes.
  • Numerical tests and R parity were not rerun because NumPy, SciPy, pandas, and pytest are unavailable.

Methodology

Cross-checked the benchmark specification and averaging formula against the Registry’s Freyaldenhoven/Wooldridge references, and the survey benchmark’s HonestDiD inputs against the local Rambachan–Roth source review. No undocumented mismatch found.

The new average correctly includes cross-period covariance through (1'V1/m^2) and delegates inference to safe_inference() (benchmarks/python/benchmark_twfe_event_study.py:L138-L166).

  • Severity: P3 — Documented inference convention.
    Impact: Python and R headline p-values/CIs may differ because their degrees of freedom differ. The benchmark explains this, and the Registry documents the convention (docs/methodology/REGISTRY.md:L593-L618).
    Concrete fix: None required.

Code Quality

No findings. The new averaging path handles empty selections, missing covariance, NaN effects, and invalid variance. Zero or non-finite SE produces undefined inference consistently.

Performance

No findings. Summary extraction remains outside the timed estimator fit.

Maintainability

No findings. The live and frozen benchmark paths are clearly distinguished. Static comparison confirms the frozen Python benchmark’s executable code is unchanged.

Tech Debt

  • Severity: P3 — Independent R regression fixture remains tracked.
    Impact: The retained self-capture detects subsequent regressions but cannot establish correctness against an external implementation. This limitation is tracked in TODO.md:L97.
    Concrete fix: No approval action required; complete the tracked fixest coefficient and covariance fixture in a follow-up.

Security

No findings in the changed code or fixture data.

Documentation/Tests

No actionable findings. The changelog fragment follows repository conventions, and fixture documentation explains capture provenance and re-baselining.

Static validation confirmed that the fixture changes only remove MPD records and echo keys, and that the new benchmark’s standard CLI arguments/defaults match the frozen arm. Runtime numerical behavior remains independently unverified in this environment.

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