Repository navigation
fix(dml): fail closed when cross-fit learners cannot be isolated - #816
Conversation
|
The agreed copy-isolation boundary, preflight placement, error sanitisation, documentation surfaces, changelog fragment, and regression cases are now in place. Local validation covers the DML/cross-fit and documentation suites, targeted Python 3.9 execution, static checks, and native built-in output parity. Could you apply |
|
Pushed follow-up commit bb187a3 to complete the fail-closed contract.
Verification: Python 3.9 scoped suite passed ( |
|
Thanks - this lands the boundary exactly as agreed, and the test coverage is thorough. Fork PRs cannot run the label-gated CI or the CI AI reviewer here, so I opened a mirror of your branch as #817 to get that feedback. The CI review came back clean (no P0/P1) with one P3, and while preparing the mirror I hit one P2-level wording issue. I would like you to make these changes on this PR so the work stays yours; I will refresh the mirror from your branch afterwards. Three items: 1. Narrow the "before any cell" claim to the one-copy preflight (P2). The changelog fragment, 2. Stale fallback-era phrase (P3 from the CI review). The DMLDiD 3. Make the scikit-learn tests actually run in CI. The two new Once those are pushed I will refresh #817, let CI run, and then move this PR forward. |
Context: - PR igerber#816 review identified overbroad pre-cell failure claims and optional sklearn tests that CI never exercised. Changes: - Adapt the documentation corrections from mirror commit 50109fe to distinguish preflight failures from later hard TypeErrors. - Require unfitted templates and distinct copies in both seed descriptions. - Add unpinned scikit-learn to the dev extra and both Rust workflow dependency lists; retain importorskip guards. - Leave estimator execution and the separate StackedDiD sentinel contract unchanged. Verification: - Python 3.12 focused cross-fit, DML, changelog and documentation suites: 582 passed. - Both sklearn integration tests passed without skips on Python 3.9. - Black, Ruff, changelog compiler, TOML/YAML and notebook JSON validation passed. - Known failure: test_sklearn_clone_round_trip_if_available[StackedDiD] fails on sentinel parameter equality with sklearn installed; reproduced on Python 3.9 in both this branch and unchanged base e982340. - Not run: full repository suite, Sphinx build or remote CI matrix.
|
Thanks, pushed 47e98ad with all three requested changes. I used 50109fe as the reference for the preflight/later-fold distinction, replaced both "where copyable" descriptions with the unfitted-template requirement, and added unpinned scikit-learn to the dev extra and both workflow install steps. The importorskip guards remain. Validation: 582 focused tests passed on Python 3.12, and both DML/cross-fit sklearn tests ran without skips on Python 3.9. Black, Ruff, changelog and configuration-format checks passed. One additional CI consequence surfaced during review: installing sklearn enables the existing python -m pytest -q tests/test_base_estimator.py -k 'sklearn_clone and StackedDiD'The requested dependency additions are in place, but this newly enabled failure needs resolving before CI can pass. I left the shared deprecation-sentinel behaviour unchanged to keep this revision within the agreed scope. The full repository suite and remote CI matrix were not run locally. |
|
I have a tested local fix for the newly enabled StackedDiD clone test. The production change is limited to the stateless def __copy__(self) -> "_NotSupplied":
return self
def __deepcopy__(self, memo: Any) -> "_NotSupplied":
return selfThis preserves the omitted-parameter marker during sklearn cloning without changing type-based alias resolution. Tests cover direct copy identity, nested mutable values remaining independently copied, and default/canonical/deprecated StackedDiD configurations with their existing warning behaviour. It does not relax the distinct-object requirement for DML learner templates. The base-estimator, mechanical/semantic rename, cross-fit and DML suites pass: Python 3.9/sklearn 1.6.1: 590 passed, 19 skipped; Python 3.12/sklearn 1.7.2: 596 passed, 13 skipped. The previously failing clone test runs and passes on both. Black, Ruff, scoped mypy and changelog checks also pass; the full suite and remote CI were not run. I recommend adding this as one small follow-up commit here, with regression tests and a changelog fragment, to unblock the dependency change. Would you prefer that or a separate PR? The patch is prepared locally; #816 remains at 47e98ad pending your choice. |
|
Thanks - all three items landed as asked, and I confirmed the scikit-learn consequence you found: with sklearn installed, One more thing before I can refresh the mirror: this branch is now conflicting with
When you rebase onto Once the rebased branch is up, I will refresh #817 from it so the CI reviewer runs on the final shape. |
Context: - Cross-fit reuse after a failed or identity-returning deepcopy can carry fitted state and prior-fold training data into a later fold. - DMLDiD must reject an invalid learner template before any group-time cell is fitted. Changes: - Require a distinct top-level deepcopy for cross-fit learner templates and raise a sanitised TypeError otherwise. - Probe custom DML learner specifications during configuration validation while retaining the direct cross_fit_predict backstop. - Document the top-level clone boundary and cover copy failures, identity copies, sklearn templates, exact OOF isolation, and pre-cell error propagation. Verification: - pytest -q tests/test_crossfit.py tests/test_dml_did.py tests/test_changelog_fragments.py tests/test_docs_ia.py tests/test_doc_deps_integrity.py - Python 3.9 targeted regression tests, including sklearn preflight - ruff, black, scoped mypy, changelog compiler, and native cross-fit output comparison against upstream/main - Not run: full default suite was stopped after an interrupted long-running attempt.
Context: - Cross-fit learner templates must fail closed when cloning cannot provide an independent, usable learner, without exposing stale DML results after a failed re-fit. Changes: - Validate clone protocol and weighted-fit capability during DML preflight and for each direct cross-fit fold; delayed errors name the public nuisance parameter. - Clear DMLDiD fitted state before every fit attempt, and cover panel, repeated-cross-section, survey, and direct helper failure paths. - Correct the DML tutorial and documentation registry, and record the warning-to-TypeError behavioural change. Verification: - /tmp/diff-diff-814-python39/bin/python -m pytest -q tests/test_crossfit.py tests/test_dml_did.py tests/test_survey_dml.py tests/test_changelog_fragments.py tests/test_docs_ia.py tests/test_doc_deps_integrity.py (665 passed, 6 skipped) - black --check, ruff check, and mypy --follow-imports=skip on touched source/test modules - python -m json.tool docs/tutorials/32_dml_did.ipynb and python .claude/scripts/changelog_compile.py check - Not run: full repository test suite.
Context: - PR igerber#816 review identified overbroad pre-cell failure claims and optional sklearn tests that CI never exercised. Changes: - Adapt the documentation corrections from mirror commit 50109fe to distinguish preflight failures from later hard TypeErrors. - Require unfitted templates and distinct copies in both seed descriptions. - Add unpinned scikit-learn to the dev extra and both Rust workflow dependency lists; retain importorskip guards. - Leave estimator execution and the separate StackedDiD sentinel contract unchanged. Verification: - Python 3.12 focused cross-fit, DML, changelog and documentation suites: 582 passed. - Both sklearn integration tests passed without skips on Python 3.9. - Black, Ruff, changelog compiler, TOML/YAML and notebook JSON validation passed. - Known failure: test_sklearn_clone_round_trip_if_available[StackedDiD] fails on sentinel parameter equality with sklearn installed; reproduced on Python 3.9 in both this branch and unchanged base e982340. - Not run: full repository suite, Sphinx build or remote CI matrix.
Context: - Adding scikit-learn to CI enables the StackedDiD clone round-trip test, which fails because copied omitted-parameter sentinels compare unequal. Changes: - Return self from the stateless _NotSupplied marker's shallow and deep copy methods. - Cover sentinel identity, isolation of neighbouring mutable values, and canonical and deprecated StackedDiD control parameters during sklearn cloning. - Preserve alias resolution, deprecation warnings and existing pickle behaviour; add a changelog fragment and correct the clone-test dependency description. Verification: - Reproduced six copy/clone failures before the source fix. - Python 3.13 focused estimator, clone, changelog and documentation checks: 1010 passed, 13 skipped. - Python 3.9 estimator, clone, survey DML, methodology and bad-control parity checks: 790 passed, 13 skipped, 5 deselected. - Ruff and Black across diff_diff and tests, whole-package mypy, changelog compiler, notebook JSON and git diff --check passed. - Not run: Rust backend tests, notebook execution and remote CI matrix. The broader pure-Python suite is still running.
Context: - The rebase retained two descriptions of the removed warning-and-reuse path, contradicting the fail-closed learner contract. Changes: - Align the methodology registry and documentation dependency note with lazy per-fold cloning and sanitised TypeError propagation. - State that failures never reuse the template or become DegenerateFoldError skips; leave runtime code and unrelated baseline failures unchanged. Verification: - Documentation-dependency, documentation-IA, tutorial-drift and changelog tests: 367 passed. - Changelog compiler, YAML parsing, stale-fallback text scan and git diff --check passed. - Not run for this documentation-only correction: full suite, Rust matrix, Sphinx build or notebook execution; Sphinx and nbmake are absent from the verification environment.
47e98ad to
ac1ef63
Compare
|
Rebased onto The rebase preserves Fresh checks passed on Python 3.13 (1,137 tests) and 3.9 (790 tests), plus Ruff, Black, mypy, documentation/changelog checks and the pre-merge scan. The final documentation-only correction passed another 367 checks. The prior broad run, before that prose correction, recorded 14,542 passes and one failure: Could you refresh #817 from this head when convenient? |
…optional at runtime Supersedes the 2026-08-01 decision row that kept scikit-learn out of the dev extra; igerber#816 adds it (unpinned) to the dev extra and both rust-test.yml install steps so the sklearn clone round-trip and DML cross-fit learner tests always run in CI. Runtime posture unchanged; importorskip guards stay.
igerber
left a comment
There was a problem hiding this comment.
Looks good, thank you for the repeated contributions!
Fixes #814.
Summary
deepcopyfails, returns the original object, or loses the required learner protocol. A preflight failure raises before any group-time cell is fitted.TypeError, never reuse the template and never become a NaN-cell skip. Custom__deepcopy__implementations remain responsible for isolating nested mutable state.iter_fold_fitsarchitecture and expanded bad-control validation. Cover ordinary panel/RCS fits and nested bad-control stages; failed re-fits clear prior fitted results.Verification
Rebased onto
abcd785a. Current head:ac1ef63c.git diff --checkpassed.dfe900ea, before that final documentation-only correction, recorded 14542 passed, 256 skipped, one failed. The failure istest_allowlists_are_reachable, reporting staleDMLDiDResults.groupsandDMLDiDResults.overall_attallowlist entries. It was rerun on both the rebased branch and unchanged main and fails identically; left outside this PR's scope.Not run in this verification pass: the full suite, Rust backend matrix, Sphinx build or notebook execution. The repository's default slow-test exclusion remains in effect. Mirror #817 needs refreshing to evaluate the rebased head in hosted CI.