From 2619150028dc2eb0ba463971d2a494704d1e8937 Mon Sep 17 00:00:00 2001 From: Spherrrical Date: Fri, 24 Apr 2026 11:45:47 -0700 Subject: [PATCH] fix(config_generator): gate routing_preferences migration on version < v0.4.0 Short-circuit the migration when the config already declares v0.4.0 or newer. Anything at v0.4.0+ is assumed to be on the canonical top-level shape and is passed through untouched, including stray inline preferences (which are the author's bug to fix). Only v0.3.0 and older configs are rewritten and bumped. --- cli/planoai/config_generator.py | 30 +++++++++++++++++------------- cli/test/test_config_generator.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/cli/planoai/config_generator.py b/cli/planoai/config_generator.py index f97df5fc..cb07767e 100644 --- a/cli/planoai/config_generator.py +++ b/cli/planoai/config_generator.py @@ -63,22 +63,26 @@ def migrate_inline_routing_preferences(config_yaml): ``model_providers`` entry to the v0.4.0 top-level ``routing_preferences`` list with ``models: [...]``. - Preferences with the same ``name`` across multiple providers are merged - into a single top-level entry whose ``models`` list contains every - provider's full ``/`` string in declaration order. The - first ``description`` encountered wins; conflicts are warned, not - errored, so existing v0.3.0 configs keep compiling. Any top-level - preference already defined by the user is preserved as-is. + This function is a no-op for configs whose ``version`` is already + ``v0.4.0`` or newer — those are assumed to be on the canonical + top-level shape and are passed through untouched. - Also bumps ``version`` to ``v0.4.0`` up front (when the config is - ``v0.3.0`` or older) so brightstaff's v0.4.0 gate for top-level - ``routing_preferences`` accepts the rendered config, including configs - that already declare top-level ``routing_preferences`` on a v0.3.0 - version string. + For older configs, the version is bumped to ``v0.4.0`` up front so + brightstaff's v0.4.0 gate for top-level ``routing_preferences`` + accepts the rendered config, then inline preferences under each + provider are lifted into the top-level list. Preferences with the + same ``name`` across multiple providers are merged into a single + top-level entry whose ``models`` list contains every provider's + full ``/`` string in declaration order. The first + ``description`` encountered wins; conflicts are warned, not errored, + so existing v0.3.0 configs keep compiling. Any top-level preference + already defined by the user is preserved as-is. """ current_version = str(config_yaml.get("version", "")) - if _version_tuple(current_version) < (0, 4, 0): - config_yaml["version"] = "v0.4.0" + if _version_tuple(current_version) >= (0, 4, 0): + return + + config_yaml["version"] = "v0.4.0" model_providers = config_yaml.get("model_providers") or [] if not model_providers: diff --git a/cli/test/test_config_generator.py b/cli/test/test_config_generator.py index 3836ce53..77b5b480 100644 --- a/cli/test/test_config_generator.py +++ b/cli/test/test_config_generator.py @@ -692,6 +692,35 @@ model_providers: assert config_yaml["version"] == "v0.4.0" +def test_migration_is_noop_on_v040_config_with_stray_inline_preferences(): + # v0.4.0 configs are assumed to be on the canonical top-level shape. + # The migration intentionally does not rescue stray inline preferences + # at v0.4.0+ so that the deprecation boundary is a clean version gate. + plano_config = """ +version: v0.4.0 + +listeners: + - type: model + name: model_listener + port: 12000 + +model_providers: + - model: openai/gpt-4o + access_key: $OPENAI_API_KEY + routing_preferences: + - name: code generation + description: generating new code +""" + config_yaml = yaml.safe_load(plano_config) + migrate_inline_routing_preferences(config_yaml) + + assert config_yaml["version"] == "v0.4.0" + assert "routing_preferences" not in config_yaml + assert config_yaml["model_providers"][0]["routing_preferences"] == [ + {"name": "code generation", "description": "generating new code"} + ] + + def test_migration_does_not_downgrade_newer_versions(): plano_config = """ version: v0.5.0