Skip to content

Commit ff05328

Browse files
authored
test(v4): naming-completeness guard - section-8 surface sweep, section-9 phase-table agreement, rule-11 consumer coverage (#740)
1 parent 6fcc520 commit ff05328

6 files changed

Lines changed: 1553 additions & 34 deletions

File tree

.github/workflows/docs-tests.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
- 'tests/test_doc_deps_integrity.py'
1111
- 'tests/test_docs_ia.py'
1212
- 'tests/test_v4_matrix.py'
13+
- 'tests/test_naming_guard.py'
1314
# tests/conftest.py is auto-loaded by pytest for the snippet
1415
# test run and mutates sys.path + MPLBACKEND (conftest.py:14, 18);
1516
# changes there can break snippet exec without touching the test
@@ -32,6 +33,7 @@ on:
3233
- 'tests/test_doc_deps_integrity.py'
3334
- 'tests/test_docs_ia.py'
3435
- 'tests/test_v4_matrix.py'
36+
- 'tests/test_naming_guard.py'
3537
- 'tests/conftest.py'
3638
- 'pyproject.toml'
3739
# sphinx-build job mirrors RTD setup; trigger when RTD config drifts
@@ -102,6 +104,14 @@ jobs:
102104
# the main test matrix does not trigger on docs-only diffs.
103105
run: PYTHONPATH=. DIFF_DIFF_BACKEND=python pytest tests/test_v4_matrix.py -v
104106

107+
- name: Run naming-completeness guard
108+
# The section-8/section-9 naming guard (spec in the module docstring):
109+
# public-surface vocabulary sweep, phase-table agreement in both
110+
# directions, and rename consumer coverage against code_refs. Runs
111+
# here for the same reason as the matrix step - docs-only ledger/spec
112+
# edits must not bypass it.
113+
run: PYTHONPATH=. DIFF_DIFF_BACKEND=python pytest tests/test_naming_guard.py -v
114+
105115
sphinx-build:
106116
name: Sphinx HTML build (-W warnings as errors)
107117
# Skip unrelated label churn: a non-ready-for-ci label add/remove won't run this job.

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
- **Naming-completeness guard test** (`tests/test_naming_guard.py`, internal):
12+
mechanical enforcement of the three v4-program naming invariants that were
13+
previously prose-only - (1) a public-surface sweep of every
14+
`diff_diff.__all__` export's signatures, dataclass fields and property names
15+
against the section-8 rename vocabulary (every hit must be a
16+
`docs/v4-deprecations.yaml` row, structurally exempt, or allowlisted with a
17+
stated reason - first run: 0 unrowed violations, 49 documented carve-outs);
18+
(2) phase-table agreement between the ledger and `docs/v4-design.md`
19+
section 9 in both directions (bracketed-citation expansion, ship-version
20+
predicate, M-008/M-031 as named fixtures); (3) rename consumer coverage per
21+
section-8 rule 11, with tiered regex/AST lanes (ambiguous tokens like `time`
22+
match at call sites only) over `diff_diff/` source, the packaged
23+
`guides/*.txt`, and `docs/methodology/`. The guard's first run completed 26
24+
rows' `code_refs` with newly confirmed readers (e.g. `diagnostics.py`'s
25+
`fit(time=...)` call sites on [M-030], `diagnostic_report.py`'s bare
26+
`bacon_decompose(...)` call on [M-076], the packaged-guide readers of
27+
`cohort`/`aggregation`/the wrapper functions), rule 11 gained the
28+
token-family `code_refs` union clause, and the `plot_group_effects` cohort
29+
selector joined the section-8 domain-vocabulary carve-outs. Wired into the
30+
docs-tests workflow so docs-only ledger/spec edits cannot bypass it; the
31+
completed TODO guard row is removed (spec now lives in the test module
32+
docstring).
33+
1034
### Fixed
1135
- **Normal-theory tail inference on clustered analytical paths — the D4 +
1236
tail-df defect fix of the 3.9 variance-consolidation program** ([M-127]).

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ generic sparse-FE, QR+SVD rank-detection redundancy, `check_finite` bypass — m
6161
| Tighten the mypy suppressions that back the enforced-zero posture: burn down `prep_dgp`'s per-module `[index]` override (needs a None-vs-array restructure that preserves the seeded RNG stream), and evaluate re-enabling the globally disabled codes (`arg-type`, `return-value`, `var-annotated`, `assignment`) one at a time — `assignment` alone hid several real annotation drifts found during the 2026-07 triage. | `pyproject.toml` `[tool.mypy]`, `diff_diff/prep_dgp.py` | lint-CI | Mid | Low |
6262
| MMM interop follow-up: Meridian `roi_calibration_period` mask builder - accept the MMM's time index + channel order and emit the boolean `(n_media_times, n_media_channels)` mask so `.to_code()` scopes the prior to the experiment window automatically (today the caller passes a mask expression / `full_model_window=True`). | `diff_diff/mmm.py` | mmm-interop | Quick | Low |
6363
| MMM interop PR-B: calibration tutorial notebook (fit DiD/CS -> scope -> `to_pymc_marketing_lift_test` / `to_meridian_roi_prior`) + a `llms-practitioner.txt` Step 8 pointer to the exporters as the MMM hand-off. | `docs/tutorials/`, `diff_diff/guides/llms-practitioner.txt` | mmm-interop | Mid | Low |
64-
| Section-8 naming-completeness guard test: sweep the public surface (module-level functions, class `__init__`/`fit` params, results dataclass fields) for contract-rename violations and fail unless each hit is either a `docs/v4-deprecations.yaml` row or an allowlist entry carrying a stated reason (seed the allowlist with the documented domain-vocabulary exemptions: the staggered family's ATT(g,t) `group`/`groups`, `TripleDifference.fit[group]`, wrapper params dying with `M-070..M-077`, and params inherited from `DifferenceInDifferences.__init__`). Today the rules are normative prose and the ledger holds only what an audit remembered — two successive manual sweeps each found surfaces the prior one missed (`M-094`/`M-095`, then the 19-row `M-097..M-115` function sweep). Should land BEFORE Phase 2c so the rename PR works from a mechanically-verified list. Two further checks belong in the same guard, each having been missed by hand across successive review rounds: (a) **phase-table agreement** (spec amended 2026-07-31; three separate rounds caught a stale table and nothing verifies the section 9 checklist's assertion) — direction 1: every row's current `phase` appears in the matching `docs/v4-design.md` section 9 table entry; direction 2: every id cited in a cell resolves to a live ledger row whose lifecycle includes work in that phase, mechanically: a citation of row R in the cell of phase P is legitimate iff P's ship-version (phases 2/3/4 → "3.9", phase 5 → "4.0", phase 6 → "4.1") appears among R's `introduced_in`/`deprecated_in`/`removed_in` — or `decision_due` for `env-default` rows (M-008 is scheduled solely via `decision_due: "4.0"` and its phase-5 citation must pass; make it a guard fixture) — (accepts shim-phase rows re-listed in the phase-5 removal roster; rejects a row with no lifecycle work at that version, e.g. M-031 cited in phase 5). Terminal rows (`done`/`removed`) are exempt in both directions. Parsing rule: cells cite ids as single tokens (`[M-122]`), compound single brackets (`[M-030..M-047]`, comma compounds), and endpoint-bracket ranges (`[M-132]..[M-135]`) — the guard's parser expands BOTH range forms plus comma compounds when computing cell membership (single-token matching alone yields zero interior ids). Known accepted limitation: phases 2 and 3 both ship 3.9, so the predicate cannot catch a row cited in the wrong 3.9 phase; (b) **consumer coverage** (section 8 rule 11) — for each rename row, grep `diff_diff/` and `docs/methodology/` for the old name and require every hit to be either in the row's `code_refs` or allowlisted, since `getattr(obj, "old_name", default)` degrades silently rather than raising after removal. (Sequenced in v4-design §9.) | `tests/`, `docs/v4-design.md` | gating-completeness amendment | Mid | Medium |
6564
| Tracking-file contract guard test: reject NEW active deferred-work pointers at `TODO.md` (deferred rows live in `DEFERRED.md`; allowlist for historical/past-tense prose and actionable-row pointers) and assert rows cross-linking a `docs/v4-deprecations.yaml` `M-xxx` id don't restate ledger status. Origin: tracking-split local review R2. | `tests/`, `TODO.md`, `DEFERRED.md` | tracking-split | Quick | Low |
6665
| Real-data CI canary for dataset-backed replication tests: `test_methodology_lwdid.py`'s Prop 99 / Walmart goldens skip (visibly) when loaders fall back to synthetic; add a lane or canary asserting `df.attrs["source"] == "lwdid_ssc_ancillary"` in CI so network regressions cannot silently de-gate the replication tests. Follow-on from the loader-fallback repair (#723), which made provenance explicit but deliberately did not add a network-dependent CI lane. | `tests/test_methodology_lwdid.py`, `.github/workflows/` | LWDiD validation suite | Quick | Low |
6766
| df-provenance completion follow-up (PR C descoping): a Wooldridge public per-row df channel (a per-key dict that can also carry the hc2_bm BM contrast dofs the scalar `_df_analytic_fallback` cannot represent — the unified surface's Wooldridge df column stays NaN until then); ImputationDiD lead-horizon `event_study_df` (needs the `_compute_lead_coefficients`/`_aggregate_event_study` return-path change plus bootstrap-clearing semantics for post rows); and the `inference_df` (DiD/MPD/SA/StackedDiD) vs `df_inference` (CS/dCDH) adapter-naming unification. | `diff_diff/wooldridge_results.py`, `diff_diff/imputation.py`, `diff_diff/results_base.py` | #variance-PR-C | Mid | Low |

0 commit comments

Comments
 (0)