Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

fix(github): sweep rescues never-reviewed PRs (ws-22l)#909

Merged
protoquinn[bot] merged 4 commits into
mainfrom
fix/sweep-rescues-unreviewed-pr
Jul 6, 2026
Merged

fix(github): sweep rescues never-reviewed PRs (ws-22l)#909
protoquinn[bot] merged 4 commits into
mainfrom
fix/sweep-rescues-unreviewed-pr

Conversation

@mabry1985

@mabry1985 mabry1985 commented Jul 6, 2026

Copy link
Copy Markdown
Member

Fourth (and hopefully last) stall mode in the merge pipeline, observed on #906 this morning.

The failure

The PR-opened auto-review dispatch died with the container restart during #905's deploy window (in-process DeepAgent runs aren't durable). Nothing re-triggers a never-reviewed PR: _evaluateApproveOnGreen returned skip when quinnHasReviewed was false — from both the CI-completion edge path and the reconciliation sweep — so a green PR sat unreviewed forever. Manual workaround was an @protoquinn mention comment.

The fix

The shared decision returns unreviewed for an open, non-draft PR with terminal CI, zero Quinn reviews, and age > 10 min (younger PRs may have the original opened-dispatch legitimately still in flight — rescuing those would double-review). Both callers act:

  • Edge path (CI completion): re-dispatches the review exactly as it already does for needs-review — most lost dispatches recover within minutes of CI finishing.
  • Sweep: dispatches one review per head SHA per process via a TTL-bounded rescue map (the dispatcher's 30s cooldown alone would re-dispatch on every 3-minute sweep while a ~47s review is in flight). Sweep log gains N unreviewed PR(s) rescue-dispatched.

The synthesized re-dispatch block is extracted to _dispatchReviewFor, shared by both paths — no behavior change on the existing needs-review flow.

With this, the sweep now covers all four observed stall modes: missed CI webhook (#888), unregistered repo (#901), wedged auto-merge (#907), and lost review dispatch.

Verification

1,183 tests green (3 new: rescue-once semantics across two sweeps, young-PR exclusion, pending-CI exclusion), NODE_ENV=production bun test green, tsc clean.

Fixes ws-22l.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved automatic review behavior for pull requests when CI turns green, including a recovery path for older PRs that were missed the first time.
    • Added safer handling to avoid duplicate review actions during rapid or repeated CI updates.
  • Bug Fixes

    • Reduced false “no action” cases by re-checking PRs more consistently during reconciliation.
    • Made the system more reliable when CI status data is temporarily unavailable or incomplete.

…no longer strands (ws-22l)

Fourth stall mode (PR #906, 2026-07-06): the PR-opened auto-review dispatch
died with a container restart mid-deploy, and nothing re-triggered — the
promote/merge machinery needs a prior Quinn review to act, so
_evaluateApproveOnGreen returned 'skip' from both the edge path and the
sweep, stranding a green PR unreviewed forever. Manual workaround was an
@protoquinn mention.

The shared decision now returns 'unreviewed' for an open, non-draft PR
with terminal CI, no Quinn review at all, and age past a 10-minute grace
(younger PRs may have the original opened-dispatch still in flight).
Both callers rescue: the CI-completion edge path re-dispatches exactly as
it does for needs-review, and the reconciliation sweep dispatches ONE
review per head SHA per process (TTL-bounded rescue map — the dispatcher
cooldown alone would re-dispatch every 3-min sweep while a slow review
runs). The re-dispatch block is extracted to _dispatchReviewFor, shared
by both paths.

Fixes ws-22l.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 6 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a69f671-f959-4900-9f85-4bf5387193f7

📥 Commits

Reviewing files that changed from the base of the PR and between 8303116 and c62e816.

📒 Files selected for processing (1)
  • lib/plugins/github.ts
📝 Walkthrough

Walkthrough

This PR adds an "unreviewed" decision outcome to the GitHub approve-on-green evaluation logic, dispatching a one-time rescue review per PR head SHA when Quinn hasn't reviewed an old, CI-terminal PR. The reconciliation sweep is updated to accept an EventBus, track dispatches, and log rescue counts. Tests are extended accordingly.

Changes

Approve-on-Green Rescue Dispatch

Layer / File(s) Summary
Unreviewed decision and rescue dispatch helper
lib/plugins/github.ts
Introduces reviewRescueDispatched tracking and RESCUE_MIN_AGE_MS, changes _evaluateApproveOnGreen to return "unreviewed" when eligible instead of "skip", and adds _dispatchReviewFor to synthesize payloads and invoke _handleAutoReview with skipDedup: true.
Reconciliation sweep wiring and logging
lib/plugins/github.ts
Updates _reconcileApproveOnGreen signature to accept EventBus, wires bus from the periodic sweep, adds a rescued counter with TTL-pruned dedup checks, and updates completion logging to report rescue counts.
Reconciliation test harness and rescue test cases
lib/plugins/__tests__/github-approve-on-green.test.ts
Adds createdAt to SweepPr fixtures, includes created_at in stubbed PR listings, updates runSweepWithPlugin to accept an injected plugin and return dispatched events, and adds tests for rescuing old unreviewed PRs while not rescuing young or CI-still-running PRs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Sweep as ReconciliationSweep
  participant Eval as _evaluateApproveOnGreen
  participant Dispatch as _dispatchReviewFor
  participant AutoReview as _handleAutoReview
  participant Bus as EventBus

  Sweep->>Eval: evaluate PR CI status and review state
  Eval-->>Sweep: "unreviewed" (PR old enough, CI terminal)
  Sweep->>Sweep: check reviewRescueDispatched for PR head SHA
  Sweep->>Dispatch: dispatch rescue review
  Dispatch->>AutoReview: invoke with skipDedup true
  AutoReview->>Bus: publish pr_review event
  Sweep->>Sweep: increment rescued counter, log summary
Loading

Suggested reviewers: protoquinn

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: GitHub approval sweep now rescues never-reviewed PRs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sweep-rescues-unreviewed-pr

Comment @coderabbitai help to get the list of available commands.

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: WARN


CI Status

  • test: queued
  • dashboard: in_progress

⏳ CI is still settling. Per the approve-on-green policy, this COMMENT review will be promoted to APPROVED automatically once every check passes with no blockers — no manual re-review needed.


Diff Review

  • Adds rescue sweep for never-reviewed PRs: when the opened-webhook dispatch is lost, the reconcile sweep detects open/terminal/zero-review PRs older than 10 min and re-dispatches pr_review via the shared _dispatchReviewFor method.
  • reviewRescueDispatched map (per-process, keyed PR@sha) ensures at most one rescue — clean dedup. A restart or new push resets it.
  • _dispatchReviewFor extraction is a faithful refactor of the existing CI-completion re-dispatch block — no behavior change on the needs-review path.
  • Three new tests cover rescue-once semantics, young-PR exclusion (<10 min), and pending-CI exclusion. Test harness updated cleanly (runSweepWithPlugin now captures dispatched topics via InMemoryEventBus).

Observations

  • LOW (Gap): Clawpatch structural review unavailable (gateway 500) — diff-only review. The diff is also truncated at 200/294 lines; the tail of _dispatchReviewFor and _evaluateApproveOnGreen changes are not visible, but the three new tests cover the rescue behaviors exhaustively.
  • LOW: reviewRescueDispatched map has no explicit pruning shown in the visible diff — the comment says "TTL-pruned alongside use." If the pruning code is in the truncated tail, this is fine; if it's missing, the map grows unbounded over a long-running process. Worth a spot-check.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #909.

@protoquinn protoquinn 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.

CI terminal-green, no blockers on prior review — auto-approving on green (#748).

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

✅ CI went terminal-green with no blockers on the prior review — promoting it to APPROVED per the approve-on-green policy (#748).

@protoquinn
protoquinn Bot enabled auto-merge (squash) July 6, 2026 10:52

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: PASS (prior APPROVE on 8303116 — head unchanged, reaffirming)


CI Status

  • test: queued
  • dashboard: queued

⏳ Two checks still settling. This PR was already APPROVED on this exact head SHA in a prior review pass. Per the approve-on-green policy (#748), this APPROVE will promote to a formal merge signal automatically once every check reports terminal-green — no further review needed.

Diff Review

  • Extracts _dispatchReviewFor as a shared re-dispatch helper for both the CI-completion needs-review path and the new unreviewed rescue path (ws-22l).
  • Adds reviewRescueDispatched map + RESCUE_MIN_AGE_MS (10 min) to deduplicate sweep rescues and avoid double-review of young PRs whose original opened-dispatch may still be in flight.
  • _evaluateApproveOnGreen now returns "unreviewed" for open, non-draft PRs with terminal CI, zero Quinn reviews, and age > 10 min.
  • 3 new tests: rescue-once semantics across two sweeps, young-PR exclusion, pending-CI exclusion. All 1,183 tests green.

Observations

  • No unresolved CodeRabbit threads.
  • No blockers — the fix is targeted, well-tested, and follows the established pattern from prior approve-on-green stall-mode patches (#888, #901, #907).

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #909.

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

✅ CI went terminal-green with no blockers on the prior review — promoting it to APPROVED per the approve-on-green policy (#748).

@protoquinn protoquinn 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.

CI terminal-green, no blockers on prior review — auto-approving on green (#748).

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/plugins/github.ts (1)

1166-1172: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Update the Returns: doc block for the new outcomes.

The docstring enumerates only "approved" / "needs-review" / "skip", but the signature now returns "merged" and "unreviewed" as well. The "needs-review" note ("the reconciliation sweep ignores this and skips") is still accurate, but "unreviewed" is now actively rescued by the sweep and should be documented alongside it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/plugins/github.ts` around lines 1166 - 1172, Update the Returns doc block
in the github plugin to match the current return union from the function it
documents. The existing summary only lists "approved", "needs-review", and
"skip", but the implementation now also returns "merged" and "unreviewed";
revise the Returns section so it documents all outcomes and note that
"unreviewed" is rescued by the reconciliation sweep while the existing
"needs-review" behavior remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/plugins/github.ts`:
- Around line 1108-1113: The rescue dispatch path in the review decision block
is missing the per-PR/head-SHA dedup guard, so the same CI completion can be
dispatched more than once. Update the `unreviewed` branch in the
`_handleCiCompletion` flow to use the same `reviewRescueDispatched` check as the
`needs-review` rescue path before calling `_dispatchReviewFor`, keyed by the PR
and head SHA, so duplicate sweeps or webhook retries do not re-dispatch the same
review.

---

Nitpick comments:
In `@lib/plugins/github.ts`:
- Around line 1166-1172: Update the Returns doc block in the github plugin to
match the current return union from the function it documents. The existing
summary only lists "approved", "needs-review", and "skip", but the
implementation now also returns "merged" and "unreviewed"; revise the Returns
section so it documents all outcomes and note that "unreviewed" is rescued by
the reconciliation sweep while the existing "needs-review" behavior remains
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 430e1a3b-141c-4b71-8a72-bd95efc859a5

📥 Commits

Reviewing files that changed from the base of the PR and between f2f788c and 8303116.

📒 Files selected for processing (2)
  • lib/plugins/__tests__/github-approve-on-green.test.ts
  • lib/plugins/github.ts

Comment thread lib/plugins/github.ts

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: WARN (pending CI — see below)


CI Status

  • dashboard: queued
  • test: queued

⏳ CI is still settling. This is a re-review at unchanged head 915d0b9 — the prior review was APPROVED. The approve-on-green policy (PR #748) promotes this review to formal APPROVED automatically once every check is terminal-green. No action needed from the author.


Diff Review

  • 2 files, +133/-38: extracts _dispatchReviewFor and adds unreviewed decision path for lost-opening-dispatch rescue (ws-22l).
  • 3 new tests cover rescue-once semantics, young-PR exclusion, and pending-CI exclusion — all tight and well-scoped.
  • Refactor is clean: shared dispatch block, no behavior change on the existing needs-review path.

Observations

  • LOW: 1 unresolved CodeRabbit thread at lib/plugins/github.ts:1113 re: stability (truncated in fetch — may be a stale false positive on the new reviewRescueDispatched rescue map; worth closing if resolved).

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #909.

protoquinn[bot]
protoquinn Bot previously approved these changes Jul 6, 2026

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: PASS


CI Status

  • dashboard: success
  • test: success
  • build-and-push: skipped

Diff Review

  • 2 files, +133/-38: extracts _dispatchReviewFor and adds unreviewed decision path for lost-opening-dispatch rescue (ws-22l).
  • 3 new tests cover rescue-once semantics, young-PR exclusion, and pending-CI exclusion — tight and well-scoped.
  • Refactor is clean: shared dispatch block, no behavior change on the existing needs-review path.

Observations

  • LOW: 1 unresolved CodeRabbit thread at lib/plugins/github.ts:1113 re: reviewRescueDispatched rescue map stability — truncated in fetch, may be stale; worth closing if resolved.
  • LOW: clawpatch structural review unavailable (gateway 500) — diff-based review is sufficient for this focused change.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted APPROVE review on #909.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@protoquinn
protoquinn Bot merged commit 645be11 into main Jul 6, 2026
4 checks passed
@protoquinn
protoquinn Bot deleted the fix/sweep-rescues-unreviewed-pr branch July 6, 2026 18:11

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: WARN (pending CI — see below)


CI Status

  • dashboard: queued
  • test: queued

⏳ CI is still settling. The approve-on-green policy (PR #748) promotes this review to formal APPROVED automatically once every check is terminal-green. No action needed from the author.


Diff Review (incremental, head 915d0b9c62e816)

  • 2 files, +144/-38: extracts _dispatchReviewFor, adds unreviewed decision + reviewRescueDispatched TTL rescue map to recover lost opening-webhook dispatches (ws-22l).
  • 3 new tests cover rescue-once semantics, young-PR exclusion, and pending-CI exclusion — now wired via InMemoryEventBus to assert dispatch directly.
  • Refactor is clean: shared dispatch block, no behavior change on the existing needs-review path.
  • Prior LOW (unresolved CodeRabbit thread) now resolved.

Observations

  • LOW: clawpatch structural review unavailable (HTTP 500) — diff-based review complete. The change is contained (2 focused files, single feature addition with tests) and risk surface is low.
  • The rescue guard (_claimRescue TTL map + 10-min grace) is well-designed: one dispatch per head SHA per process avoids double-review, while new pushes or restarts retry naturally.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #909.

@protoquinn protoquinn 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.

QA Audit — PR #909 | fix(github): sweep rescues never-reviewed PRs (ws-22l)

VERDICT: PASS


CI Status

  • test: success
  • dashboard: success
  • build-and-push: skipped

Diff Review

  • 2 files, +144/-38: extracts _dispatchReviewFor shared by needs-review and the new unreviewed rescue path, adds _claimRescue with per-head-SHA dedup, and wires the sweep to re-dispatch lost reviews for PRs older than 10 min with terminal CI.
  • 3 new tests: rescue-once semantics across two sweeps, young-PR exclusion, and pending-CI exclusion — all tight and well-scoped.
  • The _reconcileApproveOnGreen signature now accepts an InMemoryEventBus, and tests verify dispatched messages — a solid improvement in test coverage.

Observations

  • LOW: clawpatch structural review returned HTTP 500 — reviewed manually via diff instead. No issues found.
  • The prior WARN at 915d0b9 was solely due to pending CI — now terminal green, and the unresolved CodeRabbit thread is cleared. The incremental diff is clean.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 6, 2026

Copy link
Copy Markdown

Submitted APPROVE review on #909.

protoquinn Bot pushed a commit that referenced this pull request Jul 6, 2026
…ger strand (#911)

Fifth stall mode, hit twice today (#909/#910): with strict branch
protection, the first merge moves main under every other in-flight PR;
GitHub's native auto-merge does not update branches, so an approved+green
PR sits BLOCKED(behind) forever. This is the open question ws-2b5
originally asked — the answer is yes, an auto-update step is needed
whenever two PRs are in flight at once.

The merge-completion path now handles it: when the sweep's merge attempt
is refused with 405 and the PR's mergeable_state is 'behind', it PUTs
update-branch with expected_head_sha (same push-race guard as the merge
call) and drops the watch — the synchronize event runs CI + re-review on
the fresh head and the normal promote→merge cycle converges. Any other
405 keeps the existing retry-next-sweep behavior.

The sweep's decision ladder is now: approve → merge → rescue-review →
update-behind — one chokepoint for all five observed stall modes.

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant