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
⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
packages/loopover-miner/lib/claim-ledger.ts's recordClaim uses a single atomic INSERT ... ON CONFLICT ... DO UPDATE ... WHERE miner_claims.status <> 'active' statement (around
lines 224-233), deliberately designed so that re-recording a claim for an issue that is ALREADY
actively claimed is a true no-op — its own comment says so explicitly: "The WHERE status <> 'active' guard makes re-claiming an already-active issue a true no-op (no row churn), never a
duplicate row."
claimIssueWithinCap (around lines 341-370), the concurrency-capped wrapper attempt-cli.ts
actually calls, never reaches that no-op logic for a same-issue re-claim once the repo is at its
concurrency cap — the cap check runs FIRST, unconditionally, before any check of whether the
specific (repoFullName, issueNumber) pair is the SAME claim already active:
Reproduction: with cap = 1, claim issue #1 in a repo (activeClaimCount 0 → 1). Call claimIssueWithinCap again for the SAME issue #1 in the SAME repo: activeClaimCount is now 1, 1 >= cap(1) is true, so the function returns { claimed: false, ... } — even though recordClaim's own WHERE status <> 'active' guard would have made re-recording issue #1 a
genuine no-op that never increases the active count, and even though the repo is not actually over
capacity in any real sense (the "new" claim IS the claim already counted).
This is directly reachable via this module's own documented crash-recovery path: attempt-cli.ts (lines ~791-825) calls claimIssueWithinCap per attempt and only releases the
claim in a finally block on a terminal outcome — a hard process kill mid-attempt leaves the claim
row active (until the ledger's DEFAULT_MAX_CLAIM_AGE_MS sweep expires it, up to 14 days later).
A retry of the SAME issue while its repo sits at cap is incorrectly rejected as max_concurrent_claims_exceeded, even though resuming/re-affirming its own already-active claim
should succeed.
test/unit/miner-claim-ledger.test.ts's describe("claimIssueWithinCap...") block (lines
~543-631) tests under-cap claims, at-cap rejection of a DIFFERENT issue, per-repo isolation,
cross-process racing, and sweep-then-claim — but never tests re-claiming the SAME (repo, issue)
pair while already at cap.
Requirements
In claimIssueWithinCap, check whether an existing ACTIVE claim row already exists for the exact (repoFullName, issueNumber) pair being claimed BEFORE applying the cap gate. If one exists,
proceed to call recordClaim (which will correctly no-op via its own WHERE status <> 'active'
guard) regardless of the current activeClaimCount relative to cap — do not reject a same-
issue re-claim as over-cap.
For any OTHER issue (a genuinely new claim, or a claim on a different issue number), the existing
cap-gate behavior must be unchanged — this issue only fixes the same-issue-re-claim case, not the
cap enforcement itself.
Keep the fix inside the existing single BEGIN IMMEDIATE transaction — do not introduce a second
transaction or a read outside the lock that could reintroduce the race the function's own comment
says the current design closes.
Deliverables
claimIssueWithinCap no longer rejects a re-claim of an issue that is already the caller's
own active claim, even when the repo's active-claim count equals or exceeds cap.
claimIssueWithinCap still correctly rejects a claim for a DIFFERENT issue once the repo is
at cap (the pre-existing, still-required behavior).
A new regression test asserting that, in the same at-cap scenario, claiming a DIFFERENT issue chore(release): prepare public gittensory launch #2 in the same repo still correctly returns { claimed: false, ... } — guarding against an
overly-broad fix that accidentally lets any claim through once one active claim exists.
Both regression tests and the fix are required in this single PR.
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ (branch-counted) on packages/loopover-miner/lib/**. Add
the new regression tests to test/unit/miner-claim-ledger.test.ts, inside or alongside the
existing describe("claimIssueWithinCap...") block (this package's tests live in the shared root test/ directory, not packages/loopover-miner/test/**). The new same-issue-existing-active-claim
branch must be exercised, along with the pre-existing different-issue-at-cap rejection branch
remaining covered.
Expected Outcome
A miner process that crashes mid-attempt and is retried against the SAME issue can successfully
re-affirm its own already-active claim even when the repo is at its concurrency cap — matching recordClaim's own documented idempotent-re-claim guarantee, which claimIssueWithinCap currently
defeats for exactly the crash-recovery scenario the claim ledger exists to support.
Context
packages/loopover-miner/lib/claim-ledger.ts'srecordClaimuses a single atomicINSERT ... ON CONFLICT ... DO UPDATE ... WHERE miner_claims.status <> 'active'statement (aroundlines 224-233), deliberately designed so that re-recording a claim for an issue that is ALREADY
actively claimed is a true no-op — its own comment says so explicitly: "The
WHERE status <> 'active'guard makes re-claiming an already-active issue a true no-op (no row churn), never aduplicate row."
claimIssueWithinCap(around lines 341-370), the concurrency-capped wrapperattempt-cli.tsactually calls, never reaches that no-op logic for a same-issue re-claim once the repo is at its
concurrency cap — the cap check runs FIRST, unconditionally, before any check of whether the
specific
(repoFullName, issueNumber)pair is the SAME claim already active:Reproduction: with
cap = 1, claim issue #1 in a repo (activeClaimCount0 → 1). CallclaimIssueWithinCapagain for the SAME issue #1 in the SAME repo:activeClaimCountis now 1,1 >= cap(1)is true, so the function returns{ claimed: false, ... }— even thoughrecordClaim's ownWHERE status <> 'active'guard would have made re-recording issue #1 agenuine no-op that never increases the active count, and even though the repo is not actually over
capacity in any real sense (the "new" claim IS the claim already counted).
This is directly reachable via this module's own documented crash-recovery path:
attempt-cli.ts(lines ~791-825) callsclaimIssueWithinCapper attempt and only releases theclaim in a
finallyblock on a terminal outcome — a hard process kill mid-attempt leaves the claimrow
active(until the ledger'sDEFAULT_MAX_CLAIM_AGE_MSsweep expires it, up to 14 days later).A retry of the SAME issue while its repo sits at cap is incorrectly rejected as
max_concurrent_claims_exceeded, even though resuming/re-affirming its own already-active claimshould succeed.
test/unit/miner-claim-ledger.test.ts'sdescribe("claimIssueWithinCap...")block (lines~543-631) tests under-cap claims, at-cap rejection of a DIFFERENT issue, per-repo isolation,
cross-process racing, and sweep-then-claim — but never tests re-claiming the SAME
(repo, issue)pair while already at cap.
Requirements
claimIssueWithinCap, check whether an existing ACTIVE claim row already exists for the exact(repoFullName, issueNumber)pair being claimed BEFORE applying the cap gate. If one exists,proceed to call
recordClaim(which will correctly no-op via its ownWHERE status <> 'active'guard) regardless of the current
activeClaimCountrelative tocap— do not reject a same-issue re-claim as over-cap.
cap-gate behavior must be unchanged — this issue only fixes the same-issue-re-claim case, not the
cap enforcement itself.
BEGIN IMMEDIATEtransaction — do not introduce a secondtransaction or a read outside the lock that could reintroduce the race the function's own comment
says the current design closes.
Deliverables
claimIssueWithinCapno longer rejects a re-claim of an issue that is already the caller'sown active claim, even when the repo's active-claim count equals or exceeds
cap.claimIssueWithinCapstill correctly rejects a claim for a DIFFERENT issue once the repo isat cap (the pre-existing, still-required behavior).
cap = 1, then callingclaimIssueWithinCapagain for the SAME issue feat(docs): add install site and mcp diagnostics #1 in the same repo, returns{ claimed: true, ... }(or the equivalent success shape reflecting the no-op re-affirm),not
{ claimed: false, ... }.chore(release): prepare public gittensory launch #2 in the same repo still correctly returns
{ claimed: false, ... }— guarding against anoverly-broad fix that accidentally lets any claim through once one active claim exists.
Both regression tests and the fix are required in this single PR.
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ (branch-counted) on
packages/loopover-miner/lib/**. Addthe new regression tests to
test/unit/miner-claim-ledger.test.ts, inside or alongside theexisting
describe("claimIssueWithinCap...")block (this package's tests live in the shared roottest/directory, notpackages/loopover-miner/test/**). The new same-issue-existing-active-claimbranch must be exercised, along with the pre-existing different-issue-at-cap rejection branch
remaining covered.
Expected Outcome
A miner process that crashes mid-attempt and is retried against the SAME issue can successfully
re-affirm its own already-active claim even when the repo is at its concurrency cap — matching
recordClaim's own documented idempotent-re-claim guarantee, whichclaimIssueWithinCapcurrentlydefeats for exactly the crash-recovery scenario the claim ledger exists to support.
Links & Resources
packages/loopover-miner/lib/claim-ledger.ts—recordClaim(~lines 224-233),claimIssueWithinCap(~lines 341-370).packages/loopover-miner/lib/attempt-cli.ts— the crash-recovery-relevant caller (~lines791-825).
test/unit/miner-claim-ledger.test.ts— the existingclaimIssueWithinCaptest block (~lines543-631) to extend.