Skip to content

Threat detection 'needs' does not incorporate jobs in engine.env expressions #43917

Description

@jeffhandley

Follow up to Agent 'needs' does not incorporate jobs in engine.env expressions (#30232) (fixed in #30239) and Activation 'needs' does not incorporate jobs in engine.env expressions (#30790).

When an engine.env value contains a needs.<job>.outputs.* expression, the auto-generated detection (threat-detection) job reuses that same engine.env mapping but does not gain a needs entry for the referenced custom job. Inside the detection job, needs.<job> is therefore undefined, so the value resolves to empty even though the agent job received the intended value.

The agent job (fixed in #30239), the activation job (via the #30790 on.permissions: {} workaround), and the conclusion job all correctly gain the custom job in their needs. Only the detection job is left out, and none of the known workarounds coerce it in.

The example below sets COPILOT_MODEL from a select_model job that runs earlier in the workflow, but the same happens for any engine.env value built from a needs.<job>.outputs.* expression.

repro-detection-needs-bug.md

---
engine:
  id: copilot
  env:
    COPILOT_MODEL: ${{ needs.select_model.outputs.model }}

jobs:
  select_model:
    needs: [pre_activation]
    runs-on: ubuntu-slim
    steps:
      - id: pick
        shell: bash
        run: echo "model=claude-sonnet-4.6" >> "$GITHUB_OUTPUT"
    outputs:
      model: ${{ steps.pick.outputs.model }}

safe-outputs:
  create-issue:

on:
  permissions: {}
  workflow_dispatch:
---

# Repro detection needs bug

safe-outputs auto-enables threat detection, so a detection job is generated. The on.permissions: {} + needs: [pre_activation] workaround from #30790 is applied so that activation, agent, and conclusion all correctly gain select_model, isolating detection as the only job that does not.

Expected Result

The detection job gains select_model in its needs, matching agent and conclusion:

jobs:
  detection:
    needs:
      - activation
      - agent
      - select_model
# ...

Actual Result

detection is missing select_model, even though its engine step still sets COPILOT_MODEL from needs.select_model:

jobs:
  detection:
    needs:
      - activation
      - agent
# ...
    steps:
      # ...
      - env:
          COPILOT_MODEL: ${{ needs.select_model.outputs.model }}
          # REPRO NOTE: `needs.select_model` is `undefined` here, so `COPILOT_MODEL` resolves to empty

For comparison, every other job that consumes the expression gains select_model:

jobs:
  activation:
    needs: [pre_activation, select_model]
  agent:
    needs: [activation, select_model]
  conclusion:
    needs: [activation, agent, detection, safe_outputs, select_model]
  select_model:
    needs: pre_activation

No known workaround

Impact

Any workflow that derives an engine.env value from an earlier job's output--for example, selecting the model dynamically--gets that value silently blanked in the threat-detection job. Detection then runs with a different engine configuration than the agent, and there is no compile-time error or warning because a needs.<job> reference outside the job's needs list resolves to empty rather than failing.

Suggested Fix

Extend the engine.env needs-expression scan added in #30239 (and applied to activation per #30790) so that the detection job's needs list is populated from the same needs.<job>.outputs.* references. The detection job inherits the engine.env mapping, so it should inherit the corresponding job dependencies as well.

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions