Repository navigation
feat: notify about reviews and merge conflicts, not just CI - #19
Conversation
When a workflow runs more than once for the same commit (e.g. an older run cancelled by `concurrency: cancel-in-progress`), the check-runs API returns one run per check suite, so the cancelled one kept the PR "failed" forever even after the newer run passed. Only the newest run per app and check name counts now, like GitHub's merge box and required checks. Verified live on zephyrproject-rtos/zephyr#105183: previously reported as failure ("Prevent Merging" cancelled), now success. Addresses Codex review on #6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GovSlack serves incoming webhooks from its own slack-gov.com domain, so auto mode sent them the generic payload, which Slack rejects. Addresses Codex review on #7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PR auto-watched with --review-requested that was merged or closed while prw was stopped no longer matched the is:open search, so the startup sync removed it as a withdrawn request before it was checked, and its final merged/closed notification was lost. Before removing review-request PRs that stopped matching, the sync now looks them up and keeps the closed ones; the regular check then notifies and drops them. If the lookup fails, the PR is removed as before. Addresses Codex review on #16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any comment or label change bumps a PR's updated_at, which restarted the no-CI grace period: `prw run` stored `pending` and, with the default `change` filter, notified "no_ci → pending" although no CI had appeared, then silently went back to no_ci three minutes later. A PR that was no_ci now stays no_ci until an actual check or status shows up. Addresses Codex review on #17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… before returning Context: the GitHub client methods now take a context.Context, passed down to each HTTP request. Previously `prw wait --timeout`, MCP `wait_for_ci` `timeout_seconds` and client cancellation, and Ctrl-C were only noticed between polls: a stalled network could keep a poll (PR + status + check-run pages, each with a 15s HTTP timeout) running well past the deadline, and Ctrl-C did nothing until it finished. A poll cancelled this way now ends as timed out (exit 2) or interrupted (exit 130) instead of being retried as a transient error. `prw run` also stops immediately on Ctrl-C, leaving the PRs it hadn't checked unchanged. Head re-check: `prw wait` / `wait_for_ci` look up the PR and then its CI, so a commit pushed in between made them return the final result of the previous commit (e.g. `prw wait && deploy` deploying unchecked code). Before returning a final result, the PR is read again (a free 304 when unchanged); if the head moved, waiting continues on the new commit. Addresses Codex reviews on #13 and #15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the 512-entry cache was full, every new 200 response wiped it. With more paths polled in rotation than fit (~170 PRs, fewer with paginated check runs) it was cleared over and over and nearly every request became unconditional, losing the free 304s. A new path now evicts a single random entry (refreshing a cached path evicts nothing). Random rather than LRU on purpose: cyclic polling is LRU's worst case. Simulated with 640 paths: 64% hits, was 0%. Addresses Codex review on #14. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`prw run` now also tells you when a watched PR is approved, gets changes
requested, runs into merge conflicts, or becomes ready to merge after branch
protection, conflicts or an outdated branch were holding it back.
- Review decision from /pulls/{n}/reviews: each reviewer's latest approval or
change request counts, comments don't, dismissals withdraw it. One extra
request per PR per poll, a free 304 when nothing changed.
- Merge state from the PR's mergeable_state (already fetched); "unknown" while
GitHub computes it keeps the last value.
- Quiet by design: nothing on the first check of a PR; "ready" is not sent
with the CI or approval notification that caused it, nor for repositories
without branch protection (unstable -> clean is just CI passing).
- Events carry a Kind; console, native, Slack/Discord ("Review:" / "Merge:")
and generic webhooks ("pr_review_change" / "pr_merge_state_change") render
them. --on fail keeps changes_requested and conflict, --on success approved
and ready.
- `prw list` gains REVIEW and MERGE columns (review/merge in --json and MCP
list_watched); `prw wait --json` and the MCP tools report merge_state.
- `prw config set pr_events false` turns it off.
Verified live: #12 ready, cli/cli#14727 blocked; a simulated
blocked -> ready notified once, ready -> blocked stayed silent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 04ad985815
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // GetReviews fetches the reviews of a pull request, oldest first. | ||
| func (c *Client) GetReviews(ctx context.Context, owner, repo string, number int) ([]Review, error) { | ||
| var reviews []Review | ||
| for page := 1; page <= maxReviewsPages; page++ { |
There was a problem hiding this comment.
Fetch all review pages before deriving the verdict
For PRs with more than 300 submitted reviews, this hard stop permanently excludes the newest reviews. GitHub returns reviews in chronological order with at most 100 per page, so an approval, change request, or dismissal on page 4 or later never reaches ReviewDecision; the watcher can consequently persist and report a stale review state even on subsequent polls. Continue pagination until the final short page or follow GitHub's pagination links. GitHub's review endpoint documentation
Useful? React with 👍 / 👎.
If the request confirming the PR head failed, `prw wait` returned the final result of the commit it had polled, even if a push had just landed, and a timeout or Ctrl-C during that request ended as success/failure instead of timed out / interrupted. Its errors now go through the same path as poll errors: retried when transient, reported as timeout or interruption when the context is done. Addresses Codex review on #18. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A review-request PR that stopped matching its search was dropped whenever the lookup meant to tell "merged/closed" apart from "request withdrawn" failed. Interrupting `prw run` (Ctrl-C) during that lookup, a network error or a rate limit therefore silently removed PRs together with their final merged/closed notification. Now a PR is only dropped when it's confirmed open (the request was withdrawn or answered) or GitHub answers 404; otherwise it's kept until the next sync. A cancelled sync returns before touching the config. Addresses Codex review on #18. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviews come oldest first, so the 3-page cap dropped the newest approvals, change requests and dismissals on PRs with more than 300 reviews, and the review state stayed stale. Pagination now runs to the last page, with a 100-page cap only as a runaway guard. Addresses Codex review on #19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Roadmap #11.
prw runnow also notifies when a watched PR is approved, gets changes requested, runs into merge conflicts, or becomes ready to merge after branch protection / conflicts / an outdated branch were holding it back.How
GET /pulls/{n}/reviews(paginated) →ReviewDecision: each reviewer's latest approve / request-changes counts; comments don't; dismissals withdraw it. One extra request per PR per poll, a free304when unchanged.mergeable_state(already fetched) →ready/conflict/blocked/behind/unstable/draft.unknown(still computing) keeps the last value.readyisn't sent together with the CI or approval notification that caused it;readyfor repos without branch protection (unstable → cleanis just CI passing, already notified);none.Kind: console ("Review Update!" / "Merge Status Change!"), native title, Slack/Discord (Review:/Merge:, 📝typepr_review_change/pr_merge_state_change. CI events are unchanged.--on fail→ changes_requested, conflict;--on success→ approved, ready.prw listgainsREVIEW/MERGEcolumns (review/mergein--jsonand MCPlist_watched);prw wait --jsonand the MCP tools reportmerge_state.prw config set pr_events falseturns it off.Test plan
go test ./... -race,go vet ./...ready, fix(issue create): resolve --type before creating the issue cli/cli#14727 →blocked; a simulatedblocked → readynotified once,ready → blockedstayed silentStacked on #18.
🤖 Generated with Claude Code