diff --git a/.github/workflows/docs-tests.yml b/.github/workflows/docs-tests.yml index b4681d34..2881f168 100644 --- a/.github/workflows/docs-tests.yml +++ b/.github/workflows/docs-tests.yml @@ -109,7 +109,7 @@ jobs: # docs/conf.py imports diff_diff via sys.path from checked-out source. # ipython provides the 'ipython3' Pygments lexer that nbsphinx uses # for notebook code cells; without it -W fires highlighting_failure. - run: pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "matplotlib>=3.5" "ipython>=8.0" + run: pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "myst-parser>=2.0" "matplotlib>=3.5" "ipython>=8.0" - name: Build docs with warnings as errors # SPHINXOPTS="-W" turns every Sphinx warning into a build failure. @@ -145,7 +145,7 @@ jobs: # not run a Sphinx build (sphinx-build covers full rendering on 3.11). # Failure here means the docs floor declared in pyproject.toml / # .readthedocs.yaml / above is not installable on Python 3.9. - run: pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "matplotlib>=3.5" "ipython>=8.0" + run: pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "myst-parser>=2.0" "matplotlib>=3.5" "ipython>=8.0" - name: Confirm pydata-sphinx-theme version # Surface the resolved version in the log for future debugging. diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 3fd6fc0f..f61cd9c8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -18,7 +18,7 @@ build: # # Keep in sync with pyproject.toml [project.dependencies] # and [project.optional-dependencies.docs]. - - pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "matplotlib>=3.5" "ipython>=8.0" + - pip install "numpy>=1.20.0" "pandas>=1.3.0" "scipy>=1.10" "sphinx>=6.0" "pydata-sphinx-theme>=0.16.1" "sphinxext-opengraph>=0.9" "sphinx-sitemap>=2.5" "nbsphinx>=0.9" "myst-parser>=2.0" "matplotlib>=3.5" "ipython>=8.0" # Build documentation in the "docs/" directory with Sphinx sphinx: diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b45f9c..8e2fb399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 assert between the two (defense in depth). ### Added +- **REGISTRY.md and REPORTING.md are now published on Read the Docs.** The two + methodology markdown pages render as in-site Sphinx pages (MyST) under a new + "Methodology" toctree section, so cross-references from the API docs use `:doc:` links + instead of off-site `blob/main` GitHub URLs — stable-docs readers previously landed on + a different revision than their installed version. `myst-parser` joins the docs + dependency set (pyproject `docs` extra, RTD post_install, docs-tests workflow — kept in + sync); every other repo-internal markdown under `docs/` (performance notes, paper + reviews, roadmaps) is explicitly excluded from the build. Two latent doc defects + surfaced by the `-W` build are fixed: a broken ToC anchor in REGISTRY.md (typo'd + `#differenceinifferences`, broken on GitHub too) and a heading-level jump; the + "Diagnostics & Sensitivity" heading is retitled to "Diagnostics and Sensitivity" + because GitHub and docutils slug `&` differently (double vs collapsed hyphen). Full + local `make -C docs html SPHINXOPTS="-W"` passes with 0 warnings. - **`ImputationDiD` leave-one-out conservative variance** (`leave_one_out`, default `False`) — the Borusyak-Jaravel-Spiess (2024) Supplementary Appendix A.9 finite-sample refinement. The non-LOO auxiliary aggregate `tau_tilde_g` (eq. 8) is built from the fitted `tau_hat_it` and so partially diff --git a/TODO.md b/TODO.md index 97444378..0d482477 100644 --- a/TODO.md +++ b/TODO.md @@ -59,7 +59,6 @@ generic sparse-FE, QR+SVD rank-detection redundancy, `check_finite` bypass — m | Issue | Location | Origin | Effort | Priority | |-------|----------|--------|--------|----------| | SE-audit CI-lock — remaining "fiddly bits" after the second coverage batch landed (that batch pinned C2 `dof_hc2_bm`/`dof_per_coef` via CI-inversion, C3 LOO `df`, C4 estimatr HC1/CR1 intercept SE, C5 Yatchew `p`/`sigma2_lin`/`sigma2_diff`, and the G2 fixest cluster-SE band). Still deferred, each needing a golden regeneration, new computation, or a documented-deviation call — **(a) G2 hetero/cluster is DONE (2026-07-07)**: the unbalanced/heteroskedastic-DGP regen landed, the DiD-path hetero AND cluster CR1 SEs are machine-precision-locked (the plain-OLS CR1 matches fixest exactly — the DOF-convention deviation is absorbed-FE-only), and the TWFE cluster band-pin is retained for the documented non-nested-FE ssc deviation (tracked under "Needs external reference"); TWFE has no public unclustered-hetero surface (auto-cluster convention). Remaining: **(b) PlaceboTests `boundary_gap`** — a permutation randomization-inference margin NOT computed anywhere in code (a new feature + result field, not a coverage lock); **(c) StackedDiD intercept SEs** (`se_cr1/cr2_intercept`, C1) — MEASURED to diverge ~0.3% from R: a nuisance-parameter reference-cell/parameterization gap, NOT machine-precision lockable (the event-study interaction SEs already match ~2e-13; surfacing it would add an unasserted, R-divergent public field); **(d) estimatr `classical` intercept SE** — same documented `O(1/n)` projection/DOF deviation as the slope (reference-only, excluded from parity). C6 CLOSED 2026-07-08 (dr remainder measured ~6e-11 ATT / ~2e-11 rel SE on the no-cov fixture and tightened to 1e-8 with the previously-missing SE assertion added; reg/ipw ATT bands 0.02/0.05 also tightened to 1e-8; the covariate-DR 2e-3 band stays as documented small-sample numerics). C7/C8 definitions were lost with the audit session scratchpad — re-derive from a fresh loose-tolerance inventory before acting. | `benchmarks/R/generate_fixest_did_twfe_golden.R`, `tests/test_fixest_did_twfe_parity.py`, `tests/test_methodology_stacked_did.py`, `tests/test_methodology_placebo.py` | SE-audit | Mid | Low | -| Render `docs/methodology/REPORTING.md` and `REGISTRY.md` as in-site Sphinx pages so cross-refs can use `:doc:` instead of off-site `blob/main` URLs (stable-docs readers can otherwise land on a different revision than their package version). Two paths: (a) add `myst-parser` to `conf.py` + docs extras and link with `:doc:`, or (b) convert both to `.rst`. **Note:** REGISTRY.md is ~4.5k lines of LaTeX-heavy markdown — high risk under the `-W` (warnings-as-errors) Sphinx build; budget multiple rounds. | `docs/conf.py`, `docs/api/business_report.rst`, `docs/api/diagnostic_report.rst`, tutorials 18 & 19 | follow-up | Mid | Low | | Add true half-sample BRR replicate-weight regressions per estimator family (current tests use Fay-like 0.5/1.5 perturbations; `test_survey_phase6.py` covers true BRR at the helper level). | `tests/test_replicate_weight_expansion.py` | #253 | Mid | Low | | Port the CI `` extraction into the reviewer-eval harness so `docs/tutorials/*.ipynb` cases (currently guarded out of `verify-corpus`/`run`) can be reviewed with CI-equivalent context. | `tools/reviewer-eval/adapters/ci_prompt.py` | local-review | Mid | Low | diff --git a/docs/api/_autosummary/diff_diff.LPDiD.rst b/docs/api/_autosummary/diff_diff.LPDiD.rst new file mode 100644 index 00000000..ac457021 --- /dev/null +++ b/docs/api/_autosummary/diff_diff.LPDiD.rst @@ -0,0 +1,21 @@ +diff\_diff.LPDiD +================ + +.. currentmodule:: diff_diff + +.. autoclass:: LPDiD + :no-members: + + + .. rubric:: Methods + + .. autosummary:: + + ~LPDiD.__init__ + ~LPDiD.fit + ~LPDiD.get_params + ~LPDiD.set_params + + + + diff --git a/docs/api/_autosummary/diff_diff.lpdid_results.LPDiDResults.rst b/docs/api/_autosummary/diff_diff.lpdid_results.LPDiDResults.rst new file mode 100644 index 00000000..18cfe995 --- /dev/null +++ b/docs/api/_autosummary/diff_diff.lpdid_results.LPDiDResults.rst @@ -0,0 +1,58 @@ +diff\_diff.lpdid\_results.LPDiDResults +====================================== + +.. currentmodule:: diff_diff.lpdid_results + +.. autoclass:: LPDiDResults + :no-members: + + + .. rubric:: Methods + + .. autosummary:: + + ~LPDiDResults.__init__ + ~LPDiDResults.print_summary + ~LPDiDResults.summary + ~LPDiDResults.to_dataframe + ~LPDiDResults.to_dict + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~LPDiDResults.absorb + ~LPDiDResults.alpha + ~LPDiDResults.att + ~LPDiDResults.cluster_name + ~LPDiDResults.conf_int + ~LPDiDResults.covariates + ~LPDiDResults.dylags + ~LPDiDResults.estimand + ~LPDiDResults.n_clusters + ~LPDiDResults.n_psu + ~LPDiDResults.n_strata + ~LPDiDResults.non_absorbing + ~LPDiDResults.p_value + ~LPDiDResults.rank_deficient_action + ~LPDiDResults.se + ~LPDiDResults.stabilization_window + ~LPDiDResults.survey_metadata + ~LPDiDResults.t_stat + ~LPDiDResults.vcov_type + ~LPDiDResults.ylags + ~LPDiDResults.event_study + ~LPDiDResults.pooled + ~LPDiDResults.n_obs + ~LPDiDResults.n_treated_units + ~LPDiDResults.n_control_units + ~LPDiDResults.pre_window + ~LPDiDResults.post_window + ~LPDiDResults.control_group + ~LPDiDResults.reweight + ~LPDiDResults.no_composition + ~LPDiDResults.pmd + diff --git a/docs/api/business_report.rst b/docs/api/business_report.rst index 8209a864..7e9c9022 100644 --- a/docs/api/business_report.rst +++ b/docs/api/business_report.rst @@ -47,8 +47,7 @@ pretest to opt in. Methodology deviations (no traffic-light gates, pre-trends verdict thresholds, power-aware phrasing, unit-translation policy, schema -stability) are documented in `docs/methodology/REPORTING.md -`_. +stability) are documented in :doc:`/methodology/REPORTING`. The schema carries a top-level ``target_parameter`` block (experimental) naming what the headline scalar represents per @@ -63,8 +62,7 @@ distinguishes the populated-surface subcase (per-horizon table available on ``linear_trends_effects``) from the empty-surface subcase (no horizons survived estimation; re-fit with a larger ``L_max`` or with ``trends_linear=False``). See the "Target -parameter" section of `docs/methodology/REPORTING.md -`_ +parameter" section of :doc:`/methodology/REPORTING` for the full per-estimator dispatch table and schema shape. Example diff --git a/docs/api/diagnostic_report.rst b/docs/api/diagnostic_report.rst index 3ed8328f..4dc1c8b7 100644 --- a/docs/api/diagnostic_report.rst +++ b/docs/api/diagnostic_report.rst @@ -13,14 +13,12 @@ result. Methodology deviations (no traffic-light gates, opt-in placebo battery, estimator-native diagnostic routing, power-aware phrasing -threshold) are documented in `docs/methodology/REPORTING.md -`_. +threshold) are documented in :doc:`/methodology/REPORTING`. The schema carries a top-level ``target_parameter`` block (experimental) naming what the headline scalar represents per estimator. See the "Target parameter" section of -`docs/methodology/REPORTING.md -`_ +:doc:`/methodology/REPORTING` for the per-estimator dispatch and schema shape. Data-dependent checks (2x2 parallel trends on simple DiD, diff --git a/docs/conf.py b/docs/conf.py index 4baa9ccb..8cd3286e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,10 +30,38 @@ "sphinxext.opengraph", "sphinx_sitemap", "nbsphinx", + "myst_parser", ] +# MyST renders the two in-site methodology markdown pages (REGISTRY.md, +# REPORTING.md) so cross-refs use :doc: instead of off-site blob/main URLs +# (stable-docs readers otherwise land on a different revision than their +# package version). dollarmath/amsmath cover the registry's LaTeX; +# heading anchors to depth 4 make its GitHub-style #section links resolve. +myst_enable_extensions = ["dollarmath", "amsmath"] +myst_heading_anchors = 4 + + templates_path = ["_templates"] -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +# Only the two methodology pages are published; every other repo-internal +# markdown under docs/ stays out of the build (performance/benchmark notes +# are deliberately NOT on RTD — see the repo convention — and un-toctree'd +# .md files would fail the -W build as orphans). +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "business-strategy.md", + "performance-plan.md", + "performance-scenarios.md", + "practitioner-guide-evaluation.md", + "survey-roadmap.md", + "methodology/continuous-did.md", + "methodology/survey-theory.md", + # Internal paper-review notes (methodology validation artifacts). + "methodology/papers/*", + "tutorials/README.md", +] # -- Options for autodoc ----------------------------------------------------- autodoc_default_options = { diff --git a/docs/index.rst b/docs/index.rst index 59dadd96..33ed0d44 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -139,6 +139,14 @@ Quick Links api/index +.. toctree:: + :maxdepth: 1 + :caption: Methodology + :hidden: + + methodology/REGISTRY + methodology/REPORTING + What is Difference-in-Differences? ---------------------------------- diff --git a/docs/methodology/REGISTRY.md b/docs/methodology/REGISTRY.md index c99f4c1f..1ad6e535 100644 --- a/docs/methodology/REGISTRY.md +++ b/docs/methodology/REGISTRY.md @@ -7,7 +7,7 @@ This document provides the academic foundations and key implementation requireme ## Table of Contents 1. [Core DiD Estimators](#core-did-estimators) - - [DifferenceInDifferences](#differenceinifferences) + - [DifferenceInDifferences](#differenceindifferences) - [MultiPeriodDiD](#multiperioddid) - [TwoWayFixedEffects](#twowayfixedeffects) 2. [Modern Staggered Estimators](#modern-staggered-estimators) @@ -28,7 +28,7 @@ This document provides the academic foundations and key implementation requireme - [TROP](#trop) - [HeterogeneousAdoptionDiD](#heterogeneousadoptiondid) - [SpilloverDiD](#spilloverdid) -4. [Diagnostics & Sensitivity](#diagnostics--sensitivity) +4. [Diagnostics and Sensitivity](#diagnostics-and-sensitivity) - [PlaceboTests](#placebotests) - [BaconDecomposition](#bacondecomposition) - [HonestDiD](#honestdid) @@ -3449,7 +3449,7 @@ Shipped in `diff_diff/had_pretests.py` as `stute_joint_pretest()` (residuals-in --- -# Diagnostics & Sensitivity +# Diagnostics and Sensitivity ## PlaceboTests @@ -4960,7 +4960,7 @@ The 8-step workflow in `diff_diff/guides/llms-practitioner.txt` is adapted from covariates). Paper's Step 8 is "Keep learning." The mandatory with/without covariate comparison is a diff-diff convention. -### Survey DGP (`generate_survey_did_data`) +## Survey DGP (`generate_survey_did_data`) - **Note:** The `icc` parameter calibrates `psu_re_sd` using the full variance decomposition `Var(Y) = sigma²_psu * (1 + psu_period_factor²) + sigma²_unit + diff --git a/pyproject.toml b/pyproject.toml index 5c894d29..8a13efe6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ docs = [ "sphinxext-opengraph>=0.9", "sphinx-sitemap>=2.5", "nbsphinx>=0.9", + "myst-parser>=2.0", "matplotlib>=3.5", # ipython provides the 'ipython3' Pygments lexer used by nbsphinx for # notebook code cells; without it Sphinx emits highlighting_failure