Skip to content

fix(dispatch): label-aware concurrency prevents routing-label cancellation#5472

Open
ggallen wants to merge 1 commit into
fullsend-ai:mainfrom
ggallen:fix/2452-label-aware-dispatch-concurrency
Open

fix(dispatch): label-aware concurrency prevents routing-label cancellation#5472
ggallen wants to merge 1 commit into
fullsend-ai:mainfrom
ggallen:fix/2452-label-aware-dispatch-concurrency

Conversation

@ggallen

@ggallen ggallen commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #2452. When triage applies multiple labels rapidly, all issues/labeled events shared the same concurrency group (fullsend-dispatch-{number}). GitHub Actions cancels intermediate pending runs, so the ready-to-code event could be cancelled while a non-routing label (type/bug) survives — the code agent never dispatches.

Two complementary fixes:

  • Label-aware concurrency key: for labeled events, include the label name in the group so ready-to-code and type/bug get separate concurrency slots. Non-labeled events share a common dispatch suffix to preserve existing queuing behavior.
  • Early-exit non-routing labels: if: guard skips the dispatch job entirely for labeled events where the label isn't ready-to-code, ready-for-review, or ready-for-triage. Prevents non-routing labels from consuming concurrency slots or wasting runner time.

Applied to all three shim files:

  • .github/workflows/fullsend.yaml (this repo)
  • internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml (per-org template)
  • internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml (per-repo template, if: guard only — no concurrency group)

Test plan

  • All existing scaffold alignment tests pass (100+)
  • Added TestShimLabeledEventFiltering — validates all three shims have the routing-label whitelist and the workflow-call shims have label-aware concurrency keys
  • actionlint passes on fullsend.yaml
  • All pre-commit hooks pass (go vet, gofmt, secret scan, workflow lint)
  • Apply 5+ labels to a test issue in rapid succession (including ready-to-code as a non-final label). Verify the ready-to-code run completes with "Routed to stage: code" rather than being cancelled

🤖 Generated with Claude Code

@ggallen
ggallen requested a review from a team as a code owner July 22, 2026 15:55
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix dispatch concurrency for labeled events to prevent routing cancellation

🐞 Bug fix ⚙️ Configuration changes 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Make dispatch concurrency label-aware for labeled events to avoid run cancellations.
• Skip dispatch entirely for non-routing labels to preserve runner time and queue slots.
• Add alignment test to prevent regressions across all shim workflow templates.
Diagram

graph TD
  A([GitHub event]) --> B[Fullsend shim workflow] --> C{Routing label?} --> D[Label-aware concurrency group] --> E[dispatch.yml (reusable)] --> F[Agent stage runs]
  C -->|no| G[Skip dispatch job]

  subgraph Legend
    direction LR
    _ext([Event]) ~~~ _wf[Workflow/job] ~~~ _dec{Decision}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Filter labeled events inside dispatch.yml only
  • ➕ Centralizes routing/label logic in one place
  • ➕ Avoids duplicating label whitelist across shims
  • ➖ Still consumes concurrency slots and runner startup time for non-routing labels
  • ➖ Does not address cancellations caused by concurrency collisions without additional changes
2. Disable/relax concurrency for labeled events
  • ➕ Eliminates cancellation/queue interactions for label bursts
  • ➖ Can trigger multiple redundant dispatches for the same issue/PR
  • ➖ Higher runner usage and more noise during rapid label application

Recommendation: Current approach is the best tradeoff: the if: guard prevents wasted runs for non-routing labels, and the label-aware concurrency key prevents routing labels from being starved/canceled by unrelated labels during rapid triage bursts. Keeping the whitelist in the shims is acceptable since it’s now enforced by an alignment test.

Files changed (4) +83 / -8

Bug fix (2) +34 / -6
fullsend.yamlAdd label-aware concurrency and routing-label guard to dispatch job +17/-3

Add label-aware concurrency and routing-label guard to dispatch job

