diff --git a/src/queue/processors.ts b/src/queue/processors.ts index cfd1fcad3e..1b4121f19e 100644 --- a/src/queue/processors.ts +++ b/src/queue/processors.ts @@ -1939,17 +1939,22 @@ async function sweepRepoRegate( } // With an active backlog (regateBacklog > 0), a priority repair PR earns an EXCEPTION to the "yield to // backlog" rule above, not a license for the whole sweep to also drag along a full SWEEP_MAX_PRS batch of - // ordinary stale PRs -- selectRegateCandidates sorts priority PRs first, so capping max to exactly - // priorityPullNumbers.length restricts the candidate set to repairs only. No backlog pressure ⇒ a normal, - // full-size sweep as before. + // ordinary stale PRs. Repair priority only affects selectRegateCandidates eligibility, not final ordering, so + // the backlog path must narrow the input pool to priority repairs before applying the normal stale ordering cap. + // No backlog pressure ⇒ a normal, full-size sweep as before. + const priorityPullNumberSet = new Set(priorityPullNumbers); const repairCandidateLimit = priorityPullNumbers.length > 0 ? regateBacklog > 0 ? priorityPullNumbers.length : Math.max(SWEEP_MAX_PRS, priorityPullNumbers.length) : null; + const candidatePullRequests = + regateBacklog > 0 && priorityPullNumbers.length > 0 + ? openPullRequests.filter((pr) => priorityPullNumberSet.has(pr.number)) + : openPullRequests; const candidates = selectRegateCandidates({ - pulls: openPullRequests, + pulls: candidatePullRequests, now: nowIso(), priorityPullNumbers, priorityBypassesFreshness: priorityPullNumbers.length > 0, @@ -2020,7 +2025,6 @@ async function sweepRepoRegate( // isScheduledRegateSweepJob (queue-common.ts) misclassifies it as background maintenance and it inherits the // exact starvation this priority mechanism exists to avoid. Ordinary stale candidates keep the sweep prefix // unchanged. - const priorityPullNumberSet = new Set(priorityPullNumbers); for (const [index, pr] of candidates.entries()) { const others = openPullRequests.filter( (other) => other.number !== pr.number, diff --git a/test/unit/queue.test.ts b/test/unit/queue.test.ts index cbb9a341e7..b16d1b96f0 100644 --- a/test/unit/queue.test.ts +++ b/test/unit/queue.test.ts @@ -7481,15 +7481,22 @@ describe("queue processors", () => { await upsertInstallation(env, { action: "created", installation: { id: 9403, account: { login: "owner", id: 1, type: "Organization" }, target_type: "Organization", repository_selection: "selected", permissions: {}, events: [] } }); await upsertRepositoryFromGitHub(env, { name: "agent-repo", full_name: "owner/agent-repo", private: false, owner: { login: "owner" } }, 9403); await upsertRepositorySettings(env, { repoFullName: "owner/agent-repo", autonomy: { merge: "auto" }, gateCheckMode: "enabled", checkRunMode: "off", commentMode: "off", publicSurface: "off" }); - // PR 1: missing its current Gate check -- the one priority repair. + // PR 1: missing its current Gate check -- the one priority repair. Make it newer-by-regate than the + // ordinary stale PRs below, reproducing the backlog bug where a max=1 staleness slice could drop the repair. await upsertPullRequestFromGitHub(env, "owner/agent-repo", { number: 1, title: "Repair 1", state: "open", user: { login: "c" }, head: { sha: "repair-1" }, labels: [], body: "" }); await repositoriesModule.markPullRequestSurfacePublished(env, "owner/agent-repo", 1, "repair-1"); + await env.DB.prepare("update pull_requests set last_regated_at = ? where repo_full_name = ? and number = ?") + .bind("2026-05-28T01:59:00.000Z", "owner/agent-repo", 1) + .run(); // PRs 2-5: ordinary, already-current, stale-by-time PRs -- a normal (no-backlog) sweep would pick these up // too, but while the backlog is draining they must sit out so the sweep only carries the priority repair. for (const number of [2, 3, 4, 5]) { const headSha = `stale-${number}`; await upsertPullRequestFromGitHub(env, "owner/agent-repo", { number, title: `Stale ${number}`, state: "open", user: { login: "c" }, head: { sha: headSha }, labels: [], body: "" }); await repositoriesModule.markPullRequestSurfacePublished(env, "owner/agent-repo", number, headSha); + await env.DB.prepare("update pull_requests set last_regated_at = ? where repo_full_name = ? and number = ?") + .bind(`2026-05-28T01:0${number}:00.000Z`, "owner/agent-repo", number) + .run(); await upsertCheckSummary(env, { id: `gate-current-${number}`, repoFullName: "owner/agent-repo",