Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gittensor/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class Issue:
discovery_time_decay_multiplier: float = 1.0
discovery_credibility_multiplier: float = 1.0
discovery_open_issue_spam_multiplier: float = 1.0
discovery_label_multiplier: float = 1.0

@property
def is_transferred(self) -> bool:
Expand Down
11 changes: 11 additions & 0 deletions gittensor/validator/issue_discovery/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
calculate_open_issue_spam_multiplier,
check_issue_eligibility,
)
from gittensor.validator.oss_contributions.label_resolution import resolve_highest_label_multiplier
from gittensor.validator.oss_contributions.mirror.adapters import mirror_files_to_legacy
from gittensor.validator.oss_contributions.mirror.scoring import (
calculate_base_score_for_pr_files,
Expand Down Expand Up @@ -555,6 +556,7 @@ def _finalize_repo_issue_scores(
issue.discovery_open_issue_spam_multiplier = spam_mult
issue.discovery_earned_score = round(
issue.discovery_base_score
* issue.discovery_label_multiplier
* issue.discovery_time_decay_multiplier
* issue.discovery_review_quality_multiplier
* issue.discovery_credibility_multiplier
Expand Down Expand Up @@ -753,4 +755,13 @@ def _mirror_issue_for_scoring(
2,
)

trusted = repo_config.trusted_label_pipeline
candidate_names = [
(label.name or '').lower()
for label in solving_pr.labels
if label.name and (trusted or label.actor_association in MAINTAINER_ASSOCIATIONS)
]
_, label_multiplier = resolve_highest_label_multiplier(candidate_names, repo_config)
adapted.discovery_label_multiplier = label_multiplier

return adapted
164 changes: 163 additions & 1 deletion tests/validator/issue_discovery/test_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
run_issue_discovery = scan_module.run_issue_discovery
_classify_issue = scan_module._classify_issue
_build_solving_pr_cache = scan_module._build_solving_pr_cache
_mirror_issue_for_scoring = scan_module._mirror_issue_for_scoring
CachedSolvingPR = scan_module.CachedSolvingPR
MirrorIssue = mirror_models.MirrorIssue
MirrorIssuesResponse = mirror_models.MirrorIssuesResponse
MirrorPullRequest = mirror_models.MirrorPullRequest
MirrorPullRequestFilesResponse = mirror_models.MirrorPullRequestFilesResponse
MirrorSolvingPR = mirror_models.MirrorSolvingPR
MirrorRequestError = mirror_client_mod.MirrorRequestError
MinerEvaluation = classes.MinerEvaluation
MinerEvaluationCache = classes.MinerEvaluationCache
Expand Down Expand Up @@ -112,6 +114,7 @@ def _issue_dict(
repo: str = 'entrius/gittensor-ui',
created_at: str = '2026-04-01T00:00:00Z',
author_association: str = 'CONTRIBUTOR',
solving_pr_labels: Optional[list] = None,
) -> dict:
sp = None
if solved_by_pr:
Expand All @@ -125,7 +128,7 @@ def _issue_dict(
'head_sha': 'h',
'base_sha': 'b',
'merge_base_sha': 'mb',
'labels': [],
'labels': solving_pr_labels or [],
'review_summary': {'maintainer_changes_requested_count': 0},
}
return {
Expand Down Expand Up @@ -1435,3 +1438,162 @@ def test_dev_mode_bypasses_maintainer_skip(self, monkeypatch):
)

assert eval_.total_solved_issues == 1


# ============================================================================
# Repository label policy applied to solving-PR discovery scoring
# ============================================================================


class TestMirrorIssueForScoringLabelMultiplier:
"""Unit tests: _mirror_issue_for_scoring resolves discovery_label_multiplier
from solving_pr.labels using the same trust-gate logic as OSS PR scoring."""

def test_zero_default_multiplier_applied_when_no_labels(self):
issue = MirrorIssue.from_dict(_issue_dict())
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
default_label_multiplier=0.0,
label_multipliers={'benchmark-improvement': 1.0},
)
result = _mirror_issue_for_scoring(issue, issue.solving_pr, repo_config, base_score=1.0)
assert result is not None
assert result.discovery_label_multiplier == pytest.approx(0.0)

def test_matching_label_overrides_zero_default_multiplier(self):
label = {'name': 'benchmark-improvement', 'actor_association': 'OWNER'}
issue = MirrorIssue.from_dict(_issue_dict(solving_pr_labels=[label]))
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
default_label_multiplier=0.0,
label_multipliers={'benchmark-improvement': 1.0},
)
result = _mirror_issue_for_scoring(issue, issue.solving_pr, repo_config, base_score=1.0)
assert result is not None
assert result.discovery_label_multiplier == pytest.approx(1.0)

def test_downweight_label_sets_multiplier(self):
label = {'name': 'refactor', 'actor_association': 'OWNER'}
issue = MirrorIssue.from_dict(_issue_dict(solving_pr_labels=[label]))
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
label_multipliers={'refactor': 0.25},
)
result = _mirror_issue_for_scoring(issue, issue.solving_pr, repo_config, base_score=1.0)
assert result is not None
assert result.discovery_label_multiplier == pytest.approx(0.25)

