From ea20928a25730fe4fdf5c86dc62f85e6f102041a Mon Sep 17 00:00:00 2001 From: Oleg Miagkov Date: Fri, 12 Jun 2026 23:19:35 +0400 Subject: [PATCH] fix(coder): replace claude-family phase default with the provider's own model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-build finding (2026-06-12, run 7): with AGENT_PROVIDER_CODER=openai and no explicit model anywhere, the coder session was created with the phase-model DEFAULT — which phase_config resolves to a claude-* id — and the OpenAI provider 404'd on claude-sonnet-4-5-20250929. The smart router used to mask this by replacing the model on its own; once an explicit provider pin makes the router stand down (#335), the claude-centric default leaks straight into the direct provider session. After provider/runtime resolution (and only when nothing pinned a model: no --model, no smart route, no runner route, no task_metadata phase model), a claude-family phase model headed to a non-Claude provider is now replaced with that provider's configured model (AGENT_MODEL_ / _MODEL / provider default), with an info line stating the substitution. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Oleg Miagkov --- apps/backend/agents/coder.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/backend/agents/coder.py b/apps/backend/agents/coder.py index ab989a1fb..b62fd512c 100644 --- a/apps/backend/agents/coder.py +++ b/apps/backend/agents/coder.py @@ -1032,6 +1032,26 @@ def _validate_and_fix_implementation_plan() -> tuple[bool, list[str]]: ) print_status(f"Runner route details: {route_artifact}", "info") + # Provider/model coherence: the phase-model default is Claude-centric + # (phase_config tiers resolve to claude-* ids). When the resolved + # provider is a non-Claude provider and nothing pinned a model for it + # (no --model, no smart route, no runner route, no task_metadata + # phase model), sending the Claude id verbatim 404s the session + # (live-build finding, 2026-06-12). Fall back to the provider's own + # configured model (AGENT_MODEL_ / _MODEL / default). + if provider_config.provider != "claude" and (phase_model or "").startswith( + "claude" + ): + provider_model = provider_config.get_model_for(provider_config.provider) + if provider_model: + phase_model = provider_model + print_status( + "Provider model: " + f"{provider_config.provider}/{phase_model} " + "(claude-family phase default replaced)", + "info", + ) + # Filled after provider/runtime resolution so plugin hooks can see the # runtime context client before the session starts. client = None