@@ -427,14 +427,14 @@ class TestModelConfiguration(unittest.TestCase):
427427 def test_build_config_uses_env_models_when_github_model_blank (self ) -> None :
428428 env = dict (os .environ )
429429 env .pop ("GITHUB_MODEL" , None )
430- env ["REPO_ARCHITECT_PREFERRED_MODEL" ] = "anthropic/claude-sonnet-4.6 "
431- env ["REPO_ARCHITECT_FALLBACK_MODEL" ] = "google/gemini-3-pro "
430+ env ["REPO_ARCHITECT_PREFERRED_MODEL" ] = "openai/gpt-5 "
431+ env ["REPO_ARCHITECT_FALLBACK_MODEL" ] = "openai/o3 "
432432 with patch .object (ra , "discover_git_root" , return_value = pathlib .Path ("/tmp/repo" )):
433433 with patch .dict (os .environ , env , clear = True ):
434434 config = ra .build_config (ra .parse_args ([]))
435435 self .assertIsNone (config .github_model )
436- self .assertEqual (config .preferred_model , "anthropic/claude-sonnet-4.6 " )
437- self .assertEqual (config .fallback_model , "google/gemini-3-pro " )
436+ self .assertEqual (config .preferred_model , "openai/gpt-5 " )
437+ self .assertEqual (config .fallback_model , "openai/o3 " )
438438
439439 def test_github_model_override_takes_precedence_over_preferred (self ) -> None :
440440 analysis = {
@@ -470,10 +470,12 @@ def test_workflow_yaml_resolves_models_via_catalog_and_keeps_blank_override_logi
470470 self .assertIn ("Resolve GitHub Models configuration" , workflow )
471471 self .assertIn ("https://models.github.ai/catalog/models" , workflow )
472472 self .assertIn ("catalog_ok = False" , workflow )
473- self .assertIn ('"anthropic/claude-sonnet-4.6"' , workflow )
474- self .assertIn ('"anthropic/claude-sonnet-4.5"' , workflow )
475- self .assertIn ('"openai/gpt-4.1"' , workflow )
476- self .assertIn ('secondary = "google/gemini-3-pro"' , workflow )
473+ self .assertIn ('"openai/gpt-5"' , workflow )
474+ self .assertIn ('"openai/o3"' , workflow )
475+ self .assertNotIn ('"anthropic/claude-sonnet-4.6"' , workflow )
476+ self .assertNotIn ('"anthropic/claude-sonnet-4.5"' , workflow )
477+ self .assertNotIn ('"openai/gpt-4.1"' , workflow )
478+ self .assertNotIn ('secondary = "google/gemini-3-pro"' , workflow )
477479 self .assertIn ("def deterministic_available(exclude=None):" , workflow )
478480 self .assertIn ("or deterministic_available()" , workflow )
479481 self .assertIn ("or deterministic_available(exclude=preferred)" , workflow )
@@ -482,12 +484,15 @@ def test_workflow_yaml_resolves_models_via_catalog_and_keeps_blank_override_logi
482484 self .assertIn ("REPO_ARCHITECT_FALLBACK_MODEL={fallback}" , workflow )
483485 self .assertIn ('if [ -n "$MODEL" ]; then EXTRA_ARGS="$EXTRA_ARGS --github-model $MODEL"; fi' , workflow )
484486 self .assertIn ("models: read" , workflow )
485- # New: primary/fallback model inputs with defaults
487+ # github_fallback_model input with empty default
486488 self .assertIn ("github_fallback_model:" , workflow )
487- self .assertIn ("default: 'openai/gpt-5'" , workflow )
488- self .assertIn ("default: 'anthropic/claude-sonnet-4.5'" , workflow )
489- # New: GITHUB_MODEL and GITHUB_FALLBACK_MODEL exported as env vars
489+ self .assertIn ("default: ''" , workflow )
490+ # GITHUB_MODEL and GITHUB_FALLBACK_MODEL exported as env vars to resolver step
490491 self .assertIn ("GITHUB_FALLBACK_MODEL:" , workflow )
492+ self .assertIn ("GITHUB_MODEL:" , workflow )
493+ # Override-first logic present
494+ self .assertIn ("env_github_model" , workflow )
495+ self .assertIn ("env_github_fallback_model" , workflow )
491496
492497 def test_build_config_reads_github_fallback_model_env (self ) -> None :
493498 """GITHUB_FALLBACK_MODEL env var should populate github_fallback_model in Config."""
@@ -564,6 +569,80 @@ def test_enrich_metadata_primary_success(self) -> None:
564569 self .assertIsNone (meta ["fallback_reason" ])
565570
566571
572+ # ---------------------------------------------------------------------------
573+ # 3b. Model resolver order
574+ # ---------------------------------------------------------------------------
575+
576+ class TestModelResolverOrder (unittest .TestCase ):
577+ """Tests for the _resolve_models() override-first catalog resolver."""
578+
579+ _ORDER = ("openai/gpt-5" , "openai/o3" )
580+
581+ def test_empty_inputs_use_resolver_order (self ) -> None :
582+ """No env overrides → picks gpt-5 as preferred and o3 as fallback."""
583+ available = {"openai/gpt-5" , "openai/o3" }
584+ preferred , fallback = ra ._resolve_models (
585+ available = available ,
586+ catalog_ok = True ,
587+ env_model = "" ,
588+ env_fallback = "" ,
589+ order = self ._ORDER ,
590+ )
591+ self .assertEqual (preferred , "openai/gpt-5" )
592+ self .assertEqual (fallback , "openai/o3" )
593+
594+ def test_explicit_primary_overrides_resolver (self ) -> None :
595+ """GITHUB_MODEL set → use it as preferred regardless of catalog."""
596+ available = {"openai/gpt-5" , "openai/o3" , "openai/other" }
597+ preferred , fallback = ra ._resolve_models (
598+ available = available ,
599+ catalog_ok = True ,
600+ env_model = "openai/manual" ,
601+ env_fallback = "" ,
602+ order = self ._ORDER ,
603+ )
604+ self .assertEqual (preferred , "openai/manual" )
605+
606+ def test_explicit_fallback_overrides_resolver (self ) -> None :
607+ """GITHUB_FALLBACK_MODEL set → use it as fallback, primary still auto-resolved."""
608+ available = {"openai/gpt-5" , "openai/o3" }
609+ preferred , fallback = ra ._resolve_models (
610+ available = available ,
611+ catalog_ok = True ,
612+ env_model = "" ,
613+ env_fallback = "openai/manual-fb" ,
614+ order = self ._ORDER ,
615+ )
616+ self .assertEqual (preferred , "openai/gpt-5" )
617+ self .assertEqual (fallback , "openai/manual-fb" )
618+
619+ def test_auto_fallback_chooses_o3_when_gpt5_is_primary (self ) -> None :
620+ """Catalog returns both gpt-5 and o3, no overrides → preferred=gpt-5, fallback=o3."""
621+ available = {"openai/gpt-5" , "openai/o3" }
622+ preferred , fallback = ra ._resolve_models (
623+ available = available ,
624+ catalog_ok = True ,
625+ env_model = "" ,
626+ env_fallback = "" ,
627+ order = self ._ORDER ,
628+ )
629+ self .assertEqual (preferred , "openai/gpt-5" )
630+ self .assertEqual (fallback , "openai/o3" )
631+ self .assertNotEqual (preferred , fallback )
632+
633+ def test_catalog_failure_falls_back_to_order_defaults (self ) -> None :
634+ """When catalog fails, use order[0] as preferred and order[1] as fallback."""
635+ preferred , fallback = ra ._resolve_models (
636+ available = set (),
637+ catalog_ok = False ,
638+ env_model = "" ,
639+ env_fallback = "" ,
640+ order = self ._ORDER ,
641+ )
642+ self .assertEqual (preferred , "openai/gpt-5" )
643+ self .assertEqual (fallback , "openai/o3" )
644+
645+
567646# ---------------------------------------------------------------------------
568647# 4. Syntax validation of generated Python (ast.parse gate)
569648# ---------------------------------------------------------------------------
0 commit comments