Skip to content

fix: allow Copilot bot in mattpocock-skills-reviewer; handle GitHub App "not a user" in checkBotStatus#33451

Closed
Copilot wants to merge 6 commits into
mainfrom
copilot/investigate-action-skip
Closed

fix: allow Copilot bot in mattpocock-skills-reviewer; handle GitHub App "not a user" in checkBotStatus#33451
Copilot wants to merge 6 commits into
mainfrom
copilot/investigate-action-skip

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

The mattpocock-skills-reviewer workflow was being skipped for PRs authored by the Copilot GitHub App due to two compounding bugs in the permission check pipeline.

Root causes

1. GH_AW_ALLOWED_BOTS was never emitted

mattpocock-skills-reviewer.md had no bots: field, so the compiled lock file omitted GH_AW_ALLOWED_BOTS. The bot-allowlist fallback in check_membership.cjs is gated on allowedBots.length > 0 — with an empty list it was skipped entirely and the workflow was denied with result=api_error.

2. checkBotStatus classified GitHub Apps as inactive

The collaborators API returns 404 "Copilot is not a user" for GitHub App actors — semantically different from a plain 404 Not Found (no repo access). checkBotStatus didn't distinguish between them and returned { isBot: true, isActive: false }, so even a properly configured GH_AW_ALLOWED_BOTS would have still denied Copilot.

Changes

  • mattpocock-skills-reviewer.md — adds bots: [Copilot] under on:; lock file recompiled with GH_AW_ALLOWED_BOTS: "Copilot" in the check_membership step.

  • check_permissions_utils.cjs — adds isGitHubAppNotUserError(error) (regex /\bis not a user\b/ on the 404 body); checkBotStatus returns active when either the [bot]-form or slug-form lookup hits this error; checkRepositoryPermission propagates isGitHubApp: boolean from the original error so callers don't reconstruct fake error objects.

  • check_membership.cjs — when result.isGitHubApp is true and the bot is denied, emits an actionable denial summary pointing the workflow author to on.bots: instead of a generic API-error message.

  • compiler_validators.go — suppresses the false-positive "slash_command + bots" warning for pull_request_reviewer workflows. These set workflowData.Command internally (to the reviewer handle) but are triggered by review requests, not comments, so the concern doesn't apply.

# mattpocock-skills-reviewer.md — before
on:
  pull_request_reviewer: matt

# after
on:
  pull_request_reviewer: matt
  bots: [Copilot]

Copilot AI and others added 3 commits May 20, 2026 03:40
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…b App not-a-user errors

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… remove fake error construction

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

@copilot pull_request_reviewer should automatically add the copilot bot to the allowed bots

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 20, 2026

@copilot pull_request_reviewer should automatically add the copilot bot to the allowed bots

Done in commit fix: auto-inject Copilot bot for pull_request_reviewer workflows. The compiler now automatically injects "Copilot" into workflowData.Bots whenever pull_request_reviewer is configured, using the existing mergeBots deduplication logic. The explicit bots: [Copilot] line has been removed from mattpocock-skills-reviewer.md — the lock file is recompiled and still contains GH_AW_ALLOWED_BOTS: "Copilot".

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot merge main and recompile

Copilot AI and others added 2 commits May 20, 2026 04:26
…ction-skip

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 20, 2026

@copilot merge main and recompile

Done — merged origin/main and recompiled all 231 workflows (0 errors, 5 lock files updated).

@pelikhan pelikhan marked this pull request as ready for review May 20, 2026 04:31
Copilot AI review requested due to automatic review settings May 20, 2026 04:31
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

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

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

The PR effectively solves the immediate problem of allowing Copilot to trigger the mattpocock-skills-reviewer workflow. The implementation is well-tested and the error messages are helpful.

Issues to address:

  1. Security/Correctness — The isGitHubAppNotUserError() regex is too permissive and could match unrelated error messages
  2. Assumption validity — The logic assumes all GitHub Apps in event payloads have repository access, which may not hold for all event types

