Generate trigger-aware concurrency defaults for scheduled and interactive runs
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 541
- Avg merge
- 5h 48m
- Merged PRs (30d)
- 773
Description
## Summary
Replace the `workflow_dispatch` missing-`job-discriminator` warning with a coherent generated default:
- scheduled runs of a workflow share stable concurrency groups and queue linearly;
- explicitly initiated runs (`workflow_dispatch` and slash-command runs) receive independent groups by default;
- existing entity-aware behavior for issue, pull-request, and similar event-driven runs remains intact;
- explicit user concurrency settings continue to override the defaults.
This should be implemented consistently across top-level workflow concurrency and gh-aw-generated job concurrency. Merely defaulting `concurrency.job-discriminator` is insufficient because that field does not affect the top-level workflow group.
## Motivation
#55813 identified a real fan-out problem: concurrent `workflow_dispatch` runs can all reach the static generated conclusion group and then drain one at a time. #55836 addressed it with a compile warning recommending:
```yaml
concurrency:
job-discriminator: ${{ github.run_id }}
```
That warning exposes an internal generated-concurrency detail and asks every author to choose a policy. It is also unsafe guidance for mixed-trigger workflows: `job-discriminator` is evaluated for every event, so an unconditional `github.run_id` also gives scheduled runs separate generated job and conclusion groups. Overlapping scheduled runs then cease to queue linearly.
The generated defaults should express the common intent directly instead:
- schedules are recurring instances of the same maintenance operation and should queue;
- manual and command-triggered invocations represent independent user intent and should be able to run concurrently.
`githubnext/agentics`' Repo Assist workflow is a concrete example. It combines `schedule`, `workflow_dispatch`, and `slash_command`, has no source `concurrency` declaration, and reasonably wants scheduled work serialized while manual and `/repo-assist` requests remain independent.
## Proposed semantics
When the author has not supplied an explicit policy, use the runtime event—not merely the set of triggers declared in the workflow—to derive generated concurrency keys.
| Runtime trigger | Default scope |
|---|---|
| `schedule` | Stable per workflow/engine; overlapping scheduled runs queue |
| `workflow_dispatch` | Per run, using `github.run_id` |
| Slash command | Preserve the existing entity-aware top-level scope; generated internal jobs must not introduce a cross-entity bottleneck |
| Issue/PR/discussion/push and other events | Preserve their existing entity/ref-aware top-level behavior |
Conceptually, the discriminator for generated internal job groups in a mixed scheduled/interactive workflow is:
```yaml
${{ github.event_name == 'schedule' && 'scheduled' || github.run_id }}
```
The implementation need not expose this expression in source YAML, but the resulting behavior should be equivalent.
### Top-level workflow concurrency
The top-level generated group must make the same distinction:
- all scheduled runs of the workflow resolve to one stable group;
- each `workflow_dispatch` run resolves to a run-specific group;
- slash-command and entity events retain their current issue/PR/discussion-aware grouping.
This is necessary because a per-run job discriminator cannot enable dispatch fan-out while all dispatches still share a static top-level workflow group.
### Generated job concurrency
Apply the runtime-aware key consistently to every gh-aw-generated concurrency group whose static key can serialize unrelated runs, including:
- agent jobs where default agent concurrency is generated;
- output and repository-memory jobs where applicable and safe;
- the conclusion job.
For scheduled runs, the generated groups remain stable and queued. For interactive runs, they become independent per run. This fixes the conclusion bottleneck from #55813 without making scheduled agents or conclusions overlap.
Shared resources that genuinely require serialization, such as writes to the same repository-memory file, should retain their own resource-specific concurrency groups rather than relying on a workflow-wide or conclusion-wide group.
## Explicit configuration and compatibility
Explicit settings must continue to win:
- an author-provided top-level `concurrency.group` defines workflow-level policy;
- `engine.concurrency` defines agent-level policy;
- an author-provided `concurrency.job-discriminator` overrides the generated internal-job discriminator and remains available for input-keyed fan-out such as `${{ inputs.organization }}`.
This is a behavior change for workflows currently relying implicitly on static dispatch or conclusion groups. That reliance should become explicit. An author who wants all manual dispatches serialized can configure a stable group or discriminator.
## Compiler diagnostic
Once this default is generated, remove the broad warning introduced by #55836. There should be no warning merely because `workflow_dispatch` has inputs and `concurrency.job-discriminator` is absent—the default should be safe and useful.
A diagnostic remains appropriate only when explicit settings conflict or leave an identifiable bottleneck, and it should describe the exact generated or configured group involved rather than prescribing `github.run_id` unconditionally.
## Acceptance criteria
1. Two `workflow_dispatch` runs can execute concurrently with no concurrency configuration.
2. Two overlapping scheduled runs of the same workflow queue and execute linearly.
3. In a workflow containing both triggers, dispatch runs use per-run groups while schedule runs share stable groups.
4. Adding `slash_command` does not change the scheduled-run policy.
5. Slash commands for independent entities are not serialized by a static generated conclusion group.
6. Explicit top-level, engine, and discriminator settings retain precedence.
7. Resource-specific locks such as repository-memory writes remain serialized.
8. The warning from #55836 is removed or narrowed to genuinely conflicting explicit configurations.
9. Tests cover dispatch-only, schedule-only, schedule + dispatch, schedule + dispatch + slash command, and explicit overrides at both workflow and generated-job levels.
10. The concurrency documentation describes these defaults at the trigger level, including migration guidance for users who intentionally want serialized manual dispatches.
## Relevant implementation areas
- `pkg/workflow/concurrency.go`
- `pkg/workflow/notify_comment_conclusion_helpers.go`
- `pkg/workflow/compiler_validators.go`
- `pkg/workflow/workflow_dispatch_concurrency_warning_test.go`
- `pkg/workflow/concurrency_test.go`
- `docs/src/content/docs/reference/concurrency.md`
Related: #55813, #55836, #24037.
Contributor guide
Assessment
This issue has not been assessed yet.