From 084e9376a43f7cd89cce4ca105ecd70bf7a63625 Mon Sep 17 00:00:00 2001 From: jaso0n0818 Date: Sun, 28 Jun 2026 08:52:32 +0000 Subject: [PATCH 1/2] feat(signals): validate label-multiplier ranges in config quality `buildConfigQuality` grades registry config but never checks that each configured label multiplier is a usable value. A non-positive, non-finite, or non-numeric multiplier (0, negative, NaN, Infinity, or a malformed raw-config value) silently misweights scoring, yet the config-quality score stays clean. Add an `invalid_label_multipliers` dimension: any multiplier that is not a positive finite number is surfaced as a warning finding and deducts from the score (10 each, capped at 30), mirroring the existing per-label penalties. A penalty multiplier below 1 is still positive and remains valid, so legitimate sub-1 multipliers are untouched. Distinct from `configured_labels_not_observed`, which checks whether a label is used, not whether its multiplier is valid. --- src/signals/engine.ts | 18 ++++++++++++++++++ test/unit/signals.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/signals/engine.ts b/src/signals/engine.ts index eddf1a29f7..5a8167896d 100644 --- a/src/signals/engine.ts +++ b/src/signals/engine.ts @@ -1048,6 +1048,24 @@ export function buildConfigQuality( action: "Verify those labels exist and are actually used by maintainers or trusted automation.", }); } + // A label multiplier must be a positive, finite number — a penalty multiplier is below 1 but still + // positive, so it is valid; 0, negative, NaN, or Infinity are config errors that would silently + // misweight scoring. Distinct from notObservedConfiguredLabels (which checks whether a label is *used*, + // not whether its multiplier is *valid*). + const invalidLabelMultipliers = Object.entries(repo?.registryConfig?.labelMultipliers ?? {}) + .filter(([, multiplier]) => !(typeof multiplier === "number" && Number.isFinite(multiplier) && multiplier > 0)) + .map(([label]) => label) + .sort(); + if (invalidLabelMultipliers.length > 0) { + score -= Math.min(30, invalidLabelMultipliers.length * 10); + findings.push({ + code: "invalid_label_multipliers", + severity: "warning", + title: "Configured label multipliers are out of range", + detail: `Label multipliers must be positive, finite numbers; these are not: ${invalidLabelMultipliers.join(", ")}.`, + action: "Correct these label multipliers to positive numbers in the registry config.", + }); + } const finalScore = clamp(score, 0, 100); return { diff --git a/test/unit/signals.test.ts b/test/unit/signals.test.ts index a16ed1e508..176bf56bae 100644 --- a/test/unit/signals.test.ts +++ b/test/unit/signals.test.ts @@ -597,6 +597,31 @@ describe("world-class backend signals", () => { expect(noMultiplierQuality.findings.map((finding) => finding.code)).toContain("trusted_labels_without_multipliers"); }); + it("flags non-positive, non-finite, or non-numeric label multipliers as a config error", () => { + const badRepo = { + ...repo, + registryConfig: { + ...repo.registryConfig!, + labelMultipliers: { bug: -1, stale: 0, broken: Number.NaN, weird: "x" as unknown as number, good: 1.2, penalty: 0.5 }, + }, + }; + // Observe every configured label so the unrelated "not observed" penalty does not also fire. + const observed = [{ ...issues[0]!, labels: ["bug", "stale", "broken", "weird", "good", "penalty"] }]; + const quality = buildConfigQuality(badRepo, observed, [], repo.fullName); + const invalid = quality.findings.find((finding) => finding.code === "invalid_label_multipliers"); + expect(invalid).toBeDefined(); + // Only the non-positive / non-finite / non-numeric multipliers are named (sorted); valid ones are not. + expect(invalid?.detail).toMatch(/broken, bug, stale, weird/); + expect(invalid?.detail).not.toMatch(/good|penalty/); + expect(quality.score).toBeLessThan(100); + }); + + it("does not flag valid positive label multipliers, including penalty (<1) multipliers", () => { + const okRepo = { ...repo, registryConfig: { ...repo.registryConfig!, labelMultipliers: { feature: 1.25, refactor: 0.5 } } }; + const quality = buildConfigQuality(okRepo, [{ ...issues[0]!, labels: ["feature", "refactor"] }], [], repo.fullName); + expect(quality.findings.map((finding) => finding.code)).not.toContain("invalid_label_multipliers"); + }); + it("keeps contributor detection and comment modes conservative", () => { const currentPr = pullRequests[0]!; const settings: RepositorySettings = { From 7a4e31c062b545ee2ea634a47c74e7026c815240 Mon Sep 17 00:00:00 2001 From: jaso0n0818 Date: Sun, 28 Jun 2026 14:51:04 +0000 Subject: [PATCH 2/2] feat(signals): validate label-multiplier ranges in config quality `buildConfigQuality` grades registry config but never checks that each configured label multiplier is a usable value. A non-positive, non-finite, or non-numeric multiplier (0, negative, NaN, Infinity, or a malformed raw-config value) silently misweights scoring, yet the config-quality score stays clean. Add an `invalid_label_multipliers` dimension: any multiplier that is not a positive finite number is surfaced as a warning finding (named inline as `label=value`) and deducts from the score (10 each, capped at 30), mirroring the existing per-label penalties. A penalty multiplier below 1 is still positive and remains valid, so legitimate sub-1 multipliers are untouched. Distinct from `configured_labels_not_observed`, which checks whether a label is used, not whether its multiplier is valid. Validated with the full local gate: npm run test:ci green, codecov/patch 100% on changed lines, branch coverage on every added line, OpenAPI unchanged. --- src/signals/engine.ts | 5 +++-- test/unit/signals.test.ts | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/signals/engine.ts b/src/signals/engine.ts index 5a8167896d..8d06036123 100644 --- a/src/signals/engine.ts +++ b/src/signals/engine.ts @@ -1052,9 +1052,10 @@ export function buildConfigQuality( // positive, so it is valid; 0, negative, NaN, or Infinity are config errors that would silently // misweight scoring. Distinct from notObservedConfiguredLabels (which checks whether a label is *used*, // not whether its multiplier is *valid*). + // Surface each bad multiplier as `label=value` so a maintainer sees the offending value inline. const invalidLabelMultipliers = Object.entries(repo?.registryConfig?.labelMultipliers ?? {}) .filter(([, multiplier]) => !(typeof multiplier === "number" && Number.isFinite(multiplier) && multiplier > 0)) - .map(([label]) => label) + .map(([label, multiplier]) => `${label}=${String(multiplier)}`) .sort(); if (invalidLabelMultipliers.length > 0) { score -= Math.min(30, invalidLabelMultipliers.length * 10); @@ -1063,7 +1064,7 @@ export function buildConfigQuality( severity: "warning", title: "Configured label multipliers are out of range", detail: `Label multipliers must be positive, finite numbers; these are not: ${invalidLabelMultipliers.join(", ")}.`, - action: "Correct these label multipliers to positive numbers in the registry config.", + action: "Set each flagged label multiplier to a positive, finite number (a penalty multiplier below 1 is allowed) in the registry config.", }); } diff --git a/test/unit/signals.test.ts b/test/unit/signals.test.ts index 176bf56bae..fc0e6b4bb0 100644 --- a/test/unit/signals.test.ts +++ b/test/unit/signals.test.ts @@ -610,10 +610,12 @@ describe("world-class backend signals", () => { const quality = buildConfigQuality(badRepo, observed, [], repo.fullName); const invalid = quality.findings.find((finding) => finding.code === "invalid_label_multipliers"); expect(invalid).toBeDefined(); - // Only the non-positive / non-finite / non-numeric multipliers are named (sorted); valid ones are not. - expect(invalid?.detail).toMatch(/broken, bug, stale, weird/); + // Each bad multiplier is named with its value (sorted); valid ones are not listed. + expect(invalid?.detail).toMatch(/broken=NaN, bug=-1, stale=0, weird=x/); expect(invalid?.detail).not.toMatch(/good|penalty/); - expect(quality.score).toBeLessThan(100); + // Four invalid entries deduct the capped 30 points; valid ones do not contribute. + const baseline = buildConfigQuality({ ...badRepo, registryConfig: { ...badRepo.registryConfig!, labelMultipliers: { good: 1.2, penalty: 0.5 } } }, observed, [], repo.fullName).score; + expect(quality.score).toBe(baseline - 30); }); it("does not flag valid positive label multipliers, including penalty (<1) multipliers", () => { @@ -622,6 +624,12 @@ describe("world-class backend signals", () => { expect(quality.findings.map((finding) => finding.code)).not.toContain("invalid_label_multipliers"); }); + it("treats a repo with no labelMultipliers key as having no invalid multipliers", () => { + const noLabelsRepo = { ...repo, registryConfig: { ...repo.registryConfig!, labelMultipliers: undefined as unknown as Record } }; + const quality = buildConfigQuality(noLabelsRepo, [], [], repo.fullName); + expect(quality.findings.map((finding) => finding.code)).not.toContain("invalid_label_multipliers"); + }); + it("keeps contributor detection and comment modes conservative", () => { const currentPr = pullRequests[0]!; const settings: RepositorySettings = {