Skip to content

feat: remove default job-level concurrency (none is now the default)#18142

Closed
Copilot wants to merge 4 commits intomainfrom
copilot/fix-job-concurrency-issue
Closed

feat: remove default job-level concurrency (none is now the default)#18142
Copilot wants to merge 4 commits intomainfrom
copilot/fix-job-concurrency-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 24, 2026

workflow_dispatch workflows that process individual issues were incorrectly serialized at the agent/detection job level because the compiler injected a shared gh-aw-{engine-id}-${{ github.workflow }} concurrency group — overriding the user's intent expressed via workflow-level concurrency.

Changes

  • pkg/workflow/engine.go: Recognize "none" as a special string value for engine.concurrency; store the sentinel directly instead of wrapping it in YAML concurrency block syntax
  • pkg/workflow/concurrency.go: GenerateJobConcurrencyConfig() now returns "" by default — no job-level concurrency is injected unless engine.concurrency is explicitly set. Removed the hasSpecialTriggers() helper (no longer needed).
  • pkg/workflow/concurrency_test.go: Updated tests to reflect the new default (no job-level concurrency for workflow_dispatch, schedule, etc.); retained test for explicit engine.concurrency: none
  • pkg/parser/schemas/main_workflow_schema.json: Document none as a valid value for engine.concurrency
  • docs/src/content/docs/reference/concurrency.md: Updated to reflect that none is the default; engine.concurrency must be explicitly set to opt in to job-level serialization

Behavior

By default, no job-level concurrency group is applied to agent or detection jobs. Multiple workflow runs — even across different workflows — execute in parallel without engine-level serialization.

To opt in to job-level concurrency (e.g., to limit concurrent AI agent resource usage), set engine.concurrency explicitly:

engine:
  id: copilot
  concurrency:
    group: "gh-aw-copilot-${{ github.workflow }}"

The none value can also be set explicitly for clarity:

engine:
  id: copilot
  concurrency: none  # explicit opt-out (also the default)
Original prompt

This section details on the original issue you should resolve

<issue_title>Bug: Per-engine job concurrency blocks workflow_dispatch issue workflows from running in parallel</issue_title>
<issue_description>## Summary

The default per-engine job-level concurrency group (gh-aw-{engine-id}-${{ github.workflow }}) prevents workflow_dispatch-triggered issue workflows from processing different issues simultaneously. Two runs of the same workflow for different issues are serialized at the agent and detection job level, even though they operate on completely independent data.

Reproduction

  1. Create an issue-triage workflow triggered via workflow_dispatch (not on: issues:):
on:
    workflow_dispatch:
        inputs:
            issue_number:
                required: true
concurrency:
    group: issue-triage-${{ github.event.inputs.issue_number }}`
    cancel-in-progress: true
engine:
    id: copilot
  1. Compile with gh aw compile
  2. Dispatch two runs simultaneously for different issues (e.g., github/gh-aw#277977 and github/gh-aw#295537)
  3. Expected: Both run in parallel since they process different issues
  4. Actual: The second run's agent job is queued, waiting for the first to complete

Root Cause

In pkg/workflow/concurrency.go GenerateJobConcurrencyConfig():

func GenerateJobConcurrencyConfig(workflowData *WorkflowData) string {
    // ...
    if hasSpecialTriggers(workflowData) {
        return ""  // Skip for issues, PRs, push, etc.
    }
    // For generic triggers like workflow_dispatch, apply default concurrency
    groupValue := fmt.Sprintf("gh-aw-%s-${{ github.workflow }}", engineID)
    // ...
}

hasSpecialTriggers() checks workflowData.On for substrings like "issues", "pull_request", etc. But workflows that use workflow_dispatch to process issues (common for triage workflows triggered by other workflows) don't contain these substrings, so they fall through to the default engine-scoped concurrency.

The compiled lock file gets:

jobs:
  agent:
    concurrency:
      group: "gh-aw-copilot-${{ github.workflow }}"  # Shared across ALL runs
  detection:
    concurrency:
      group: "gh-aw-copilot-${{ github.workflow }}"  # Same group

Meanwhile, the workflow-level concurrency is correctly per-issue:

concurrency:
  group: issue-triage-${{ github.event.inputs.repo_owner }}-${{ github.event.inputs.repo_name }}-${{ github.event.inputs.issue_number }}`
  cancel-in-progress: true

