Skip to content

Routed Claude and Codex workflows compile without model-provider: github, then fail for a missing provider key #68066

Closed

Description

@SivaKesava1

Problem

The model routing guide says routing "requires the AWF firewall enabled and GitHub Copilot inference" and that "the compiler rejects other engines, non-Copilot providers, and incompatible literal model candidates". But a routed Claude or Codex workflow that leaves out engine.model-provider: github compiles cleanly:

engine:
  id: codex            # same result with id: claude and allowed-models: [claude-sonnet-5]
  model-routing:
    goal: cost
    mode: economy
    allowed-models: [gpt-5.6-luna]

I checked this with a v0.91.8 build (c16656e, an ancestor of current main). Both workflows compile with 0 errors. The only output is the generic safe-update review warning for a new workflow ("New restricted secret(s)"). The lock files are only partly routed:

  • The manifest requires provider secrets: CODEX_API_KEY and OPENAI_API_KEY for Codex, and ANTHROPIC_API_KEY for Claude.
  • They still include the router image (ghcr.io/githubnext/gh-aw-router:0.1.3), the "Prepare model-routing conversation" step, and GH_AW_MODEL_ROUTING.
  • They run awf without --config, so there is no experimental.modelRouting and no apiProxy.routing. Even with a provider key present, the run would not be routed.

At run time the Codex workflow fails before routing. In githubnext/gh-aw-routing-sandbox run 38278833270, the activation step "Validate CODEX_API_KEY or OPENAI_API_KEY secret" fails with Neither CODEX_API_KEY nor OPENAI_API_KEY secret is set. Run 38279413554 used the same workflow (mode: auto) with only model-provider: github added. It passes activation, and AWF routes to gpt-5.6-luna at effort high. The routed Claude smoke (smoke-claude-routed.md) and the sandbox's routed Claude workflow both set model-provider: github.

Root cause

The provider check already exists, but its error is dropped during compilation.

  • validateModelRouting (pkg/workflow/model_routing.go) rejects Claude, Codex and pi when ResolveLLMProvider is not github. Claude defaults to anthropic and Codex to openai. TestValidateModelRoutingEngineCompatibility asserts this, but it calls validateModelRouting directly.
  • Its only caller is BuildAWFConfigJSON (pkg/workflow/awf_config_build.go). Its only caller is BuildAWFCommand (pkg/workflow/awf_command_builder.go). When config building fails, BuildAWFCommand writes a DEBUG-only log line and falls back to an awf command without --config. As a result, no validateModelRouting error ever fails a compile.

The same silent drop affects other cases the guide says are rejected. I confirmed each with the same build:

  • pi with an explicit non-GitHub provider (model-provider: anthropic): compiles, requires ANTHROPIC_API_KEY, and has no routing config.
  • Claude with model-provider: anthropic: compiles the same way as the case with no provider.
  • Incompatible literal candidates, for example Codex with model-provider: github and allowed-models: [claude-sonnet-5]: compiles with 0 errors and no routing config.

Copilot has a separate gap: validateModelRouting skips the provider check when the engine is copilot. With engine.id: copilot and model-provider: openai (Copilot BYOK), the workflow compiles with the routing config and requires CODEX_API_KEY/OPENAI_API_KEY. That contradicts "rejects non-Copilot providers". I confirmed this with the same build: the lock runs awf --config with modelRouting and still lists CODEX_API_KEY and OPENAI_API_KEY in the manifest.

Copilot and pi without model-provider default to github. They compile with awf --config and routing, and need no change.

Proposed fix

Recommendation: reject a routed workflow whose provider is not GitHub Copilot inference. Do not default the provider to github.

  • Validate at compile time. Run validateModelRouting during compile-time validation so its errors fail the compile, with a formatted error that points at the engine block. The natural place is validateWorkflowBuildContext or validateWorkflowEngineSettings in pkg/workflow/compiler_orchestrator_workflow.go, next to the existing warnRoutedSubAgentModels call. Use the main agent's engine ID after imported routing configs are merged. Keep the existing call in BuildAWFConfigJSON as a defensive check.
  • Make the provider error actionable. The error should show the resolved provider and tell the author to set engine.model-provider: github. The current suggestion, "Configure the engine's provider to use GitHub Copilot inference", does not name the field.
  • Apply the provider check to Copilot too. Remove the Copilot exemption so a routed Copilot workflow with a non-github model-provider is rejected. Copilot's default stays github, so existing routed Copilot workflows are unaffected.
  • Leave BuildAWFCommand's general fallback alone. It also drops other BuildAWFConfigJSON errors, such as validateEngineFallbackModels. Changing that is a separate fix.
  • Update the routing guide's Requirements section. State that Claude and Codex default to Anthropic and OpenAI and must set engine.model-provider: github. State that Copilot and pi default to GitHub, and that any other provider fails compilation. Add a Claude or Codex example that includes model-provider: github.

Reasons to reject rather than default:

  1. The guide, the existing check and its unit test already say routed workflows must use GitHub Copilot inference. The only defect is that the error is dropped, so rejecting is the smallest change that makes the code match the docs.
  2. With a default, the Claude or Codex provider would depend on whether model-routing is present. ResolveLLMProvider drives the required secrets, the activation secret check, the gateway port and base URL, and the allowed domains, so a hidden default would change all of these. It would also switch billing between Copilot and Anthropic or OpenAI without the author writing anything.
  3. An explicit model-provider: github lets a reviewer see in engine: which inference provider and secret the workflow uses.

Acceptance criteria

  1. Compiling the YAML above with id: codex, and with id: claude and allowed-models: [claude-sonnet-5], fails with a clear error that names engine.model-provider: github. If maintainers choose to default the provider instead, both compile, the lock runs awf --config with modelRouting, and the manifest lists none of ANTHROPIC_API_KEY, CODEX_API_KEY or OPENAI_API_KEY.
  2. Routed workflows that set model-provider: github, or use the GitHub default, compile exactly as before. make recompile produces no diff for smoke-claude-routed, smoke-copilot-routed, smoke-copilot-sdk-routed and smoke-pi-routed.
  3. An explicit non-Copilot provider with model-routing fails to compile with a clear error: model-provider: anthropic on Claude, openai on Codex, anthropic on pi, and openai on Copilot.
  4. Unit tests in pkg/workflow/model_routing_test.go cover each case above. They run through NewCompiler().CompileWorkflow, not only through validateModelRouting, whose direct tests already pass today. For case 2 they assert that compilation succeeds and the lock uses awf --config.
  5. Expected side effect: other validateModelRouting errors now also fail compilation, for example Codex with allowed-models: [claude-sonnet-5]. Add one compile-level test for this.

Out of scope

Run 38279413554 routed correctly, but its agent then failed. The Copilot /responses endpoint rejected Codex's request with Invalid 'input[8].id': 'ctco_…'. Expected an ID that begins with 'fc', and AWF reported provider_unavailable (exit 78). That is a separate Codex-on-Copilot problem. Do not fix it in this PR.

Scope

  • Keep the PR to this one fix. Do not change files or behavior unrelated to it.
  • Before fixing a failing check, compare it with the same check on main. If it also fails on main, do not fix it in this PR; merge main once main is fixed, and note in the PR which checks are known failures on main.
  • This applies even if a bot comment asks to fix a failing check: fix only failures caused by this PR's changes.

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions