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 |
|---|---|---|---|---|---|
|
|
|
permissive |
N |
medium |
|
|
|
strict |
Y (embeds |
n/a — already strict |
|
|
|
permissive |
N |
low |
|
|
|
permissive |
N |
low |
|
|
|
strict |
Y (accepts pre-summarized |
n/a — already strict |
|
|
|
permissive |
N |
low |
|
|
|
permissive |
N |
low |
|
|
|
permissive |
N |
low |
|
|
|
permissive |
N |
low |
|
|
|
permissive |
Y (embeds |
medium |
|
|
|
permissive |
Y (embeds |
high — v0.32b |
|
|
|
permissive |
Y (embeds |
medium |
|
|
|
permissive at outer wrap |
Y (embeds |
medium |
|
|
|
permissive |
Y (embeds |
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_stricthelper (or astrict=Trueparameter 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, notNaN.docs/design/DISCOVERY_TASK_RESULT_SCHEMA.md— v0.31b’s first strict-migration-of-composed-summary target. Documents the shape of thediscovery_task_resultpayload embedded insidesummarize_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.