V0.31 Scope - Downstream Discovery Task Bridge#

Status: COMPLETE (v0.31.0 release close)

v0.31.0 closed the downstream discovery task bridge. Release conclusion (recorded at release close):

downstream_discovery_task_bridge

The v0.31 arc delivered exactly what this scope document froze — the executable PySINDy PDELibrary task runner (v0.31b1), the diagnostic-only WeakPDELibrary wrapper (v0.31b2), a temporary PySINDy 1.x compatibility policy (v0.31b3), a public JSON-only example (v0.31c), and an adversarial-audit setuptools cap (v0.31c1). No new report schema was introduced; no root export was added; no WSINDy or noise-robustness claim was made. See docs/releases/V0_31_RELEASE_READINESS.md for the full release-close ledger.


Historical scope-freeze record (kept for provenance)#

v0.31 is the downstream-discovery task-bridge release. It designs a narrow, submodule-only bridge from canonical PDELie objects into PySINDy’s PDELibrary (and, as a diagnostic-only wrapper, WeakPDELibrary), records a stable TaskResult schema for the composed task output, and freezes what stays out of scope.

The first executed slice is the design-only sub-release v0.31a, which adds documents, configs, and audit tests without modifying any runtime code, adding any new optional dependency, or bumping the package version. v0.31a is design-only; runtime lands in v0.31b+.

Release conclusion (recorded by v0.31a):

downstream_discovery_task_bridge_design_only

Sequencing#

Per the current planning agreement recorded in docs/planning/ROADMAP.md, v0.31 sequences before v0.30.1: the discovery task bridge delivers the larger user-facing scientific delta, while the submodule-only symmetry-method registry MVP is smaller and can follow. In particular, SymmetryCandidate wrapper is v0.30.1 responsibility not v0.31 — no SymmetryCandidate contract or wrapper lands in this release.

Stable Intent#

v0.31 widens the downstream-discovery surface from the existing narrow bridge (pdelie.discovery.to_pysindy_trajectories, pdelie.discovery.fit_pysindy_discovery) to a small task layer that composes canonical PDELie inputs, PySINDy’s PDE-form libraries, and PDELie’s existing recovery / residual reporting into one stable JSON-compatible artifact.

Two user paths are unlocked by the runtime that this scope freezes:

  1. PySINDy PDELibrary bridge (periodic-only in v0.31): fit_pysindy_discovery accepts a caller-supplied pysindy_model configured with a PDELibrary and a differentiation_method compatible with PDELie’s canonical scalar 1D periodic layout. The scope-frozen policy for this milestone is: PySINDy PDELibrary bridge is periodic-only in v0.31; FD-nonperiodic extension is explicitly deferred. The v0.30c finite_difference backend does not become a supported PySINDy-side derivative source in this release. Runtime enforcement is two-layered: the existing to_pysindy_trajectories gate at src/pdelie/discovery/pysindy_bridge.py:27-28 continues to reject nonperiodic fields, and v0.31b runtime adds a pdelie.tasks.discovery entry-point BC guard that raises PySINDyDiscoveryUnsupportedBoundaryError before assembling PySINDy trajectories. Both layers are required because fit_pysindy_discovery accepts raw trajectory arrays and does not consult boundary metadata — bridge-only enforcement leaves a hole for callers who assemble trajectories directly. The composed TaskResult records pysindy_bridge_variant = "periodic_only_v1" so a future FD-nonperiodic bridge variant (v0.32.5 target) can be distinguished at read time without a schema-version bump.

  2. WeakPDELibrary diagnostic wrapper: an optional wrapper that composes PySINDy’s WeakPDELibrary output into a PDELie-side diagnostic report. The wrapper is diagnostic-only — it is not a public weak derivative backend, not a WSINDy design-matrix layer, not a claim of weak-form supportability, and not a replacement for the existing pdelie.residuals.weak_1d slice.

The composed task output is a TaskResult payload (design in docs/design/DISCOVERY_TASK_RESULT_SCHEMA.md) with summary_type = "discovery_task_result" and summary_schema_version = "0.1".

Scope#

The v0.31 release scope is:

  • a new submodule pdelie.tasks.discovery that composes canonical FieldBatch inputs, PySINDy PDE-library fitting, and PDELie recovery/residual reporting into a stable TaskResult artifact

  • loosening of pdelie.discovery.fit_pysindy_discovery to accept a caller-supplied pysindy_model (currently rejected as config != None at src/pdelie/discovery/pysindy_adapter.py:196-204), routed through the existing periodic-only to_pysindy_trajectories bridge

  • a WeakPDELibrary diagnostic wrapper with summary_type = "pdelie_weak_pde_library_diagnostic" carrying diagnostic_only: true and using method/test-function/quadrature identifier strings distinct from the pdelie-native weak_1d path

  • an additive supportability-policy update that adds supports_pysindy_weak_library_diagnostic and re-scopes supports_weak_derivative_backend to explicitly cover the public strong-derivative-only pdelie-native path while noting that the WeakPDELibrary wrapper is a diagnostic-scope weak derivative backend

  • release-gate consolidation via a row in configs/release_gate_manifest.json replayed by tests/test_release_gates.pythe v0.31 release-gate is a row in configs/release_gate_manifest.json and not a new tests/test_v0_31_release_gate.py file, following the v0.30f consolidation pattern

