Repository navigation
Description
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_KEYandOPENAI_API_KEYfor Codex, andANTHROPIC_API_KEYfor Claude. - They still include the router image (
ghcr.io/githubnext/gh-aw-router:0.1.3), the "Prepare model-routing conversation" step, andGH_AW_MODEL_ROUTING. - They run
awfwithout--config, so there is noexperimental.modelRoutingand noapiProxy.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 whenResolveLLMProvideris notgithub. Claude defaults toanthropicand Codex toopenai.TestValidateModelRoutingEngineCompatibilityasserts this, but it callsvalidateModelRoutingdirectly.- Its only caller is
BuildAWFConfigJSON(pkg/workflow/awf_config_build.go). Its only caller isBuildAWFCommand(pkg/workflow/awf_command_builder.go). When config building fails,BuildAWFCommandwrites aDEBUG-only log line and falls back to anawfcommand without--config. As a result, novalidateModelRoutingerror 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, requiresANTHROPIC_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: githubandallowed-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
validateModelRoutingduring compile-time validation so its errors fail the compile, with a formatted error that points at theengineblock. The natural place isvalidateWorkflowBuildContextorvalidateWorkflowEngineSettingsinpkg/workflow/compiler_orchestrator_workflow.go, next to the existingwarnRoutedSubAgentModelscall. Use the main agent's engine ID after imported routing configs are merged. Keep the existing call inBuildAWFConfigJSONas 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-
githubmodel-provideris rejected. Copilot's default staysgithub, so existing routed Copilot workflows are unaffected. - Leave
BuildAWFCommand's general fallback alone. It also drops otherBuildAWFConfigJSONerrors, such asvalidateEngineFallbackModels. 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 includesmodel-provider: github.
Reasons to reject rather than default:
- 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.
- With a default, the Claude or Codex provider would depend on whether
model-routingis present.ResolveLLMProviderdrives 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. - An explicit
model-provider: githublets a reviewer see inengine:which inference provider and secret the workflow uses.
Acceptance criteria
- Compiling the YAML above with
id: codex, and withid: claudeandallowed-models: [claude-sonnet-5], fails with a clear error that namesengine.model-provider: github. If maintainers choose to default the provider instead, both compile, the lock runsawf --configwithmodelRouting, and the manifest lists none ofANTHROPIC_API_KEY,CODEX_API_KEYorOPENAI_API_KEY. - Routed workflows that set
model-provider: github, or use the GitHub default, compile exactly as before.make recompileproduces no diff forsmoke-claude-routed,smoke-copilot-routed,smoke-copilot-sdk-routedandsmoke-pi-routed. - An explicit non-Copilot provider with
model-routingfails to compile with a clear error:model-provider: anthropicon Claude,openaion Codex,anthropicon pi, andopenaion Copilot. - Unit tests in
pkg/workflow/model_routing_test.gocover each case above. They run throughNewCompiler().CompileWorkflow, not only throughvalidateModelRouting, whose direct tests already pass today. For case 2 they assert that compilation succeeds and the lock usesawf --config. - Expected side effect: other
validateModelRoutingerrors now also fail compilation, for example Codex withallowed-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.
Activity