Exclude PR author from reviewer candidates in SuggestedReviewerActors

Add author { login } to the SuggestedReviewerActors GraphQL query
and pre-seed the seen map with the author login so they are excluded
from all sources (suggestions, collaborators, teams). Previously the
author was only skipped via the isAuthor flag in the suggestions loop
but could still appear as a collaborator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kynan Ware 2026-03-05 15:47:24 -07:00
parent 1bba50b3e0
commit 24fb7657cd
2 changed files with 40 additions and 5 deletions

View file

@ -413,6 +413,9 @@ func SuggestedReviewerActors(client *Client, repo ghrepo.Interface, prID string,
type responseData struct {
Node struct {
PullRequest struct {
Author struct {
Login string
}
SuggestedActors struct {
Nodes []struct {
IsAuthor bool
@ -472,7 +475,11 @@ func SuggestedReviewerActors(client *Client, repo ghrepo.Interface, prID string,
// Build candidates using cascading quota logic:
// Each source has a base quota of 5, plus any unfilled quota from previous sources.
// This ensures we show up to 15 total candidates, filling gaps when earlier sources have fewer.
// Pre-seed seen with the PR author since you cannot review your own PR.
seen := make(map[string]bool)
if authorLogin := result.Node.PullRequest.Author.Login; authorLogin != "" {
seen[authorLogin] = true
}
var candidates []ReviewerCandidate
const baseQuota = 5

View file

@ -160,7 +160,7 @@ func mockReviewerResponse(suggestions, collabs, teams, totalCollabs, totalTeams
return fmt.Sprintf(`{
"data": {
"node": {"suggestedReviewerActors": {"nodes": [%s]}},
"node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [%s]}},
"repository": {
"collaborators": {"nodes": [%s]},
"collaboratorsTotalCount": {"totalCount": %d}
@ -235,7 +235,7 @@ func TestSuggestedReviewerActors(t *testing.T) {
httpmock.GraphQL(`query SuggestedReviewerActors\b`),
httpmock.StringResponse(`{
"data": {
"node": {"suggestedReviewerActors": {"nodes": [
"node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [
{"isAuthor": true, "reviewer": {"__typename": "User", "login": "author", "name": "Author"}},
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}},
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "s2", "name": "S2"}}
@ -255,6 +255,34 @@ func TestSuggestedReviewerActors(t *testing.T) {
expectedLogins: []string{"s1", "s2", "c1", "OWNER/team1"},
expectedMore: 8,
},
{
name: "author excluded from collaborators",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query SuggestedReviewerActors\b`),
httpmock.StringResponse(`{
"data": {
"node": {"author": {"login": "theauthor"}, "suggestedReviewerActors": {"nodes": [
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}}
]}},
"repository": {
"collaborators": {"nodes": [
{"login": "theauthor", "name": "The Author"},
{"login": "c1", "name": "C1"}
]},
"collaboratorsTotalCount": {"totalCount": 5}
},
"organization": {
"teams": {"nodes": []},
"teamsTotalCount": {"totalCount": 0}
}
}
}`))
},
expectedCount: 2,
expectedLogins: []string{"s1", "c1"},
expectedMore: 5,
},
{
name: "deduplication across sources",
httpStubs: func(reg *httpmock.Registry) {
@ -263,7 +291,7 @@ func TestSuggestedReviewerActors(t *testing.T) {
httpmock.GraphQL(`query SuggestedReviewerActors\b`),
httpmock.StringResponse(`{
"data": {
"node": {"suggestedReviewerActors": {"nodes": [
"node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "shareduser", "name": "Shared"}}
]}},
"repository": {
@ -291,7 +319,7 @@ func TestSuggestedReviewerActors(t *testing.T) {
httpmock.GraphQL(`query SuggestedReviewerActors\b`),
httpmock.StringResponse(`{
"data": {
"node": {"suggestedReviewerActors": {"nodes": [
"node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}}
]}},
"repository": {
@ -314,7 +342,7 @@ func TestSuggestedReviewerActors(t *testing.T) {
httpmock.GraphQL(`query SuggestedReviewerActors\b`),
httpmock.StringResponse(`{
"data": {
"node": {"suggestedReviewerActors": {"nodes": [
"node": {"author": {"login": "testauthor"}, "suggestedReviewerActors": {"nodes": [
{"isAuthor": false, "reviewer": {"__typename": "Bot", "login": "copilot-pull-request-reviewer"}},
{"isAuthor": false, "reviewer": {"__typename": "User", "login": "s1", "name": "S1"}}
]}},