From 59f027693a153d90cc85fe10b1fd4504c9c45b58 Mon Sep 17 00:00:00 2001 From: joaovictor91123 Date: Sun, 26 Jul 2026 06:42:48 -0700 Subject: [PATCH] fix(engine): align no-preference miner lane-fit score to 0.5 computeMinerGoalLaneFit returned 1 when preferredLabels were empty; match computeLaneFit's documented neutral of 0.5 so metadata ranking does not over-weight unconfigured goals. Closes #8870 --- packages/loopover-engine/src/miner-goal-lane-fit.ts | 3 ++- .../loopover-engine/test/miner-goal-lane-fit.test.ts | 7 ++++--- test/unit/miner-goal-lane-fit.test.ts | 9 ++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/loopover-engine/src/miner-goal-lane-fit.ts b/packages/loopover-engine/src/miner-goal-lane-fit.ts index 8ef0ebff50..baeb13d3eb 100644 --- a/packages/loopover-engine/src/miner-goal-lane-fit.ts +++ b/packages/loopover-engine/src/miner-goal-lane-fit.ts @@ -36,7 +36,8 @@ export function computeMinerGoalLaneFit( let score: number; if (preferred.length === 0) { - score = 1; + // Match computeLaneFit's documented neutral: no preference configured → fixed 0.5, never 0 or 1 (#8870). + score = 0.5; } else { const preferredMatch = preferred.some((want) => issueLabels.includes(want)); if (preferredMatch) { diff --git a/packages/loopover-engine/test/miner-goal-lane-fit.test.ts b/packages/loopover-engine/test/miner-goal-lane-fit.test.ts index 756a602b7a..d0a8d7447c 100644 --- a/packages/loopover-engine/test/miner-goal-lane-fit.test.ts +++ b/packages/loopover-engine/test/miner-goal-lane-fit.test.ts @@ -9,8 +9,9 @@ test("isMinerRepoTargetable respects minerEnabled opt-out", () => { assert.equal(isMinerRepoTargetable({ ...DEFAULT_MINER_GOAL_SPEC, minerEnabled: false }), false); }); -test("computeMinerGoalLaneFit returns 1 when no preferred labels are configured", () => { - assert.equal(computeMinerGoalLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC), 1); +test("computeMinerGoalLaneFit returns 0.5 when no preferred labels are configured (#8870)", () => { + // Aligns with computeLaneFit's documented neutral (goal-model.ts): no preference → 0.5, never 0 or 1. + assert.equal(computeMinerGoalLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC), 0.5); }); test("computeMinerGoalLaneFit matches preferred labels case-insensitively", () => { @@ -39,7 +40,7 @@ test("computeMinerGoalLaneFit applies issueDiscoveryPolicy modifiers", () => { test("computeMinerGoalLaneFit returns 0 when a blocked label matches case-insensitively", () => { const spec = { ...DEFAULT_MINER_GOAL_SPEC, blockedLabels: ["wontfix"] }; assert.equal(computeMinerGoalLaneFit({ labels: ["WontFix"] }, spec), 0); - assert.equal(computeMinerGoalLaneFit({ labels: ["bug"] }, spec), 1); + assert.equal(computeMinerGoalLaneFit({ labels: ["bug"] }, spec), 0.5); }); test("computeMinerGoalLaneFit ignores malformed label entries safely", () => { diff --git a/test/unit/miner-goal-lane-fit.test.ts b/test/unit/miner-goal-lane-fit.test.ts index 4f67e8d8e6..c57282dd36 100644 --- a/test/unit/miner-goal-lane-fit.test.ts +++ b/test/unit/miner-goal-lane-fit.test.ts @@ -89,7 +89,14 @@ describe("computeMinerGoalLaneFit", () => { expect(computeMinerGoalLaneFit({ labels: ["feature"] }, spec)).toBe(0.25); }); + it("returns 0.5 when no preferred labels are configured (#8870)", () => { + // Aligns with computeLaneFit's documented neutral: no preference → 0.5, never 0 or 1. + expect(computeMinerGoalLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC)).toBe(0.5); + expect(computeMetadataLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC)).toBe(0.5); + }); + it("scores normally when no blocked labels are configured", () => { - expect(computeMinerGoalLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC)).toBe(1); + // No preferredLabels → neutral 0.5, matching computeLaneFit (#8870). + expect(computeMinerGoalLaneFit({ labels: ["docs"] }, DEFAULT_MINER_GOAL_SPEC)).toBe(0.5); }); });