CONTRIBUTOR_CAP_LOCK_TTL_SECONDS = 30 (src/queue/transient-locks.ts ~170) was sized for the executor's pre-merge recheck, which holds it around a single contributorCapMergeRecheck() call (agent-action-executor.ts ~572-590).
But maybeCloseForContributorCapOnOpen holds the same lock across a much longer body (src/queue/processors.ts ~2770-2853): token mint, isBelowAccountAgeThreshold, resolvePerRepoContributorCapMatch (GitHub calls), ensurePullRequestLabel, then the nested pr-actuation-lock and the full executeAgentMaintenanceActions (live-CI recheck + GitHub close).
Under GitHub rate-limit backoff that body exceeds 30s → Redis expires the lock while the holder is still inside the executor → a concurrent sibling PR's cap check (or the executor's pre-merge recheck) acquires it and evaluates the author's open-PR count before the in-flight close lands. Both act: a double cap-close, or a merge that pushes the author over cap — precisely the TOCTOU #7284 added this lock to close.
Fix
Either raise the cap-lock TTL to cover the early-close path (~600s, matching pr-actuation-lock), or stop holding it across the executor — hold it for the decision only and let the executor's own pre-merge cap recheck be the serialization point.
CONTRIBUTOR_CAP_LOCK_TTL_SECONDS = 30(src/queue/transient-locks.ts~170) was sized for the executor's pre-merge recheck, which holds it around a singlecontributorCapMergeRecheck()call (agent-action-executor.ts~572-590).But
maybeCloseForContributorCapOnOpenholds the same lock across a much longer body (src/queue/processors.ts~2770-2853): token mint,isBelowAccountAgeThreshold,resolvePerRepoContributorCapMatch(GitHub calls),ensurePullRequestLabel, then the nestedpr-actuation-lockand the fullexecuteAgentMaintenanceActions(live-CI recheck + GitHub close).Under GitHub rate-limit backoff that body exceeds 30s → Redis expires the lock while the holder is still inside the executor → a concurrent sibling PR's cap check (or the executor's pre-merge recheck) acquires it and evaluates the author's open-PR count before the in-flight close lands. Both act: a double cap-close, or a merge that pushes the author over cap — precisely the TOCTOU #7284 added this lock to close.
Fix
Either raise the cap-lock TTL to cover the early-close path (~600s, matching
pr-actuation-lock), or stop holding it across the executor — hold it for the decision only and let the executor's own pre-merge cap recheck be the serialization point.