def test_untrusted_actor_label_falls_back_to_default_multiplier(self):
label = {'name': 'benchmark-improvement', 'actor_association': 'CONTRIBUTOR'}
issue = MirrorIssue.from_dict(_issue_dict(solving_pr_labels=[label]))
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=False,
default_label_multiplier=0.0,
label_multipliers={'benchmark-improvement': 1.0},
)
result = _mirror_issue_for_scoring(issue, issue.solving_pr, repo_config, base_score=1.0)
assert result is not None
assert result.discovery_label_multiplier == pytest.approx(0.0)


class TestLabelPolicyIssueDiscovery:
"""Integration tests: repository label policy flows through run_issue_discovery
to issue_discovery_score via solving_pr.labels."""

def _seven_issues(self, solving_pr_labels=None):
return [
_issue_dict(issue_number=50 + i, solved_by_pr=100 + i, solving_pr_labels=solving_pr_labels)
for i in range(7)
]

def _seed(self, uid=2, base_score=42.0):
seed = MinerEvaluation(uid=uid, hotkey='hk2', github_id='seed')
seed.merged_prs = [_scored_mirror_pr('entrius/gittensor-ui', 100 + i, base_score=base_score) for i in range(7)]
return seed

def test_zero_default_multiplier_unlabeled_solving_prs_earn_zero_score(self):
client = Mock()
client.get_miner_issues.return_value = _response(self._seven_issues())
eval_ = _eval()
seed = self._seed()
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
default_label_multiplier=0.0,
label_multipliers={'benchmark-improvement': 1.0},
)
_run(
run_issue_discovery(
{1: eval_, 2: seed},
{'entrius/gittensor-ui': repo_config},
_EMPTY_LANGS,
_EMPTY_TOKEN_CONFIG,
client=client,
)
)
assert eval_.is_issue_eligible is True
assert eval_.issue_discovery_score == pytest.approx(0.0)
assert all(i.discovery_label_multiplier == pytest.approx(0.0) for i in eval_.issue_discovery_issues)

def test_matching_label_earns_nonzero_score_with_zero_default_multiplier(self):
label = [{'name': 'benchmark-improvement', 'actor_association': 'OWNER'}]
client = Mock()
client.get_miner_issues.return_value = _response(self._seven_issues(solving_pr_labels=label))
eval_ = _eval()
seed = self._seed()
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
default_label_multiplier=0.0,
label_multipliers={'benchmark-improvement': 1.0},
)
_run(
run_issue_discovery(
{1: eval_, 2: seed},
{'entrius/gittensor-ui': repo_config},
_EMPTY_LANGS,
_EMPTY_TOKEN_CONFIG,
client=client,
)
)
assert eval_.is_issue_eligible is True
assert eval_.issue_discovery_score > 0.0
assert all(i.discovery_label_multiplier == pytest.approx(1.0) for i in eval_.issue_discovery_issues)

def test_downweight_label_reduces_discovery_score(self):
"""A refactor=0.25 label on the solving PR sets discovery_label_multiplier=0.25
on each scored issue and reduces the aggregate discovery score vs unlabeled."""
label = [{'name': 'refactor', 'actor_association': 'OWNER'}]
repo_config = RepositoryConfig(
emission_share=0.5,
trusted_label_pipeline=True,
label_multipliers={'refactor': 0.25},
)

def _run_discovery(issues):
client = Mock()
client.get_miner_issues.return_value = _response(issues)
ev = _eval(uid=1, github_id='999')
seed = MinerEvaluation(uid=2, hotkey='hk2', github_id='seed')
seed.merged_prs = [_scored_mirror_pr('entrius/gittensor-ui', 100 + i) for i in range(7)]
_run(
run_issue_discovery(
{1: ev, 2: seed},
{'entrius/gittensor-ui': repo_config},
_EMPTY_LANGS,
_EMPTY_TOKEN_CONFIG,
client=client,
)
)
return ev

ev_unlabeled = _run_discovery(self._seven_issues())
ev_labeled = _run_discovery(self._seven_issues(solving_pr_labels=label))

assert ev_unlabeled.issue_discovery_score > 0.0
assert ev_labeled.issue_discovery_score < ev_unlabeled.issue_discovery_score
assert all(i.discovery_label_multiplier == pytest.approx(0.25) for i in ev_labeled.issue_discovery_issues)
Loading