fix(dispatch): add label-based handoffs for bot-to-bot dispatch paths - #2679
Conversation
E2E tests did not runE2E tests run automatically for org/repo members and collaborators on pull requests. For other contributors, a maintainer must add the See E2E testing guide for details. |
PR Summary by QodoDispatch: add label-based bot-to-bot handoff triggers Description
Diagram
High-Level Assessment
Files changed (8)
|
Site previewPreview: https://4c4cffa8-site.fullsend-ai.workers.dev Commit: |
|
🤖 Finished Review · ✅ Success · Started 10:14 PM UTC · Completed 10:29 PM UTC |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Code Review by Qodo
Context used✅ Tickets:
🎫 Adopt label-based gating for agent dispatch and trusted-process identification 🎫 dispatch: retro-filed proposal issues skip auto-triage under ADR 0054 authorization gate✅ Compliance rules (platform):
58 rules✅ Skills:
writing-user-docs, writing-adrs 1. dispatch.yml max-lines raised
|
ReviewFindingsMedium
Previous runReviewFindingsMedium
Labels: PR modifies dispatch routing logic and scaffold scripts |
ADR 0054's is_event_actor_authorized check blocks GitHub App bot accounts (which return empty role_name from the collaborator API) from triggering dispatch via issues.opened and pull_request_target.opened. This breaks two agent handoff paths: - retro → triage: post-retro.sh creates bare issues, bot fails auth - code → review: post-code.sh creates PRs, bot fails auth Fix by adding label-based handoffs consistent with how triage → code already works (via ready-to-code label). Label application requires write access, serving as an implicit authorization gate per ADR 0054. Changes: - Add ready-for-triage label trigger to dispatch routing - post-retro.sh applies ready-for-triage on issue creation - post-code.sh applies ready-for-review after PR creation - Update glossary, ADR 0054 annotation, and agent docs with ready-for-triage - retro.md: document ready-for-triage as a control label - triage.md: document ready-for-triage as an input trigger Closes fullsend-ai#2636 Closes fullsend-ai#2669 Signed-off-by: Adam Scerra <ascerra@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
30628dd to
c04131b
Compare
|
🤖 Finished Review · ✅ Success · Started 10:59 PM UTC · Completed 11:14 PM UTC |
|
🤖 Finished Retro · ✅ Success · Started 1:14 PM UTC · Completed 1:22 PM UTC |
Retro: PR #2679 — Label-based handoffs for bot-to-bot dispatchWorkflow went well overall. The review agent produced high-quality findings — it correctly identified an error-handling inconsistency in What happened: PR was opened 2026-06-25, received two review agent runs (commits No new proposals filed — all identified improvement opportunities are already covered by existing open issues:
Notable positive: The review agent's Priority recommendation: Issue #1870 (skip review dispatch on |
…ursor/b69a09e5 Pull upstream main including ready-for-triage label dispatch (fullsend-ai#2679 / fullsend-ai#2636), standalone mint, repos management, and VitePress docs site migration. Signed-off-by: Barak Korren <bkorren@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Adopt main's fullsend-ai#2679 bot-to-bot triage path: mint token creates the issue, then applies ready-for-triage in a separate API call so issues.labeled fires. Removes the interim E2E_GITHUB_PASSWORD PAT workaround. Signed-off-by: Barak Korren <bkorren@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…st-retro.sh post-retro.sh passed --label "ready-for-triage" to gh issue create without ensuring the label existed in the target repo. This caused the retro workflow to fail with exit code 1 on repos that predate PR fullsend-ai#2679 (which introduced the label in the scaffold template). Add a gh label create --force call before gh issue create, following the same idempotent pattern used in post-review.sh for the ready-for-merge and requires-manual-review labels. The --force flag makes the call a no-op when the label already exists. Also adds a mock handler for gh label create in the test harness and a test case verifying the label is created before the issue. Note: pre-commit could not run (shellcheck-py failed to install in sandbox due to network restrictions). Post-script runs an authoritative pre-commit check. Closes fullsend-ai#2797
…lsend-ai#2674) Bot-authored PRs (e.g. fullsend-ai-coder[bot]) fail the is_event_actor_authorized check on pull_request_target.opened because GitHub App accounts lack a collaborator role in the permission API. PR fullsend-ai#2679 added a ready-for-review label in post-code.sh to use the label-based dispatch path, but pull_request_target.labeled was not handled in the dispatch routing — only issues.labeled was. Since GitHub does not fire issues.labeled for PRs, the label was silently ignored. Add a labeled) case under pull_request_target) in both dispatch files so ready-for-review triggers review dispatch. The label path needs no explicit auth gate because label application already requires write access. Also add Go test coverage for bot-opened PR detection (ActorBot + RoleNone) and document that the pre-CEL auth gate intentionally does not bypass write checks for bot-opened events. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Hector Martinez <hemartin@redhat.com>
pull_request_target.synchronize fires when the fix agent pushes, but its actor-identity authorization check is gated on PR_USER_LOGIN — the PR's original author, which for agent-authored PRs is the code agent's bot account regardless of who triggered this fix run. GitHub App bots have no collaborator role, so that check always fails closed and review is never re-dispatched after a fix-agent push (fullsend-ai#5188). post-fix.sh now removes then re-adds the ready-for-review label after a successful push, forcing a fresh labeled webhook event. That path has no actor-authorization gate at all — label application itself already requires write access, so it needs no separate identity check — mirroring post-code.sh's identical handling of the PR-open case. GitHub does not fire a new labeled event when a label already present is re-added, hence the remove-then-add sequence. This supersedes fullsend-ai#5415, which attempted the same fix via an actor-identity-recognition function (is_org_bot()) extended across several dispatch-authorization gates. That approach was closed after review: it reintroduced a design (recognizing bots by name for dispatch authorization) that issue fullsend-ai#2669 had already evaluated and rejected in favor of label-based gating — "actors can be spoofed and the approach is fragile across workflow changes" — a decision PR fullsend-ai#2679 already implemented and shipped for the retro-to-triage handoff (closing fullsend-ai#2636). The label-based fix here needs no changes to the CEL-based dispatch path (internal/harnessdispatch), which already trusts label-added events unconditionally, and needs no forge-specific bot-identity logic, since labels work identically across GitHub and GitLab. A separate, unrelated bug that fullsend-ai#5415 also touched — the fix agent's own review-body content-attribution lookups missing the shared fullsend-ai-review[bot] identity — is tracked independently as fullsend-ai#5550, since it is not a dispatch-authorization question and is unaffected by this change. Signed-off-by: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Greg Allen <gallen@redhat.com>
pull_request_target.synchronize fires when the fix agent pushes, but its actor-identity authorization check is gated on PR_USER_LOGIN — the PR's original author, which for agent-authored PRs is the code agent's bot account regardless of who triggered this fix run. GitHub App bots have no collaborator role, so that check always fails closed and review is never re-dispatched after a fix-agent push (fullsend-ai#5188). post-fix.sh now removes then re-adds the ready-for-review label after a successful push, forcing a fresh labeled webhook event. That path has no actor-authorization gate at all — label application itself already requires write access, so it needs no separate identity check — mirroring post-code.sh's identical handling of the PR-open case. GitHub does not fire a new labeled event when a label already present is re-added, hence the remove-then-add sequence. This supersedes fullsend-ai#5415, which attempted the same fix via an actor-identity-recognition function (is_org_bot()) extended across several dispatch-authorization gates. That approach was closed after review: it reintroduced a design (recognizing bots by name for dispatch authorization) that issue fullsend-ai#2669 had already evaluated and rejected in favor of label-based gating — "actors can be spoofed and the approach is fragile across workflow changes" — a decision PR fullsend-ai#2679 already implemented and shipped for the retro-to-triage handoff (closing fullsend-ai#2636). The label-based fix here needs no changes to the CEL-based dispatch path (internal/harnessdispatch), which already trusts label-added events unconditionally, and needs no forge-specific bot-identity logic, since labels work identically across GitHub and GitLab. A separate, unrelated bug that fullsend-ai#5415 also touched — the fix agent's own review-body content-attribution lookups missing the shared fullsend-ai-review[bot] identity — is tracked independently as fullsend-ai#5550, since it is not a dispatch-authorization question and is unaffected by this change. Signed-off-by: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Greg Allen <gallen@redhat.com>
Summary
ADR 0054's
is_event_actor_authorizedcheck blocks GitHub App bot accounts from triggering dispatch viaissues.openedandpull_request_target.opened. Bot accounts (e.g.fullsend-ai-retro[bot],fullsend-ai-coder[bot]) return emptyrole_namefrom the collaborator permission API because they get permissions through installation tokens, not the collaborator model.This breaks two agent handoff paths:
post-retro.shcreates bare issues (no labels), bot fails theissues.openedauth gatepost-code.shcreates PRs, bot fails thepull_request_target.openedauth gateFix
Add label-based handoffs consistent with how triage → code already works (via
ready-to-code). Label application requires write access, serving as an implicit authorization gate per ADR 0054's "Bot-to-bot workflows are preserved" section.reusable-dispatch.yml+ scaffolddispatch.yml): Addready-for-triageas a new label trigger for triage in theissues.labeledpathpost-retro.sh: Applyready-for-triagelabel at issue creation (--label "ready-for-triage")post-code.sh: Applyready-for-reviewlabel to PR after creation viagh issue edit --add-labelready-for-triagescaffold_test.goassertion,post-retro-test.shlabel verificationSecurity review
No new abuse surface opened — label application requires write access at the GitHub platform level. No privilege escalation (tokens already have the required scopes). No injection vectors (label names are hardcoded string literals). No exploitable race conditions.
Review notes
issues.openedevent will still fire and no-op for bot actors (harmlessly logs "No stage matched"). Theissues.labeledevent fires separately and dispatches via the label path.ready-for-reviewlabel path already existed in dispatch;post-code.shnow applies it.ready-for-triageis new — repos may need the label pre-provisioned for clean UX (auto-created byghif missing, but without color/description).Closes #2636
Closes #2669
Made with Cursor