2525run_issue_discovery = scan_module .run_issue_discovery
2626_classify_issue = scan_module ._classify_issue
2727_build_solving_pr_cache = scan_module ._build_solving_pr_cache
28+ _mirror_issue_for_scoring = scan_module ._mirror_issue_for_scoring
2829CachedSolvingPR = scan_module .CachedSolvingPR
2930MirrorIssue = mirror_models .MirrorIssue
3031MirrorIssuesResponse = mirror_models .MirrorIssuesResponse
3132MirrorPullRequest = mirror_models .MirrorPullRequest
3233MirrorPullRequestFilesResponse = mirror_models .MirrorPullRequestFilesResponse
34+ MirrorSolvingPR = mirror_models .MirrorSolvingPR
3335MirrorRequestError = mirror_client_mod .MirrorRequestError
3436MinerEvaluation = classes .MinerEvaluation
3537MinerEvaluationCache = classes .MinerEvaluationCache
@@ -111,6 +113,7 @@ def _issue_dict(
111113 last_edited_at : Optional [str ] = None ,
112114 repo : str = 'entrius/gittensor-ui' ,
113115 created_at : str = '2026-04-01T00:00:00Z' ,
116+ solving_pr_labels : Optional [list ] = None ,
114117) -> dict :
115118 sp = None
116119 if solved_by_pr :
@@ -124,7 +127,7 @@ def _issue_dict(
124127 'head_sha' : 'h' ,
125128 'base_sha' : 'b' ,
126129 'merge_base_sha' : 'mb' ,
127- 'labels' : [],
130+ 'labels' : solving_pr_labels or [],
128131 'review_summary' : {'maintainer_changes_requested_count' : 0 },
129132 }
130133 return {
@@ -1349,3 +1352,168 @@ def _score_with_emission_share(emission_share: float) -> float:
13491352 return evaluation .issue_discovery_score
13501353
13511354 assert _score_with_emission_share (0.1 ) == pytest .approx (_score_with_emission_share (0.9 ))
1355+
1356+
1357+ # ============================================================================
1358+ # Repository label policy applied to solving-PR discovery scoring
1359+ # ============================================================================
1360+
1361+
1362+ class TestMirrorIssueForScoringLabelMultiplier :
1363+ """Unit tests: _mirror_issue_for_scoring resolves discovery_label_multiplier
1364+ from solving_pr.labels using the same trust-gate logic as OSS PR scoring."""
1365+
1366+ def test_zero_default_multiplier_applied_when_no_labels (self ):
1367+ issue = MirrorIssue .from_dict (_issue_dict ())
1368+ repo_config = RepositoryConfig (
1369+ emission_share = 0.5 ,
1370+ trusted_label_pipeline = True ,
1371+ default_label_multiplier = 0.0 ,
1372+ label_multipliers = {'benchmark-improvement' : 1.0 },
1373+ )
1374+ result = _mirror_issue_for_scoring (issue , issue .solving_pr , repo_config , base_score = 1.0 )
1375+ assert result is not None
1376+ assert result .discovery_label_multiplier == pytest .approx (0.0 )
1377+
1378+ def test_matching_label_overrides_zero_default_multiplier (self ):
1379+ label = {'name' : 'benchmark-improvement' , 'actor_association' : 'OWNER' }
1380+ issue = MirrorIssue .from_dict (_issue_dict (solving_pr_labels = [label ]))
1381+ repo_config = RepositoryConfig (
1382+ emission_share = 0.5 ,
1383+ trusted_label_pipeline = True ,
1384+ default_label_multiplier = 0.0 ,
1385+ label_multipliers = {'benchmark-improvement' : 1.0 },
1386+ )
1387+ result = _mirror_issue_for_scoring (issue , issue .solving_pr , repo_config , base_score = 1.0 )
1388+ assert result is not None
1389+ assert result .discovery_label_multiplier == pytest .approx (1.0 )
1390+
1391+ def test_downweight_label_sets_multiplier (self ):
1392+ label = {'name' : 'refactor' , 'actor_association' : 'OWNER' }
1393+ issue = MirrorIssue .from_dict (_issue_dict (solving_pr_labels = [label ]))
1394+ repo_config = RepositoryConfig (
1395+ emission_share = 0.5 ,
1396+ trusted_label_pipeline = True ,
1397+ label_multipliers = {'refactor' : 0.25 },
1398+ )
1399+ result = _mirror_issue_for_scoring (issue , issue .solving_pr , repo_config , base_score = 1.0 )
1400+ assert result is not None
1401+ assert result .discovery_label_multiplier == pytest .approx (0.25 )
1402+
1403+ def test_untrusted_actor_label_falls_back_to_default_multiplier (self ):
1404+ label = {'name' : 'benchmark-improvement' , 'actor_association' : 'CONTRIBUTOR' }
1405+ issue = MirrorIssue .from_dict (_issue_dict (solving_pr_labels = [label ]))
1406+ repo_config = RepositoryConfig (
1407+ emission_share = 0.5 ,
1408+ trusted_label_pipeline = False ,
1409+ default_label_multiplier = 0.0 ,
1410+ label_multipliers = {'benchmark-improvement' : 1.0 },
1411+ )
1412+ result = _mirror_issue_for_scoring (issue , issue .solving_pr , repo_config , base_score = 1.0 )
1413+ assert result is not None
1414+ assert result .discovery_label_multiplier == pytest .approx (0.0 )
1415+
1416+
1417+ class TestLabelPolicyIssueDiscovery :
1418+ """Integration tests: repository label policy flows through run_issue_discovery
1419+ to issue_discovery_score via solving_pr.labels."""
1420+
1421+ def _seven_issues (self , solving_pr_labels = None ):
1422+ return [
1423+ _issue_dict (issue_number = 50 + i , solved_by_pr = 100 + i , solving_pr_labels = solving_pr_labels )
1424+ for i in range (7 )
1425+ ]
1426+
1427+ def _seed (self , uid = 2 , base_score = 42.0 ):
1428+ seed = MinerEvaluation (uid = uid , hotkey = 'hk2' , github_id = 'seed' )
1429+ seed .merged_prs = [
1430+ _scored_mirror_pr ('entrius/gittensor-ui' , 100 + i , base_score = base_score )
1431+ for i in range (7 )
1432+ ]
1433+ return seed
1434+
1435+ def test_zero_default_multiplier_unlabeled_solving_prs_earn_zero_score (self ):
1436+ client = Mock ()
1437+ client .get_miner_issues .return_value = _response (self ._seven_issues ())
1438+ eval_ = _eval ()
1439+ seed = self ._seed ()
1440+ repo_config = RepositoryConfig (
1441+ emission_share = 0.5 ,
1442+ trusted_label_pipeline = True ,
1443+ default_label_multiplier = 0.0 ,
1444+ label_multipliers = {'benchmark-improvement' : 1.0 },
1445+ )
1446+ _run (
1447+ run_issue_discovery (
1448+ {1 : eval_ , 2 : seed },
1449+ {'entrius/gittensor-ui' : repo_config },
1450+ _EMPTY_LANGS ,
1451+ _EMPTY_TOKEN_CONFIG ,
1452+ client = client ,
1453+ )
1454+ )
1455+ assert eval_ .is_issue_eligible is True
1456+ assert eval_ .issue_discovery_score == pytest .approx (0.0 )
1457+ assert all (i .discovery_label_multiplier == pytest .approx (0.0 ) for i in eval_ .issue_discovery_issues )
1458+
1459+ def test_matching_label_earns_nonzero_score_with_zero_default_multiplier (self ):
1460+ label = [{'name' : 'benchmark-improvement' , 'actor_association' : 'OWNER' }]
1461+ client = Mock ()
1462+ client .get_miner_issues .return_value = _response (self ._seven_issues (solving_pr_labels = label ))
1463+ eval_ = _eval ()
1464+ seed = self ._seed ()
1465+ repo_config = RepositoryConfig (
1466+ emission_share = 0.5 ,
1467+ trusted_label_pipeline = True ,
1468+ default_label_multiplier = 0.0 ,
1469+ label_multipliers = {'benchmark-improvement' : 1.0 },
1470+ )
1471+ _run (
1472+ run_issue_discovery (
1473+ {1 : eval_ , 2 : seed },
1474+ {'entrius/gittensor-ui' : repo_config },
1475+ _EMPTY_LANGS ,
1476+ _EMPTY_TOKEN_CONFIG ,
1477+ client = client ,
1478+ )
1479+ )
1480+ assert eval_ .is_issue_eligible is True
1481+ assert eval_ .issue_discovery_score > 0.0
1482+ assert all (i .discovery_label_multiplier == pytest .approx (1.0 ) for i in eval_ .issue_discovery_issues )
1483+
1484+ def test_downweight_label_reduces_discovery_score (self ):
1485+ """A refactor=0.25 label on the solving PR sets discovery_label_multiplier=0.25
1486+ on each scored issue and reduces the aggregate discovery score vs unlabeled."""
1487+ label = [{'name' : 'refactor' , 'actor_association' : 'OWNER' }]
1488+ repo_config = RepositoryConfig (
1489+ emission_share = 0.5 ,
1490+ trusted_label_pipeline = True ,
1491+ label_multipliers = {'refactor' : 0.25 },
1492+ )
1493+
1494+ def _run_discovery (issues ):
1495+ client = Mock ()
1496+ client .get_miner_issues .return_value = _response (issues )
1497+ ev = _eval (uid = 1 , github_id = '999' )
1498+ seed = MinerEvaluation (uid = 2 , hotkey = 'hk2' , github_id = 'seed' )
1499+ seed .merged_prs = [_scored_mirror_pr ('entrius/gittensor-ui' , 100 + i ) for i in range (7 )]
1500+ _run (
1501+ run_issue_discovery (
1502+ {1 : ev , 2 : seed },
1503+ {'entrius/gittensor-ui' : repo_config },
1504+ _EMPTY_LANGS ,
1505+ _EMPTY_TOKEN_CONFIG ,
1506+ client = client ,
1507+ )
1508+ )
1509+ return ev
1510+
1511+ ev_unlabeled = _run_discovery (self ._seven_issues ())
1512+ ev_labeled = _run_discovery (self ._seven_issues (solving_pr_labels = label ))
1513+
1514+ assert ev_unlabeled .issue_discovery_score > 0.0
1515+ assert ev_labeled .issue_discovery_score < ev_unlabeled .issue_discovery_score
1516+ assert all (
1517+ i .discovery_label_multiplier == pytest .approx (0.25 )
1518+ for i in ev_labeled .issue_discovery_issues
1519+ )
0 commit comments