Run before pushing — the ungated Lint workflow enforces these on every PR
push, and its Lint Gate job is a required status check:
ruff check diff_diff tests
black diff_diff tests
mypy diff_diff- Tool versions are pinned exactly in the
devextra ofpyproject.toml(black,ruff,mypy) and mirrored in.github/workflows/lint.yml(all three; the sync is enforced byTestLintWorkflowPinSync).mypy diff_diffis enforced at zero errors by the Lint workflow's Mypy job. A version bump is a deliberate PR updating both surfaces together. Refresh withpip install -e ".[dev]". The pinned tools require Python >= 3.10 (dev tooling only; the library itself still supports Python 3.9). [tool.ruff.lint.per-file-ignores]entries are deliberate (trop logger-before-imports E402, honest_did math-notationlE741,__init__re-export F401, conftest import ordering E402). Do not "fix" those patterns or remove the ignores.- The 2026-07 repo-wide normalization commits are listed in
.git-blame-ignore-revs; rungit config blame.ignoreRevsFile .git-blame-ignore-revsonce sogit blameattributes lines to their real authors. - Branches created before the normalization commits: rebase onto main, then
run
ruff check --fix+blackon your touched files before pushing.
When implementing new functionality, always include accompanying documentation updates.
README.md is the GitHub/PyPI first-impression surface. Keep it lean (~190 lines). Most new content does NOT belong here.
Only edit README.md when:
- A new estimator is added (one line in the
## Estimatorsflat catalog) - A new top-level capability lands (one paragraph in
## Diagnostics & Sensitivityor## Survey Support) - Hero image, badges, or top-of-fold value-prop changes
- Documentation links rot
If you find yourself adding a usage example, a parameter table, or a multi-paragraph explanation to the README, you are in the wrong file - those belong on RTD or in diff_diff/guides/llms.txt.
-
diff_diff/guides/llms.txt(AI-agent source of truth) - Add:- One-line catalog entry in the
## Estimatorssection with paper citation + RTD link - One-line entry in
## Diagnostics and Sensitivity Analysisif applicable - This file is published on RTD via
docs/conf.pyhtml_extra_pathand bundled in the wheel viaget_llm_guide()- it is the canonical machine-readable contract
- One-line catalog entry in the
-
docs/api/*.rst(technical source of truth) - Add:- RST documentation with
autoclassdirectives - Method summaries
- References to academic papers
- RST documentation with
-
docs/tutorials/*.ipynb- Update relevant tutorial or create new one:- Working code examples
- Explanation of when/why to use the feature
-
docs/references.rst(bibliography source of truth) - Add:- Full citation under the appropriate sub-section (matches the
### Subsectionheadings already in that file) - Use the RST format:
**Author (Year).** "Title." *Journal*, vol(num), pages. <https://doi.org/X>
- Full citation under the appropriate sub-section (matches the
-
README.md- Add ONLY:- One line in the
## Estimatorscatalog with the paper citation and RTD link
- One line in the
-
CHANGELOG.md- Add a release-note bullet under the next unreleased version. -
CLAUDE.md- Update only if adding new critical rules or design patterns. -
ROADMAP.md- Update only if shipping moves an item from planned to current. -
docs/doc-deps.yaml- Add source-to-doc mappings for the new module.
- Update relevant docstrings
- Add/update tests
- Update
CHANGELOG.md - If methodology-related: Update
docs/methodology/REGISTRY.mdedge cases section - README is almost never the right place - skip it unless the bug was in a README claim
For methods based on academic papers, always include:
- Full citation in
docs/references.rstunder the appropriate### Subsectionheading (NOT in README) - Reference in RST API docs with paper details
- Citation in tutorial summary
- Optional: methodology reference in
docs/methodology/REGISTRY.mdfor non-trivial design choices
Example format (RST):
- **Sun, L., & Abraham, S. (2021).** "Estimating Dynamic Treatment Effects in Event Studies with Heterogeneous Treatment Effects." *Journal of Econometrics*, 225(2), 175-199. https://doi.org/10.1016/j.jeconom.2020.09.006
- Don't just test that code runs without exception
- Assert the expected behavior actually occurred
- Bad:
result = func(bad_input)(only tests no crash) - Good:
result = func(bad_input); assert np.isnan(result.coef)(tests behavior)
- Test parameter appears in
get_params()output - Test
set_params()modifies the attribute - Test parameter actually affects behavior (not just stored)
- Capture warnings with
warnings.catch_warnings(record=True) - Assert warning message was emitted
- Assert the warned-about behavior occurred
Use assert_nan_inference() from conftest.py to validate ALL inference fields are
NaN-consistent. Don't check individual fields separately.