Skip to content

Commit 0ecb635

Browse files
igerberclaude
andcommitted
Address PR #402 R5 review (1 P3, doc-drift fix)
P3 dataclass-docstring drift: PR #402 R3 fixed the llms-full.txt field descriptions to acknowledge that weighted mass-point HAD fits populate variance_formula in {"pweight_2sls", "survey_binder_tsl_2sls"} and effective_dose_mean as the weighted Wald-IV dose gap (per had.py:3585-3660), but the HeterogeneousAdoptionDiDResults dataclass field docstrings in had.py:347-366 still said those fields were continuous-only / None on mass-point - leaving two source-of-truth surfaces disagreeing about the same public result object. Updated both field docstrings to enumerate all four variance_formula labels (continuous + mass-point variants under both `weights=` shortcut and `survey_design=` paths) and to describe the mass-point weighted Wald-IV dose-gap denominator semantics (`mean(D | Z=1, w) - mean(D | Z=0, w)` where Z = 1{D > d_lower}). Tests added (1 new, 90 total): - test_had_results_dataclass_docstrings_match_weighted_mass_point_contract: uses inspect.getsource(HeterogeneousAdoptionDiDResults) to scan the class source and assert the variance_formula docstring mentions both pweight_2sls and survey_binder_tsl_2sls labels, and the effective_dose_mean docstring mentions mass-point Wald-IV semantics. Locks both field docstrings against drift back to the continuous-only framing now that the llms-full.txt guide and the actual fit() code populate these on mass-point fits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d4b9091 commit 0ecb635

2 files changed

Lines changed: 73 additions & 18 deletions

File tree

diff_diff/had.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -345,25 +345,31 @@ class HeterogeneousAdoptionDiDResults:
345345

346346
# Phase 4.5 weighted-path extras (optional so unweighted fits stay unchanged)
347347
variance_formula: Optional[str] = None
348-
"""HAD-specific label for the SE formula on the weighted continuous
349-
path: ``"pweight"`` (weighted-robust CCT 2014) under ``weights=``,
350-
``"survey_binder_tsl"`` (Binder 1983 TSL with PSU/strata/FPC) under
351-
``survey=SurveyDesign(...)``, ``None`` on unweighted or mass-point
352-
fits. Orthogonal to ``survey_metadata`` which is the repo-standard
353-
:class:`diff_diff.survey.SurveyMetadata` shared with downstream
354-
report/diagnostic consumers (no HAD-specific leakage)."""
348+
"""HAD-specific label for the SE formula on weighted fits, populated
349+
on BOTH continuous and mass-point designs (Phase 4.5 A / B):
350+
``"pweight"`` (continuous, weighted-robust CCT 2014 under the
351+
``weights=`` shortcut), ``"survey_binder_tsl"`` (continuous, Binder
352+
1983 TSL with PSU/strata/FPC under ``survey_design=SurveyDesign(...)``),
353+
``"pweight_2sls"`` (mass-point, weighted 2SLS HC1/CR1 sandwich
354+
under the ``weights=`` shortcut), or ``"survey_binder_tsl_2sls"``
355+
(mass-point, Binder 1983 TSL under ``survey_design=``). ``None`` on
356+
unweighted fits. Orthogonal to ``survey_metadata`` which is the
357+
repo-standard :class:`diff_diff.survey.SurveyMetadata` shared with
358+
downstream report/diagnostic consumers (no HAD-specific leakage)."""
355359
effective_dose_mean: Optional[float] = None
356-
"""Weighted denominator used by the beta-scale rescaling on the
357-
continuous path: ``sum(w_g · D_g) / sum(w_g)`` for
358-
``continuous_at_zero`` or ``sum(w_g · (D_g - d_lower)) / sum(w_g)``
359-
for ``continuous_near_d_lower``. Reduces bit-exactly to
360-
``dose_mean`` / ``mean(D - d_lower)`` when weights are uniform or
361-
absent. ``None`` when ``fit()`` was called without
362-
``survey=`` / ``weights=`` (use ``dose_mean`` there). Exists because
363-
``dose_mean`` is the raw sample mean of the dose column; under
364-
weighted fits the estimator's actual denominator is the weighted
365-
mean, and users reconstructing the β-scale value by hand need the
366-
weighted one."""
360+
"""Weighted denominator used by the beta-scale rescaling, populated
361+
on weighted fits across all designs: ``sum(w_g · D_g) / sum(w_g)``
362+
on ``continuous_at_zero``, ``sum(w_g · (D_g - d_lower)) / sum(w_g)``
363+
on ``continuous_near_d_lower``, and the weighted Wald-IV dose gap
364+
``mean(D | Z=1, w) - mean(D | Z=0, w)`` on ``mass_point`` (where
365+
``Z = 1{D > d_lower}``). On the continuous designs reduces
366+
bit-exactly to ``dose_mean`` / ``mean(D - d_lower)`` when weights
367+
are uniform or absent. ``None`` when ``fit()`` was called without
368+
``survey_design=`` / ``survey=`` / ``weights=`` (use ``dose_mean``
369+
there). Exists because ``dose_mean`` is the raw sample mean of the
370+
dose column; under weighted fits the estimator's actual denominator
371+
is the weighted form above, and users reconstructing the β-scale
372+
value by hand need the weighted one."""
367373

368374
def __repr__(self) -> str:
369375
base = (

tests/test_practitioner.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,55 @@ def test_handle_continuous_step_4_snippet_is_valid_python(self, mock_continuous_
690690
if code.strip():
691691
ast.parse(code) # raises SyntaxError on failure
692692

693+
def test_had_results_dataclass_docstrings_match_weighted_mass_point_contract(self):
694+
# PR #402 R3 fixed the llms-full.txt field descriptions to
695+
# acknowledge that weighted mass-point fits populate
696+
# variance_formula in {"pweight_2sls", "survey_binder_tsl_2sls"}
697+
# and effective_dose_mean as the weighted Wald-IV dose gap (per
698+
# had.py:3585-3660). PR #402 R5 P3 caught that the dataclass
699+
# field docstrings still said those fields were continuous-only
700+
# / None on mass-point - leaving two source-of-truth surfaces
701+
# disagreeing about the same public result object. Lock the
702+
# dataclass docstrings against drift back to the continuous-only
703+
# framing.
704+
import inspect
705+
706+
from diff_diff.had import HeterogeneousAdoptionDiDResults
707+
708+
# Field docstrings live as raw __doc__ on the FieldDescriptor /
709+
# in __dataclass_fields__'s metadata; read them via the type's
710+
# source-level docstring attached to the class via the field's
711+
# `__doc__` after assignment in the class body.
712+
# Easier: read the class source via inspect.getsource() and check
713+
# the field-docstring blocks we care about.
714+
src = inspect.getsource(HeterogeneousAdoptionDiDResults)
715+
# variance_formula docstring must enumerate all 4 labels.
716+
assert "pweight_2sls" in src, (
717+
"HeterogeneousAdoptionDiDResults.variance_formula docstring "
718+
"must mention `pweight_2sls` (weighted mass-point HC1/CR1 "
719+
"label per had.py:3585-3629). Otherwise the dataclass "
720+
"docstring contradicts llms-full.txt and the actual "
721+
"implementation."
722+
)
723+
assert "survey_binder_tsl_2sls" in src, (
724+
"HeterogeneousAdoptionDiDResults.variance_formula docstring "
725+
"must mention `survey_binder_tsl_2sls` (weighted mass-point "
726+
"Binder-TSL label)."
727+
)
728+
# effective_dose_mean docstring must mention mass-point Wald-IV.
729+
assert "mass_point" in src or "mass-point" in src, (
730+
"HeterogeneousAdoptionDiDResults.effective_dose_mean "
731+
"docstring must mention mass-point semantics; weighted "
732+
"mass-point fits populate it as the weighted Wald-IV dose "
733+
"gap per had.py:3642-3660."
734+
)
735+
assert "Wald-IV" in src or "Z=1" in src, (
736+
"HeterogeneousAdoptionDiDResults.effective_dose_mean "
737+
"docstring must describe the weighted Wald-IV dose gap "
738+
"semantics (or the underlying Z=1/Z=0 subgroup-mean form) "
739+
"for mass-point fits."
740+
)
741+
693742
def test_had_step_3_flags_qug_under_survey_deferral(
694743
self, mock_had_results, mock_had_event_study_results
695744
):

0 commit comments

Comments
 (0)