Skip to content

feat: replace uniqueness multiplier with binary pioneer reward mechanism#248

Open
eureka928 wants to merge 8 commits intoentrius:testfrom
eureka928:feat/pioneer-reward-mechanism
Open

feat: replace uniqueness multiplier with binary pioneer reward mechanism#248
eureka928 wants to merge 8 commits intoentrius:testfrom
eureka928:feat/pioneer-reward-mechanism

Conversation

@eureka928
Copy link
Contributor

@eureka928 eureka928 commented Mar 1, 2026

Summary

Replace the repository uniqueness multiplier with a binary pioneer reward that gives the first miner to land a quality merged PR on an untouched repo a substantial score bonus. All followers score at 1.0x, creating a sharp incentive to explore rather than pile on.

Problem

Despite 1400+ repos across all tiers, contribution activity clusters on a small subset. The existing repository_uniqueness_multiplier gives a shared bonus that shrinks as miners pile on, but the reward differential isn't compelling enough to change behavior.

Solution

A binary pioneer reward: exactly one winner per repo, decided by earliest eligible merge timestamp. The multiplier is a pure function with two constants:

def calculate_pioneer_multiplier(rank: int, pioneered_repo_count: int = 1) -> float:
    if rank != 1:
        return 1.0
    damping = max(1, pioneered_repo_count) ** PIONEER_MULTI_REPO_DAMPING
    return 1.0 + PIONEER_BASE_BONUS / damping

How it works

  1. For each repo in the scoring window, collect all merged PRs that pass the quality gate (token_score >= MIN_TOKEN_SCORE_FOR_BASE_SCORE, valid tier config, non-null merged_at).
  2. Per miner, keep only their earliest eligible PR on that repo.
  3. Sort by (merged_at, pr_number) — position 1 is the pioneer. The pioneer's specific PR gets the bonus; all other PRs (followers, later PRs from the same miner) get 1.0x.
  4. If a miner pioneers multiple repos in the same cycle, the bonus shrinks per repo via square-root decay (diminishing returns).
Pioneered repos Pioneer multiplier Follower multiplier
1 2.50x 1.0x
4 1.75x 1.0x
9 1.50x 1.0x

Constants: PIONEER_BASE_BONUS = 1.5, PIONEER_MULTI_REPO_DAMPING = 0.5

Design decisions

  • Quality gate — PRs below MIN_TOKEN_SCORE_FOR_BASE_SCORE are excluded from pioneer ranking entirely. This prevents low-effort snipe merges from capturing pioneer status. If a low-quality PR merges first, the next quality contributor becomes the pioneer instead.
  • Single-PR scoping — the bonus applies only to the one PR that earned pioneer status, not to follow-up PRs from the same miner on that repo. This keeps the incentive targeted.
  • Multi-repo damping — a miner who rushes to pioneer 4 repos gets 1.75x each, while a miner who pioneers 1 repo gets 2.50x — 43% higher per repo. This discourages land-grab strategies.
  • Deterministic tie-breaking — same-second merges (e.g. from merge bots processing a queue) are resolved by lower PR number. Single-winner design avoids multiplying the bonus on repos with aggressive merge bots.
  • Logging — each PR's multiplier log shows pioneer=2.50(P) for pioneers and pioneer=1.00(F) for followers, making scoring transparent in validator output.

Why PIONEER_BASE_BONUS = 1.5 (2.50x)?

Strong enough to overcome the risk premium of exploring an unknown codebase. With multi-repo damping, pioneering 4 repos brings it to 1.75x — still meaningful but not exploitable. Game theory research (congestion games, Tullock contests) shows binary first-mover rewards are optimal for maximizing the number of distinct repos with at least one contributor.

Related Issues

Closes #240

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other (describe below)

Changes

File Change
gittensor/constants.py Add PIONEER_BASE_BONUS = 1.5 and PIONEER_MULTI_REPO_DAMPING = 0.5; remove UNIQUE_PR_BOOST
gittensor/classes.py Replace repository_uniqueness_multiplier with pioneer_multiplier and pioneer_rank; update calculate_final_earned_score with (P)/(F) tag
gittensor/validator/evaluation/scoring.py Add calculate_pioneer_ranks(), count_pioneered_repos(), calculate_pioneer_multiplier(); update finalize_miner_scores(); remove calculate_uniqueness_multiplier() and count_repository_contributors()
gittensor/validator/storage/queries.py Replace repository_uniqueness_multiplier column with pioneer_multiplier and pioneer_rank in INSERT/UPSERT
gittensor/validator/storage/repository.py Update storage tuple to write pioneer_multiplier and pioneer_rank
tests/validator/conftest.py Extend PRBuilder.create() to accept merged_at and uid parameters
tests/validator/test_pioneer_multiplier.py 18 tests across 5 classes (326 lines)

