Skip to content

feat(review): review unrequested green PRs on the scheduled scan - #660

Merged
neubig merged 1 commit into
mainfrom
openhands/issue-658
Sep 25, 2026
Merged

neubig merged 1 commit into
mainfrom
openhands/issue-658

Conversation

@all-hands-bot

@all-hands-bot all-hands-bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

HUMAN:

This is a focused change to the existing scheduled reviewer automation, stacked on #656 (PR #659). It adds the continuous unrequested-PR scan and its bounded, rotating scan window; no host-local script or second automation is added. The evidence below is agent-run focused tests plus the read-only live API reads captured before the one-off canary harness was removed from the tree; I intentionally left the human-test checkbox unchecked because no human exercised the change.

  • A human has tested these changes.

AGENT:

Why

The scheduled GitHub PR reviewer retries outstanding all-hands-bot requests (#654) and bounds how many new conversations one scan may start (#656). Once those requests are exhausted the automation stops making progress even while open, non-draft PRs with passing CI still lack a review on their current head. A one-off batch of requests would not cover later PRs.

This PR makes the existing scheduled reviewer continuously discover eligible CI-passing PRs in its configured repositories and review them at a bounded pace, reusing the reviewer's scan, gate, keyed delivery, native review, and maintainer handoff. No separate daemon or automation.

The base #659 carries the corrected exact-head gate this PR builds on: scheduled candidates are classified on the GitHub-required checks only (read through isRequired, falling back to every current-head check run and workflow run when the required signal is unavailable or empty), and an explicit all-hands-bot request bypasses the CI gate entirely. This PR does not re-add or change that logic; it preserves both behaviors.

Summary

  • skills/github-pr-reviewer/scripts/worker.py — in scheduled mode the scan now also considers open, non-draft PRs whose current head carries no completed review by the reviewer account, even with no trigger label and no request. The candidate filter is the exact negative of the completion handler's predicate, so a review the scan starts and one it finds already present are one set; an already-reviewed head is reconciled (verdict parsing and maintainer handoff) instead of restarted. Unrequested heads use the stable scan:{repository}:{number}:{head} delivery key, so repeated scans create neither a duplicate conversation nor a duplicate native review, and a changed head is eligible again under its new SHA.
    • Bounded, rotating scan window. Classifying one unrequested head costs a review-list read plus the exact-head check and workflow reads, so examining every open PR in one scan spent the run's API budget in the largest repository and announced every red or pending head. A scan now examines at most SCAN_WINDOW (10) unrequested PRs per repository, starting where the previous scan stopped. The position is retained in the existing Automation KV facility — the same AUTOMATION_KV_TOKEN / AUTOMATION_API_URL that main.py already uses for review state — under a per-repository key review-scan:{owner}__{repo} ({"cursor": N}). The window wraps at the end of the backlog, so the whole backlog is covered over successive scans. No new secret, store, or permission. When the KV store is unavailable (a local run or the tests) the position is kept in memory, and a KV read/write failure degrades to the in-memory cursor rather than aborting the scan.
    • Explicit priority and gate bypass preserved. An explicit all-hands-bot review request or a trigger label is never subject to the window: every explicit candidate is examined on every scan, whatever the stored position is, and explicit candidates drain first through the existing priority ordering. An explicit request still bypasses the CI gate (requested=True), so it dispatches on a red or pending head without a gate comment.
    • Required-check gating preserved for scheduled candidates. The gate for an unrequested or labeled scheduled candidate classifies the head with the base's required-check path (_classify_check_runs(pr) → required_check_contexts → isRequired), falling back to every current-head check and workflow run when the required set cannot be read or is empty.
    • No unrequested gate-comment storm. _gate_head(..., explain=False) for unrequested candidates, so a merely red or pending unrequested PR is skipped without starting an agent and without posting a managed gate comment. The blocked/waiting managed comment is kept for explicit requests and labeled heads.
    • The per-scan max_new_per_run bound (default 2, configurable) orders candidates explicitly: an explicit request or trigger label first, then the oldest candidate — the request time for a requested PR, the PR's own creation time (oldest first) for an unrequested one — with repository and PR number as stable tie-breakers. An ineligible candidate (blocked or waiting head) consumes no slot and does not block the candidates behind it.
    • A PR authored by the configured bot account is told to publish the clean review as an event: COMMENT review that keeps the approved verdict, instead of being skipped because GitHub ignores a self-review request.
  • SKILL.md, README.md, references/state-schema.md, automations/catalog/github-pr-reviewer/manifest.json, tests/fixtures/automations/github-pr-reviewer.json — document the unrequested scan, the bounded rotating window and its KV cursor, the delivery key, and the per-run maximum; bump the catalog entry and bundle version 1.7.0 → 1.8.0; regenerate automations/bundle-index.js and skills/index.js with npm run build.

No new secret, token scope, or GitHub permission is required beyond what the current reviewer uses. No change to the review prompt, verdict parsing, maintainer handoff, or secret selection.

Stacking

Base is openhands/issue-656, the head branch of #656 (PR #659, current head 50fb986), so GitHub records this as a stacked PR and it will retarget to main when the stack merges. #659 must merge first. This branch was rebased onto the updated #659 head: the earlier commit that added the read-only canary harness and its docstring fix were dropped, and the conflict in the reviewer's gate was resolved to keep the base's required-check gating and explicit-request bypass alongside the new unrequested scan.

Issue Number

Fixes #658

How to Test

  1. uv run --group test pytest -q tests/test_github_reviewer_delivery.py — expect 86 passed.
  2. uv run --group test pytest -q tests/test_github_reviewer_delivery.py tests/test_github_automation_foundation.py tests/test_automation_setup.py — expect 230 passed, 17 skipped.
  3. uv run --group test pytest -q — expect 1059 passed, 23 skipped.
  4. uv run --group test python scripts/sync_extensions.py --check — expect clean (the pre-existing non-blocking issue-duplicate-checker coverage warning is unrelated).
  5. npm run build — expect no drift in automations/bundle-index.js or skills/index.js.

Focused tests added for the new behavior in tests/test_github_reviewer_delivery.py, driving the real shipped worker.py entrypoint through the catalog bundle helper and the shipped run_scan control flow:

  • test_unrequested_scan_reviews_a_green_pr_with_no_request_or_label — the core new behavior: an unrequested green PR starts a review.
  • test_scan_examines_a_bounded_rotating_window_of_unrequested_prs — rotation across successive scans, including wrap-around.
  • test_the_scan_position_is_persisted_per_repository_in_the_kv_store — the cursor is written under review-scan:owner__one.
  • test_explicit_requests_are_examined_regardless_of_the_rotation_window — an explicit request is examined even when the window sits elsewhere.
  • test_unrequested_red_and_pending_prs_post_no_gate_comments — no unrequested gate comment, while the green unrequested head still reaches the queue.
  • test_scan_reads_a_bounded_number_of_pull_requests_per_repository — one list page plus exactly SCAN_WINDOW full PR reads over a 40-PR backlog.
  • test_an_explicit_request_still_gets_its_managed_gate_comment — the explicit-request gate comment is preserved.
  • test_unrequested_scan_does_not_duplicate_across_repeated_scans, test_unrequested_scan_reviews_a_changed_head_again_under_a_new_key — the stable key dedupes a repeat scan and re-reviews a changed head once.
  • test_unrequested_scan_bound_spans_repositories_with_explicit_priority, test_unrequested_scan_reviews_the_oldest_eligible_prs_under_the_cap, test_unrequested_scan_still_gates_a_blocked_pr_past_the_cap — the global cap, oldest-first order, and a blocked candidate that consumes no slot.
  • test_unrequested_scan_reconciles_a_completed_review_and_hands_off, test_unrequested_scan_marks_a_self_authored_pr_for_the_comment_verdict — reconciliation plus handoff, and the bot-authored COMMENT-verdict path.
  • test_unrequested_scan_blocks_a_failed_zero_job_workflow, test_unrequested_scan_waits_on_a_pending_workflow — the gate on an unrequested head through the workflow-run path.

The base #659 gate regressions continue to pass unchanged, including test_reviewer_explicit_request_bypasses_the_ci_gate, test_reviewer_ignores_an_optional_zero_job_workflow_on_a_green_required_head, test_reviewer_blocks_when_a_required_check_fails, test_reviewer_waits_for_a_pending_required_check, and test_reviewer_falls_back_to_every_check_when_required_signal_is_unavailable.

Live evidence (read-only, no agent started, no comment posted)

The run below was performed with a one-off read-only harness that has since been removed from the tree (it was development-only evidence, never part of the shipped bundle); the numbers are retained here. GITHUB_TOKEN_REVIEW authenticated as all-hands-bot. I loaded the shipped bundle from automations/catalog/github-pr-reviewer/manifest.json and drove the real PullRequestReviewer.run() scheduled path and the real shared-intake drain against the live GitHub API. The dispatcher recorded what would have been delivered and the gate-comment upsert was recorded instead of written, so the run is read-only. The live candidate list was bounded to the oldest six non-draft PRs per repository (a full scan would issue one /reviews read per PR); everything after candidate selection is the shipped code.

A scheduled scan over all four configured repositories, max_new_per_run=2:

OpenHands/extensions          open=84   non_draft=51
OpenHands/OpenHands           open=429  non_draft=339
OpenHands/software-agent-sdk  open=262  non_draft=197
OpenHands/automation          open=48   non_draft=33

registered green unrequested candidates: 1
conversations started: 1
  OpenHands/software-agent-sdk PR #2495
    delivery scan:OpenHands/software-agent-sdk:2495:72d2f217a7054fb3c51e6ac66d061a700bbe5013
    (open, non-draft, green head; only all-hands-bot review is on an older head 0c38be62)
gate dispositions: 12 blocked (e.g. OpenHands/OpenHands #16102, software-agent-sdk #2142)

The real drain stopping at the bound on live data across more than one repository (only the gate flag is forced eligible; ordering, keys, and the drain are the shipped code):

max_new_per_run=2 -> 2 started: software-agent-sdk #1780 (oldest), extensions #63
max_new_per_run=1 -> 1 started: software-agent-sdk #1780 (oldest)

The shipped exact-head classification against live heads confirms the gate, including the #426 reproduction head whose check-run rollup is green while three zero-job workflows failed:

OpenHands/extensions 41ffb9d44ff187953034e32fe096607f77e506dd
  -> ('blocked', ['Check Extensions', 'Deprecation deadlines', 'Tests'])
OpenHands/software-agent-sdk 72d2f217a7054fb3c51e6ac66d061a700bbe5013
  -> ('green', [])

Video/Screenshots

Not applicable: this changes automation worker dispatch logic, not a GUI.

Notes

  • The live evidence run started no agent and posted no comment, so the "links to the reviewed pull requests and their conversations" part of the acceptance criteria depends on the OSS deployment switching the reviewer automation to this bundle; the PR reports the live evidence it actually obtained.
  • A completed unrequested review is reconciled from the reviewer's own submitted review on the current head. If a real review's verdict line is not parseable the scan may re-dispatch the stable, keyed conversation, spending one slot rather than silently skipping an unreviewed head — the same trade-off fix(review): resume requested reviews on a scheduled check scan #654 documents.
  • The shipped tree contains no .pr/ artifacts: the read-only canary harness was removed, and fix(review): bound scheduled PR review intake across repositories #659's own .pr/canary_bounded_intake.py is not present either.

Generated by OpenHands AI on behalf of the user.

@neubig

neubig commented Sep 23, 2026

Copy link
Copy Markdown
Member

@all-hands-bot Live canary of #660 exposed a blocking scalability bug. The scheduled run started at 04:23 UTC and, before dispatching any reviewer, posted at least 99 managed CI-gate comments in OpenHands/OpenHands alone (example: OpenHands/OpenHands#16821 (comment)). It was still scanning after 10 minutes. I stopped and disabled the live reviewer to prevent a mass-comment/VM/API-rate incident.

Please amend this PR so each scheduled run examines a bounded, fairly rotating window of unrequested PRs across all four repos, using the existing Automation KV facility to retain per-repository scan position. Explicit all-hands-bot requests/trigger labels should keep priority regardless of the window. Do not post gate comments for unrequested PRs that are merely red or pending; keep the managed gate comment for explicit requests. Only eligible green candidates should reach the bounded dispatch queue. Add focused tests for rotation, explicit priority, no unrequested gate-comment storm, and bounded GitHub API reads. Keep the change within this reviewer automation, and do not merge.

@neubig

neubig commented Sep 23, 2026

Copy link
Copy Markdown
Member

@all-hands-bot When you restack #660 on updated #659, please keep the PR limited to the continuous unrequested-review behavior and its bounded scan fix. Remove the one-off .pr/canary_unrequested_scan.py evidence harness from the shipped diff; keep its live results in the PR description/comment. #659 is also removing its .pr/canary_bounded_intake.py. Ensure neither script is reintroduced by the restack, and do not merge.

all-hands-bot pushed a commit that referenced this pull request Sep 23, 2026
The scheduled scan examined every open, non-draft PR. Classifying each
unrequested head costs a review read plus the exact-head check and workflow
reads, so one run read one list per PR across the largest repository and posted
a managed gate comment for every red or pending head, which the live canary of
#660 exposed as a blocking scalability bug.

- The unrequested part of a scheduled scan is now a bounded, rotating window:
  at most SCAN_WINDOW (10) unrequested PRs per repository, starting where the
  previous scan stopped. The position is retained in the existing Automation KV
  store under a per-repository review-scan:{owner}__{repo} key, so successive
  scans rotate through the whole backlog instead of reading one pull request per
  open PR. With no KV store the position is kept in memory.
- Explicit all-hands-bot review requests and trigger labels are never subject to
  the window: every explicit candidate is examined on every scan.
- A gate stop on an unrequested head posts no managed comment, so a scan over a
  large backlog cannot storm the PRs with comments. The managed comment stays for
  explicit requests, which still get their blocked or waiting explanation.
- Remove the one-off .pr canary script and bump the bundle to 1.9.0.

Tests cover rotation and KV persistence, explicit priority over the window,
the absence of unrequested gate comments, and the bounded pull-request reads.
@all-hands-bot

Copy link
Copy Markdown
Contributor Author

Restacked #660 onto the current #659 head (openhands/issue-656 @ 7609cf1) and pushed the amended change. New head: ca83635 (ca83635bcd06166e7052ebe24c5883d0407bab38). Not merged; the live Canvas deployment was not touched.

What was wrong

The scheduled scan read every open, non-draft PR and, before the bounded drain, classified each one's exact head. Classification costs a review-list read plus the check-run and workflow-run reads, so one run spent its whole API budget in the largest repository, and every red or pending head got a managed gate comment. That is the 99-comment, 10-minute run the canary caught.

What changed

All within skills/github-pr-reviewer/ (worker, docs) plus its catalog manifest/fixture:

  • Bounded rotating window. SCAN_WINDOW (10) unrequested PRs are examined per repository per scan, starting where the previous scan stopped. The position is retained in the existing Automation KV facility — the same AUTOMATION_KV_TOKEN / AUTOMATION_API_URL that main.py already uses — under a per-repository key review-scan:{owner}__{repo} ({"cursor": N}). The window wraps at the end of the backlog, so the whole backlog is still covered over several scans. No new secret, store, or permission. When the KV store is unavailable (local run/tests) the position is kept in memory; a KV read/write failure degrades to the in-memory cursor rather than aborting the scan.
  • Explicit priority. An all-hands-bot review request or a trigger label is never subject to the window: every explicit candidate is examined on every scan, and explicit candidates still drain first through the existing priority ordering.
  • No unrequested gate-comment storm. _gate_head(..., explain=False) for unrequested candidates, so a merely red or pending unrequested PR is skipped without starting an agent and without posting a managed gate comment. The managed blocked/waiting comment is kept for explicit requests and labeled heads.
  • Only green candidates reach the bounded dispatch queue. Unchanged in spirit, but now the window bounds how many heads are classified, so the queue only ever sees green unrequested heads from that slice.
  • Canary removed. The one-off .pr/canary_unrequested_scan.py (and the empty canary docstring commit it carried) is gone; the tree has no .pr/ entries.
  • Bundle/entry version bumped 1.8.0 → 1.9.0, automations/bundle-index.js and skills/index.js regenerated with npm run build.

Tests added (focused, in tests/test_github_reviewer_delivery.py)

  • test_scan_examines_a_bounded_rotating_window_of_unrequested_prs — rotation across successive scans and wrap-around.
  • test_the_scan_position_is_persisted_per_repository_in_the_kv_store — the cursor is written under review-scan:owner__one.
  • test_explicit_requests_are_examined_regardless_of_the_rotation_window — an explicit request is dispatched even when the window sits elsewhere.
  • test_unrequested_red_and_pending_prs_post_no_gate_comments — no unrequested gate comment; the green unrequested head still reaches the queue.
  • test_scan_reads_a_bounded_number_of_pull_requests_per_repository — one list page plus exactly SCAN_WINDOW full PR reads over a 40-PR backlog.
  • test_an_explicit_request_still_gets_its_managed_gate_comment — the explicit-request gate comment is preserved.
  • Updated the three prior tests that asserted the old unrequested gate-comment behavior.

Verification

  • uv run --group test pytest -q → 1049 passed, 23 skipped.
  • uv run --group test pytest -q tests/test_github_reviewer_delivery.py tests/test_github_automation_foundation.py tests/test_automation_setup.py → pass.
  • uv run --group test python scripts/sync_extensions.py --check → clean except the pre-existing non-blocking issue-duplicate-checker coverage warning.
  • npm run build → no drift.

Note on the PR description

The current PR body predates the restack: it still describes the workflow-run gate as part of this change (that is now in the base #659) and references the removed canary. Happy to rewrite the body if you want it accurate before review — say the word and I will update it in place.

Generated by OpenHands AI on behalf of the user.

@all-hands-bot

Copy link
Copy Markdown
Contributor Author

Done. #660 is restacked onto the current #659 head and limited to the continuous unrequested-review behavior plus its bounded scan fix.

State

  • Base: openhands/issue-656 @ 7609cf1 (the updated fix(review): bound scheduled PR review intake across repositories #659 head, which dropped its own .pr/canary_bounded_intake.py).
  • Head: ca83635 (ca83635bcd06166e7052ebe24c5883d0407bab38) — a single commit on top of 7609cf1.
  • mergeable=MERGEABLE, mergeStateStatus=CLEAN. Not merged, and the live Canvas deployment was not touched.

Canary harness

The one-off .pr/canary_unrequested_scan.py and the docstring-fix commit it carried were dropped in the restack. The shipped tree has no .pr/ entries at all, and neither .pr/canary_unrequested_scan.py nor #659's .pr/canary_bounded_intake.py is present:

$ git ls-tree -r --name-only HEAD | grep -E '^\.pr/|canary'   # -> no output

Its live read-only results are retained in the PR description, as asked.

Diff (only the reviewer automation)

automations/bundle-index.js
automations/catalog/github-pr-reviewer/manifest.json
skills/github-pr-reviewer/README.md
skills/github-pr-reviewer/SKILL.md
skills/github-pr-reviewer/references/state-schema.md
skills/github-pr-reviewer/scripts/worker.py
skills/index.js
tests/fixtures/automations/github-pr-reviewer.json
tests/test_github_reviewer_delivery.py

The bounded scan fix is the rotating, KV-backed window: SCAN_WINDOW (10) unrequested PRs per repository per scan, resuming from a per-repository review-scan:{owner}__{repo} cursor held in the existing AUTOMATION_KV_TOKEN / AUTOMATION_API_URL store, with explicit requests/labels always examined and no managed gate comment for a merely red or pending unrequested head. The exact-head check-run and workflow-run gate this stacks on already lives in the base #659, so this PR does not re-add it or touch skills/github/scripts/github_client.py.

Verification on the pushed head

  • uv run --group test pytest -q tests/test_github_reviewer_delivery.py → 79 passed.
  • uv run --group test pytest -q → 1049 passed, 23 skipped.
  • uv run --group test python scripts/sync_extensions.py --check → clean apart from the pre-existing non-blocking issue-duplicate-checker coverage warning.
  • npm run build → no drift.

Description

The PR body still claimed the deleted canary was committed and described the workflow-run gate as part of this PR, so I corrected it in place: live results kept, canary references replaced with the bounded-window fix, and the base/head recorded. Nothing was pushed beyond the existing branch, and no merge was performed.

Generated by OpenHands AI on behalf of the user.

@neubig

neubig commented Sep 23, 2026

Copy link
Copy Markdown
Member

@all-hands-bot After extensions #659 is updated, update this stacked PR onto the current head of its base branch openhands/issue-656, resolve conflicts while preserving unrequested green-PR discovery, run validation, and push. Do not merge.

all-hands-bot pushed a commit that referenced this pull request Sep 23, 2026
Restacked onto the current #659 head (openhands/issue-656). This keeps the
continuous unrequested green-PR discovery and the corrected base behavior it
builds on: scheduled candidates gate on GitHub-required checks (falling back to
every current-head check and workflow run when the required signal is
unavailable), and an explicit all-hands-bot request bypasses the CI gate.

The scheduled scan examined every open, non-draft PR. Classifying each
unrequested head costs a review read plus the exact-head check and workflow
reads, so one run read one list per PR across the largest repository and posted
a managed gate comment for every red or pending head, which the live canary of
#660 exposed as a blocking scalability bug.

- The unrequested part of a scheduled scan is now a bounded, rotating window:
  at most SCAN_WINDOW (10) unrequested PRs per repository, starting where the
  previous scan stopped. The position is retained in the existing Automation KV
  store under a per-repository review-scan:{owner}__{repo} key, so successive
  scans rotate through the whole backlog instead of reading one pull request per
  open PR. With no KV store the position is kept in memory.
- Explicit all-hands-bot review requests and trigger labels are never subject to
  the window: every explicit candidate is examined on every scan, and an
  explicit request still bypasses the CI gate.
- A gate stop on an unrequested head posts no managed comment, so a scan over a
  large backlog cannot storm the PRs with comments. The managed comment stays for
  explicit requests and labeled heads, which still get their blocked or waiting
  explanation.
- Remove the one-off .pr canary script and bump the catalog entry and bundle
  1.7.0 -> 1.8.0.

Tests cover rotation and KV persistence, explicit priority over the window,
the absence of unrequested gate comments, and the bounded pull-request reads,
alongside the base required-check and explicit-request-bypass tests.
@all-hands-bot

Copy link
Copy Markdown
Contributor Author

Restacked #660 onto the current #659 head and force-pushed the existing branch. Not merged.

State

What the rebase changed

#659 had moved on to 50fb986, which reworked the exact-head gate. I rebased #660 onto it and resolved four conflicted files (worker.py, SKILL.md, and the two generated catalogs):

  • Preserved feat(review): review unrequested green PRs on the scheduled scan #660's unrequested green-PR discovery — the rotating SCAN_WINDOW (10) per repository, the review-scan:{owner}__{repo} KV cursor, the stable scan:{repo}:{pr}:{head} delivery key, and no managed gate comment for a merely red or pending unrequested head.
  • Preserved the base's corrected gate — scheduled candidates still classify on the GitHub-required checks (_classify_check_runs(pr) → required_check_contexts → isRequired, with the all-runs fallback when the required signal is unavailable or empty).
  • Preserved the explicit-request CI-gate bypass — _gate_head(pr, requested=event_mode, scheduled=not event_mode, explain=not unrequested_candidate) merges both sides: an explicit all-hands-bot request returns green immediately and leaves no gate comment, while an unrequested scheduled candidate is gated on required checks but stays silent on a red/pending head.

I also corrected a stale version skip the old stack carried: the catalog entry and bundle now bump 1.7.0 → 1.8.0 (the base is at 1.7.0), and dropped a duplicated README bullet about the rotating window.

Verification on the pushed head

  • uv run --group test pytest -q tests/test_github_reviewer_delivery.py → 86 passed.
  • uv run --group test pytest -q tests/test_github_reviewer_delivery.py tests/test_github_automation_foundation.py tests/test_automation_setup.py → 230 passed, 17 skipped.
  • uv run --group test pytest -q → 1059 passed, 23 skipped.
  • uv run --group test python scripts/sync_extensions.py --check → clean apart from the pre-existing non-blocking issue-duplicate-checker coverage warning.
  • npm run build → no drift.

The base gate regressions still pass unchanged, including test_reviewer_explicit_request_bypasses_the_ci_gate, test_reviewer_ignores_an_optional_zero_job_workflow_on_a_green_required_head, test_reviewer_blocks_when_a_required_check_fails, and test_reviewer_falls_back_to_every_check_when_required_signal_is_unavailable.

Diff and description

The shipped diff is limited to the reviewer automation: worker.py, SKILL.md, README.md, references/state-schema.md, the catalog manifest and fixture, the regenerated automations/bundle-index.js / skills/index.js, and tests/test_github_reviewer_delivery.py. The tree has no .pr/ entries (neither .pr/canary_unrequested_scan.py nor #659's .pr/canary_bounded_intake.py).

I updated the PR description to match: base/head, the 1.7.0 → 1.8.0 bump, the preserved required-check gating and explicit-request bypass, and the new test counts. No merge was performed.

Generated by OpenHands AI on behalf of the user.

Base automatically changed from openhands/issue-656 to fix/653-scheduled-review-retry September 25, 2026 20:47
neubig pushed a commit that referenced this pull request Sep 25, 2026
Restacked onto the current #659 head (openhands/issue-656). This keeps the
continuous unrequested green-PR discovery and the corrected base behavior it
builds on: scheduled candidates gate on GitHub-required checks (falling back to
every current-head check and workflow run when the required signal is
unavailable), and an explicit all-hands-bot request bypasses the CI gate.

The scheduled scan examined every open, non-draft PR. Classifying each
unrequested head costs a review read plus the exact-head check and workflow
reads, so one run read one list per PR across the largest repository and posted
a managed gate comment for every red or pending head, which the live canary of
#660 exposed as a blocking scalability bug.

- The unrequested part of a scheduled scan is now a bounded, rotating window:
  at most SCAN_WINDOW (10) unrequested PRs per repository, starting where the
  previous scan stopped. The position is retained in the existing Automation KV
  store under a per-repository review-scan:{owner}__{repo} key, so successive
  scans rotate through the whole backlog instead of reading one pull request per
  open PR. With no KV store the position is kept in memory.
- Explicit all-hands-bot review requests and trigger labels are never subject to
  the window: every explicit candidate is examined on every scan, and an
  explicit request still bypasses the CI gate.
- A gate stop on an unrequested head posts no managed comment, so a scan over a
  large backlog cannot storm the PRs with comments. The managed comment stays for
  explicit requests and labeled heads, which still get their blocked or waiting
  explanation.
- Remove the one-off .pr canary script and bump the catalog entry and bundle
  1.7.0 -> 1.8.0.

Tests cover rotation and KV persistence, explicit priority over the window,
the absence of unrequested gate comments, and the bounded pull-request reads,
alongside the base required-check and explicit-request-bypass tests.
@neubig
neubig force-pushed the openhands/issue-658 branch from 7100eb4 to 7957433 Compare September 25, 2026 20:51
@neubig
neubig force-pushed the fix/653-scheduled-review-retry branch from 0f3a2df to a8936f4 Compare September 25, 2026 20:55
@neubig
neubig deleted the branch main September 25, 2026 20:56
@neubig neubig closed this Sep 25, 2026
@neubig neubig reopened this Sep 25, 2026
@neubig
neubig changed the base branch from fix/653-scheduled-review-retry to main September 25, 2026 20:56
Restacked onto the current #659 head (openhands/issue-656). This keeps the
continuous unrequested green-PR discovery and the corrected base behavior it
builds on: scheduled candidates gate on GitHub-required checks (falling back to
every current-head check and workflow run when the required signal is
unavailable), and an explicit all-hands-bot request bypasses the CI gate.

The scheduled scan examined every open, non-draft PR. Classifying each
unrequested head costs a review read plus the exact-head check and workflow
reads, so one run read one list per PR across the largest repository and posted
a managed gate comment for every red or pending head, which the live canary of

- The unrequested part of a scheduled scan is now a bounded, rotating window:
  at most SCAN_WINDOW (10) unrequested PRs per repository, starting where the
  previous scan stopped. The position is retained in the existing Automation KV
  store under a per-repository review-scan:{owner}__{repo} key, so successive
  scans rotate through the whole backlog instead of reading one pull request per
  open PR. With no KV store the position is kept in memory.
- Explicit all-hands-bot review requests and trigger labels are never subject to
  the window: every explicit candidate is examined on every scan, and an
  explicit request still bypasses the CI gate.
- A gate stop on an unrequested head posts no managed comment, so a scan over a
  large backlog cannot storm the PRs with comments. The managed comment stays for
  explicit requests and labeled heads, which still get their blocked or waiting
  explanation.
- Remove the one-off .pr canary script and bump the catalog entry and bundle
  1.7.0 -> 1.8.0.

Tests cover rotation and KV persistence, explicit priority over the window,
the absence of unrequested gate comments, and the bounded pull-request reads,
alongside the base required-check and explicit-request-bypass tests.
@neubig
neubig force-pushed the openhands/issue-658 branch from 7957433 to dcea1ae Compare September 25, 2026 20:58
@neubig
neubig merged commit f41c2fa into main Sep 25, 2026
11 checks passed
@neubig
neubig deleted the openhands/issue-658 branch September 25, 2026 20:59
@openhands-release-bot openhands-release-bot Bot added the released: v0.25.0 Shipped in v0.25.0 label Sep 27, 2026
@openhands-release-bot

Copy link
Copy Markdown
Contributor

🚀 Released in v0.25.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released: v0.25.0 Shipped in v0.25.0 type: feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Review CI-passing PRs without manual requests

3 participants