PDL-JSON-1: Strict-JSON Inventory of Public summarize_* Functions#

Status: OPEN debt ticket. Filed in v0.31b0. Migration executes across v0.31b1 (discovery_task_result), v0.32b (generator_confidence), and follow-up per-summary PRs on a rolling basis.

Motivation#

src/pdelie/reporting/summaries.py currently exposes two JSON-validation helpers side by side. _validate_json_compatible(value, *, name) at lines 187–193 calls json.dumps(safe_value) with the default allow_nan=True; _validate_strict_json_compatible(value, *, name) at lines 196–202 calls json.dumps(safe_value, allow_nan=False) and rejects NaN, +Inf, -Inf, and non-JSON-native types. Both are reachable from public summarize_* entry points, and the choice between them is made at the summary-writing site rather than at a central schema layer. The permissive helper is wired into _summary_payload(summary_type, **items) at line 261, which is the common return path for every summarizer that does not open-code its own strict wrap.

The concrete risk is that any producer inside a permissive summary can emit float("nan") — from numpy division-by-zero, from an unpopulated diagnostics slot, from a fit path that fell back to sentinel values — and that value will pass through _summary_payload unchanged, serialize under allow_nan=True, and become a downstream NaN token in JSON reports, notebook outputs, and workflow manifests. The strict-JSON gate documented in docs/design/V0_30_HYGIENE_AUDIT.md under “Strict JSON / no-NaN policy” only applies at the sites that opted in; permissive summaries are outside that gate today.

A bulk migration — flipping every _summary_payload invocation to strict validation in one PR — is not the right shape. Sub-summaries currently emit NaN under the permissive contract in paths that today’s tests explicitly exercise (fit fallbacks, missing-verification stubs, unavailable robustness metrics). Migrating a summarizer to strict without first hardening each of its embedded producers (replacing NaN with None or an explicit unavailable-status string, per the audit policy) would surface the debt as test failures rather than as behavior change. The migration must therefore be per-summary, with the composed summaries (those that embed other summarize_* outputs) migrated only after their leaf dependencies have been hardened.

Inventory table#

Line numbers below reference src/pdelie/reporting/summaries.py at feat/v0.31b0-prep HEAD. All permissive-category entries route their final payload through _summary_payload(...), which calls _validate_json_compatible at line 261. Strict-category entries call _validate_strict_json_compatible directly on the outgoing payload dict.

Function

file:line

Validation helper

Category

Composed?

Migration priority

summarize_field_batch_readiness

src/pdelie/reporting/summaries.py:888

_validate_json_compatible (via _summary_payload)

permissive

N

medium

summarize_xarray_dataset_readiness

src/pdelie/reporting/summaries.py:1155

_validate_strict_json_compatible (line 1311)

strict

Y (embeds field_batch_readiness via internal summarize_field_batch_readiness call)

n/a — already strict

summarize_residual_batch

src/pdelie/reporting/summaries.py:1314

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_weak_residual_report

src/pdelie/reporting/summaries.py:1341

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_weak_form_supportability

src/pdelie/reporting/summaries.py:1845

_validate_strict_json_compatible (line 1954)

strict

Y (accepts pre-summarized weak_summary and strong_summary inputs)

n/a — already strict

summarize_generator_fit_diagnostics

src/pdelie/reporting/summaries.py:2003

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_generator_family

src/pdelie/reporting/summaries.py:2042

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_formula_generator_family

src/pdelie/reporting/summaries.py:2097

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_verification_report

src/pdelie/reporting/summaries.py:2145

_validate_json_compatible (via _summary_payload)

permissive

N

low

summarize_vertical_slice

src/pdelie/reporting/summaries.py:2165

_validate_json_compatible (via _summary_payload)

permissive

Y (embeds summarize_residual_batch, summarize_generator_family, summarize_verification_report)

medium

summarize_generator_confidence

src/pdelie/reporting/summaries.py:2193

_validate_json_compatible (via _summary_payload)

permissive

