Repository navigation
github source: collect the review requester on reason=review_requested notifications - #510
Draft
polyglotAI-bot wants to merge 1 commit into
Draft
polyglotAI-bot wants to merge 1 commit into
polyglotAI-bot wants to merge 1 commit into
Conversation
…d notifications A review request carries no comment, and neither the notification nor the PR payload names who requested it, so the requester was never an actor. With a non-empty allow_actors, a maintainer's review request on a PR whose author, reviewers and commenters are all outside the allowlist (e.g. a dependency-update bot's PR) was dropped fail-closed and lost. Resolve the requester from the PR's event history: the newest review_requested event that names the authenticated account. Read all pages (gh api --paginate --slurp), because a re-review request is the newest event on a busy history. Credit nobody when that request was withdrawn, has no identifiable requester, is a self-request, or when a different login made a newer team request. Never fall back to an older request. Render the requester as "Review requested by: <login>". Follow-up to ClickHouse#285, which fixed the same gap for reason=assign. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
polyglotAI-bot
marked this pull request as draft
October 16, 2026 14:20
Open
5 tasks done
Contributor
Author
|
Moved to draft in favour of #513. This PR guesses the requester from the PR's event history, because a notification carries no actor and no event id. Two limits stay, whatever the guessing rule:
#513 adds a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #285, which named this gap and left it out: a
review_requestednotification has the same blind spot as an assignment./notificationscarries no actor, a review request has no comment body, and neither the notification nor the PR payload names who requested the review. So the requester is never inactors.With a non-empty
allow_actors, this drops a maintainer's review request on any PR whose author, reviewers and commenters are all outside the allowlist. A dependency-update bot's PR is the typical case: the author is a bot, the automatic reviewer is a bot, and every comment is from CI. The guardrail drops the record fail-closed, the cursor advances past it, and the request is lost. The only trace is one INFO line:As with assignments, no configuration recovers it: the login that would pass the allowlist is not in the record.
What changed
_enrich_review_request()resolves the requester forreason=review_requestedon a PR. It reads the PR's/issues/{n}/eventsand takes the newestreview_requestedevent whoserequested_revieweris the authenticated account. The requester is read fromreview_requester, withactoras the fallback.gh api user, read once and cached on the source. A failure is not cached.actorsand is rendered asReview requested by: <login>._gh_api_get_all()reads every page withgh api --paginate --slurp(gh 2.48 or later). It logs failures at warning level and kills the process on timeout.docs/sources.mddocuments the review-request case under the actor guardrail.Deliberate choices worth a reviewer's eye:
Only the request to this account counts. The other review requests on the PR are skipped. For example, a bot that opens a PR requests its default reviewers, and that requester did not ask the inbox account for anything.
The requester is not assumed to be trusted. Unlike an assigner, a PR author can re-request a review from a past reviewer without write access. Verified on live PRs: the newest request naming the account was the PR author's own re-request. It is credited, and then the allowlist rejects it. The code never falls back to an older request by a maintainer.
Fail closed, never fall back. The scan stops at the newest event about the account's own review. It credits nobody in these cases:
review_request_removed(the request was withdrawn);A team request by the same login does not block. One action that asks the account and a team writes two events, in either order.
All pages, unlike
_enrich_assignment. A re-review request on a long-lived PR is the newest event on a busy history. That makes it exactly the entry that a single first page of 100 misses.Open question: how long the credit lasts
GitHub keeps
review_requestedas the thread'sreasonafter later activity. Verified on a live thread: a bot review two hours after the request updated the thread, and the reason stayedreview_requested. So, exactly as with the assigner from #285, the requester stays among the actors for later updates of that thread, until someone makes a newer request naming the account. Later comments by non-allowlisted users on that PR then pass. This matches the guardrail's existing semantics ("the thread involves a trusted login"): the recent-comments enrichment and the assigner behave the same way. I documented it indocs/sources.mdand did not add a time bound.A bound is possible, but each obvious anchor has a cost:
last_read_at: this is null for an inbox account that never marks notifications read.since: this does not bound anything when the request's own notification was the last one fetched.updated_at: this drops a real request when the first fetch after the request is late (for example after downtime), which is the failure this PR fixes.If you want it bounded, I suggest one shared rule for the assigner and the requester, in a separate PR.
Test plan
tests/test_github_source_actors.py: new tests for_collect_actors, newest-request selection, case-insensitive match,actorfallback, other-reviewer skip, no fallback on an unidentifiable requester, withdrawn request, re-request after removal, PR-author re-request, self-request, newer team request by another or unknown login, team request by the same login before and after, older team request, malformed events, a non-PR URL, an owner namedpulls, missing login and missing history, login caching and retry,_gh_api_get_allpage flattening, failure paths and timeout kill, and dispatch only forreview_requested.test_maintainer_review_request_on_bot_pr_passes_guardraildrives the realfetch+ enrichment + the production guardrail frombuild_source_runners. With onlynerve/sources/github.pyreverted, it fails atassert actors == [...]:'maintainer'is missing.review_requestedthreads.5769 passed, 38 skipped.review_requesterdefaults to"", and every other reason is unchanged. It costs onegh api usercall per process and one event-history read perreview_requestednotification.--slurpfailure visibility, timeout kill, anchored/pulls/rewrite). The remaining point is the open question above.🤖 Generated with Claude Code