• Updates the dispatch job concurrency group to include the label name for 'issues/pull_request' 'labeled' events, preventing routing labels from sharing a slot with non-routing labels. Adds an 'if:' guard to skip dispatch on labeled events unless the label is one of the routing labels (ready-to-code/review/triage).

.github/workflows/fullsend.yaml

shim-workflow-call.yamlAdd label-aware concurrency and routing-label guard to workflow-call shim +17/-3

Add label-aware concurrency and routing-label guard to workflow-call shim

• Updates the workflow-call shim dispatch job concurrency group to include the label name for labeled events. Adds an 'if:' guard to skip the dispatch job for non-routing labeled events, matching repo-level behavior.

internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml

Tests (1) +39 / -0
workflow_call_alignment_test.goAdd regression test for labeled-event filtering and concurrency keys +39/-0

Add regression test for labeled-event filtering and concurrency keys

• Introduces 'TestShimLabeledEventFiltering' to assert all shim variants whitelist only routing labels at the 'if:' guard and that workflow-call based shims include 'github.event.label.name' in the concurrency key. Prevents future scaffold drift/regressions for #2452.

internal/scaffold/workflow_call_alignment_test.go

Other (1) +10 / -2
shim-per-repo.yamlSkip dispatch for non-routing labeled events in per-repo shim +10/-2

Skip dispatch for non-routing labeled events in per-repo shim

• Adds an 'if:' guard to the per-repo shim dispatch job to ignore labeled events unless the label is a routing label. This avoids consuming runner time and avoids interfering with routing-label runs.

internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 3:57 PM UTC · Ended 4:00 PM UTC
Commit: 4ec46ff · View workflow run →

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Site preview

Preview: https://df697b2a-site.fullsend-ai.workers.dev

Commit: 5262b338c07a9a2e49675920985db64f343b9268

@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from 4ec46ff to 761aadf Compare July 22, 2026 15:59
@ggallen ggallen changed the title fix(dispatch): label-aware concurrency prevents routing-label cancellation fix(dispatch): label-aware concurrency prevents routing-label cancellation (#2452) Jul 22, 2026
@ggallen ggallen changed the title fix(dispatch): label-aware concurrency prevents routing-label cancellation (#2452) fix(dispatch): label-aware concurrency prevents routing-label cancellation Jul 22, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:01 PM UTC · Completed 4:19 PM UTC
Commit: 761aadf · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 61 rules

Grey Divider


Remediation recommended

1. PR label whitelist mismatch 🐞 Bug ➹ Performance
Description
The shim if: whitelist allows ready-to-code and ready-for-triage for all labeled events,
including pull_request_target labeled events, but the downstream router only routes
pull_request_target labeled when the triggering label is ready-for-review. This causes avoidable
dispatch runs that immediately skip with “No stage matched”.
Code

.github/workflows/fullsend.yaml[R56-61]

+      && (
+        github.event.action != 'labeled'
+        || github.event.label.name == 'ready-to-code'
+        || github.event.label.name == 'ready-for-review'
+        || github.event.label.name == 'ready-for-triage'
+      )
Relevance

⭐⭐⭐ High

PR #5015 routes pull_request_target.labeled only for ready-for-review; tightening shim whitelist
avoids known no-op “stage empty” runs.

PR-#5015
PR-#5198

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The shim whitelist includes three routing labels regardless of event type, but the router’s
pull_request_target labeled branch only routes on ready-for-review and otherwise returns an
empty stage, which the dispatcher treats as a no-op skip.

.github/workflows/fullsend.yaml[51-61]
internal/scaffold/fullsend-repo/.github/workflows/dispatch.yml[201-212]
.github/workflows/reusable-dispatch.yml[269-316]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The shim job `if:` condition whitelists three labels for any `labeled` action. However, the router logic for `pull_request_target` only routes labeled events when the triggering label is `ready-for-review`; other whitelisted labels (e.g. `ready-to-code`) won’t route and will lead to a no-op dispatch run.

### Issue Context
This wastes runner time and can still consume a per-label concurrency slot for a run that cannot route.

### Fix Focus Areas
- .github/workflows/fullsend.yaml[52-61]
- internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml[50-59]
- internal/scaffold/fullsend-repo/templates/shim-per-repo.yaml[41-50]

### Implementation guidance
- Refine the labeled-event guard to align with routing behavior:
 - For `pull_request_target` + `action == 'labeled'`: only allow `github.event.label.name == 'ready-for-review'`.
 - For `issues` + `action == 'labeled'`: allow `ready-to-code`, `ready-for-triage` (and `ready-for-review` only if you explicitly want to support it).
- Update `TestShimLabeledEventFiltering` accordingly (it currently assumes the same whitelist for all shims).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Concurrency test false-positive ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
TestShimLabeledEventFiltering’s “concurrency” subtest only checks that github.event.label.name
appears anywhere in the workflow text, but that substring already exists in the if: guard. This
can let a future regression remove label-awareness from concurrency.group without failing tests.
Code

internal/scaffold/workflow_call_alignment_test.go[R618-624]

+	// Workflow-call shim must also have a label-aware concurrency key.
+	for _, tc := range cases[:2] {
+		t.Run(tc.name+"/concurrency", func(t *testing.T) {
+			s := string(tc.content(t))
+			assert.Contains(t, s, "github.event.label.name",
+				"%s concurrency group must include label name for labeled events", tc.name)
+		})
Relevance

⭐ Low

Team previously rejected stricter concurrency assertions; PR #2465 rejected replacing Contains
checks with tighter/anchored validation.

PR-#2465

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test’s concurrency assertion is not scoped to concurrency.group, and github.event.label.name
is already present in the newly-added if: guard, so the test can pass even if the concurrency
group regresses.

internal/scaffold/workflow_call_alignment_test.go[589-625]
.github/workflows/fullsend.yaml[51-61]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TestShimLabeledEventFiltering` claims to validate that `jobs.dispatch.concurrency.group` includes the label name, but its assertion is scoped to the entire file content. Because `github.event.label.name` is also present in the `if:` guard, the test can pass even when `concurrency.group` does not include the label.

### Issue Context
This weakens regression detection for the concurrency change introduced in #2452.

### Fix Focus Areas
- internal/scaffold/workflow_call_alignment_test.go[589-626]

### Implementation guidance
- Unmarshal each workflow YAML into a minimal struct (you already have `callerWorkflow`, `callerJob`, `jobConcurrency`).
- Assert specifically on `caller.Jobs["dispatch"].Concurrency.Group` containing `github.event.label.name` (and optionally containing the `github.event.action == 'labeled'` guard), instead of `assert.Contains(t, s, ...)` on the whole file string.
- Keep the existing string-based assertions for the `if:` guard if desired, but make the concurrency assertion field-scoped.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread .github/workflows/fullsend.yaml
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review

Findings

Medium

Previous run

Review

Findings

Medium

Low

  • [adr-annotation-format] docs/ADRs/0034-centralized-shim-routing-via-dispatch.md:147 — ADR update annotation uses **Update (#2452, PR #5472):** instead of the established **Note (YYYY-MM, [#ISSUE](link)):** format used elsewhere in this repo's ADRs (e.g., ADRs 0054, 0066). Consider changing to **Note (2026-07, [#2452](https://github.com/fullsend-ai/fullsend/issues/2452)):** to match repo conventions.

  • [scope-documentation] docs/glossary.md:118 — The ready- prefix requirement is a new architectural constraint enforced by the if: guard on all shims. All existing routing labels already use this prefix, and the PR properly documents the convention in the glossary, ADR, and bring-your-own-agent guide. Consider noting in the PR description that this convention is an intentional design decision formalized by this PR.

Previous run (2)

Review

Findings

Medium

Previous run (3)

Review

Findings

Medium

  • [protected-path] .github/workflows/fullsend.yaml — Protected path modified. The PR links to issue Concurrency group cancels code dispatch when triage applies multiple labels #2452 and the description explains the rationale for the concurrency group and if: guard changes. Human approval is required for all protected-path changes.

  • [breaking-change] internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml:50 — The startsWith(github.event.label.name, 'ready-') filter formalizes an implicit convention as an enforced behavioral change in the shim templates. All existing routing labels already use the ready- prefix and dispatch.yml routing only matches ready--prefixed labels (so non-ready- labels were never functionally routed), but the shim-level enforcement is new. Custom agents using label-based triggers must follow the ready- prefix convention. The PR documents this requirement in the glossary, ADR 0034, and bring-your-own-agent guide.

Low

  • [implicit-contract] docs/guides/user/bring-your-own-agent.md:187 — The ready- prefix requirement is documented as an inline comment in the harness field reference YAML block. A more prominent callout (e.g., in the "Registering your agent" section) would improve discoverability for custom agent authors.

  • [documentation-gap] docs/glossary.md:118 — The glossary update mentions the shim if: guard skips non-matching labels. Making the silent nature of the skip more explicit (e.g., "silently skips") would help users understand the consequences of using a non-ready- label.

Previous run (4)

Review

Findings

Medium

Low

  • [incomplete-doc] docs/guides/dev/e2e-testing.md — The e2e-testing guide discusses label filtering limitations at the on: level and uses concurrency groups plus gate jobs to handle non-actionable labels. The PR introduces a complementary if: startsWith() pattern for dispatch label filtering at the job level. A cross-reference could be useful, though these are different subsystems (e2e authorization vs dispatch routing).
Previous run (5)

Review

Findings

Medium

Low

  • [code-organization] internal/scaffold/workflow_call_alignment_test.goTestShimLabeledEventFiltering validates both label filtering and label-aware concurrency in a single test function. The concurrency sub-tests cover shim dispatcher concurrency (a different layer than TestReusableDispatchStageConcurrency which tests stage-level agent workflows in reusable-dispatch.yml), so the overlap is cosmetic, not functional. Keeping both concerns together is reasonable since they implement the same feature (Concurrency group cancels code dispatch when triage applies multiple labels #2452).
Previous run (6)

Review

Findings

Medium

  • [protected-path] .github/workflows/fullsend.yaml — Protected path modified. The PR links to issue Concurrency group cancels code dispatch when triage applies multiple labels #2452 and the description explains the rationale for the change. Human approval is required for all protected-path changes.

  • [stale-behavioral-description] docs/ADRs/0034-centralized-shim-routing-via-dispatch.md:142 — The Consequences section describes enrolled repos having "a single concurrency group" that "cancels any in-progress run for the same issue/PR." This PR introduces label-aware concurrency groups where different routing labels get separate slots, making that description inaccurate. A minor annotation cross-referencing fix(dispatch): label-aware concurrency prevents routing-label cancellation #5472 would be appropriate (per ADR immutability rules).

Low

  • [test-inadequate] internal/scaffold/workflow_call_alignment_test.go:622 — The concurrency sub-test asserts github.event.label.name appears in the file content, but this string also appears in the if: guard. If the concurrency group were accidentally removed while the if: guard remained, this test would still pass. A more specific assertion (e.g., checking for the ternary expression github.event.action == 'labeled' && github.event.label.name) would make the test robust against this false-positive scenario.

Labels: PR modifies dispatch workflow routing (concurrency groups and labeled-event filtering)

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment component/dispatch Workflow dispatch and triggers type/bug Confirmed defect in existing behavior labels Jul 22, 2026
@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from 761aadf to e909805 Compare July 22, 2026 16:43
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:45 PM UTC · Completed 4:58 PM UTC
Commit: e909805 · View workflow run →

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-agent review squad pass (4 reviewers: Claude x2, Grok, Gemini) — no Critical/High findings survived verification. 7 Medium findings posted inline covering: unverified core fix behavior in a live GH Actions environment, a weak test assertion (substring-only, not semantic), a fragile positional test slice, a stale ADR invariant re: per-role concurrency, an ADR update that overgeneralizes which shim gained label-aware concurrency, an unacknowledged "Confidence: Medium" root cause from the source issue, and hand-copied if:/group: blocks with no single source of truth. Two prior review threads (label-whitelist mismatch, weak concurrency test) are already resolved/declared-intentional and not re-flagged.

Comment thread .github/workflows/fullsend.yaml
Comment thread .github/workflows/fullsend.yaml
Comment thread docs/ADRs/0034-centralized-shim-routing-via-dispatch.md Outdated
Comment thread docs/ADRs/0034-centralized-shim-routing-via-dispatch.md Outdated
Comment thread docs/ADRs/0034-centralized-shim-routing-via-dispatch.md Outdated
Comment thread internal/scaffold/workflow_call_alignment_test.go Outdated
Comment thread internal/scaffold/workflow_call_alignment_test.go Outdated
Comment thread internal/scaffold/fullsend-repo/templates/shim-workflow-call.yaml
@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from e909805 to aed6789 Compare July 22, 2026 18:59
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:00 PM UTC · Completed 7:14 PM UTC
Commit: aed6789 · View workflow run →

Comment thread .github/workflows/fullsend.yaml
@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from aed6789 to 9ea85ce Compare July 22, 2026 20:20
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 8:22 PM UTC · Ended 8:26 PM UTC
Commit: 9ea85ce · View workflow run →

@ggallen
ggallen requested a review from ifireball July 22, 2026 20:24
@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from 9ea85ce to f3f1315 Compare July 22, 2026 20:26
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:27 PM UTC · Completed 8:41 PM UTC
Commit: f3f1315 · View workflow run →

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from f3f1315 to fe15fd5 Compare July 23, 2026 11:56
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:57 AM UTC · Completed 12:10 PM UTC
Commit: fe15fd5 · View workflow run →

@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from fe15fd5 to 4a399c0 Compare July 23, 2026 20:48
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:49 PM UTC · Completed 9:06 PM UTC
Commit: 4a399c0 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

…ation (fullsend-ai#2452)

When triage applies multiple labels rapidly, all issues/labeled events
shared the same concurrency group (fullsend-dispatch-{number}). GitHub
Actions cancels intermediate pending runs, so the ready-to-code event
could be cancelled while a non-routing label (type/bug) survives and
exits with "No stage matched" — the code agent never dispatches.

Two complementary fixes:

1. Label-aware concurrency key: for labeled events, include the label
   name in the group so ready-to-code and type/bug get separate slots.
   Non-labeled events share a common "dispatch" suffix to preserve
   existing queuing behavior.

2. Early-exit non-routing labels: add an if: guard that skips the
   dispatch job entirely for labeled events where the label isn't one
   of the three routing labels (ready-to-code, ready-for-review,
   ready-for-triage). This prevents non-routing labels from consuming
   concurrency slots or wasting runner time.

Applied to all three shim files (fullsend.yaml, shim-workflow-call.yaml,
shim-per-repo.yaml). Added TestShimLabeledEventFiltering to prevent
regression.

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>

docs: note ready- prefix convention for routing labels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>

docs: note ready- prefix convention in glossary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>

docs: promote ready- prefix note to callout, clarify silent skip

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>

docs: use standard ADR annotation format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
@ggallen
ggallen force-pushed the fix/2452-label-aware-dispatch-concurrency branch from 4a399c0 to 5262b33 Compare July 23, 2026 21:13
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:15 PM UTC · Completed 9:28 PM UTC
Commit: 5262b33 · View workflow run →

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

Labels

component/dispatch Workflow dispatch and triggers requires-manual-review Review requires human judgment type/bug Confirmed defect in existing behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concurrency group cancels code dispatch when triage applies multiple labels

3 participants