You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The merge-queue bot forked its dashboard into a second issue on 2026-09-01: the same GET /issues?labels=merge-queue&state=open lookup returned the dashboard at 13:56 and omitted it at 14:00 (the issue was open, labeled, never renamed — the listing is index-backed and was momentarily stale). The script took its "no dashboard exists" branch and created a new one. Since the listing is newest-first and the lookup used .find(), every run since moved to the new issue and the original was orphaned with a frozen queue table.
The lookup now returns all matching dashboards, oldest first, and the oldest open one is canonical — its URL is the "Details" link on every gate status, so it must not hop to whatever was created last.
Two independent read paths (REST listing + GraphQL) are queried every run and their candidates unioned, so neither index can hide a dashboard from the other. A partial miss matters as much as an empty one: dropping the oldest while keeping a newer duplicate would crown the duplicate and hide the original from cleanup.
Every candidate is then re-read by number — a direct record lookup rather than an index query — and re-checked for state, title, label and not being a PR. That also catches staleness in the other direction: a duplicate an earlier run already closed would otherwise be closed and commented on again on every run.
Extra dashboards are closed automatically with a comment pointing at the canonical one, so a duplicate that still slips through heals on the next run instead of lingering.
Verified with MERGE_QUEUE_DRY_RUN=true against the live repo, and with a stubbed harness covering the partial-miss, fully-stale, closed-duplicate, PR-masquerading-as-dashboard and deleted-candidate cases. The issue orphaned by the original incident has since been closed by hand, so only one dashboard is open and this change is a no-op for it.
• Confirms empty REST dashboard searches through GraphQL before creating an issue.
• Keeps the oldest dashboard canonical, preserving stable gate-status details links.
• Comments on and closes extra dashboards without failing queue reconciliation.
Diagram
sequenceDiagram
participant Bot as Merge Queue
participant REST as REST Issues
participant GQL as GraphQL Issues
participant Gates as Gate Statuses
participant Canon as Canonical Issue
participant Dupes as Duplicate Issues
Bot->>REST: Find open dashboards
alt REST finds dashboards
REST-->>Bot: Return all matches
else REST misses
Bot->>GQL: Confirm absence
GQL-->>Bot: Return matching issues
end
Bot->>Bot: Select oldest issue
Bot->>Gates: Link canonical dashboard
Bot->>Canon: Update queue table
opt Extra dashboards exist
Bot->>Dupes: Comment and close
end
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Use GraphQL exclusively
➕ Removes duplicate REST and GraphQL lookup implementations.
➕ Avoids the REST endpoint returning pull requests alongside issues.
➖ A single read path cannot independently confirm a stale miss.
➖ Reduces resilience if the GraphQL connection is temporarily inconsistent or unavailable.
2. Persist the canonical issue number
➕ Provides deterministic constant-time lookup.
➕ Eliminates title-and-label discovery during normal reconciliation.
➖ Introduces state and recovery procedures when the issue is closed or repository settings change.
➖ Conflicts with the script's stateless, self-healing design.
Recommendation: Keep the PR's REST-first, GraphQL-confirmed approach. It preserves stateless reconciliation, maintains a stable oldest-dashboard URL, and automatically repairs duplicates without making cosmetic cleanup a queue failure.
• Returns every matching dashboard, confirms REST misses through GraphQL, and consistently selects the oldest issue as canonical. Newer duplicates are commented on and closed with best-effort failure handling and dry-run support.
findDashboardIssues() accepts any non-empty REST result without GraphQL confirmation, so if REST
omits the oldest dashboard but returns a newer duplicate, the newer issue becomes canonical. This
changes every gate's Details URL, updates the wrong dashboard, and leaves the omitted original open
instead of healing the fork.
+ const dashboards = await findDashboardIssuesViaRest();+ if (dashboards.length) return dashboards.sort((a, b) => a.number - b.number);+ const confirmation = await findDashboardIssuesViaGraphql();
Evidence
The code documents that the oldest open dashboard must remain canonical, but GraphQL is only called
after REST returns zero matches. The first REST result is selected at lines 629-631, passed as the
Details URL at lines 703-713, updated at lines 608-614, and only dashboards present in that same
partial result can be closed at lines 739-741.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The REST lookup is trusted whenever it returns at least one dashboard. A partially stale response can omit the oldest dashboard while retaining a newer duplicate, causing the newer issue to become canonical despite the oldest-first invariant.
## Issue Context
Query both independent paths and reconcile their results by issue number before sorting oldest-first. Ensure a partial result from either path cannot replace the established canonical dashboard or hide a duplicate from cleanup.
## Fix Focus Areas
- .github/scripts/merge-queue.js[537-548]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history
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
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.
The merge-queue bot forked its dashboard into a second issue on 2026-09-01: the same
GET /issues?labels=merge-queue&state=openlookup returned the dashboard at 13:56 and omitted it at 14:00 (the issue was open, labeled, never renamed — the listing is index-backed and was momentarily stale). The script took its "no dashboard exists" branch and created a new one. Since the listing is newest-first and the lookup used.find(), every run since moved to the new issue and the original was orphaned with a frozen queue table.Verified with
MERGE_QUEUE_DRY_RUN=trueagainst the live repo, and with a stubbed harness covering the partial-miss, fully-stale, closed-duplicate, PR-masquerading-as-dashboard and deleted-candidate cases. The issue orphaned by the original incident has since been closed by hand, so only one dashboard is open and this change is a no-op for it.