Skip to content
GitHub Agentic Workflows

Concurrency Control

GitHub Agentic Workflows uses dual-level concurrency control to prevent resource exhaustion and ensure predictable execution:

  • Per-workflow: Limits based on workflow name and trigger context (issue, PR, branch)
  • Per-engine: Limits AI execution across all workflows via engine.concurrency

Workflow-level concurrency groups include the workflow name plus context-specific identifiers:

Trigger TypeConcurrency GroupCancel In Progress
Issuesgh-aw-${{ github.workflow }}-${{ issue.number }}No
Pull Requestsgh-aw-${{ github.workflow }}-${{ pr.number || ref }}Yes (new commits cancel outdated runs)
Pushgh-aw-${{ github.workflow }}-${{ github.ref }}No
Schedule/Othergh-aw-${{ github.workflow }}No

This ensures workflows on different issues, PRs, or branches run concurrently without interference.

The default per-engine pattern gh-aw-{engine-id} ensures only one agent job runs per engine across all workflows, preventing AI resource exhaustion. The group includes only the engine ID and gh-aw- prefix - workflow name, issue/PR numbers, and branches are excluded.

jobs:
agent:
concurrency:
group: "gh-aw-{engine-id}"

Override either level independently:

---
on: push
concurrency: # Workflow-level
group: custom-group-${{ github.ref }}
cancel-in-progress: true
engine:
id: copilot
concurrency: # Engine-level
group: "gh-aw-copilot-${{ github.workflow }}"
tools:
github:
allowed: [list_issues]
---

The safe_outputs job runs independently from the agent job and can process outputs concurrently across workflow runs. Use safe-outputs.concurrency-group to serialize access when needed:

safe-outputs:
concurrency-group: "safe-outputs-${{ github.repository }}"
create-issue:

When set, the safe_outputs job uses cancel-in-progress: false — meaning queued runs wait for the in-progress run to finish rather than being cancelled. This is useful for workflows that create issues or pull requests where duplicate operations would be undesirable.

See Safe Outputs for details.

The conclusion job — which handles reporting and post-agent cleanup — automatically receives a workflow-specific concurrency group derived from the workflow filename:

conclusion:
concurrency:
group: "gh-aw-conclusion-my-workflow"
cancel-in-progress: false

This prevents conclusion jobs from colliding when multiple agents run the same workflow concurrently. The group uses cancel-in-progress: false so queued conclusion runs complete in order rather than being discarded.

This concurrency group is set automatically during compilation and requires no manual configuration.

When multiple workflow instances are dispatched concurrently with different inputs (fan-out pattern), compiler-generated job-level concurrency groups are static across all runs — causing all but the latest dispatched run to be cancelled as they compete for the same slot.

Use concurrency.job-discriminator to append a unique expression to compiler-generated job-level concurrency groups (agent and output jobs), making each dispatched run’s group distinct:

concurrency:
job-discriminator: ${{ inputs.finding_id }}

This generates a unique job-level concurrency group per dispatched run, preventing fan-out cancellations while preserving the per-workflow concurrency group at the workflow level.

Example usage:

concurrency:
job-discriminator: ${{ inputs.finding_id }}

Common expressions:

ScenarioExpression
Fan-out by a specific input${{ inputs.finding_id }}
Universal uniqueness (e.g. scheduled runs)${{ github.run_id }}
Dispatched or scheduled fallback${{ inputs.organization || github.run_id }}