fix: restore interaction permissions for workflow_call workflows with reaction/status-comment#39652
Conversation
…s-comments Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…th reactions/status-comments Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ermissions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR restores activation-job write permissions for workflows that are reusable (on: workflow_call) when reaction and/or status-comment is configured. Because the caller event type is unknowable at compile time for reusable workflows, the compiler now applies a broad interaction-permissions fallback so reactions/status-comments can function correctly.
Changes:
- Add
addWorkflowCallActivationPermissionsand invoke it during activation permission construction whenworkflow_callis present. - Add end-to-end tests asserting the expected activation permissions for
workflow_call+reaction/status-commentcombinations. - Regenerate the
pr-code-quality-reviewerlock workflow artifact.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/compiler_activation_job_builder.go | Adds workflow_call-specific broad fallback permissions for reactions/status-comments in activation jobs. |
| pkg/workflow/activation_permissions_scope_test.go | Adds new end-to-end tests covering workflow_call permission behavior for reaction/status-comment. |
| .github/workflows/pr-code-quality-reviewer.lock.yml | Updates generated metadata as part of recompilation/regeneration. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 2
| hasStatusComment: ctx.hasStatusComment, | ||
| statusCommentIncludesIssues: ctx.statusCommentIssues, | ||
| statusCommentIncludesPullRequests: ctx.statusCommentPRs, | ||
| statusCommentIncludesDiscussions: ctx.statusCommentDiscussions, | ||
| }) |
| // Tests for workflow_call trigger + reaction/status-comment permissions (issue #39372). | ||
| // When a workflow declares workflow_call as its trigger it acts as a reusable workflow and can | ||
| // be called from ANY caller event. The compiler cannot know the caller's event type at compile | ||
| // time, so it must grant the full set of permissions that the configured reactions / status-comments | ||
| // could require. |
|
✅ Test Quality Sentinel completed test quality analysis. |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
|
❌ Design Decision Gate 🏗️ failed during design decision gate check. |
|
✅ PR Code Quality Reviewer completed the code quality review. |
🧪 Test Quality Sentinel Report
📊 Metrics & Test Classification (3 tests analyzed)
Go: 3 ( Scoring breakdown:
Verdict
|
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnose and /tdd — requesting changes for one correctness gap in the app-token path.
📋 Key Themes & Highlights
Key Themes
- Incomplete fix:
addWorkflowCallActivationPermissionscorrectly patches the GITHUB_TOKEN path butbuildActivationAppTokenPermissionscallsaddActivationInteractionPermissionswith the rawonSectionunchanged — the same root cause that silently drops write scopes forworkflow_calltriggers. A workflow usingactivation.github-app+reaction/status-comment+workflow_callwill still produce a zero-permission app token. - Test coverage gap: The three new tests verify the GITHUB_TOKEN
permissions:block only; there is no test that exercisesbuildActivationAppTokenPermissionsfor theworkflow_callcase.
Positive Highlights
- ✅ Excellent PR description — root cause, fix rationale, and all three permutation scenarios are clearly documented
- ✅ The new function is small, focused, and delegates to the already-proven
addBroadActivationInteractionPermissions - ✅ Test ratio (99 new test lines vs 32 production lines) is a healthy regression-test pattern
- ✅ Per-type opt-outs (
reaction.issues: false, etc.) are correctly preserved through the broad fallback path - ✅ Doc comment accurately explains the compile-time unknowability of the caller event and its connection to
addCentralizedCommandActivationPermissions
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer
| // | ||
| // This mirrors the handling for centralized slash_command workflows that compile to workflow_dispatch | ||
| // (see addCentralizedCommandActivationPermissions). | ||
| func (c *Compiler) addWorkflowCallActivationPermissions(permsMap map[PermissionScope]PermissionLevel, ctx *activationJobBuildContext) { |
There was a problem hiding this comment.
[/diagnose] The app-token path (buildActivationAppTokenPermissions) has the same gap this PR is fixing for the GITHUB_TOKEN path — it still calls addActivationInteractionPermissions with the raw onSection, which will silently drop write scopes for a workflow_call-only trigger.
💡 Root-cause detail
buildActivationAppTokenPermissions (line 236) calls:
addActivationInteractionPermissions(appPerms, activationInteractionPermissionsOptions{
onSection: ctx.data.On, // workflow_call-only: no issues/pull_request events
...
})addActivationInteractionPermissionsMap parses the event set and finds only workflow_call — none of the hasIssuesEvent / hasPullRequestEvent / hasDiscussionEvent flags fire, so no write permission is granted to the minted app token.
The fix should mirror what this new function does: detect hasWorkflowCallTrigger in buildActivationAppTokenPermissions and call addBroadActivationInteractionPermissions there too (or extract a shared helper both paths call).
| // time, so it must grant the full set of permissions that the configured reactions / status-comments | ||
| // could require. | ||
|
|
||
| func TestActivationPermissionsWorkflowCallReaction(t *testing.T) { |
There was a problem hiding this comment.
[/tdd] These three scenarios cover the GITHUB_TOKEN path well, but there is no test for the GitHub App path (buildActivationAppTokenPermissions) — a workflow_call + reaction/status-comment workflow that also configures activation.github-app would silently produce an app token with no write scopes.
💡 Suggested test scenario
Add a fourth test with activation.github-app configured to verify that the minted app-token permissions section also includes issues: write, pull-requests: write, and discussions: write. The existing tests only exercise the GITHUB_TOKEN permissions: block in the activation job, not the gh-app-token-permissions: block.
|
@copilot run pr-finisher skill |
There was a problem hiding this comment.
Two issues must be resolved before merging. Both are already flagged in earlier inline comments; this review validates them.
🔴 Blocking issues
1. Permission over-grant via unguarded statusComment* defaults
addBroadActivationInteractionPermissions evaluates statusCommentIncludesIssues, statusCommentIncludesPullRequests, and statusCommentIncludesDiscussions without gating them on hasStatusComment. All three default to true when not explicitly configured. A workflow_call workflow with reaction.issues: false and reaction.pull-requests: false (discussions-only reaction, no status-comment) would still receive issues: write from the status-comment default path.
The bug exists in addBroadActivationInteractionPermissions itself and therefore also affects the existing fallback paths in addActivationInteractionPermissionsMap (empty or unparseable onSection). Fixing it only at this call site by gating the flags before passing them would leave the fallback paths unchanged. The cleaner fix is inside addBroadActivationInteractionPermissions:
needsIssuesWriteForStatusComment := options.hasStatusComment &&
(options.statusCommentIncludesIssues || options.statusCommentIncludesPullRequests)
...
if (options.hasReaction && options.reactionIncludesDiscussions) ||
(options.hasStatusComment && options.statusCommentIncludesDiscussions) {2. Tests don't cover per-type opt-out combinations
TestActivationPermissionsWorkflowCallReaction uses reaction: eyes with all defaults, so all three write permissions are correctly granted through the reaction path alone — the test passes whether the hasStatusComment bug is fixed or not. No test exercises reaction.issues: false or reaction.pull-requests: false under a workflow_call trigger, which is the scenario the PR description explicitly promises to handle correctly.
🔎 Code quality review by PR Code Quality Reviewer
| // opt-out flags (reaction.issues, reaction.pull-requests, etc.). | ||
| // | ||
| // This mirrors the handling for centralized slash_command workflows that compile to workflow_dispatch | ||
| // (see addCentralizedCommandActivationPermissions). |
There was a problem hiding this comment.
Misleading equivalence: the claim that this "mirrors" addCentralizedCommandActivationPermissions is inaccurate and could confuse future maintainers tracking permission drift between the two paths.
💡 Details
addCentralizedCommandActivationPermissions calls addActivationInteractionPermissionsMap with a synthetic event-specific on-section built from CommandEvents, so it goes through the event-aware permission logic and only reaches the broad fallback on parse failure.
This function calls addBroadActivationInteractionPermissions directly — which is correct since caller event types are unknowable for workflow_call — but that makes the two paths not equivalent. A maintainer reading "mirrors centralized command handling" and then inspecting addCentralizedCommandActivationPermissions will see a different call stack and be confused about the discrepancy.
Suggested replacement for the final doc sentence:
// Because the caller's event type is unknown at compile time, this function always uses the
// broad fallback (addBroadActivationInteractionPermissions) rather than the event-aware
// addActivationInteractionPermissionsMap used by the centralized-command path.
|
@copilot add tests. run pr-finisher skill |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done. I ran the pr-finisher pass, applied the requested fixes, and pushed commit |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added test coverage in |
Workflows using
workflow_callas their trigger lostissues: write,pull-requests: write, anddiscussions: writefrom the activation job whenreactionorstatus-commentwas configured. The compiler infers interaction permissions from the declared event types (issues,pull_request, etc.), but aworkflow_call-onlyon:section contains none of those — so all write permissions were silently dropped.Changes
compiler_activation_job_builder.go— addsaddWorkflowCallActivationPermissions, called frombuildActivationPermissionsafter the existingaddCentralizedCommandActivationPermissionsstep. Whenworkflow_callis in the trigger, the caller's event type is unknowable at compile time, so the function applies broad interaction permissions while still respecting per-type opt-outs (reaction.issues: false, etc.).compiler_activation_job_builder.go— updatesbuildActivationAppTokenPermissionsso the activation GitHub App token path also applies the sameworkflow_callbroad-fallback behavior for reaction/status-comment scopes.compiler_activation_job.go— tightensaddBroadActivationInteractionPermissionsso status-comment defaults are only considered whenstatus-commentis actually enabled (hasStatusComment), preventing over-grants in reaction-only scenarios.activation_permissions_scope_test.go— expands coverage with new tests for:workflow_call+reaction→issues: write+pull-requests: write+discussions: writeworkflow_call+status-comment→issues: write+discussions: write(nopull-requests: write; PR comments use the issues API scope)workflow_call+issues+reaction→ full broad set (theworkflow_callleg trumps event-scoped narrowing)workflow_call+ discussions-only reaction object (reaction.issues: false,reaction.pull-requests: false) → onlydiscussions: writeworkflow_call+github-app+reactionto verify activation app-token permission fields includepermission-issues: write,permission-pull-requests: write, andpermission-discussions: writeworkflow_call+github-app+ discussions-only reaction object (reaction.issues: false,reaction.pull-requests: false) to verify app-token permissions also respect opt-outs and include onlypermission-discussions: writeExample — a reusable workflow that previously compiled without write permissions:
Previously compiled activation job had only
contents: read; now correctly includesissues: write,pull-requests: write, anddiscussions: write(and the activation GitHub App token path now gets equivalent interaction scopes when configured).