Skip to content

feat: notify about reviews and merge conflicts, not just CI - #19

Merged
devblac merged 12 commits into
mainfrom
feat/review-merge-events
Oct 25, 2026
Merged

devblac merged 12 commits into
mainfrom
feat/review-merge-events

Conversation

@devblac

@devblac devblac commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Roadmap #11. prw run now 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

  • Reviews: 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 free 304 when unchanged.
  • Merge state: the PR's mergeable_state (already fetched) → ready / conflict / blocked / behind / unstable / draft. unknown (still computing) keeps the last value.
  • Kept quiet on purpose:
    • nothing on the first check of a PR (existing watch lists don't get a burst);
    • ready isn't sent together with the CI or approval notification that caused it;
    • no ready for repos without branch protection (unstable → clean is just CI passing, already notified);
    • dismissals go silently back to none.
  • Notifications carry a Kind: console ("Review Update!" / "Merge Status Change!"), native title, Slack/Discord (Review: / Merge:, 📝 ⚠️ 🚀, colors), generic webhook type pr_review_change / pr_merge_state_change. CI events are unchanged.
  • Filters: --on fail → changes_requested, conflict; --on success → approved, ready.
  • Visibility: prw list gains REVIEW / 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.

Test plan

Stacked on #18.

🤖 Generated with Claude Code

devblac and others added 8 commits October 18, 2026 02:23
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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T06:15:34.663064Z 04ad985 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread internal/github/review.go
// 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++ {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

devblac and others added 4 commits October 24, 2026 15:34
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>
@devblac
devblac changed the base branch from fix/codex-review to main October 25, 2026 00:16
@devblac
devblac merged commit b54d598 into main Oct 25, 2026
@devblac
devblac deleted the feat/review-merge-events branch October 25, 2026 00:16
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.

1 participant