From 5e70ecc51060371f8cab2cf150a9d82841f261e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:31:43 +0000 Subject: [PATCH 1/2] Initial plan From 90de6ec5b5475dcd681718b1d29a547128235cf3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:47:46 +0000 Subject: [PATCH 2/2] Disable engine-level concurrency for workflow_dispatch-only workflows Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/concurrency.go | 48 +++++++++++++++++-- pkg/workflow/concurrency_test.go | 24 ++++++---- .../engine_concurrency_integration_test.go | 14 +++--- .../basic-copilot.golden | 2 - .../with-imports.golden | 2 - 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/pkg/workflow/concurrency.go b/pkg/workflow/concurrency.go index c79452ceaa..782999ae5b 100644 --- a/pkg/workflow/concurrency.go +++ b/pkg/workflow/concurrency.go @@ -48,14 +48,14 @@ func GenerateJobConcurrencyConfig(workflowData *WorkflowData) string { return workflowData.EngineConfig.Concurrency } - // Check if this workflow has special trigger handling (issues, PRs, discussions, push, command) - // For these cases, no default concurrency should be applied at agent level + // Check if this workflow has special trigger handling (issues, PRs, discussions, push, command, + // or workflow_dispatch-only). For these cases, no default concurrency should be applied at agent level if hasSpecialTriggers(workflowData) { concurrencyLog.Print("Workflow has special triggers, skipping default job concurrency") return "" } - // For generic triggers like workflow_dispatch, apply default concurrency + // For remaining generic triggers like schedule, apply default concurrency // Pattern: gh-aw-{engine-id}-${{ github.workflow }} engineID := "" if workflowData.EngineConfig != nil && workflowData.EngineConfig.ID != "" { @@ -75,7 +75,8 @@ func GenerateJobConcurrencyConfig(workflowData *WorkflowData) string { } // hasSpecialTriggers checks if the workflow has special trigger types that require -// workflow-level concurrency handling (issues, PRs, discussions, push, command) +// workflow-level concurrency handling (issues, PRs, discussions, push, command, +// or workflow_dispatch-only) func hasSpecialTriggers(workflowData *WorkflowData) bool { // Check for specific trigger types that have special concurrency handling on := workflowData.On @@ -100,8 +101,14 @@ func hasSpecialTriggers(workflowData *WorkflowData) bool { return true } + // workflow_dispatch-only workflows represent explicit user intent, so the + // top-level workflow concurrency group is sufficient – no engine-level group needed + if isWorkflowDispatchOnly(on) { + return true + } + // If none of the special triggers are detected, return false - // This means workflow_dispatch and other generic triggers will get default concurrency + // This means other generic triggers (e.g. schedule) will get default concurrency return false } @@ -120,6 +127,37 @@ func isDiscussionWorkflow(on string) bool { return strings.Contains(on, "discussion") } +// isWorkflowDispatchOnly returns true when workflow_dispatch is the only trigger in the +// "on" section, indicating the workflow is always started by explicit user intent. +func isWorkflowDispatchOnly(on string) bool { + if !strings.Contains(on, "workflow_dispatch") { + return false + } + // If any other common trigger is present as a YAML key, this is not a + // workflow_dispatch-only workflow. We check for the trigger name followed by + // ':' (YAML key in object form) or as the sole inline value to avoid false + // matches from input parameter names (e.g., "push_branch" ≠ "push" trigger). + otherTriggers := []string{ + "push", "pull_request", "pull_request_review", "pull_request_review_comment", + "pull_request_target", "issues", "issue_comment", "discussion", + "discussion_comment", "schedule", "repository_dispatch", "workflow_run", + "create", "delete", "release", "deployment", "fork", "gollum", + "label", "milestone", "page_build", "public", "registry_package", + "status", "watch", "merge_group", "check_run", "check_suite", + } + for _, trigger := range otherTriggers { + // Trigger in object format: "push:" / " push:" + if strings.Contains(on, trigger+":") { + return false + } + // Trigger in inline format: "on: push" (no colon, trigger is the last token) + if strings.HasSuffix(strings.TrimSpace(on), " "+trigger) { + return false + } + } + return true +} + // isPushWorkflow checks if a workflow's "on" section contains push triggers func isPushWorkflow(on string) bool { return strings.Contains(on, "push") diff --git a/pkg/workflow/concurrency_test.go b/pkg/workflow/concurrency_test.go index cbdd136ec2..c12537394a 100644 --- a/pkg/workflow/concurrency_test.go +++ b/pkg/workflow/concurrency_test.go @@ -316,24 +316,22 @@ func TestGenerateJobConcurrencyConfig(t *testing.T) { description string }{ { - name: "Default concurrency for workflow_dispatch with copilot engine", + name: "No default concurrency for workflow_dispatch-only with copilot engine", workflowData: &WorkflowData{ On: "on:\n workflow_dispatch:", EngineConfig: &EngineConfig{ID: "copilot"}, }, - expected: `concurrency: - group: "gh-aw-copilot-${{ github.workflow }}"`, - description: "Copilot with workflow_dispatch should get default concurrency", + expected: "", + description: "Copilot with workflow_dispatch-only should NOT get default concurrency (user intent, top-level group is sufficient)", }, { - name: "Default concurrency for workflow_dispatch with claude engine", + name: "No default concurrency for workflow_dispatch-only with claude engine", workflowData: &WorkflowData{ On: "on:\n workflow_dispatch:", EngineConfig: &EngineConfig{ID: "claude"}, }, - expected: `concurrency: - group: "gh-aw-claude-${{ github.workflow }}"`, - description: "Claude with workflow_dispatch should get default concurrency", + expected: "", + description: "Claude with workflow_dispatch-only should NOT get default concurrency (user intent, top-level group is sufficient)", }, { name: "No default concurrency for push workflows", @@ -391,6 +389,16 @@ func TestGenerateJobConcurrencyConfig(t *testing.T) { group: "gh-aw-codex-${{ github.workflow }}"`, description: "Codex with schedule should get default concurrency", }, + { + name: "Default concurrency for workflow_dispatch combined with schedule", + workflowData: &WorkflowData{ + On: "on:\n workflow_dispatch:\n schedule:\n - cron: '0 0 * * *'", + EngineConfig: &EngineConfig{ID: "copilot"}, + }, + expected: `concurrency: + group: "gh-aw-copilot-${{ github.workflow }}"`, + description: "workflow_dispatch combined with schedule should still get default concurrency (not workflow_dispatch-only)", + }, } for _, tt := range tests { diff --git a/pkg/workflow/engine_concurrency_integration_test.go b/pkg/workflow/engine_concurrency_integration_test.go index 9a512b9457..7453553945 100644 --- a/pkg/workflow/engine_concurrency_integration_test.go +++ b/pkg/workflow/engine_concurrency_integration_test.go @@ -36,7 +36,7 @@ Test content`, description: "Copilot with push trigger should NOT have default concurrency (special case)", }, { - name: "Copilot with workflow_dispatch HAS default concurrency", + name: "Copilot with workflow_dispatch does NOT have default concurrency", markdown: `--- on: workflow_dispatch engine: @@ -48,9 +48,8 @@ tools: # Test workflow Test content`, - expectedInJob: `concurrency: - group: "gh-aw-copilot-${{ github.workflow }}"`, - description: "Copilot with workflow_dispatch should have default concurrency", + notExpectedInJob: `concurrency:`, + description: "Copilot with workflow_dispatch-only should NOT have engine-level concurrency (user intent, top-level group is sufficient)", }, { name: "Claude with issues does NOT have default concurrency", @@ -71,7 +70,7 @@ Test content`, description: "Claude with issues trigger should NOT have default concurrency (special case)", }, { - name: "Claude with workflow_dispatch HAS default concurrency", + name: "Claude with workflow_dispatch does NOT have default concurrency", markdown: `--- on: workflow_dispatch engine: @@ -83,9 +82,8 @@ tools: # Test workflow Test content`, - expectedInJob: `concurrency: - group: "gh-aw-claude-${{ github.workflow }}"`, - description: "Claude with workflow_dispatch should have default concurrency", + notExpectedInJob: `concurrency:`, + description: "Claude with workflow_dispatch-only should NOT have engine-level concurrency (user intent, top-level group is sufficient)", }, { name: "Custom concurrency with string format", diff --git a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden index 617a760def..4367169618 100644 --- a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden +++ b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/basic-copilot.golden @@ -182,8 +182,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" env: GH_AW_WORKFLOW_ID_SANITIZED: basiccopilot outputs: diff --git a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden index b0b165ed8c..708fbce99a 100644 --- a/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden +++ b/pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/with-imports.golden @@ -185,8 +185,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" env: GH_AW_WORKFLOW_ID_SANITIZED: withimports outputs: