# V0.31.0 Release Readiness ## Decision `v0.31.0` is ready as a Git-tag-only release. Release conclusion: ```text downstream_discovery_task_bridge ``` ## Package Metadata - package version: `0.31.0` - git tag: `v0.31.0` (do not create until explicitly authorized after review) - PyPI/TestPyPI: deferred until `v1.0` or later Do not publish to TestPyPI or PyPI for `v0.31.0`. ## Public Surface ### New in `v0.31` — downstream discovery task bridge (submodule-only) The v0.31 arc adds one narrow scientific delta: a submodule-only downstream discovery task bridge under `pdelie.tasks`. No root `pdelie` export is added. The `pdelie.residuals.weak_1d` PDELie-native weak-derivative path remains available and distinct. Stable public submodule surfaces: - `pdelie.tasks.run_pysindy_pde_task` — executable PySINDy `PDELibrary`-backed sparse-discovery task runner (v0.31b1). Periodic scalar 1D only. Layer-1 boundary-condition guard raises `PySINDyDiscoveryUnsupportedBoundaryError` before any PySINDy call when `is_x_periodic(field)` is false. Returns a strict-JSON `discovery_task_result`. - `pdelie.tasks.summarize_discovery_task_result` — strict-JSON payload assembler (v0.31b1). Enforces the 22-key composed schema, records `summary_type = "discovery_task_result"`, `summary_schema_version = "0.1"`, and `pysindy_bridge_variant = "periodic_only_v1"`. Embeds the v0.31b0-frozen backend-native `summarize_discovery_result` verbatim under `underlying_discovery_result`. - `pdelie.tasks.PySINDyDiscoveryUnsupportedBoundaryError` — subclass of `ScopeValidationError`. Raised at the `pdelie.tasks.discovery` entry when a nonperiodic-x `FieldBatch` is supplied. - `pdelie.tasks.inspect_pysindy_weak_pde_library` — diagnostic-only wrapper around PySINDy's `WeakPDELibrary` (v0.31b2). Two-layer scope guard (periodic-x, uniform x/t grids, batch==1, single var, K-scaled grid-sufficiency floor `max(8, 4*K)`, unsupported PySINDy API). Returns a strict-JSON `pdelie_weak_pde_library_diagnostic`. - `pdelie.tasks.summarize_pysindy_weak_pde_library_diagnostic` — strict-JSON payload assembler for the diagnostic-only wrapper (v0.31b2). Enforces the 27-key composed schema. - `pdelie.tasks.WeakPDELibraryDiagnostic` — caller-declared, JSON-safe library-configuration dataclass with `as_dict()` (v0.31b2). Rejects NaN/Inf at construction via `_validate_strict_json_compatible`. - `pdelie.examples.run_downstream_discovery_task_bridge_example` and CLI `python -m pdelie.examples.downstream_discovery_task_bridge` (v0.31c) — composed JSON-only demonstration runner. Not a new report schema. All names above are exposed from `pdelie.tasks` (or `pdelie.examples`) as their canonical submodule-only public surface. **None** appear at the root `pdelie` namespace. ### Discovery task support - input_layout: `"scalar_1d_uniform"`. - periodic scalar 1D only. - backend: PySINDy `PDELibrary` (or any caller-configured `pysindy.SINDy` model per the v0.31b1 adapter loosening). - output `summary_type`: `"discovery_task_result"`. - 22-key top-level composed schema. - Strict-JSON composition: `_validate_strict_json_compatible(payload, name="discovery_task_result summary")` invoked exactly once at the final composition boundary. - `pysindy_bridge_variant = "periodic_only_v1"` on every produced TaskResult. - Adapter loosening: `fit_pysindy_discovery` accepts a caller-supplied `pysindy_model` kwarg (v0.31b1); `config != None` still raises; the `config=None, pysindy_model=None` default path is byte-preserved for existing v0.30 callers. ### Weak diagnostic support - output `summary_type`: `"pdelie_weak_pde_library_diagnostic"`. - `diagnostic_only = True` on every produced summary. - 27-key top-level composed schema. - `method_family = "pysindy_weak_pde_library_polynomial_gauss_v1"`. - `test_function_family = "pysindy_weak_pde_library_polynomial_bump_v1"`. - `quadrature_rule = "pysindy_weak_pde_library_composite_gauss_v1"`. - **No WSINDy benchmark claim.** - **No noise-robustness claim.** - **No numerical equivalence with `pdelie.residuals.weak_1d`.** The PDELie-native weak-derivative path remains available and is retained through at least `v0.32` close. - Two-layer scope guard: nonperiodic-x rejected via `PySINDyDiscoveryUnsupportedBoundaryError` (reused from v0.31b1); scope violations (dims, var_names, batch, uniform grids, K-scaled grid-sufficiency floor, unsupported PySINDy API) rejected via `ScopeValidationError` / `SchemaValidationError`. ### Retained from earlier `v0.x` - Canonical `FieldBatch → DerivativeBatch → ResidualBatch → GeneratorFamily → VerificationReport` pipeline (frozen since `v0.1.x`). - All `v0.30` runtime and reporting surfaces including `BoundaryConditionSpec`, structured `FieldBatch` schema 0.2, low-order finite-difference derivative backend, interior-only residual diagnostics, and the ambient supportability report layer. - `pdelie.residuals.weak_1d` behavior — unchanged. ## Compatibility Policy The v0.31 downstream task bridge is a **temporary compatibility surface**. It validates against a single narrow PySINDy generation, and its compatibility constraints are all bounded: - **PySINDy**: `>=1.7.5,<2` under `python_version < '3.12'`. - **scikit-learn**: `>=1.2.2,<1.3` under `python_version < '3.12'`. - **setuptools**: `<82` under `python_version < '3.12'` — temporary constraint added by v0.31c1 because pysindy 1.7.5 imports `pkg_resources` at package init and setuptools 82 removed that module. - **Python**: downstream task support is validated on Python 3.11 only. The v0.31c1 adversarial matrix confirmed: - setuptools 81.0.0: pass end-to-end. - setuptools 82.0.0: install succeeds but `import pysindy` fails with `ModuleNotFoundError: No module named 'pkg_resources'`. - setuptools 83.0.0: same failure mode as 82. Post-fix rebuilt-wheel verification confirms pip auto-downgrades an ambient setuptools 82/83 to 81 without any user co-install. Fresh-venv baseline: setuptools stays at the bundled `65.5.0`, untouched. Deferred to `v0.31.1`: - **PySINDy 2.x support**. Four independent 2.x API breaks are documented in `docs/design/PYSINDY_COMPATIBILITY_POLICY.md` (SINDy kwargs removed; SINDy.fit signature; SINDy.differentiate removed; PDELibrary/WeakPDELibrary kwargs). - **Python 3.12+ downstream support**. The `[downstream]` extra is marker-scoped to `python_version < '3.12'`; a user on Python 3.12+ who invokes the task bridge receives a targeted, actionable error message that names the v0.31.1 deferral and states that reinstalling the same extra will not fix the environment. - **numpy>=2 floor bump**. Coordinated with the PySINDy 2.x port. No hidden manual installation step is required in the documented user path. ## Public Example The v0.31c JSON-only demonstration runner: - Public submodule surface: `pdelie.examples.run_downstream_discovery_task_bridge_example`. - CLI: `python -m pdelie.examples.downstream_discovery_task_bridge`. - Composes both v0.31 paths on one manufactured periodic scalar 1D Heat field (T=64, X=64, K=16, seed=31000). - 7-key top-level `summary_type = "downstream_discovery_task_bridge_example"`. NOT a new report schema — the composed wrapper carries the verbatim v0.31b1 `discovery_task_result` (22 keys) and v0.31b2 `pdelie_weak_pde_library_diagnostic` (27 keys) as children. - Deterministic under the frozen seed via a private `_legacy_numpy_rng_seed_scope` context manager (v0.31c1). Not thread-safe — PySINDy 1.7.5 uses the legacy `np.random` global RNG and does not accept a modern `Generator`. - Uses only public submodule APIs (AST-checked in `tests/test_v0_31c_downstream_task_bridge_example.py`). ## Expected xfails ledger The v0.31.0 release close retains three declared xfails, each with a non-empty reason and each assigned to a documented follow-up milestone. None represent a genuine failure masked to make the suite green. | Node id | Reason | Strictness | Owner milestone | Planned disposition | |---|---|---|---|---| | `tests/test_v0_31b3_pysindy_compatibility_policy.py::test_unsupported_major_pysindy_version_rejected_by_discovery_task_runner` | The runtime version guard on `run_pysindy_pde_task` has not been landed. The test monkeypatches `pysindy.__version__` to `"999.0.0"` and expects a `ScopeValidationError`; no such guard exists today. | runtime-conditional `pytest.xfail(...)` inside `try/except pytest.fail.Exception:` (not decorator strict) | `v0.31.1` (PySINDy 2.x port introduces `_pysindy_compat._detect_api_generation` gate) | KEEP as xfail until v0.31.1 lands the guard | | `tests/test_v0_31b3_pysindy_compatibility_policy.py::test_unsupported_major_pysindy_version_rejected_by_weak_diagnostic` | The same runtime version guard has not been landed on `inspect_pysindy_weak_pde_library`. | runtime-conditional `pytest.xfail(...)` | `v0.31.1` (PySINDy 2.x port) | KEEP as xfail until v0.31.1 | | `tests/test_v0_31b3_pysindy_compatibility_policy.py::test_discovery_task_backend_version_records_exact_pysindy_and_sklearn` | Asks `pdelie.tasks.discovery._resolve_backend_version` to include `scipy` for uniformity with the weak diagnostic's provenance. `scipy` is currently a nested-value extension, not required for v0.31 release close. | runtime-conditional `pytest.xfail(...)` on the `"scipy" not in backend_version` branch | provenance follow-up (v0.31.1 or earlier) | KEEP as xfail until the follow-up lands | ## Compatibility test / release-close validation - pytest full suite baseline at v0.31.0 close: **1252 passed, 3 skipped, 3 xfailed** (v0.31c1 close baseline) → **1253 passed, 3 skipped, 3 xfailed** after adding the Python 3.12+ UX test. - `ruff check .` clean. - `mypy src/pdelie/tasks/` clean. - `sphinx -b html -W --keep-going docs docs/_build/html` clean. - `python -m build --sdist --wheel` produces `pdelie-0.31.0.tar.gz` and `pdelie-0.31.0-py3-none-any.whl`. - `git diff --check` clean. Clean-install smoke matrix: - Core-only (Python 3.11, no `[downstream]`): install passes; task runner raises `ImportError` naming `pdelie[downstream]`. - Downstream (Python 3.11, ambient `setuptools>=82`): pip auto-downgrades setuptools to 81; `run_pysindy_pde_task` passes (22-key summary, strict JSON); `inspect_pysindy_weak_pde_library` passes (27-key summary, `diagnostic_only=True`, strict JSON); example runner + CLI pass. - Downstream (Python 3.11, fresh venv): setuptools stays at the bundled 65.5.0; smoke passes. - Python 3.12+ unsupported downstream behavior: targeted `ImportError` (or `ScopeValidationError` on the weak path) message names Python 3.12, the v0.31.1 deferral, and states that reinstalling the same extra will not fix the environment. Verified by `tests/test_v0_31c1_downstream_packaging_policy.py::test_v0_31_python_312_downstream_missing_extra_message_is_actionable` under monkeypatched `sys.version_info`. ## Boundaries / non-goals for v0.31.0 - **No new discovery backend.** PySINDy 1.x only. - **No new summary type.** `discovery_task_result` (22 keys) and `pdelie_weak_pde_library_diagnostic` (27 keys) are the entire v0.31 report surface. - **No WSINDy benchmark claim.** The `WeakPDELibrary` wrapper is diagnostic-only. - **No noise-robustness claim.** - **No nonperiodic PySINDy discovery.** Both task paths raise on nonperiodic-x inputs. - **No PySINDy 2.x code.** Deferred to `v0.31.1`. - **No symmetry-method registry, no `SymmetryCandidate` runtime.** Deferred to `v0.30.1`. - **No PDEBench / The Well support claim.** - **No multi-channel / 2D widening.** Deferred to `v0.34+`. - **No new PDE.** - **No root pdelie export added.** - **No package version bump beyond `0.31.0`.** - **No PyPI or TestPyPI publication.** ## Deferred scope confirmed - `v0.31.1`: PySINDy 2.x migration (four independent API breaks + numpy>=2 floor + Python 3.12+ downstream support + runtime version guard on unsupported pysindy versions). - `v0.30.1`: submodule-only `SymmetryMethod` registry MVP + `SymmetryCandidate` wrapper contract freeze. - `v0.31.5`: nonperiodic orbit/action scope decision. - `v0.32`: external dataset readiness cookbooks (no benchmark claim). - `v0.33`: first external symmetry candidate-generator method. - `v0.34`: multi-channel / 2D contract widening scope decision. - `v0.35`: trained-model extraction layer. - `v1.0`: stable public engine + PyPI publish + optional root-entry decision. ## Validation checklist - [x] version bump `0.30.0 → 0.31.0` in `pyproject.toml` and `docs/conf.py`. - [x] release readiness document exists (this file). - [x] `CHANGELOG.md` v0.31.0 entry with the five subsections (Added / Compatibility / Diagnostics / Examples / Boundaries). - [x] `docs/specs/support_matrix.v0_31.json` written as strict JSON with the v0.31 additions. - [x] `docs/planning/V0_31_DISCOVERY_TASK_BRIDGE_SCOPE.md` status → COMPLETE. - [x] `docs/planning/PLAN.md` v0.31.0 release-close record. - [x] `docs/planning/ROADMAP.md` v0.31 moved to completed releases; v0.31c1 marked completed; next active items called out explicitly. - [x] `docs/specs/API_STABILITY.md` v0.31 final stable-surface note. - [x] `configs/release_gate_manifest.json` — `"0.31"` row extended in place; `current_release_gate_job_name` renamed to `v0_31-release-gate`. - [x] `.github/workflows/ci.yml` release-gate job renamed `v0_30-release-gate → v0_31-release-gate`. - [x] `tests/test_current_release_gate.py` updated to the new job name. - [x] Full validation gates pass (pytest / ruff / mypy / sphinx / build / git diff-check). - [x] Clean-install smoke matrix (core-only, downstream, ambient setuptools 82/83, Python 3.12+ UX message). - [ ] Git tag `v0.31.0` — deferred to explicit authorization after review.