Repository navigation
fix(pr-review): retry the merge-queue state read and surface a failed read - #2223
Conversation
… read mq_dequeue_if_unapproved runs right after an approval is dismissed. Its one read of the PR's live queue and review state could fail transiently, and the only trace was a warning in the run log while a queued PR went on to merge with no approval standing. Retry the read a bounded number of times (MQ_READ_ATTEMPTS, default 3, with MQ_READ_RETRY_DELAY_SEC between, default 2). If every attempt fails, also write the unknown state to the job step summary. No PR comment on that path: most PRs that reach it are not in the queue. Tests: one failed read then success still dequeues; every read failing makes exactly MQ_READ_ATTEMPTS reads, no dequeue, no comment, a summary entry and exit 0; a malformed setting falls back to the default. Refs #2186 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 17 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a retry mechanism with bounded attempts and delays for reading the live merge-queue and review state, addressing transient GraphQL errors. It also logs failures to the GitHub step summary when all retries fail. The review feedback highlights a potential issue in the retry logic where a standalone conditional expression (&&) could cause unintended script termination under set -e if the delay is zero, and suggests using an explicit if block instead.
CodeAnt Nitpicks2 code suggestions1. Any large valid attempt count or delay can keep this synchronous call retrying for hours before it surfaces the unknown state.Performance · 2. This summary also runs after a new approval is posted and verified; if the state read fails, the heading falsely says it was withdrawn and can prompt an unnecessary queue removal.Logic error · |
CodeAnt PR Risk: Low Risk
Assessed commit: |
|
dev-lead is withholding action on this item. It is labeled To re-enable automated pickup: remove the |
QA Lead — test-risk advisoryRisk tier: LOW. The change only adds to a guard on the approval path. The new tests are deterministic ( What I'd shore up (highest leverage first):
Escalate? no Advisory only — I comment, I do not change code. Ground: BMAD Test Architecture |
donpetry-bot
left a comment
There was a problem hiding this comment.
Automated review — APPROVED ✓
Risk: LOW
Reviewed commit: 11fe0e9361d9cf737c38b5527256196236f6d997
Review mode: triage-approved (single reviewer)
Summary
Adds a bounded retry (MQ_READ_ATTEMPTS, default 3; MQ_READ_RETRY_DELAY_SEC, default 2) around the live merge-queue/review state read in mq_dequeue_if_unapproved. When every attempt fails, it also writes a step-summary entry naming the PR. It still makes no dequeue call, posts no PR comment, and returns 0. AGENTS.md documents the change, and 3 bats cases are added. The triage-approved LOW assessment holds: the change is confined to one library function on a best-effort path, and the existing tests are unchanged.
Linked issue analysis
Closes #2186. All three scope items are met: a bounded retry with backoff, a step-summary entry on total failure, and no PR comment on that path. All three acceptance criteria are covered by tests:
- Fail once then succeed → dequeued, with one marker comment.
- Every read fails → no dequeue, no comment, a warning, a summary entry, exit 0.
- The tunables can be overridden in tests (
MQ_READ_RETRY_DELAY_SEC=0in setup), and the 24 existing tests are untouched.
Findings
No blocking findings.
- Resolved thread (Gemini,
set -ewith[ "$delay" -gt 0 ] && sleep): I agree with the author's rebuttal. A failing left operand of&&is exempt from errexit, and the call site isst=$(...) || status_st=$?, which disables errexit inside anyway. The existing "under set -e the helper never aborts its caller" test also covers this. - Nit (non-blocking): The malformed-setting test sets
MQ_READ_RETRY_DELAY_SEC=-1and then immediately overrides it to0, so only the malformedMQ_READ_ATTEMPTSfallback is tested. The delay-regex fallback is untested. This is acceptable because exercising it would mean a real 2s sleep. - Nit (non-blocking, CodeAnt #2):
post-pr-review.sh:532also calls the helper right after a new approval is posted. The cleanup did dismiss a prior approval first, so the summary heading is accurate, but on that path the wording "may merge without an approval" is overly alarming. It only matters when every read fails. - Nit (non-blocking, CodeAnt #1): The tunables have no upper bound. They are set only by the operator or tests, so this is low concern.
- Secret-scan MCP tool not available in this run; gitleaks CI check passed.
CI status
All required checks are green: bats, unit-tests, shellcheck/ShellCheck, Lint, CodeQL, SonarCloud (Quality Gate passed), gitleaks, actionlint, duplicate-decl-gate, AgentShield, and all stub/standards guards. The cancelled dev-lead / * runs are expected under the dev-lead:hands-off label. The queued pr-auto-review run is this review.
Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.
|
pr-review approved on PARTIAL advisory evidence: 3/5 required advisory bots reported before the gate's head-age-timeout fallback proceeded. Recorded for the miss-rate metric (#1596). |
Dismissing approval due to a PR issue comment lacking a verified disposition (#1813)
Removed from the merge queue: approval withdrawnThe automated review withdrew its approval while this PR was in the merge queue, and no other approval stands for the current head commit What happens next: the PR needs an approval for its head commit again. Address the review findings, or a maintainer approves it. Once an approval stands, dev-lead re-enables auto-merge on its next pass and the PR rejoins the queue; a maintainer can also re-add it by hand. |
|



Closes #2186
Problem
mq_dequeue_if_unapprovedruns right after an approval is dismissed. It read the PR's live queue and review state once; if that read failed (a transient GraphQL error, a rate limit) it logged one warning and returned, and a PR sitting in the merge queue could then merge with no approval standing. Raised by CodeRabbit on the pull request for #2174 and accepted there as a follow-up.What changes
MQ_READ_ATTEMPTS, default 3;MQ_READ_RETRY_DELAY_SECbetween attempts, default 2).AGENTS.mddocuments the retry and the two settings.Risk
Low. One library function gains a retry loop; a malformed setting falls back to the default. Worst case is up to two extra reads and four seconds on a path that only runs after a dismissal.
Test plan
bats tests/test_merge_queue_dequeue.bats: 27 of 27 pass locally (three new cases: one failed read then success still dequeues; every read failing makes exactlyMQ_READ_ATTEMPTSreads, no dequeue, no comment, a summary entry, exit 0; a malformed setting uses the default). The 24 existing tests are unchanged.scripts/check-duplicate-decls.shpasses.shellcheck -xreports only the two existing SC2016 info notes on the GraphQL query strings.Rollback
Revert this commit. The dequeue behaviour returns to a single read.
Monitoring
A failed read shows as a
::warning::in the pr-review run and as a "Merge queue: state unknown" entry in its step summary.This PR is authored directly in the session and labelled
dev-lead:hands-off.🤖 Generated with Claude Code