The v0.31 deliverables, design-frozen in v0.31a and implemented from v0.31b onward:

  • pdelie.tasks.discovery skeleton (run_pysindy_pde_task or similar; final name pinned in v0.31b design)

  • fit_pysindy_discovery(config=...) config loosening; the existing ScopeValidationError gate is narrowed to a runtime schema-validation gate

  • TaskResult schema runtime under strict JSON (_validate_strict_json_compatible, mirroring src/pdelie/reporting/summaries.py:1311 and :1954)

  • WeakPDELibraryDiagnostic wrapper, submodule-only (pdelie.tasks.discovery, not pdelie.residuals)

  • supportability_policy dict update covering both bullet points above

Deferred Scope#

Explicit non-goals (the scope-freeze test asserts every phrase is present):

  • no root API — no root pdelie.run_discovery_task, no root pdelie.fit_pysindy_pde_task, no root pdelie.TaskResult, no root pdelie.WeakPDELibraryDiagnostic, no root pdelie.SymmetryCandidate, no root pdelie.SymmetryMethod, no root pdelie.discover_symmetries

  • no WSINDy implementation — v0.31 does not add a WSINDy design matrix, a weak sparse recovery layer, or an SR3-style weak solver

  • no weak nonperiodic — the v0.30 nonperiodic readiness surface stays strong-form-only; no nonperiodic weak residuals in v0.31

  • no noise robustness claim — v0.31 does not certify robustness of discovery to additive noise; pdelie.data.add_gaussian_noise remains an ingestion helper, not a discovery-side robustness gate

  • no clean/noisy gate — v0.31 does not add a policy that gates a discovery TaskResult on clean-vs-noisy inputs

  • no external dataset benchmark claim — v0.31 does not claim recovery on any external dataset; readiness reports and TaskResult are configured evidence, not benchmarks

  • no PDEBench support claim — external-dataset readiness cookbooks remain a v0.32 scope

  • no The Well support claim — same as above

  • no multi-channel — TaskResult carries input_layout = "scalar_1d_uniform" as a first-class field so v0.34a-c multi-channel widening does not retrofit the schema, but multi-channel dispatch itself is out of v0.31

  • no 2D — the canonical ('batch','time','x','var') layout remains the sole supported layout

  • scalar 1D only — every v0.31 code path assumes a single scalar variable

  • no promotion of lint/typecheck/coverage to blocking in v0.31 — the v0.30e advisory jobs stay advisory

  • no LieGG / trained-model extraction in v0.31 — deferred to v0.35a

  • no multi-channel or 2D FieldBatch dispatch in v0.31 — deferred to v0.34

  • no SymmetryCandidate contract in v0.31 — deferred to v0.30.1 (submodule-only symmetry-method registry MVP)

  • no symmetry-method registry in v0.31 — the pdelie.symmetry.SymmetryMethod registry MVP + SymmetryCandidate wrapper contract freeze both land in v0.30.1, sequenced after v0.31 per the ROADMAP

  • no PyPI or TestPyPI publication — Git-tag-only through the v0.x series

Additional retention:

  • weak_1d retained through v0.32 close; removal contingent on the v0.31 PDELibrary bridge shipping a documented replacement path AND the WeakPDELibrary diagnostic wrapper landing a validated parity harness with a documented O((dx)^p) tolerance.

TaskResult Schema (Designed in v0.31a)#

The composed task output has summary_schema_version = "0.1" and summary_type = "discovery_task_result". The full field-by-field design is documented in docs/design/DISCOVERY_TASK_RESULT_SCHEMA.md. Summary:

  • task_name: str

  • backend_name: Literal["pysindy"] (Literal expansion is v0.32+)

  • backend_version: dict[str, str] with required keys pysindy, sklearn, pdelie

  • target_convention: Literal["pde_library", "weak_pde_library"]

  • derivative_backend: str — promoted from fit_config.pysindy_model.differentiation_method, referencing the v0.30c spectral_fd / finite_difference dispatch

  • input_layout: Literal["scalar_1d_uniform"] (reserved enum for v0.34+ multi-channel/2D expansion)

  • library_feature_names: list[str] (renamed from library_terms for continuity with summarize_discovery_result at src/pdelie/discovery/contracts.py:395)

  • selected_terms: dict[str, dict[str, float]] (verbatim from equation_terms of the embedded discovery_result)

  • coefficients: dict | None (optional; opt-in shape, not required)

  • support_precision: float, support_recall: float, support_f1: float, exact_support: bool, coefficient_relative_l2: float

  • train_residual: dict | None, heldout_residual: dict | None (both strict-numeric {size, l2_norm, rms, max_abs} when present, matching the normative shape from _residual_summary_or_none at src/pdelie/discovery/contracts.py:198-214)

  • weak_contract: dict | None (non-null iff target_convention == "weak_pde_library"; carries method_family, test_function_family, quadrature_rule, diagnostic_only: true)

  • warnings: list[str]

  • underlying_discovery_result: dict (embedded summarize_discovery_result payload; sibling wrapper, not parent/child)

