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
2 changes: 1 addition & 1 deletion automations/bundle-index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion automations/catalog/github-pr-reviewer/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"popularityRank": 100,
"estimatedSetupMinutes": 4,
"exampleImplementation": "A scheduled scan of the trigger label, of open non-draft PRs holding an outstanding reviewer request, and of every open non-draft PR nobody requested whose current head may be green and unreviewed, or a GitHub reviewer-request event, selects a PR head and submits an idempotent subject turn. Each scheduled scan classifies the full backlog, while the global per-run quota limits only newly created review conversations; blocked and pending heads consume no launch slot, and explicit requests retain priority. The scheduled scan orders eligible heads with explicit requests first and then oldest-first, and starts at most a small configurable number of new conversations per scan across every repository. The profile-backed agent clones that exact head in its runtime, applies an early repository-ownership and product/architecture scope gate, then reuses the established review workflow, runs appropriate tests, and publishes a readable native review. A native approval, or a maintainer-decision scope stop, can then be handed to a code-aware human maintainer.",
"exampleImplementation": "A scheduled scan of the trigger label, of open PRs (including drafts) holding an outstanding reviewer request, and of every open non-draft PR nobody requested whose current head may be green and unreviewed, or a GitHub reviewer-request event, selects a PR head and submits an idempotent subject turn. Each scheduled scan classifies the full backlog, while the global per-run quota limits only newly created review conversations; blocked and pending heads consume no launch slot, and explicit requests retain priority. The scheduled scan orders eligible heads with explicit requests first and then oldest-first, and starts at most a small configurable number of new conversations per scan across every repository. The profile-backed agent clones that exact head in its runtime, applies an early repository-ownership and product/architecture scope gate, then reuses the established review workflow, runs appropriate tests, and publishes a readable native review. A native approval, or a maintainer-decision scope stop, can then be handed to a code-aware human maintainer.",
"impact": {
"basis": "completed-runs",
"one": "1 PR review sweep completed",
Expand Down
2 changes: 2 additions & 0 deletions skills/github-pr-reviewer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ This skill is activated by:
red or pending; the CI gate applies to scheduled discovery
- Ignores checks and workflow runs recorded for an obsolete head, so a stale
failure cannot block the push that fixed it
- Explicit reviewer requests include draft PRs in both event mode and scheduled
scans; drafts without a request or trigger label remain excluded
- Resumes an outstanding reviewer request on the next scheduled scan once the
requested head's checks are green, so a request that arrives during CI is not
lost; the explicit-request event path is kept for event-only deployments
Expand Down
2 changes: 1 addition & 1 deletion skills/github-pr-reviewer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ symbolic base branch carries no branch rules of its own:
`gh pr checks` both report success and only the workflow run reveals the red
CI. A workflow run whose check suite already reported check runs is left to
those runs, so a workflow is never counted twice.
- A scheduled scan considers the trigger label **and** every open, non-draft PR
- A scheduled scan considers the trigger label **and** every open PR, including drafts,
that still holds an outstanding `all-hands-bot` review request. That is what
resumes a request made while CI was running: the request is keyed by its own
`review_requested` event, so repeated scans reuse one conversation and one
Expand Down
6 changes: 2 additions & 4 deletions skills/github-pr-reviewer/scripts/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,14 +720,12 @@ def _gate_head(self, pr, requested=False, scheduled=False, explain=True):
return state, sha

def _outstanding_review_request(self, pr):
"""Whether an open, non-draft PR still holds a request for the reviewer.
"""Whether an open PR, including a draft, still requests this reviewer.

The list endpoint already answers this: `requested_reviewers` is the live
set, so a review that was submitted, or a request that was withdrawn, is
simply absent. Drafts are excluded because a draft is not reviewable.
simply absent. An explicit request opts a draft into review.
"""
if pr.get("draft"):
return False
return any(
(item.get("login") or "").lower() == self.trigger_reviewer
for item in pr.get("requested_reviewers") or []
Expand Down
2 changes: 1 addition & 1 deletion skills/index.js

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions tests/test_github_reviewer_delivery.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,11 @@ def test_reviewer_prompt_forbids_republishing_an_unchanged_reviewed_head(
assert "Review the head again only when the head has moved" in prompt


def test_reviewer_submits_requested_exact_head(tmp_path, monkeypatch):
@pytest.mark.parametrize("draft", [False, True])
def test_reviewer_submits_requested_exact_head(tmp_path, monkeypatch, draft):
_module, run = _reviewer(tmp_path, monkeypatch)
_event(monkeypatch)
pr = {"number": 2, "head": {"sha": "head-2"}, "labels": []}
pr = {"number": 2, "head": {"sha": "head-2"}, "labels": [], "draft": draft}
request = {
"id": 42,
"event": "review_requested",
Expand Down Expand Up @@ -1769,10 +1770,11 @@ def _request_pages(pr, request, comments):
)


def test_reviewer_scheduled_scan_reviews_an_outstanding_request(tmp_path, monkeypatch):
@pytest.mark.parametrize("draft", [False, True])
def test_reviewer_scheduled_scan_reviews_an_outstanding_request(tmp_path, monkeypatch, draft):
"""A request made before CI finished is reviewed once the head is green."""
_module, run = _reviewer(tmp_path, monkeypatch)
pr = _requested_pr()
pr = _requested_pr(draft=draft)
state = {"runs": _checks(("slow-e2e", "queued", None), sha="head-2")}
run.check_runs = lambda sha: state["runs"]
run.gh_pages = _request_pages(pr, _review_request_event(), [])
Expand Down Expand Up @@ -1801,12 +1803,13 @@ def test_reviewer_scheduled_scan_reviews_an_outstanding_request(tmp_path, monkey
assert "latest review request for `all-hands-bot` event 42" in call["prompt"]


@pytest.mark.parametrize("draft", [False, True])
def test_reviewer_repeated_scans_do_not_duplicate_a_requested_review(
tmp_path, monkeypatch
tmp_path, monkeypatch, draft
):
"""Two scans over one outstanding request produce one stable delivery key."""
_module, run = _reviewer(tmp_path, monkeypatch)
pr = _requested_pr()
pr = _requested_pr(draft=draft)
run.check_runs = lambda sha: _checks(("ci", "completed", "success"), sha=sha)
run.dispatcher.deliver.return_value = {
"disposition": "created",
Expand All @@ -1825,11 +1828,12 @@ def test_reviewer_repeated_scans_do_not_duplicate_a_requested_review(
assert deliveries == {"42:head-2"}


def test_reviewer_scheduled_scan_ignores_a_draft_with_a_request(
def test_reviewer_scheduled_scan_ignores_a_draft_requested_from_someone_else(
tmp_path, monkeypatch
):
_module, run = _reviewer(tmp_path, monkeypatch)
pr = _requested_pr(draft=True)
pr["requested_reviewers"] = [{"login": "another-reviewer"}]
run.gh_pages = lambda path: [pr]
run.gh = Mock(return_value={"id": 99})

Expand Down
Loading