Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fixed-14437-prs-changes-requested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Hive Chat `/prs waiting on review` no longer lists pull requests whose review decision is changes requested; those wait on their author, not on a reviewer ([#14437](https://github.com/hivecommons/hive/issues/14437)).
2 changes: 1 addition & 1 deletion src/docs/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ by repo, one row each — `owner/repo#N — title (@author · stage labels · ag
— under the aggregate count, capped at 25 rows with an "… and N more" footer.
The remainder of the command narrows the list and every filter is a
conjunct: `waiting on review` / `needs review` (not a draft, no approving
review), `needs decision` (`stage/085-needs-decision` or
review, no outstanding change request), `needs decision` (`stage/085-needs-decision` or
`stage/045-needs-decision`), `hold` (held set or a hold label), `failing ci`,
`mine` (the signed-in viewer) / `by <login>` / `@login`, `repo <owner/repo>` or
a bare `owner/repo` token (the short repo name also works), and `stale`
Expand Down
15 changes: 11 additions & 4 deletions src/pkg/dashboard/api_chat_prs.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,17 @@ func chatPRApproved(pr github.PullRequest) bool {
return pr.Protection.ReviewDecision == github.ReviewDecisionApproved || pr.Protection.ApprovalsGiven > 0
}

// chatPRWaitingOnReview is the `waiting on review` predicate: not a draft and
// no approving review yet.
// chatPRChangesRequested is true when GitHub's review decision asks the author
// for changes.
func chatPRChangesRequested(pr github.PullRequest) bool {
return pr.Protection != nil && pr.Protection.ReviewDecision == github.ReviewDecisionChangesRequested
}

// chatPRWaitingOnReview is the `waiting on review` predicate: not a draft, no
// approving review yet, and no outstanding change request — a PR with changes
// requested is waiting on its author, not on a reviewer.
func chatPRWaitingOnReview(pr github.PullRequest) bool {
return !pr.Draft && !chatPRApproved(pr)
return !pr.Draft && !chatPRApproved(pr) && !chatPRChangesRequested(pr)
}

func (f chatPRFilter) matches(row chatPRRow, now time.Time) bool {
Expand Down Expand Up @@ -379,7 +386,7 @@ func chatPRLine(row chatPRRow, now time.Time) string {
meta = append(meta, chatPRCI(pr))
if chatPRApproved(pr) {
meta = append(meta, "approved")
} else if pr.Protection != nil && pr.Protection.ReviewDecision == github.ReviewDecisionChangesRequested {
} else if chatPRChangesRequested(pr) {
meta = append(meta, "changes requested")
}
if chatPRNeedsDecision(pr.Labels) {
Expand Down
28 changes: 25 additions & 3 deletions src/pkg/dashboard/api_chat_prs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ func TestRenderChatPRList(t *testing.T) {
absent: []string{"github.com/hivecommons/hive/pull/101", "Ignored", "… and"},
},
{
name: "waiting on review drops approved and drafts",
name: "waiting on review drops approved, drafts and changes requested",
query: "prs: waiting on review",
want: []string{"4 of 6 open pull request(s) match waiting on review:", "#101", "#104", "#105", "docs#7"},
absent: []string{"#102", "#103"},
want: []string{"3 of 6 open pull request(s) match waiting on review:", "#101", "#105", "docs#7"},
absent: []string{"#102", "#103", "#104"},
},
{
name: "needs decision",
Expand Down Expand Up @@ -195,6 +195,28 @@ func TestRenderChatPRList(t *testing.T) {
}
}

func TestChatPRWaitingOnReview(t *testing.T) {
cases := []struct {
name string
pr github.PullRequest
want bool
}{
{name: "no review yet", pr: github.PullRequest{}, want: true},
{name: "review required", pr: github.PullRequest{Protection: &github.ProtectionFacts{ReviewDecision: github.ReviewDecisionReviewRequired}}, want: true},
{name: "draft", pr: github.PullRequest{Draft: true}, want: false},
{name: "approved", pr: github.PullRequest{Protection: &github.ProtectionFacts{ReviewDecision: github.ReviewDecisionApproved}}, want: false},
{name: "changes requested waits on the author", pr: github.PullRequest{Protection: &github.ProtectionFacts{ReviewDecision: github.ReviewDecisionChangesRequested}}, want: false},
{name: "changes requested with an approval", pr: github.PullRequest{Protection: &github.ProtectionFacts{ReviewDecision: github.ReviewDecisionChangesRequested, ApprovalsGiven: 1}}, want: false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := chatPRWaitingOnReview(tc.pr); got != tc.want {
t.Fatalf("chatPRWaitingOnReview = %v, want %v", got, tc.want)
}
})
}
}

func TestRenderChatPRListCapsAndFooter(t *testing.T) {
now := time.Date(2026, 10, 26, 12, 0, 0, 0, time.UTC)
repo := FrontendRepo{Name: "hive", Full: "hivecommons/hive"}
Expand Down
Loading