Skip to content

feat(miner-plan): adjudicate competing soft-claims via the existing duplicate-winner election #4291

Description

@JSONbored

isDuplicateClusterWinnerByClaim(pr: DuplicateClaimMember, openSiblings: DuplicateClaimMember[]): boolean (packages/gittensory-engine/src/duplicate-winner.ts:57) already exists, is fully tested (packages/gittensory-engine/test/duplicate-winner.test.ts), and is explicitly earmarked for this: the module's own header comment says "SECOND CONSUMER (#2278): this module is intentionally engine-hosted (not src/-only) because its election logic is reusable for the miner's own soft-claim adjudication — deciding which of several miners claiming the same issue proceeds. A future contributor wiring the miner's local claim ledger should import this module rather than reimplementing the election rule, so both the maintainer gate and the miner agree on exactly one winner by construction" (duplicate-winner.ts:24-28). The claim-ledger module's own comment points at the same gap from the other side: "This table ... does NOT itself decide who 'wins' a contested issue — that adjudication (reusing isDuplicateClusterWinnerByClaim from @jsonbored/gittensory-engine) is out of scope for this foundation-phase issue" (packages/gittensory-miner/lib/claim-ledger.js:81-83, referencing #3355 — the now-closed foundation issue that deliberately deferred exactly this work).

Today isDuplicateClusterWinnerByClaim has exactly one family of real callers, all maintainer-side: src/signals/engine.ts:4605, src/rules/advisory.ts:724, src/queue/processors.ts:7979 and :8967 — each builds a DuplicateClaimMember[] from real OPEN GitHub PRs that link the same issue and elects a winner among them.

Two concrete things worth flagging before this is picked up:

  1. The local claim ledger alone can't supply the adjudication input. claim-ledger.js's own header says it is "a 100% client-side record ... The database only lives on this machine; this module never uploads, syncs, or phones home" (lines 6-10). One miner's local SQLite rows have zero visibility into what OTHER miner instances have claimed. So "adjudicate competing soft-claims" can't mean comparing rows within a single local ledger — the competing-claim signal has to come from something publicly observable, most plausibly the same kind of already-open-PR data the maintainer-side callers above already assemble (an issue with multiple open PRs linking it is itself the public signal of a contested claim). Sourcing that view is part of this issue's scope, not a solved prerequisite.
  2. Field-name mismatch to bridge. DuplicateClaimMember needs linkedIssueClaimedAt (duplicate-winner.ts:33); the local claim ledger's rows expose claimedAt (packages/gittensory-miner/lib/claim-ledger.js:59-68, rowToClaim). Whatever assembles the election input needs to map one to the other explicitly — they are not interchangeable by accident of naming.

There's also a display-only helper, resolveDuplicateClusterWinnerNumber (duplicate-winner.ts:87-94), for naming the winner in messaging without driving the actual go/no-go decision — worth using if this surfaces winner info to the miner operator (e.g. "you lost this claim to PR #N").

Deliverables

  • A function (new module, e.g. packages/gittensory-miner/lib/claim-adjudication.js or similar) that builds DuplicateClaimMember[] input for a contested issue from publicly-observable competing-PR data and calls isDuplicateClusterWinnerByClaim
  • Explicit mapping from whatever local/observed claim-timing field is available to linkedIssueClaimedAt — do not assume the field names line up
  • Decide how this plugs into the claim CLI (sibling issue: claim-ledger commands) — e.g. claim list flagging contested claims, or claim claim warning when the election would not currently favor this miner
  • Use resolveDuplicateClusterWinnerNumber (display-only) if winner identity needs to be surfaced to the operator, keeping the actual go/no-go decision driven only by isDuplicateClusterWinnerByClaim
  • Tests covering: this miner wins, this miner loses, no competing claim (trivial winner), and the fail-closed cases isDuplicateClusterWinnerByClaim already defines for sparse/invalid claim timestamps

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions