Skip to content
Merged
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
6 changes: 4 additions & 2 deletions src/services/decision-pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,9 @@ function buildContributorDecisionPack(args: {
? new Map([...args.issueQualityByRepo.entries()].map(([repoFullName, report]) => [repoFullName.toLowerCase(), report]))
: new Map<string, IssueQualityReport>();
const languageSet = new Set((args.profile.github?.topLanguages ?? []).map((language) => language.toLowerCase()));
const labelHistory = new Set(args.profile.registeredRepoActivity?.dominantLabels ?? []);
// Case-insensitive, mirroring the opportunity engine (signals/engine.ts) and this file's own languageSet:
// GitHub labels compare case-insensitively, so a mixed-case dominant label must still overlap a config label.
const labelHistory = new Set((args.profile.registeredRepoActivity?.dominantLabels ?? []).map((label) => label.toLowerCase()));
const roleContexts = registeredRepositories.map((repo) =>
buildRoleContext({
login: args.login,
Expand Down Expand Up @@ -773,7 +775,7 @@ function buildRepoDecision(args: {
};
const labelHistory = args.labelHistory;
const labelFit = labelHistory
? Object.keys(args.repo.registryConfig?.labelMultipliers ?? {}).filter((label) => labelHistory.has(label))
? Object.keys(args.repo.registryConfig?.labelMultipliers ?? {}).filter((label) => labelHistory.has(label.toLowerCase()))
: [];
const copyContext: RepoCopyContext = {
repoFullName: args.repo.fullName,
Expand Down
37 changes: 37 additions & 0 deletions test/unit/decision-pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,43 @@ describe("decision-pack service", () => {
).not.toMatch(FORBIDDEN_PUBLIC_TRADEOFF_LANGUAGE);
});

it("matches label history against config labels case-insensitively (regression)", () => {
// A mixed-case dominant label must overlap a differently-cased config label, mirroring the opportunity
// engine and this file's own case-insensitive languageSet. Covers both the built Set and the lookup.
const profile = {
login: "jsonbored",
github: { topLanguages: [] },
source: {},
gittensor: null,
registeredRepoActivity: { reposTouched: ["owner/gfi"], dominantLabels: ["Good First Issue"] },
trustSignals: {},
} as any;
const pack = __decisionPackInternals.buildContributorDecisionPack({
login: "jsonbored",
profile,
outcomeHistory: { login: "jsonbored", totals: {}, repoOutcomes: [], successPatterns: [], failurePatterns: [], summary: "" } as any,
repositories: [repoWithLabels("owner/gfi", 0.04, 0, { "good first issue": 1.5 })],
syncStates: [],
syncSegments: [],
totals: [],
scoringModelSnapshotId: "scoring-1",
contributorPullRequests: [],
contributorIssues: [],
openPrMonitor: emptyOpenPrMonitor("jsonbored"),
});
const decision = pack.repoDecisions.find((d) => d.repoFullName === "owner/gfi")!;
expect(decision.labelFit).toEqual(["good first issue"]);

// Direct lookup-side check: a mixed-case config key overlaps a lowercased history entry.
const repoDecision = __decisionPackInternals.buildRepoDecision({
repo: repoWithLabels("owner/lang", 0.005, 0, { "Good First Issue": 1.5 }),
roleContext: { maintainerLane: false } as any,
outcome: { openPullRequests: 0, mergedPullRequests: 1, closedPullRequestRate: 0, credibility: 1 } as any,
labelHistory: new Set(["good first issue"]),
} as any);
expect(repoDecision.labelFit).toEqual(["Good First Issue"]);
});

it("covers languageMatch true/false and labelFit empty/non-empty paths", () => {
const ctx = (overrides: Record<string, unknown> = {}) =>
__decisionPackInternals.buildRepoDecision({
Expand Down
Loading