What's done well:

  • Comprehensive test coverage for the new detection logic
  • Clear, actionable error messages guiding users to add on.bots:
  • Auto-injection of Copilot for pull_request_reviewer workflows reduces friction
  • Proper deduplication handling in mergeBots()

🔎 Code quality review by PR Code Quality Reviewer · ● 2.8M

if (!error || typeof error !== "object") return false;
if (!("status" in error) || error.status !== 404) return false;
const msg = getErrorMessage(error);
return typeof msg === "string" && /\bis not a user\b/.test(msg);
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.

Potential security issue: The regex /\bis not a user\b/ may be too permissive. If a future GitHub API change adds different error messages containing "is not a user" (e.g., "Repository is not a user workspace"), this function would incorrectly classify it as a GitHub App.

Suggestion: Make the pattern more specific to match the exact GitHub API response format:

return typeof msg === "string" && /^[\w-]+ is not a user\b/.test(msg);

This ensures we're matching "(login) is not a user" and not other unrelated messages.

// so if an app actor appears in an event payload it must already have repository access.
// Treat it as active to avoid false negatives for apps like Copilot.
if (isGitHubAppNotUserError(botError)) {
core.info(`Bot '${actor}' is a GitHub App; treating as active based on event trigger`);
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.

Logic concern: This assumes that any GitHub App appearing in an event payload "must already have repository access." While this may be true for installation events, it's not necessarily true for all event types.

For example:

  • A GitHub App could trigger a webhook for a repository it doesn't have access to (e.g., organization-level webhooks)
  • The app's permissions could have been revoked between the event trigger and this check

Suggestion: Consider adding a comment explaining which specific event types this assumption applies to, or add a fallback verification step. At minimum, log the event name to aid debugging:

if (isGitHubAppNotUserError(botError)) {
  core.info(`Bot '${actor}' is a GitHub App (event: ${github.context.eventName}); treating as active based on event trigger`);
  return { isBot: true, isActive: true };
}

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes permission-check behavior so workflows don’t get skipped when triggered by the Copilot GitHub App, and updates compiled workflows accordingly.

Changes:

  • Detects GitHub App “not a user” 404s and treats such bot actors (e.g., Copilot) as active in bot-status checks; propagates isGitHubApp through permission-check results.
  • Improves denial output for GitHub App actors and adds/propagates GH_AW_ALLOWED_BOTS in compiled workflow lock files.
  • Adjusts compiler validation/wiring for pull_request_reviewer workflows (suppresses a warning + auto-injects Copilot into allowed bots) and adds regression tests.
Show a summary per file
File Description
pkg/workflow/compiler_validators.go Avoids emitting the slash_command+bots warning for pull_request_reviewer workflows.
pkg/workflow/compiler_orchestrator_workflow.go Auto-injects “Copilot” into the bot allowlist for pull_request_reviewer workflows during compilation.
pkg/workflow/bots_test.go Adds tests covering Copilot auto-injection/deduplication behavior for pull_request_reviewer.
actions/setup/js/check_permissions_utils.cjs Adds GitHub App “not a user” detection + treats those cases as active; propagates isGitHubApp from repo permission checks.
actions/setup/js/check_permissions_utils.test.cjs Updates/extends tests to validate isGitHubApp propagation and GitHub App bot-status behavior.
actions/setup/js/check_membership.cjs Emits actionable denial summary when the actor is a GitHub App and isn’t authorized/allowlisted.
actions/setup/js/check_membership.test.cjs Adds a test case for the new GitHub App guidance path.
actions/setup/js/check_admin_permissions.test.cjs Updates expectations for the new isGitHubApp field in permission-check results.
.github/workflows/test-quality-sentinel.lock.yml Adds GH_AW_ALLOWED_BOTS: "Copilot" to compiled workflow env.
.github/workflows/pr-code-quality-reviewer.lock.yml Adds GH_AW_ALLOWED_BOTS: "Copilot" to compiled workflow env.
.github/workflows/mattpocock-skills-reviewer.lock.yml Adds GH_AW_ALLOWED_BOTS: "Copilot" to compiled workflow env.
.github/workflows/design-decision-gate.lock.yml Adds GH_AW_ALLOWED_BOTS: "Copilot" to compiled workflow env.
.github/workflows/smoke-otel-backends.lock.yml Fixes Datadog header env var reference (DD_APPLICATION_KEY).
.github/workflows/mcp-inspector.lock.yml Fixes Datadog header env var reference (DD_APPLICATION_KEY).

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 14/14 changed files
  • Comments generated: 3

Comment on lines +208 to +213
await writeDenialSummary(
errorMessage,
`Actor '${actorToValidate}' is a GitHub App, not a regular user. ` +
`To allow this app to trigger the workflow, add it to \`on.bots:\` in the workflow frontmatter. ` +
`Example: \`bots: [${actorToValidate}]\``,
);
Comment on lines +356 to +360
// pull_request_reviewer workflows are triggered by review-request events from Copilot.
// Automatically allow the Copilot bot so authors don't need to add bots: [Copilot] manually.
if workflowData.PullRequestReviewer {
workflowData.Bots = c.mergeBots(workflowData.Bots, []string{"Copilot"})
}
expect(mockCore.setOutput).toHaveBeenCalledWith("result", "api_error");
const errorMessageCall = mockCore.setOutput.mock.calls.find(([key]) => key === "error_message");
expect(errorMessageCall).toBeDefined();
expect(errorMessageCall[1]).toContain("Repository permission check failed");
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

PR Code Quality Reviewer completed the code quality review.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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 based on this being a bug fix with comprehensive test coverage.

Key Themes

Root Cause Analysis (✅ Excellent)

  • Two compounding bugs correctly identified: missing GH_AW_ALLOWED_BOTS config + GitHub App 404 misclassification
  • The fix addresses both the symptom (denied workflow runs) and root causes
  • New isGitHubAppNotUserError() helper precisely distinguishes GitHub App errors from permission errors

Test Coverage (✅ Strong, 1 minor gap)

  • JavaScript tests cover both [bot] and slug-form fallback paths for the new GitHub App detection
  • Go tests thoroughly validate auto-injection, deduplication, and workflow-type selectivity
  • Minor gap: Validator change (line 340 in compiler_validators.go) lacks a regression test to verify the !workflowData.PullRequestReviewer exemption

Error Messages (✅ Excellent)

  • Actionable guidance when GitHub Apps are denied: "add it to on.bots:" instead of generic API error
  • Backwards compatibility preserved for non-GitHub-App errors

Discoverability (i️ Minor suggestion)

  • Auto-injection of Copilot for pull_request_reviewer workflows happens silently during compilation
  • Consider adding a debug/info log message to make this implicit behavior more discoverable (not blocking)

Positive Highlights

  • ✅ Clean separation of concerns: detection logic (isGitHubAppNotUserError) extracted into a reusable helper
  • ✅ Comprehensive test suite prevents regressions (especially TestPullRequestReviewerAutoInjectsCopilotBot)
  • ✅ Error messages guide workflow authors to the correct fix
  • ✅ PR description thoroughly documents both root causes and the fix

Verdict

This is a high-quality bug fix. The suggested improvements are minor refinements, not blockers. Approving with optional suggestions for enhanced test coverage and discoverability.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 3.7M

Comment thread pkg/workflow/bots_test.go
assert.NotContains(t, workflowData.Bots, "Copilot",
"non-pull_request_reviewer workflows should not auto-inject Copilot")
})
}
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] Excellent comprehensive test coverage for the auto-injection behavior! Three scenarios covered:

  1. ✅ Auto-inject when no bots: field present
  2. ✅ Deduplicate when bots: [Copilot] explicitly set
  3. ✅ No auto-inject for non-pull_request_reviewer workflows