Evidence

Two runs on bv/triage-aw2 branch for different issues, dispatched simultaneously:

Run Issue agent job detection job Notes
22342722820 github/gh-aw#277977 ✅ ran first ✅ ran first Completed normally
22342722831 github/gh-aw#295537 ⏳ queued ~6min ⏳ queued Waited for github/gh-aw#277977's agent/detection to finish

Both eventually succeeded, but github/gh-aw#295537 was unnecessarily delayed by ~6 minutes waiting in the concurrency queue.

Interaction with workflow-level concurrency

The workflow-level concurrency (from the .md frontmatter) correctly includes the issue number. When both levels are set:

  • Workflow-level cancel-in-progress: true cancels runs for the same issue → correct
  • Job-level serialization blocks runs for different issues → incorrect for this use case

The user's intent (expressed via concurrency in the .md) is per-issue isolation. The compiler-injected job-level group overrides this for the agent/detection jobs.

Suggested Fixes

Option A: Inherit workflow-level concurrency context

When the user has set a custom workflow-level concurrency group, the job-level default should incorporate the same context variables. If the workflow concurrency group includes issue_number, the job group should too.

Option B: Skip job concurrency when workflow concurrency is set

If the user explicitly defined concurrency in their .md frontmatter, don't inject the default per-engine job concurrency — the user has already expressed their concurrency intent.

Option C: Allow engine.concurrency to suppress the default

Document that users can set engine.concurrency: none (or similar) to opt out of the default job-level serialization.

Workaround (current)

Users can manually edit the .lock.yml to remove the concurrency: blocks from agent and detection jobs, but this is lost on recompile.

Alternatively, set engine.concurrency in the .md frontmatter to include the issue number:

engine:
    id: copilot
    concurrency:
        group: "gh-aw-copilot-${{ github.workflow }}-${{ github.event.inputs.issue_number }}"

Environment

  • gh-aw compiler: v0.50.0
  • Copilot CLI: 0...

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits February 24, 2026 12:59
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…oncurrency

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix per-engine job concurrency for workflow_dispatch feat: add engine.concurrency: none to opt out of default job-level concurrency Feb 24, 2026
@pelikhan
Copy link
Contributor

@copilot set engine.concurrency: none as the default value

…ency by default)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 24, 2026

@copilot set engine.concurrency: none as the default value

Done in a87cf64. none is now the default — GenerateJobConcurrencyConfig() returns "" unless engine.concurrency is explicitly set to a group name or object. The hasSpecialTriggers() helper was removed since it's no longer needed. Tests and docs updated accordingly.

Copilot AI changed the title feat: add engine.concurrency: none to opt out of default job-level concurrency feat: remove default job-level concurrency (none is now the default) Feb 24, 2026
@github-actions
Copy link
Contributor

Hey @Copilot 👋 — great work on this concurrency fix! Removing the incorrectly applied default job-level concurrency for workflow_dispatch workflows is a clean, well-reasoned change that directly addresses the parallel-execution blocking reported in #18101.

This PR looks well-structured and ready for maintainer review. ✅

Here's the full evaluation summary:

Check Result
On-topic ✅ Yes — targets a real compiler bug in pkg/workflow/concurrency.go
Follows process ✅ Yes — created by Copilot coding agent, assigned to a core team member, references #18101
Focused ✅ Yes — all changes cohesively address the default job concurrency removal
New dependencies ✅ No
Has tests ✅ Yes — concurrency_test.go updated with 6 revised + 1 new test case
Has description ✅ Yes — thorough PR body with changes, behavior summary, and examples
Diff size 158 lines (81 additions / 77 deletions)

Verdict: 🟢 Aligned — Quality: lgtm

One small note for reviewers: the smoke-copilot.lock.yml change adds a "Clear MCP configuration for detection" step that isn't mentioned in the PR description and appears unrelated to the concurrency change. Since this is an auto-generated lock file (produced by make recompile), this is likely a side effect of an upstream source workflow change picked up during recompile — worth a quick confirmation that this is expected.

Generated by Contribution Check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Per-engine job concurrency blocks workflow_dispatch issue workflows from running in parallel

2 participants