From d3a17d2db7be77287ebcba9d0edd1f3f1c934e4a Mon Sep 17 00:00:00 2001 From: Koen van Greevenbroek Date: Tue, 28 Jul 2026 14:51:44 -0700 Subject: [PATCH] fix: exempt the stability calibration from the consumer-values check The deviation-penalty calibration drives its solves in-process, synthesizing a baseline and a main scenario per Broyden iteration, so the baseline named in `config/calibration/stability.yaml` is not a scenario the workflow declares. The stricter check introduced in #38 therefore rejected the config outright and `tools/calibrate stability` could not build its DAG. --- tests/test_config_validation.py | 10 ++++++++++ workflow/validation/consumer_values.py | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/tests/test_config_validation.py b/tests/test_config_validation.py index 46bf6a47..f9688d62 100644 --- a/tests/test_config_validation.py +++ b/tests/test_config_validation.py @@ -47,6 +47,16 @@ def test_piecewise_utility_requires_enforced_baseline(default_config) -> None: validate_consumer_values(config) +def test_deviation_penalty_generation_skips_baseline_check(default_config) -> None: + """The Broyden iteration names a scenario it synthesizes per iteration.""" + config = deepcopy(default_config) + config["food_utility_piecewise"]["enabled"] = True + config["consumer_values"]["baseline_scenario"] = "_cal_baseline_iter00" + config["deviation_penalty"]["calibration"]["generate"] = True + + validate_consumer_values(config) + + def test_scenario_piecewise_utility_accepts_enforced_baseline(default_config) -> None: config = deepcopy(default_config) config["scenarios"] = { diff --git a/workflow/validation/consumer_values.py b/workflow/validation/consumer_values.py index 52809844..d70a4c54 100644 --- a/workflow/validation/consumer_values.py +++ b/workflow/validation/consumer_values.py @@ -10,6 +10,12 @@ def validate_consumer_values(config: dict, _project_root=None) -> None: """Validate piecewise food utility against its configured baseline.""" + if config["deviation_penalty"]["calibration"]["generate"]: + # The Broyden iteration synthesizes a baseline and a main scenario per + # iteration in-process, so the configured baseline names one of those + # rather than a scenario the workflow declares. + return + scenario_defs = expand_scenario_defs(config["scenarios"]) effective_configs = [("base config", config)] effective_configs.extend(