DB migration

ALTER TABLE pull_requests ADD COLUMN pioneer_multiplier FLOAT DEFAULT 1.0;
ALTER TABLE pull_requests ADD COLUMN pioneer_rank INTEGER DEFAULT 0;
UPDATE pull_requests SET pioneer_multiplier = repository_uniqueness_multiplier;
ALTER TABLE pull_requests DROP COLUMN repository_uniqueness_multiplier;

Evidence

Scenario A: Clustering Reality

Setup: 10 miners, 3 repos — 6 miners on repo-X, 3 on repo-Y, 1 on repo-Z. All PRs: base_score=30.

BEFORE (old uniqueness — all miners on same repo get same multiplier):

repo-X   6 miners   × 1.37   →  41.10 each   →  246.60 total
repo-Y   3 miners   × 1.59   →  47.76 each   →  143.28 total
repo-Z   1 miner    × 1.74   →  52.20 each   →   52.20 total
                                          Network:  442.08

AFTER (binary pioneer — only rank 1 gets bonus):

repo-X   Pioneer    × 2.50   →  75.00         →   75.00
         5 Follow.  × 1.00   →  30.00 each    →  150.00
repo-Y   Pioneer    × 2.50   →  75.00         →   75.00
         2 Follow.  × 1.00   →  30.00 each    →   60.00
repo-Z   Pioneer    × 2.50   →  75.00         →   75.00
                                          Network:  435.00

Under the old system, all 6 miners on repo-X scored identically (41.10 each). Now the pioneer scores 75.00 while followers score 30.00 — clear incentive to find untouched repos instead.

Scenario B: Ideal Spread — 10 miners, 10 repos (1 each)

                    Old uniqueness         Binary pioneer
                    ──────────────         ──────────────
Per-miner score     52.20 (× 1.74)        75.00 (× 2.50)
Network total       522.00                 750.00

Spread incentive is 1.4x stronger than the old system.

Scenario C: Land-grab vs Focused Pioneer

Miner A   repo-1   Pioneer (1 repo)    × 2.50   →  75.00
Miner B   repo-2   Pioneer (4 repos)   × 1.75   →  52.50
Miner B   repo-3   Pioneer (4 repos)   × 1.75   →  52.50
Miner B   repo-4   Pioneer (4 repos)   × 1.75   →  52.50
Miner B   repo-5   Pioneer (4 repos)   × 1.75   →  52.50
Miner C   repo-1   Follower            × 1.00   →  30.00

Per-PR efficiency: Focused (75.00) > Land-grab (52.50) > Follower (30.00)

Scenario D: Tie-breaking (merge bot queue)

Two PRs merged at the exact same second by a merge bot:

Miner A   PR #3    Pioneer (lower PR number)   × 2.50   →  75.00
Miner B   PR #20   Follower                    × 1.00   →  30.00

Single winner — prevents merge bot queues from multiplying the bonus on one repo.

Testing

  • Tests added/updated
  • Manually tested

Test coverage (18 tests, 5 classes)

Class Tests What it covers
TestCalculatePioneerMultiplier 4 Formula, binary behavior, multi-repo damping
TestCalculatePioneerRanks 6 Single/multi miner, tie-breaking by PR number, earliest merge, independent repos, empty evals
TestQualityGate 2 Low token_score excluded, no tier config excluded
TestFinalization 5 Pioneer/follower, single-PR scoping, damping, snipe blocked, count
TestPioneerIncentiveEvidence 1 5 miners exploring > 5 miners piling on (5x more bonus)

Test results

$ pytest tests/validator/test_pioneer_multiplier.py -v
18 passed in 0.04s

$ pytest tests/ -v
326 passed in 1.01s

Full stack confirmation

constants.py       PIONEER_BASE_BONUS, PIONEER_MULTI_REPO_DAMPING
classes.py         pioneer_multiplier, pioneer_rank fields
scoring.py         calculate_pioneer_ranks(), calculate_pioneer_multiplier(), count_pioneered_repos()
queries.py         pioneer_multiplier, pioneer_rank columns (INSERT + UPSERT)
repository.py      pioneer_multiplier, pioneer_rank in values tuple
logging            pioneer=2.50(P) / pioneer=1.00(F) in score finalization
tests              18 tests, 326 lines

No orphaned columns, dead code, or untested logic.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Changes are documented (if applicable)

@eureka928 eureka928 force-pushed the feat/pioneer-reward-mechanism branch from ccfb48d to faa7c7b Compare March 2, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rewarding the pioneers

7 participants