Skip to content

feat(provenance): per-row event-study df threading (M-092 completion) + ContinuousDiD unit-level metadata - #803

Merged
igerber merged 2 commits into
mainfrom
fix/survey-df-provenance-cluster
Aug 29, 2026
Merged

feat(provenance): per-row event-study df threading (M-092 completion) + ContinuousDiD unit-level metadata#803
igerber merged 2 commits into
mainfrom
fix/survey-df-provenance-cluster

Conversation

@igerber

@igerber igerber commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • Close three TODO.md provenance rows in one coherent change (survey/df provenance cluster):
  • Per-row event-study df threading (M-092 completion) — the four remaining holes (EfficientDiD, ImputationDiD, ContinuousDiD, HeterogeneousAdoptionDiD) gain a results-level event_study_df scalar (appended after each class's final field; positional __init__ indexes unchanged) threaded into the unified container's per-row df column, previously all-NaN even on survey fits whose p-values used a finite survey df. Finite on analytical survey fits (ImputationDiD threads the FINAL replicate-override df, with the level-matched replay value on replicate replays and lead rows included; EfficientDiD the post-overall snapshot; HAD the unit-level design df via _from_had, plus to_dict()); None — never the replicate-undefined 0 sentinel — on non-survey fits, under bootstrap (percentile inference used no df, per the shipped staggered.py/two_stage.py producer convention), and when no event-study surface was built. Inference values byte-identical everywhere.
  • ContinuousDiD survey_metadata unified at unit level on every inference arm — the bootstrap and degenerate no-post-cells arms previously kept the obs-level resolve, so sum_weights/effective_n/n_psu (and df_survey on implicit-PSU designs) diverged from the analytic arm by panel length; the unit-level recompute is now hoisted above the branch split (metadata provenance only; estimates/inference unchanged).
  • df_survey scalar contract recorded as design — the container-level scalar (the fit's resolved scalar inference df) deliberately persists on bootstrapped fit-time and replayed surfaces (CS, DMLDiD, EfficientDiD identically): it is the consumer channel HonestDiD's container branches read as df= for their critical values, while the per-row df column is the inference-provenance channel percentile bootstrap clears (M-027). No behavior change; REGISTRY Note + cross-estimator parity pins.
  • Tracking: REGISTRY per-row-hole clauses updated (ContinuousDiD/EfficientDiD/ImputationDiD/HAD notes + a new ContinuousDiD granularity Note); docs/v4-deprecations.yaml M-092/M-093 note PROSE amended (qualifiers resolved; no status/target/lifecycle change); committed autosummary stubs regenerated for the four classes; llms-full.txt HAD field table extended (CI-enforced); TODO rows 40/41/45 removed and the #variance-PR-C row's ImputationDiD clause narrowed to the non-survey per-lead remainder.

Methodology references (required if estimator / math changes)

  • Method name(s): N/A — no methodology changes (df/metadata provenance threading only; every estimate, SE, p-value, and CI verified byte-identical on a pre/post baseline across analytical and bootstrapped survey fits of all four estimators)
  • Paper / source link(s): N/A
  • Any intentional deviations from the source (and why): None

Validation

  • Tests added/updated: new TestPerRowDfProvenanceCompletion in tests/test_aggregate_contract.py (15 pins: per-row df == survey_metadata.df_survey with per-row p-value reproduction oracles; replicate arms incl. the healthy/dropped df contrast and the level-matched cohort_zero replay; the replicate-undefined 0-sentinel-is-None attribute pins; bootstrap fit-time clearing + replay NaN + persisting scalar for CS/EfficientDiD; ImputationDiD bootstrapped replay still fails closed; absent-ES-surface gates; HAD survey/non-survey + to_dict); a DMLDiD replay two-channel pin in tests/test_survey_dml.py; Row A flips + analytic-vs-bootstrap full-field metadata parity (incl. implicit-PSU df) + a survey empty-post-cells arm in tests/test_survey_metadata_raw_scale.py.
  • Backtest / simulation / notebook evidence (if applicable): pre/post baseline dumps over the four estimators (analytical + bootstrapped survey fits) — only the intended fields move (per-row df columns gain finite values; ContinuousDiD bootstrap metadata moves obs→unit level); all estimates/SEs/p-values byte-identical.

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

…uousDiD unit-level metadata + df_survey contract

- Thread event_study_df into the unified container's per-row df column
  for EfficientDiD, ImputationDiD, ContinuousDiD, HAD (finite on
  analytical survey fits; None on non-survey/bootstrapped/no-surface
  fits and for the replicate-undefined 0 sentinel; ImputationDiD replay
  carriers use the level-matched replay df).
- ContinuousDiD survey_metadata recomputed unit-level on every arm
  (bootstrap + degenerate no-post-cells arms included).
- Document the df_survey scalar two-channel contract (persists beside
  percentile inference; HonestDiD consumer channel) with a
  cross-estimator pin; REGISTRY + v4 ledger note prose updated; TODO
  rows for granularity/scalar/per-row-df closed.
@github-actions

Copy link
Copy Markdown

Overall assessment: ✅ Looks good

Executive summary

  • No unmitigated P0/P1 findings.
  • Per-row df threading matches each estimator’s actual safe_inference path and Registry notes.
  • ContinuousDiD’s unit-level metadata change is documented and does not alter estimates or inference.
  • Two non-blocking issues: duplicated survey-design collapse and incomplete LLM-guide coverage.
  • Tests could not run because the environment lacks pytest and numpy.

Methodology

  • P3 — Documented ImputationDiD bootstrap exception
    • Impact: Bootstrap surfaces clear all per-row df values even though pretrend leads retain analytical survey-df inference.
    • Concrete fix: None required; this convention is explicitly documented in docs/methodology/REGISTRY.md:L1187.
    • The affected methods—ContinuousDiD, EfficientDiD, ImputationDiD, and HeterogeneousAdoptionDiD—otherwise thread the same df passed to safe_inference. No estimator equations, weights, SEs, or identification assumptions changed.

Code Quality

No findings.

Performance

  • P2 — ContinuousDiD collapses the survey design twice
    • Impact: diff_diff/continuous_did.py:L1002-L1022 creates a unit-level design for metadata, then analytical and bootstrap paths repeat that operation at L2094-L2100 and L2224-L2230. Replicate designs consequently copy the potentially large unit-by-replicate matrix twice.
    • Concrete fix: Construct the unit-level resolved design once and pass it into the analytical/bootstrap helpers, or reuse the helper-returned design for metadata.

Maintainability

No findings.

Tech Debt

No untracked correctness debt introduced.

Security

No findings or apparent secrets.

Documentation/Tests

  • P2 — LLM guide omits three new public fields
    • Impact: diff_diff/guides/llms-full.txt:L1986-L2037 documents the shared staggered and ContinuousDiD result fields without the new event_study_df; only HAD is updated.
    • Concrete fix: Add event_study_df entries for EfficientDiDResults, ImputationDiDResults, and ContinuousDiDResults, with their bootstrap/no-surface semantics.

…it; docs: event_study_df in llms-full tables

CI review round 1: the fit-level metadata hoist, _compute_analytical_se,
and _run_bootstrap each collapsed the panel design to unit level
(replicate designs copied the unit-by-replicate matrix twice) - build
once and thread as pre_unit_resolved (byte-identical outputs, verified
against the captured baseline). Add event_study_df rows to the shared
staggered and ContinuousDiD llms-full.txt field tables.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: ab326915b21cbc2bb888f5072e80388d1c3f0601


Overall assessment: ✅ Looks good

Executive summary

  • No unmitigated P0/P1 findings.
  • Per-row df values match each estimator’s safe_inference path and Registry contract.
  • Both prior P2 findings—duplicate survey collapse and incomplete LLM documentation—are resolved.
  • ContinuousDiD’s metadata change affects provenance only, not estimates or inference.
  • Focused tests could not run because pytest and scientific Python dependencies are unavailable; affected files passed AST parsing.

Methodology

  • P3 — Documented two-channel bootstrap df contract
    • Impact: Per-row df is cleared for percentile-bootstrap inference while the scalar df_survey persists for downstream consumers. The ImputationDiD lead-row exception is also explicit.
    • Concrete fix: None required; documented in docs/methodology/REGISTRY.md:L1187.
    • ContinuousDiD, EfficientDiD, ImputationDiD, and HAD thread the same survey df used by their analytical inference calls. No estimator equations, weights, SEs, or identification assumptions changed.

Code Quality

No findings.

Performance

No findings. The prior duplicate-collapse issue is resolved by reusing pre_unit_resolved in diff_diff/continuous_did.py:L997-L1028.

Maintainability

No findings.

Tech Debt

  • P3 — Tracked ImputationDiD non-survey lead-df provenance
    • Impact: Non-survey lead-specific df remains absent from the unified container; inference values are unaffected.
    • Concrete fix: None required for this PR; tracked in TODO.md:L91.

Security

No findings or apparent secrets.

Documentation/Tests

No findings. All four new public fields are now covered in the LLM guide and autosummary stubs. The added tests cover analytical, bootstrap, replicate, undefined-df, absent-surface, and empty-post-cell paths.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Aug 29, 2026
@igerber
igerber merged commit d9b93dd into main Aug 29, 2026
39 of 40 checks passed
@igerber
igerber deleted the fix/survey-df-provenance-cluster branch August 29, 2026 20:19
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