The composed payload MUST pass json.loads(json.dumps(payload, allow_nan=False)) == payload before return, via _validate_strict_json_compatible(payload, name="discovery_task_result summary") — mirroring src/pdelie/reporting/summaries.py:1311 and :1954. Only 2 of 14 current summarize_* functions enforce allow_nan=False; TaskResult must not inherit the assumption of strict-JSON safety from the permissive _summary_payload funnel.

WeakPDELibrary Wrapper (Designed in v0.31a)#

The wrapper composes PySINDy’s WeakPDELibrary output into a PDELie diagnostic report with:

  • summary_type = "pdelie_weak_pde_library_diagnostic"

  • method_family = "pysindy_weak_pde_library_polynomial_gauss_v1" (distinct from the pdelie-native weak_1d value "local_separable_quartic_bump_trapezoid_v1" at src/pdelie/residuals/weak_1d.py:20)

  • test_function_family distinct from the pdelie-native "separable_quartic_bump_beta" at src/pdelie/residuals/weak_1d.py:22

  • quadrature_rule distinct from the pdelie-native "composite_tensor_product_trapezoidal_native_window" at src/pdelie/residuals/weak_1d.py:21

  • explicit "diagnostic_only": true marker so downstream tooling cannot mistake the wrapper for a supportability claim

The three pdelie-native identifier strings are recorded here so any future WeakPDELibrary-wrapper TaskResult does not accidentally collide with them; there is no enum or allowlist gating these strings today, so provenance disambiguation is the design’s responsibility.

Supportability Policy Update (Designed in v0.31a)#

The supportability-policy dict gains one new key and one re-scoping note:

  • supports_pysindy_weak_library_diagnostic: True — records that the WeakPDELibrary wrapper is available as a diagnostic-only surface

  • supports_weak_derivative_backend is re-scoped to “public strong-derivative-only pdelie-native path” with an explicit note that WeakPDELibrary is a diagnostic-scope weak derivative backend, not a public weak derivative backend

Both are additive; no existing public field is removed.

Retention Window#

weak_1d retained through v0.32 close; removal contingent on the v0.31 PDELibrary bridge shipping a documented replacement path AND the WeakPDELibrary diagnostic wrapper landing a validated parity harness with a documented O((dx)^p) tolerance.

Milestones#

  • M0: scope freeze — COMPLETE (in v0.31a; this release)

  • M1: pdelie.tasks.discovery skeleton + fit_pysindy_discovery config loosening — DESIGN COMPLETE in v0.31a, IMPLEMENTATION DEFERRED to v0.31b

  • M2: TaskResult schema runtime + strict-JSON adversarial test — DESIGN COMPLETE in v0.31a, IMPLEMENTATION DEFERRED to v0.31b

  • M3: WeakPDELibrary diagnostic wrapper — DESIGN COMPLETE in v0.31a, IMPLEMENTATION DEFERRED to v0.31b (contingent on the installed PySINDy version exposing WeakPDELibrary)

  • M4: supportability-policy dict update — DESIGN COMPLETE in v0.31a, IMPLEMENTATION DEFERRED to v0.31b

  • M5: release-gate consolidation — CONFIRMED as manifest row (no new tests/test_v0_31_release_gate.py file), IMPLEMENTATION at v0.31 release close (v0.31 proper)

Release Gate (v0.31 close)#

The v0.31 release-gate is a row in configs/release_gate_manifest.json replayed by tests/test_release_gates.py, not a new tests/test_v0_31_release_gate.py file. This follows the v0.30f consolidation pattern: no standalone per-version release-gate file is added; the declarative content becomes a manifest row alongside the existing 18 rows.

v0.31a Sub-Release Gate#

v0.31a is complete when:

  • this scope document is in place and required phrases are present

  • the design document docs/design/DISCOVERY_TASK_RESULT_SCHEMA.md is in place and required schema fields are documented

  • configs/planning/v0_31_discovery_task_bridge_scope.json is present and strictly JSON-compatible (allow_nan=False)

  • docs/planning/ROADMAP.md records v0.31a

  • docs/planning/PLAN.md records v0.31a with the decision label

  • docs/specs/API_STABILITY.md contains the “Decision-only note for the frozen v0.31a” subsection

  • tests/test_v0_31_discovery_task_bridge_scope.py passes

  • tests/test_discovery_task_result_schema.py passes with the NaN adversarial test

  • the full test suite still passes

  • no file under src/pdelie/ is modified

  • no version bump in pyproject.toml

  • no new optional dependency added

  • no new CI job added