Skip to content

orb(backlog-convergence): the 5-PR cap is applied before the repair-exhausted filter — five wedged old PRs shadow every other PR needing convergence #9154

Description

@JSONbored

Problem

selectBacklogConvergenceCandidates (src/selfhost/backlog-convergence.ts:42-56) filters, sorts by
createdAt ascending, then .slice(0, 5). Only after that does the caller apply the exhaustion
filter and bail (src/queue/processors.ts:1820-1828):

const allCandidates = selectBacklogConvergenceCandidates({ pulls: openPullRequests }); // already sliced to 5
const exhaustedFlags = await Promise.all(allCandidates.map((pr) => isRegateRepairExhausted(env, repoFullName, pr)));
const candidates = allCandidates.filter((_pr, index) => !exhaustedFlags[index]);
if (candidates.length === 0) return;

The sort key is PR creation time, which never changes, and the selection predicate
(lastPublishedSurfaceSha !== headSha) only clears on a genuinely successful publish. So five old,
permanently-unpublishable PRs permanently occupy all five slots.

Trigger

Five old open PRs whose surface publish keeps failing — a lost checks:write, the duplicate-check-run
race in #9013, or a repo-level publish error. Each burns its 5 attempts per SHA, candidates becomes
empty, the sweep returns early, and the 6th…Nth PR needing convergence is never even considered.
REGATE_REPAIR_ATTEMPT_LOOKBACK_MS is 24h, so this repeats on a 24-hour cycle rather than resolving.

Impact

The durable catch-all for "this PR has no published review surface" silently stops covering every PR
behind the five oldest — precisely the stranding class it exists to repair. Nothing surfaces the fact
that the sweep is returning early every cycle.

Requirements

  • Move the isRegateRepairExhausted filter inside or ahead of the slice, so the cap selects 5
    actionable candidates.
  • Emit a metric/log when the sweep exits with an all-exhausted candidate set, so a permanently wedged head
    of the list is visible rather than silent.
  • Consider a secondary sort or rotation so a persistently failing PR cannot hold a slot forever even
    after the filter fix.

Test Coverage Requirements

99%+ patch coverage, branch-counted; a regression test with 5 exhausted + 1 actionable PR asserting the
actionable one is selected.

Links & Resources

maintainer-only — convergence sweep correctness.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions