Entity: DeliveryConfig¶
Definition¶
DeliveryConfig is a frozen Pydantic model nested inside ExperimentConfig that governs the DELIVER stage. It specifies the public-facing model name written into every delivery CSV, the set of conformal prediction interval levels to materialise as column pairs, and two post-transform flags that enforce the domain invariant that prediction uncertainty should not grow as the season progresses.
Class definition: config.py:432.
Kind¶
Frozen Pydantic model (model_config = {"frozen": True}). Validated at config load; no runtime mutation.
Source of truth¶
market_insights_models/src/commodity_hindcast/config.py:432–480
Consumed exclusively by stages/run_deliver.py:deliver_experiment (run_deliver.py:56–88) and by the equivalent forecast delivery path.
Key attributes¶
| Field | Type | Default | Description |
|---|---|---|---|
model_public_name |
str |
— (required) | Client-facing label written to the model column of every delivery CSV (e.g. TFFS_V0). Has no default; every commodity YAML must declare it. |
ci_levels |
tuple[float, ...] |
(0.80, 0.95) |
Conformal interval levels to materialise. Each level produces a lower_{pct} / upper_{pct} column pair (e.g. 0.80 → lower_80, upper_80). Must be a subset of SUPPORTED_CI_LEVELS. |
enforce_ci_narrowing |
bool |
True |
When True, CI band widths are clamped to never increase as init_date advances within a year. Enforces the season-progression invariant. Disable only for diagnostic runs that need raw calibration output. |
drop_frozen_tail |
bool |
True |
When True, trailing init_date rows where mean stops changing are removed before writing. Prevents redundant frozen vintages from appearing in the CSV. |
SUPPORTED_CI_LEVELS¶
The canonical allowed set is defined at config.py:47:
The _validate_ci_levels validator (config.py:459) rejects any level outside (0, 1) or outside this set. Levels are always stored sorted ascending.
ci_column_names()¶
Helper method at config.py:471:
def ci_column_names(self) -> tuple[str, ...]:
# (0.80, 0.95) → ("lower_80", "upper_80", "lower_95", "upper_95")
Called by deliver_experiment to derive the column order passed to build_delivery_column_order.
Real-world values (corn config)¶
From configs/corn_usa.yaml:
enforce_ci_narrowing and drop_frozen_tail are omitted and take their True defaults.
Connection to the delivery pipeline¶
cli deliver <run_dir> calls deliver_experiment(run_dir) in stages/run_deliver.py.
The delivery stage:
- Loads
ExperimentResult.from_run_dir(run_dir), which readsconfig_resolved.yamland discovers walk-forward prediction artefacts. - Reads
result.config.deliveryto extractci_levels,ci_column_names(),enforce_ci_narrowing, anddrop_frozen_tail(run_deliver.py:56–59). - Iterates three ADM levels —
("ADM0", "ADM1", "ADM2")(run_deliver.py:69) — and for each: a. Converts fold predictions toDeliveryRowobjects viawalk_forward_preds_to_delivery_rows(result, level, ci_levels, mode="hindcast"). b. Validates the row collection as aHindcastDelivery. c. Pipes the resulting DataFrame throughapply_delivery_post_transforms(enforce_narrowing=..., drop_frozen=...)fromlib/transform_utils.py. d. WritesTreefera_{experiment_key}_{ADM}_Hindcast_{YYYYMMDD}.csvunderrun_dir/delivery/.
DeliveryConfig therefore controls:
- Which CI columns appear in the output (via
ci_levels). - What the
modelcolumn contains (viamodel_public_name). - Whether the
enforce_ci_narrowinganddrop_frozen_tailpost-transforms are applied.
The ADM levels themselves (ADM0, ADM1, ADM2) are fixed by deliver_experiment and are not configurable via DeliveryConfig.
Relationship to PostprocessConfig¶
Conformal calibration is fitted during the POSTPROCESS stage and controlled by PostprocessConfig.conformalise (config.py:532). DeliveryConfig.ci_levels selects which of those fitted levels to write into delivery columns; the two configs are complementary and must be consistent. If a level in ci_levels was never calibrated in postprocess, CalibrationResult.predict_interval will fail.
PR-331 — P90 bands added¶
PR-331 (merged 2026-04-27) fixed two delivery bugs:
weather_correction_bu_acwas always null — fixed by includingsim_yield_kg_ha_detrendedin the area-weighted aggregation.- Wheat, cotton, and soybean configs were missing the
0.90level — fixed by adding0.90to theirci_levelslists.
The CI band machinery is fully dynamic: adding a level to ci_levels causes ci_column_names() to produce the new column pair, and the delivery stage writes it without any code changes. Removing a level eliminates the columns silently.
Lifecycle¶
- Declared in a commodity YAML under the
delivery:key; parsed whenExperimentConfigis constructed. - Written verbatim into
run_dir/config_resolved.yamlfor reproducibility. - Read by
deliver_experimentat deliver time; used to parameterisewalk_forward_preds_to_delivery_rowsandapply_delivery_post_transforms. - Not modified after config construction (
frozen=True).
Relationships¶
- Owned by ExperimentConfig as field
delivery: DeliveryConfig(config.py:700). - Controls column content of DeliveryRow (
ci_levels→lower_*/upper_*columns;model_public_name→modelcolumn). - Coordinates with
PostprocessConfig.conformaliseto ensure fitted CI levels are available at delivery time. ci_column_names()feedsbuild_delivery_column_orderindelivery/schemas.py.
Concepts and pipelines¶
- Delivery pipeline (pipeline, to be written).
- Conformal prediction intervals (concept, to be written).
- CI narrowing and frozen-tail transforms (concept, to be written).
PRs and commits¶
- PR-331 — added P90 bands to wheat / cotton / soybean configs; fixed
weather_correction_bu_acpopulation.
Open questions¶
enforce_ci_narrowinganddrop_frozen_taildefault toTruebut are never explicitly set in any YAML config (they rely on Pydantic defaults). A deliberate override would silence the transform; documenting when each should be disabled for diagnostic runs would be useful.model_public_name(TFFS_V0) encodes a version. There is no validation that the name changes when the model changes; version governance is currently manual.- The ADM output levels (
ADM0,ADM1,ADM2) are hardcoded indeliver_experiment. A future extension might make them configurable here.