This is exactly the kind of test suite that prevents regressions. Well done!

* @returns {boolean}
*/
function isGitHubAppNotUserError(error) {
if (!error || typeof error !== "object") return false;
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 regex /\bis not a user\b/ is a good defensive boundary check, but consider whether this pattern is stable across all GitHub API error messages.

Suggestion: Add a comment linking to the GitHub API documentation URL that appears in the error message (the one in the test: https://docs.github.com/rest/collaborators/collaborators#get-repository-permissions-for-a-user). This makes it easier to verify the pattern if GitHub changes their error format in the future.

// Regex matches GitHub API error message format documented at:
// https://docs.github.com/rest/collaborators/collaborators#get-repository-permissions-for-a-user
return typeof msg === "string" && /\bis not a user\b/.test(msg);

workflowData.Bots = c.mergeBots(c.extractBots(frontmatter), importsResult.MergedBots)
// pull_request_reviewer workflows are triggered by review-request events from Copilot.
// Automatically allow the Copilot bot so authors don't need to add bots: [Copilot] manually.
if workflowData.PullRequestReviewer {
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] Auto-injecting Copilot for pull_request_reviewer workflows is the right fix for this specific use case, but this creates an implicit dependency that might surprise workflow authors.

Consideration: Should this auto-injection be logged during compilation? Currently, it happens silently. Adding a debug or info message would make the behavior more discoverable:

if workflowData.PullRequestReviewer {
    workflowData.Bots = c.mergeBots(workflowData.Bots, []string{"Copilot"})
    fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Auto-allowing Copilot bot for pull_request_reviewer workflow"))
}

Not blocking — the current implementation is safe and correct, just less discoverable.

await writeDenialSummary(errorMessage, "The permission check failed with a GitHub API error. Check the `pre_activation` job log for details.");
// When the GitHub API responds with "is not a user", the actor is a GitHub App.
// The bot allowlist was either empty or not checked — provide actionable guidance.
if (result.isGitHubApp) {
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] Great actionable error message! The conditional isGitHubApp check provides workflow authors with precise guidance ("add it to on.bots:") instead of a generic API error.

Positive highlight: The fallback path (line 215) is preserved for non-GitHub-App errors, maintaining backwards compatibility.

// App identity (not a regular user). GitHub Apps trigger events via their installation,
// so if an app actor appears in an event payload it must already have repository access.
// Treat it as active to avoid false negatives for apps like Copilot.
if (isGitHubAppNotUserError(botError)) {
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] Excellent regression test coverage! The new isGitHubAppNotUserError() check is tested in both the [bot] form (line 541) and slug form (line 548) paths.

Minor observation: The slug-form fallback test creates a scenario where the first lookup returns plain 404 and the second returns "is not a user". This is a good edge case, but consider adding a test where both forms return "is not a user" to ensure the early-return path is also exercised.

Not blocking — current coverage is already strong.

if len(workflowData.Command) > 0 && len(workflowData.Bots) > 0 {
// pull_request_reviewer workflows set Command internally (to the reviewer handle) but are
// triggered by review requests, not comments, so this concern does not apply to them.
if !workflowData.PullRequestReviewer && len(workflowData.Command) > 0 && len(workflowData.Bots) > 0 {
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] The validator fix correctly exempts pull_request_reviewer workflows from the slash_command + bots warning, but I don't see a regression test for this specific validator change.

Suggestion: Add a test case in compiler_validators_test.go (or bots_test.go) that verifies:

  1. A pull_request_reviewer workflow with bots: [Copilot] does not emit the warning
  2. A regular slash_command workflow with bots: still does emit the warning

This ensures the conditional logic remains correct if the validator is refactored in the future.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 100/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All tests verify behavioral contracts with comprehensive error/edge case coverage.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

🧪 Test Quality Sentinel completed test quality analysis.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review Summary

This PR correctly identifies and fixes two compounding bugs preventing Copilot-authored PRs from triggering the mattpocock-skills-reviewer workflow. The approach is sound, but I found one critical correctness issue in the slug-form fallback path that needs fixing.

Critical Issue (1)

  • Slug-form fallback loses isGitHubApp tracking — When the slug-form lookup returns "is not a user", the function doesn't preserve the GitHub App flag, breaking the actionable error guidance this PR adds.

Additional Observations

  • The existing reviews correctly identified several concerns around normalization, test coverage, and messaging clarity
  • The auto-injection logic works correctly but could benefit from bot identifier normalization (as noted in other reviews)
  • Test coverage is strong overall but missing a validator regression test

What's Done Well
✅ Comprehensive test coverage for the auto-injection behavior (3 scenarios)
✅ Clear, actionable error messages when GitHub Apps are blocked
✅ Proper deduplication logic prevents "Copilot,Copilot" in allowlists
✅ Validator correctly exempts pull_request_reviewer from slash_command warning

Please address the critical fallback path issue before merging.

🔎 Code quality review by PR Code Quality Reviewer · ● 5M

@@ -199,6 +227,11 @@ async function checkBotStatus(actor, owner, repo) {
return { isBot: true, isActive: true };
} catch (slugError) {
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.

Critical bug: The slug-form fallback loses isGitHubApp tracking.

When the [bot] form returns a "not a user" error (line 211), the function correctly returns early with isActive: true. However, if the [bot] form returns a plain 404, the code falls back to checking the slug form (line 220-238).

The problem: If the slug-form lookup at line 228 also encounters the "is not a user" error, the function returns { isBot: true, isActive: true } (line 233) without preserving the isGitHubApp flag. This means checkRepositoryPermission will return { hasAccess: false, error: ... } with no isGitHubApp field, and the actionable GitHub App guidance in check_membership.cjs:207 will never trigger.

Why this matters: The entire point of this PR is to provide helpful error messages when GitHub Apps are blocked. This fallback path silently loses that context.

Suggested fix: Refactor the slug-form fallback to preserve isGitHubApp detection:

} catch (slugError) {
  if (isGitHubAppNotUserError(slugError)) {
    core.info(`Bot '${actor}' is a GitHub App (slug form); treating as active`);
    return { isBot: true, isActive: true };
  }
  if (slugError?.status === 404) {
    core.info(`Bot '${actor}' not found in repository collaborators (neither [bot] nor slug form)`);
    return { isBot: true, isActive: false };
  }
  // Other errors
  return { isBot: false, isActive: false, error: getErrorMessage(slugError) };
}

This ensures the GitHub App detection works consistently regardless of which API call triggers it.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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 based on this being a bug fix with comprehensive test coverage.

Key Themes

Root cause properly diagnosed: Two distinct bugs identified and fixed independently:

  1. Missing GH_AW_ALLOWED_BOTS environment variable (workflow config)
  2. GitHub App actors misclassified as inactive (checkBotStatus logic)

Excellent regression test coverage: 81 new lines of Go tests + 65 new lines of JavaScript tests covering all branches and edge cases

Actionable error messages: The updated denial summary now tells workflow authors exactly what to add (bots: [Copilot])

Clean separation of concerns: New isGitHubAppNotUserError() helper is reusable and testable

Positive Highlights

  • Documentation in code: Inline comments explain why GitHub Apps can be trusted ("event itself is proof of access")
  • Auto-injection for reviewer workflows: pull_request_reviewer workflows automatically allow Copilot, eliminating manual config
  • Deduplication logic: Handles cases where bots: [Copilot] is explicitly declared + auto-injected
  • False-positive suppression: Updated compiler validator to skip the slash_command + bots warning for reviewer workflows

Minor Suggestions

Left 4 inline comments with optional improvements:

  1. Defensive check for docs URL in error detection
  2. Enhanced error message with full YAML example
  3. Edge case test for bots: [OtherBot] + auto-inject
  4. Clarified security boundary in code comment

Verdict

Approving — this is solid diagnostic work with strong test coverage. The minor suggestions are polish, not blockers.


Matt Pocock Skills Applied: /diagnose, /tdd

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 4.1M

Comments that could not be inline-anchored

actions/setup/js/check_permissions_utils.cjs:209

[/diagnose] The reasoning in this comment is excellent — it explains why GitHub Apps appearing in event payloads must already have access. This prevents future maintainers from questioning the logic.

Nitpick: The comment could be even clearer about the security implication:

// If we get a 404 &quot;is not a user&quot; response, GitHub is telling us this is a GitHub
// App identity (not a regular user). GitHub Apps trigger events via their installation,
// so if an app actor appe…

</details>

<details><summary>pkg/workflow/bots_test.go:349</summary>

**[/tdd]** Strong test coverage for the auto-inject behavior! All three branches are covered:
- ✅ Auto-inject when no explicit bots
- ✅ Deduplication when explicit bots already include Copilot
- ✅ Non-reviewer workflows don&#39;t auto-inject

**Edge case to consider**: What happens if a pull_request_reviewer workflow explicitly sets `bots: [OtherBot]` without Copilot? Does Copilot still get auto-injected? Add a test case:

```go
t.Run(&quot;pull_request_reviewer_with_other_bots_still_injects_Copilot&quot;, f…

</details>

<details><summary>actions/setup/js/check_membership.cjs:131</summary>

**[/diagnose]** Excellent actionable error message! The guidance to add `bots: [${actorToValidate}]` directly addresses the root cause.

**Minor polish**: The example could benefit from showing the full frontmatter context:

```javascript
`Actor &#39;${actorToValidate}&#39; is a GitHub App, not a regular user. ` +
`To allow this app to trigger the workflow, add it to \`on.bots:\` in the workflow frontmatter:\n\n` +
`\`\`\`yaml\non:\n  pull_request_reviewer: matt\n  bots: [${actorToValidate}]\n\`\`\``,
…

</details>

<details><summary>actions/setup/js/check_permissions_utils.cjs:194</summary>

**[/diagnose]** The regex `/\bis not a user\b/` is well-chosen for matching the GitHub API error message. Word boundaries prevent false positives on partial matches.

**Suggestion**: Consider adding a defensive check for the docs URL suffix to make the detection even more robust:

```javascript
return typeof msg === &quot;string&quot; &amp;&amp; (
  /\bis not a user\b/.test(msg) ||
  msg.includes(&quot;/collaborators/collaborators#get-repository-permissions-for-a-user&quot;)
);

This guards against future GitHub API m…

@pelikhan pelikhan closed this May 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 80/100

Excellent test quality

Metric Value
New/modified tests analyzed 5
✅ Design tests (behavioral contracts) 5 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 5 (100%)
Duplicate test clusters 0
Test inflation detected Yes (1 file)
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Edge Cases
TestPullRequestReviewerAutoInjectsCopilotBot pkg/workflow/bots_test.go:430 ✅ Design Auto-injection, deduplication, negative case
should provide actionable guidance when GitHub App actor is not in allowed bots list actions/setup/js/check_membership.test.cjs:704 ✅ Design API error handling
should return isGitHubApp: true when the API says the actor is not a user actions/setup/js/check_permissions_utils.test.cjs:287 ✅ Design GitHub App detection
should return active when [bot] form returns 404 'is not a user' actions/setup/js/check_permissions_utils.test.cjs:539 ✅ Design Bot suffix handling
should return active when slug form returns 404 'is not a user' actions/setup/js/check_permissions_utils.test.cjs:551 ✅ Design Slug fallback logic

Test Quality Analysis

✅ Strengths

  1. 100% behavioral coverage — All tests verify observable contracts, not internal implementation
  2. Strong error path coverage — Every test includes error handling or edge case scenarios
  3. No mock libraries in Go — Follows project guideline (no gomock/testify mock)
  4. Proper table-driven patterns — Go test uses t.Run() with 3 sub-scenarios
  5. Appropriate JavaScript mocking — Mocks external APIs (GitHub REST) with behavioral assertions

⚠️ Test Inflation Note

Go test file: pkg/workflow/bots_test.go shows an 11.6:1 test-to-production ratio (81 new test lines vs. 7 production lines).

Context on inflation scoring

The mechanical scoring rule applies a 10-point penalty for any file exceeding 2:1 ratio. However, this test is not inflated with low-value assertions:

  • The production change is minimal (5 lines in compiler_orchestrator_workflow.go)
  • The test must exercise the full compilation pipeline (write files → compile → read output → verify)
  • Table-driven pattern with 3 distinct scenarios (auto-injection, deduplication, negative case) requires setup/teardown boilerplate
  • Each sub-test verifies a distinct behavioral contract for a critical authorization bug fix

This is a legitimate comprehensive test, not test bloat. The ratio is high because the fix is surgical but the verification must be thorough.


Detailed Test Analysis

View per-test quality assessment

Go Test: TestPullRequestReviewerAutoInjectsCopilotBot

Location: pkg/workflow/bots_test.go:430-506
Classification: ✅ Design Test (Behavioral Contract)
Assertions: 12 (require.NoError ×3, assert.Contains ×3, assert.NotContains ×1)
Error Coverage: Yes (via require.NoError on compilation errors)
Table-Driven: Yes (3 sub-tests with t.Run)

What design invariant does this test enforce?
pull_request_reviewer workflows must auto-inject "Copilot" into GH_AW_ALLOWED_BOTS without explicit declaration.

What would break if deleted?
A regression where Copilot bot authorization breaks in PR reviewer workflows would go undetected. This is a critical security boundary.

Contract vs. Implementation?
Design test — verifies the observable promise that Copilot is always allowed in PR reviewer workflows, with proper deduplication.

Sub-tests:

  1. pull_request_reviewer_without_explicit_bots_auto_injects_Copilot — verifies auto-injection works
  2. pull_request_reviewer_with_explicit_bots_deduplicates_Copilot — ensures no "Copilot,Copilot" duplication
  3. non_pull_request_reviewer_does_not_auto_inject_Copilot — verifies specificity (only PR reviewers get injection)

JS Test 1: "should provide actionable guidance when GitHub App actor is not in allowed bots list"

Location: actions/setup/js/check_membership.test.cjs:704-718
Classification: ✅ Design Test
Assertions: 4 expect() calls
Error Coverage: Yes (tests API rejection of GitHub App actor)

Invariant: When a GitHub App actor is rejected, the system must provide an actionable error message.
Value if deleted: HIGH — Would allow unclear error messages for GitHub App authentication failures.
Contract: Design test — verifies user-facing error handling behavior.


JS Test 2: "should return isGitHubApp: true when the API says the actor is not a user"

Location: actions/setup/js/check_permissions_utils.test.cjs:287-298
Classification: ✅ Design Test
Assertions: 1 expect().toEqual()
Error Coverage: Yes (detects "not a user" error)

Invariant: GitHub App detection must set isGitHubApp: true flag.
Value if deleted: HIGH — Would allow regression in GitHub App detection logic.
Contract: Design test — verifies observable output flag.


JS Test 3: "should return active when [bot] form returns 404 'is not a user'"

Location: actions/setup/js/check_permissions_utils.test.cjs:539-549
Classification: ✅ Design Test
Assertions: 2 expect() calls
Error Coverage: Yes (handles 404 "not a user" error)

Invariant: GitHub Apps with [bot] suffix are treated as active when "not a user" error occurs.
Value if deleted: HIGH — Would break bot status detection for GitHub Apps.
Contract: Design test — verifies bot authorization behavior.


JS Test 4: "should return active when slug form returns 404 'is not a user'"

Location: actions/setup/js/check_permissions_utils.test.cjs:551-563
Classification: ✅ Design Test
Assertions: 4 expect() calls
Error Coverage: Yes (tests slug fallback for GitHub Apps)

Invariant: GitHub Apps without [bot] suffix fall back to slug form and are treated as active.
Value if deleted: HIGH — Would break fallback logic for GitHub App name resolution.
Contract: Design test — verifies retry/fallback behavior.


Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 1 test function (3 table-driven sub-tests) — unit tests with //go:build !integration tag ✓
  • 🟨 JavaScript (*.test.cjs): 4 tests (vitest framework)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All tests verify behavioral contracts with strong error coverage. The test inflation penalty was applied due to the Go test's high ratio, but the comprehensive coverage is justified for this critical authorization fix.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 9.3M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 80/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All tests verify behavioral contracts with strong error coverage.

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