Summary
In a workflow with engine.model-routing, the threat-detection job inherits the routing
configuration. Its AWF run then tries to route, can't find a routing conversation, and exits
before the detection model runs. Threat detection defaults to continue-on-error, so the job
still succeeds and the safe-outputs job applies the agent's outputs (issues, pull requests) with
no threat verdict. Every run looks green; the only signals are annotations on the detection job.
This was found while testing the routing path fix in #66221. With that fix applied locally,
routing itself works, but detection fails in every routed run.
Evidence
Reproduced on GitHub-hosted runners in the private test repository
githubnext/gh-aw-routing-sandbox, compiled from main with --gh-aw-ref and using AWF v0.28.35.
It happened in all 12 routed runs since 5 Oct, for example run 37397610441. In the detection
job, "Execute threat detection with AWF" fails:
[INFO] Staging model routing conversation...
[ERROR] Fatal error: Error: The routing conversation is unavailable
Process exiting with code: 1
"Conclude threat detection" then reports:
⚠️ Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.
##[error]ERR_SYSTEM: ❌ Detection result file not found at: /tmp/gh-aw/threat-detection/detection_result.json
- The detection job concludes
success because of continue-on-error: true.
safe_outputs (needs.detection.result == 'success') then created issues and pull requests in
every one of those runs.
Cause
- The detection engine config is cloned from the main engine config, including
ModelRouting:
resolveExternalDetectorEngineConfig in pkg/workflow/threat_detection_helpers.go clones
the main EngineConfig when detection uses the same engine;
resetDetectionEngineTaskSettings then clears agent-only fields (agent, max turns, runs,
credits, tool limits, cwd, concurrency, Copilot SDK) but not ModelRouting.
- Both the external
threat-detect path (buildExternalDetectorWorkflowData) and the inline
path (threat_detection_inline_engine.go) use this config.
- So
isModelRoutingEnabled is true for the detection job's workflow data, and the detection job
gets the routing setup intended for the agent:
awf_config_build.go emits experimental.modelRouting and apiProxy.routing, with
task.conversationFile: /tmp/gh-aw/routing-conversation.json;
copilot_engine_execution.go sets GH_AW_MODEL_ROUTING=1;
sandbox_agent_images.go applies the routing image set.
- Only the agent job runs "Prepare model-routing conversation", so AWF in the detection job has
no conversation to stage and fails closed.
cloneThreatDetectionEngineConfig, used for an explicit safe-outputs.threat-detection.engine
override, doesn't apply that reset. An override that sets model-routing would hit the same
failure.
Proposed plan
- Never route the detection job.
- Clear
ModelRouting in resetDetectionEngineTaskSettings.
- Also clear it, or reject it with a compile error, on the
safe-outputs.threat-detection.engine override path in cloneThreatDetectionEngineConfig.
- Detection should run on its own fixed detection model. Routing is for the agent's task, and
the detection input isn't that task.
- With
ModelRouting cleared, the detection job no longer gets the routing AWF config,
GH_AW_MODEL_ROUTING, or the routing image set.
- Tests.
- In the threat-detection or model-routing compiler tests, compile a routed workflow and
assert that the detection job's AWF config has no experimental.modelRouting and no
apiProxy.routing, and that its execution step has no GH_AW_MODEL_ROUTING. Cover both
the external and the inline detection path.
- Assert the same for a
safe-outputs.threat-detection.engine override.
- Optional hardening. When detection fails with a tooling error, the step summary or a
job-level warning could say so more visibly, since continue-on-error hides it. That's a
separate decision, mentioned only because this failure went unnoticed.
Summary
In a workflow with
engine.model-routing, the threat-detection job inherits the routingconfiguration. Its AWF run then tries to route, can't find a routing conversation, and exits
before the detection model runs. Threat detection defaults to
continue-on-error, so the jobstill succeeds and the safe-outputs job applies the agent's outputs (issues, pull requests) with
no threat verdict. Every run looks green; the only signals are annotations on the detection job.
This was found while testing the routing path fix in #66221. With that fix applied locally,
routing itself works, but detection fails in every routed run.
Evidence
Reproduced on GitHub-hosted runners in the private test repository
githubnext/gh-aw-routing-sandbox, compiled frommainwith--gh-aw-refand using AWF v0.28.35.It happened in all 12 routed runs since 5 Oct, for example run 37397610441. In the detection
job, "Execute threat detection with AWF" fails:
"Conclude threat detection" then reports:
successbecause ofcontinue-on-error: true.safe_outputs(needs.detection.result == 'success') then created issues and pull requests inevery one of those runs.
Cause
ModelRouting:resolveExternalDetectorEngineConfiginpkg/workflow/threat_detection_helpers.goclonesthe main
EngineConfigwhen detection uses the same engine;resetDetectionEngineTaskSettingsthen clears agent-only fields (agent, max turns, runs,credits, tool limits, cwd, concurrency, Copilot SDK) but not
ModelRouting.threat-detectpath (buildExternalDetectorWorkflowData) and the inlinepath (
threat_detection_inline_engine.go) use this config.isModelRoutingEnabledis true for the detection job's workflow data, and the detection jobgets the routing setup intended for the agent:
awf_config_build.goemitsexperimental.modelRoutingandapiProxy.routing, withtask.conversationFile: /tmp/gh-aw/routing-conversation.json;copilot_engine_execution.gosetsGH_AW_MODEL_ROUTING=1;sandbox_agent_images.goapplies the routing image set.no conversation to stage and fails closed.
cloneThreatDetectionEngineConfig, used for an explicitsafe-outputs.threat-detection.engineoverride, doesn't apply that reset. An override that sets
model-routingwould hit the samefailure.
Proposed plan
ModelRoutinginresetDetectionEngineTaskSettings.safe-outputs.threat-detection.engineoverride path incloneThreatDetectionEngineConfig.the detection input isn't that task.
ModelRoutingcleared, the detection job no longer gets the routing AWF config,GH_AW_MODEL_ROUTING, or the routing image set.assert that the detection job's AWF config has no
experimental.modelRoutingand noapiProxy.routing, and that its execution step has noGH_AW_MODEL_ROUTING. Cover boththe external and the inline detection path.
safe-outputs.threat-detection.engineoverride.job-level warning could say so more visibly, since
continue-on-errorhides it. That's aseparate decision, mentioned only because this failure went unnoticed.