Context
packages/loopover-miner/lib/claim-conflict-resolver.js (#4848) is the post-submission reconciliation for the TOCTOU window submission-freshness-check.js's pre-submission check cannot close: once this miner's own PR is real and public, it checks whether another open PR also claims the same issue.
The module's own header documents a real, acknowledged gap: it "checks GitHub's live state immediately after submission. A competing PR that exists but hasn't yet propagated through GitHub's own search/GraphQL indexing in that instant would be invisible to this one-shot check — there is no retry/backoff here, which would be its own separate scope." A genuine competing claim can therefore go undetected purely because of GitHub API/index propagation lag, not because the conflict didn't exist.
Note: the actual PR-closing action this module feeds into stays maintainer-gated per the existing contributor-vs-maintainer safety framework (#4833's scope note) — this issue is scoped to the detection step (making the live-state check itself more reliable), not to the write-authorization boundary, which is unaffected.
Requirements
- Add bounded retry/backoff to the live competing-claims check this module performs post-submission (read the module directly for the exact call site — the snapshot fetch that currently runs once) — a small number of attempts with real backoff, not an unbounded retry loop.
- Follow this codebase's existing retry/backoff convention if one already exists elsewhere in
packages/loopover-miner/packages/loopover-engine (check before inventing a new one — rejection-signal.js's own header mentions bounding fan-out for a related concern, and other modules in this package may have a shared retry helper).
- The retry window must be bounded and fast enough not to meaningfully delay the attempt pipeline — this is closing a narrow propagation-lag gap, not adding a long wait.
- Pure/testable: the retry logic itself should be injectable/mockable the same way this module's existing fetch dependencies are, so tests don't need real network delay.
- Do not change the module's existing claim-adjudication logic (
adjudicateSoftClaim) or its close-decision output shape — this issue is scoped to the detection/fetch step only.
Deliverables
Test Coverage Requirements
99%+ patch coverage (branch-counted), including the retry-then-succeed and retry-exhaustion branches.
Expected Outcome
A competing claim that exists at submission time but hasn't yet propagated through GitHub's search/GraphQL index is now reliably detected within the bounded retry window, closing the propagation-lag blind spot this module's own header documents.
Links & Resources
packages/loopover-miner/lib/claim-conflict-resolver.js (header's "EVENTUAL CONSISTENCY" section, the one-shot check to make retry-capable)
packages/loopover-miner/lib/rejection-signal.js (a related bounded-fan-out precedent, DEFAULT_MAX_REJECTION_HISTORY_CHECKS)
packages/loopover-miner/lib/submission-freshness-check.js (the pre-submission sibling check this module complements)
Context
packages/loopover-miner/lib/claim-conflict-resolver.js(#4848) is the post-submission reconciliation for the TOCTOU windowsubmission-freshness-check.js's pre-submission check cannot close: once this miner's own PR is real and public, it checks whether another open PR also claims the same issue.The module's own header documents a real, acknowledged gap: it "checks GitHub's live state immediately after submission. A competing PR that exists but hasn't yet propagated through GitHub's own search/GraphQL indexing in that instant would be invisible to this one-shot check — there is no retry/backoff here, which would be its own separate scope." A genuine competing claim can therefore go undetected purely because of GitHub API/index propagation lag, not because the conflict didn't exist.
Note: the actual PR-closing action this module feeds into stays maintainer-gated per the existing contributor-vs-maintainer safety framework (
#4833's scope note) — this issue is scoped to the detection step (making the live-state check itself more reliable), not to the write-authorization boundary, which is unaffected.Requirements
packages/loopover-miner/packages/loopover-engine(check before inventing a new one —rejection-signal.js's own header mentions bounding fan-out for a related concern, and other modules in this package may have a shared retry helper).adjudicateSoftClaim) or its close-decision output shape — this issue is scoped to the detection/fetch step only.Deliverables
Test Coverage Requirements
99%+ patch coverage (branch-counted), including the retry-then-succeed and retry-exhaustion branches.
Expected Outcome
A competing claim that exists at submission time but hasn't yet propagated through GitHub's search/GraphQL index is now reliably detected within the bounded retry window, closing the propagation-lag blind spot this module's own header documents.
Links & Resources
packages/loopover-miner/lib/claim-conflict-resolver.js(header's "EVENTUAL CONSISTENCY" section, the one-shot check to make retry-capable)packages/loopover-miner/lib/rejection-signal.js(a related bounded-fan-out precedent,DEFAULT_MAX_REJECTION_HISTORY_CHECKS)packages/loopover-miner/lib/submission-freshness-check.js(the pre-submission sibling check this module complements)