From 9b2f541d9dc57e652c9680eaea651de3df191ac7 Mon Sep 17 00:00:00 2001 From: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com> Date: Tue, 30 Jun 2026 02:24:48 -1000 Subject: [PATCH] feat(scoring): surface the issue-discovery validity floor in the score breakdown explainScoreBreakdown explained every other scoring multiplier (density, contribution bonus, label, issue, credibility, review penalty, open-PR pressure, open-issue spam, merged-PR history floor in #1801) but silently omitted the issueDiscoveryHistoryMultiplier, even though it can zero an entire issue-discovery preview when a contributor's observed validSolvedIssues or issueCredibility falls below the upstream floors (MIN_VALID_SOLVED_ISSUES, MIN_ISSUE_CREDIBILITY). Add an issueDiscoveryHistoryBreakdown component mirroring the sibling mergedHistoryBreakdown (#1801): neutral when no solved-issue evidence is observed (the floor is not enforced for the preview, matching preview.ts:410 issueDiscoveryHistoryKnown), full when both observed dimensions meet the upstream floors, and blocked -- with a 100-leverage "land more solved-by-PR-validated issues and grow issue credibility" lever -- when either is below the floor. Purely additive explanation; no scoring behavior change. --- src/services/score-breakdown.ts | 40 +++++++++++++++++++++++++++++++ test/unit/score-breakdown.test.ts | 25 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/services/score-breakdown.ts b/src/services/score-breakdown.ts index fad0709b8..b39bbbfd0 100644 --- a/src/services/score-breakdown.ts +++ b/src/services/score-breakdown.ts @@ -131,6 +131,45 @@ function mergedHistoryBreakdown(preview: ScorePreviewResult): ScoreMultiplierBre }; } +// Sibling of mergedHistoryBreakdown for the issue-discovery validity floor (upstream MIN_VALID_SOLVED_ISSUES + +// MIN_ISSUE_CREDIBILITY): a contributor whose observed valid solved-issue count or issue credibility is below +// the upstream floors has the entire issue-discovery preview zeroed. Explained here so a miner in the +// issue-discovery lane sees the same actionable breakdown the merged-PR history floor already provides. +function issueDiscoveryHistoryBreakdown(preview: ScorePreviewResult): ScoreMultiplierBreakdown { + const { issueDiscoveryHistoryMultiplier } = preview.scoreEstimate; + const { + validSolvedIssuesFloor, + issueCredibilityFloor, + validSolvedIssues, + issueCredibility, + } = preview.gates; + // Either evidence dimension unobserved => the floor is not enforced for this preview => neutral (mirrors + // preview.ts:410 issueDiscoveryHistoryKnown). + if (validSolvedIssues === undefined || issueCredibility === undefined) { + return { + component: "issueDiscoveryHistoryMultiplier", + band: "neutral", + summary: `Issue-discovery validity floor is not enforced for this preview (no solved-issue evidence observed; upstream floors are ${validSolvedIssuesFloor} valid solved / ${issueCredibilityFloor.toFixed(2)} credibility).`, + lever: "No action needed for this preview; the upstream issue-discovery floors apply once solved-issue evidence is observed.", + leverageScore: 0, + }; + } + const meetsFloor = + validSolvedIssues >= validSolvedIssuesFloor && issueCredibility >= issueCredibilityFloor; + const band = bandForMultiplier(issueDiscoveryHistoryMultiplier); + return { + component: "issueDiscoveryHistoryMultiplier", + band, + summary: meetsFloor + ? `Solved-issue evidence (${validSolvedIssues} valid solved, ${issueCredibility.toFixed(2)} credibility) meets the upstream floors (${validSolvedIssuesFloor} / ${issueCredibilityFloor.toFixed(2)}).` + : `Solved-issue evidence (${validSolvedIssues} valid solved, ${issueCredibility.toFixed(2)} credibility) is below the upstream floors (${validSolvedIssuesFloor} valid solved, ${issueCredibilityFloor.toFixed(2)} credibility), so this issue-discovery preview is zeroed.`, + lever: meetsFloor + ? "Keep resolving valid open issues with solved-by-PR evidence to maintain issue-discovery eligibility." + : "Land more solved-by-PR-validated issues and grow issue credibility to clear the upstream floors before relying on this preview.", + leverageScore: meetsFloor ? 5 : 100, + }; +} + function credibilityBreakdown(preview: ScorePreviewResult): ScoreMultiplierBreakdown { const { credibilityMultiplier } = preview.scoreEstimate; const { credibilityObserved, credibilityFloor } = preview.gates; @@ -272,6 +311,7 @@ export function explainScoreBreakdown(preview: ScorePreviewResult): ScoreBreakdo openPrBreakdown(preview), openIssueBreakdown(preview), mergedHistoryBreakdown(preview), + issueDiscoveryHistoryBreakdown(preview), ].map((entry) => ({ ...entry, summary: sanitizePublicComment(entry.summary), diff --git a/test/unit/score-breakdown.test.ts b/test/unit/score-breakdown.test.ts index 73b07c3cb..e1796fe27 100644 --- a/test/unit/score-breakdown.test.ts +++ b/test/unit/score-breakdown.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { buildScorePreview } from "../../src/scoring/preview"; import { explainScoreBreakdown } from "../../src/services/score-breakdown"; import type { RepositoryRecord, ScoringModelSnapshotRecord } from "../../src/types"; +import type { LinkedIssueMultiplierContext } from "../../src/scoring/preview"; const FORBIDDEN = /\b(wallet|hotkey|coldkey|mnemonic|farming|payout|raw[-_\s]?trust)\b/i; @@ -120,6 +121,30 @@ describe("explainScoreBreakdown", () => { expect(JSON.stringify(blocked)).not.toMatch(FORBIDDEN); }); + it("explains the issue-discovery validity floor as neutral (unobserved), full (meets floors), and blocked (below floors)", () => { + const standardContext: LinkedIssueMultiplierContext = { status: "raw", source: "github_cache", issueNumbers: [12] }; + + // Unobserved evidence -> floors not enforced -> neutral. + const unobserved = explainScoreBreakdown( + buildScorePreview({ repo, snapshot, input: { repoFullName: repo.fullName, contributorLogin: "miner", sourceTokenScore: 40, totalTokenScore: 60, sourceLines: 80, openPrCount: 1, credibility: 0.9, linkedIssueMode: "standard", linkedIssueContext: standardContext } }), + ); + expect(unobserved.components.find((entry) => entry.component === "issueDiscoveryHistoryMultiplier")).toMatchObject({ band: "neutral" }); + + // Observed + both floors met -> full. + const meets = explainScoreBreakdown( + buildScorePreview({ repo, snapshot, input: { repoFullName: repo.fullName, contributorLogin: "miner", sourceTokenScore: 40, totalTokenScore: 60, sourceLines: 80, openPrCount: 1, credibility: 0.9, linkedIssueMode: "standard", linkedIssueContext: standardContext, validSolvedIssues: 5, issueCredibility: 0.95 } }), + ); + expect(meets.components.find((entry) => entry.component === "issueDiscoveryHistoryMultiplier")).toMatchObject({ band: "full" }); + + // Observed + below floors -> blocked, and the issue-discovery lever surfaces as highest-leverage. + const blocked = explainScoreBreakdown( + buildScorePreview({ repo, snapshot, input: { repoFullName: repo.fullName, contributorLogin: "miner", sourceTokenScore: 40, totalTokenScore: 60, sourceLines: 80, openPrCount: 1, credibility: 0.9, linkedIssueMode: "standard", linkedIssueContext: standardContext, validSolvedIssues: 1, issueCredibility: 0.5 } }), + ); + expect(blocked.components.find((entry) => entry.component === "issueDiscoveryHistoryMultiplier")).toMatchObject({ band: "blocked", leverageScore: 100 }); + expect(blocked.highestLeverageLever.lever).toMatch(/issue/i); + expect(JSON.stringify(blocked)).not.toMatch(FORBIDDEN); + }); + it("explains an over-threshold open-issue count as a blocked open-issue spam gate", () => { const preview = buildScorePreview({ repo,