feat(miner): wire claim-conflict resolution end-to-end - #5480
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 007aae3 | Commit Preview URL Branch Preview URL |
Jul 12 2026, 10:54 PM |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Closes #4848 claim-adjudication.js's isDuplicateClusterWinnerByClaim wrapper was correct and well-tested in isolation but had no caller that assembled a real competing-claims set from GitHub -- a genuine duplicate claim had no automated resolution path. checkSubmissionFreshness already catches the common pre-submission case (aborting before open_pr if another author's PR already references the issue), but that check can only see what's public at the moment it runs. Two miners racing closely enough that both pass their own freshness check before either's PR exists yet is a genuine TOCTOU window freshness cannot close. claim-conflict-resolver.js is the post-submission reconciliation for exactly that window: once this miner's PR is real and public, it fetches the live issue snapshot, assembles the real competing-claims set (every other OPEN PR referencing the issue, excluding this miner's own), and adjudicates via the existing claim-adjudication.js. When this miner's claim loses, it closes its own just-opened PR (never anyone else's) via a new close_pr local-write spec, citing the winner. Fails open (never closes anything) when the live snapshot can't be fetched. Wired into attempt-cli.js right after a real "submitted" outcome, using the miner's own real claim-ledger claimedAt for its side of the election and each competing PR's real GitHub createdAt as the best publicly-observable proxy for a third-party PR (documented asymmetry -- the maintainer gate's own "first observed" semantics need a continuous server-side observation history this stateless client-side tool doesn't have). Extracted the shared PR-number-from-exec-result parser (previously private to loop-cli.js) into pr-number-parse.js so both call sites agree on exactly one implementation.
536453a to
007aae3
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5480 +/- ##
=======================================
Coverage 94.73% 94.74%
=======================================
Files 561 563 +2
Lines 44794 44823 +29
Branches 14667 14669 +2
=======================================
+ Hits 42437 42467 +30
+ Misses 1622 1621 -1
Partials 735 735
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-12 23:00:40 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
[BETA] Chat with GittensoryAsk Gittensory a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://gittensory.aethereal.dev/docs/gittensory-commands 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Closes #4848
Summary
claim-adjudication.js'sadjudicateSoftClaim(wrapping the engine'sisDuplicateClusterWinnerByClaim) was correct and well-tested in isolation but had no real caller assembling a competing-claims set from GitHub -- a genuine duplicate claim had no automated resolution path.checkSubmissionFreshnessalready catches the common pre-submission case (aborts beforeopen_prif another author's PR already references the issue), but that's a point-in-time check -- it can't see a competing PR that doesn't exist yet. Two miners racing closely enough that both pass their own freshness check before either's PR exists is a genuine TOCTOU window freshness cannot close.claim-conflict-resolver.jsis the post-submission reconciliation for exactly that window: once this miner's own PR is real and public, it fetches the live issue snapshot, assembles every OTHER open PR referencing the issue (excluding this miner's own), and adjudicates via the existing, unchangedclaim-adjudication.js. When this miner's claim loses, it closes its own just-opened PR (never anyone else's) via a newclose_prlocal-write spec, citing the winner. Fails open (never closes anything) when the live snapshot can't be fetched.attempt-cli.js, only on a realoutcome === "submitted". Uses the miner's own real claim-ledgerclaimedAtfor its side of the election; each competing PR's real GitHubcreatedAtstands in for the competing side (documented asymmetry -- the maintainer gate's own "first observed" election needs a continuous server-side observation history this stateless client-side tool doesn't have for a PR it doesn't own).fetchLiveIssueSnapshot's GraphQL query with each referencing PR'screatedAt.buildClosePrSpecto the engine'slocal-write-tools.ts, mirroring the existingbuildOpenPrSpecpattern.loop-cli.js) intopr-number-parse.jsso both call sites agree on one implementation.Test plan
tsc --noEmit --incremental falseclean (root +packages/gittensory-engine)npm run build --workspace @jsonbored/gittensory-minerclean (new files added to the check-list)npx vitest run test/unit test/contract-- all passing (one unrelated, pre-existing macOS-only symlink-resolution test failure inminer-coding-task-spec.test.ts, from an already-merged, unrelated PR -- not touched by this diff, and Linux CI runners don't hit it; will file/fix separately)resolveClaimConflicthas dedicated tests for two simulated competing claims, both a win and a loss, verifying the losing claim's PR is really closed with a realclose_prwrite citing the winnerclaim-conflict-resolver.js,pr-number-parse.js,local-write-tools.ts,live-issue-snapshot.js, and the new block inattempt-cli.jsnpm run docs:drift-check/manifest:drift-check/engine-parity:drift-checkall cleannpm audit --audit-level=moderate-- 0 vulnerabilitiesgit diff --checkclean