Y (embeds summarize_generator_fit_diagnostics and accepts pre-summarized residual, generator, verification, candidate-validation, coverage, consistency, and orbit inputs)

high — v0.32b

summarize_invariant_workflow

src/pdelie/reporting/summaries.py:2279

_validate_json_compatible (via _summary_payload)

permissive

Y (embeds summarize_generator_fit_diagnostics; accepts orbit, coverage, consistency, generator, verification, fit-diagnostics reports)

medium

summarize_split_leakage_provenance

src/pdelie/reporting/summaries.py:2632

_validate_json_compatible (via _summary_payload) at the outer wrap; _validate_strict_json_compatible on orbit_batch, source_report_id, and extra_metrics sub-fields (lines 2650, 2664, 2668)

permissive at outer wrap

Y (embeds orbit_batch_summary)

medium

summarize_downstream_discovery_workflow

src/pdelie/reporting/summaries.py:2807

_validate_json_compatible (via _summary_payload)

permissive

Y (embeds field_batch_readiness, generator_confidence, orbit-batch, discovery-inputs, discovery-result, and split-provenance reports)

high — v0.31b1

Priority guidance#

summarize_generator_confidence is the high-priority target for v0.32b. It is the widest-composed summary in the current inventory: it accepts pre-summarized residual, generator, fit-diagnostics, verification, candidate-validation, coverage, consistency, and orbit reports, and it is a direct input to summarize_downstream_discovery_workflow. Migrating it to strict validation forces upstream hardening of every embedded producer, and it is the summary that most benefits from a NaN-free contract because the confidence label at line 2263 is a decision-grade output consumed by downstream discovery flows.

summarize_vertical_slice and summarize_invariant_workflow are medium priority. Both are composed summaries whose embedded leaf producers (summarize_residual_batch, summarize_generator_family, summarize_verification_report, summarize_generator_fit_diagnostics) are permissive today. Migration of the composed wrap must follow migration of at least the embedded leaves it depends on; sequencing this before the leaves are hardened would flip existing passing tests to failures without adding safety. summarize_split_leakage_provenance sits in the same medium band — it already validates its orbit_batch, source_report_id, and extra_metrics sub-fields under the strict helper (lines 2650, 2664, 2668), but the outer payload wrap is still permissive; tightening that outer wrap is a small change once the inputs are known clean. summarize_field_batch_readiness is medium because it is embedded downstream (by summarize_xarray_dataset_readiness at line 1260 and by summarize_downstream_discovery_workflow), so its migration unblocks two composed summaries at once.

The leaf permissive summaries — summarize_residual_batch, summarize_weak_residual_report, summarize_generator_fit_diagnostics, summarize_generator_family, summarize_formula_generator_family, summarize_verification_report — are low priority. Each is a self-contained wrap over a single dataclass; they can be migrated independently on a rolling basis in follow-up PRs, and the required change per file is small (open-code a _validate_strict_json_compatible call in place of _summary_payload, or introduce a strict variant of _summary_payload and switch the call site). Ordering among the leaves is not load-bearing.

Cross-references#

  • docs/planning/PDL_JSON_2_STRICT_JSON_MIGRATION.md — companion migration-plan ticket. Sketches the per-summary migration steps, the shape of a _summary_payload_strict helper (or a strict=True parameter on _summary_payload), the test-hardening steps required at each embedded producer, and the release-sequencing that avoids composed-summary migration ahead of its leaves.

  • docs/specs/LABEL_REGISTRY.md — label-vocabulary context. Several of the summaries in the inventory table emit categorical labels (readiness_label, confidence_label, supportability_label, workflow_label, risk_label); the label-registry constraints interact with strict validation because unavailable-status sentinels must be strings, not NaN.

  • docs/design/DISCOVERY_TASK_RESULT_SCHEMA.md — v0.31b’s first strict-migration-of-composed-summary target. Documents the shape of the discovery_task_result payload embedded inside summarize_downstream_discovery_workflow (line 2807), which is the high-priority v0.31b1 migration and the first composed summary in this inventory to move to the strict contract.