Skip to content

fix: restore interaction permissions for workflow_call workflows with reaction/status-comment#39652

Merged
pelikhan merged 7 commits into
mainfrom
copilot/fix-on-workflow-call-reactions
Jun 16, 2026
Merged

fix: restore interaction permissions for workflow_call workflows with reaction/status-comment#39652
pelikhan merged 7 commits into
mainfrom
copilot/fix-on-workflow-call-reactions

Conversation

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Workflows using workflow_call as their trigger lost issues: write, pull-requests: write, and discussions: write from the activation job when reaction or status-comment was configured. The compiler infers interaction permissions from the declared event types (issues, pull_request, etc.), but a workflow_call-only on: section contains none of those — so all write permissions were silently dropped.

Changes

  • compiler_activation_job_builder.go — adds addWorkflowCallActivationPermissions, called from buildActivationPermissions after the existing addCentralizedCommandActivationPermissions step. When workflow_call is 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 — updates buildActivationAppTokenPermissions so the activation GitHub App token path also applies the same workflow_call broad-fallback behavior for reaction/status-comment scopes.
  • compiler_activation_job.go — tightens addBroadActivationInteractionPermissions so status-comment defaults are only considered when status-comment is actually enabled (hasStatusComment), preventing over-grants in reaction-only scenarios.
  • activation_permissions_scope_test.go — expands coverage with new tests for:
    • workflow_call + reactionissues: write + pull-requests: write + discussions: write
    • workflow_call + status-commentissues: write + discussions: write (no pull-requests: write; PR comments use the issues API scope)
    • workflow_call + issues + reaction → full broad set (the workflow_call leg trumps event-scoped narrowing)
    • workflow_call + discussions-only reaction object (reaction.issues: false, reaction.pull-requests: false) → only discussions: write
    • broad-fallback unit coverage to ensure status-comment defaults do not widen permissions when status-comment is disabled
    • workflow_call + github-app + reaction to verify activation app-token permission fields include permission-issues: write, permission-pull-requests: write, and permission-discussions: write
    • workflow_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 only permission-discussions: write

Example — a reusable workflow that previously compiled without write permissions:

---
on:
  workflow_call:
  reaction: eyes
engine: copilot
---

Previously compiled activation job had only contents: read; now correctly includes issues: write, pull-requests: write, and discussions: write (and the activation GitHub App token path now gets equivalent interaction scopes when configured).

Copilot AI and others added 3 commits June 16, 2026 18:52
…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>
Copilot AI changed the title [WIP] Fix on.workflow_call event support for on.reactions fix: restore interaction permissions for workflow_call workflows with reaction/status-comment Jun 16, 2026
Copilot AI requested a review from pelikhan June 16, 2026 19:04
@pelikhan pelikhan marked this pull request as ready for review June 16, 2026 19:56
Copilot AI review requested due to automatic review settings June 16, 2026 19:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 addWorkflowCallActivationPermissions and invoke it during activation permission construction when workflow_call is present.
  • Add end-to-end tests asserting the expected activation permissions for workflow_call + reaction / status-comment combinations.
  • Regenerate the pr-code-quality-reviewer lock 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

Comment on lines +805 to +809
hasStatusComment: ctx.hasStatusComment,
statusCommentIncludesIssues: ctx.statusCommentIssues,
statusCommentIncludesPullRequests: ctx.statusCommentPRs,
statusCommentIncludesDiscussions: ctx.statusCommentDiscussions,
})
Comment on lines +547 to +551
// 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.
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ failed during design decision gate check.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 70/100 — Acceptable

Analyzed 3 test(s): 3 design, 0 implementation, 0 guideline violation(s). Test inflation detected (test/production line ratio: 3.09×).

📊 Metrics & Test Classification (3 tests analyzed)
Metric Value
New/modified tests analyzed 3
✅ Design tests (behavioral contracts) 3 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 1 (33%)
Duplicate test clusters 0
Test inflation detected YES (99 test lines / 32 production lines = 3.09×)
🚨 Coding-guideline violations 0
Test File Classification Issues Detected
TestActivationPermissionsWorkflowCallReaction pkg/workflow/activation_permissions_scope_test.go:~546 ✅ Design
TestActivationPermissionsWorkflowCallStatusComment pkg/workflow/activation_permissions_scope_test.go:~580 ✅ Design
TestActivationPermissionsWorkflowCallAndIssuesTriggerReaction pkg/workflow/activation_permissions_scope_test.go:~616 ✅ Design

Go: 3 (*_test.go); JavaScript: 0. No other languages detected.

Scoring breakdown:

  • Behavioral coverage: 40/40 (3/3 design tests)
  • Error/edge-case coverage: 10/30 (1/3 tests has explicit boundary assertions via NotContains)
  • Duplication penalty: 0/20 (no duplicate clusters)
  • Inflation penalty: 0/10 (inflation detected: ratio 3.09× > 2:1)

Verdict

Check passed. 0% implementation tests (threshold: 30%). All three tests compile the workflow to disk using the real compiler and assert on the generated lock-file YAML — they verify observable behavioral contracts, not internal implementation details. The single inflation flag is expected here: each test needs ~30 lines of YAML fixture setup to exercise a small but precise permission-scope boundary in compiler_activation_job_builder.go.

🧪 Test quality analysis by Test Quality Sentinel ·

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 70/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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: addWorkflowCallActivationPermissions correctly patches the GITHUB_TOKEN path but buildActivationAppTokenPermissions calls addActivationInteractionPermissions with the raw onSection unchanged — the same root cause that silently drops write scopes for workflow_call triggers. A workflow using activation.github-app + reaction/status-comment + workflow_call will 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 exercises buildActivationAppTokenPermissions for the workflow_call case.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/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.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add tests. run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Done. I ran the pr-finisher pass, applied the requested fixes, and pushed commit ab39b5a.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add tests. run pr-finisher skill

Added test coverage in 397b3f6 for workflow_call + github-app + discussions-only reaction to verify app-token permissions respect opt-outs (issues: false, pull-requests: false). I also ran the pr-finisher validation pass locally (make fmt, make lint, make test-unit, make test, make recompile).

@pelikhan pelikhan merged commit 073da7e into main Jun 16, 2026
36 checks passed
@pelikhan pelikhan deleted the copilot/fix-on-workflow-call-reactions branch June 16, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants