From 82bcbc357cfc0bd4e61ba17210722262f5b29570 Mon Sep 17 00:00:00 2001 From: galuis116 Date: Mon, 1 Jun 2026 18:10:55 -0700 Subject: [PATCH] Fix case-sensitive repoFullName match dropping maintainer-lane detection --- src/signals/engine.ts | 8 ++++---- test/unit/signals-v2.test.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/signals/engine.ts b/src/signals/engine.ts index 16a4ba5c75..1c458cac30 100644 --- a/src/signals/engine.ts +++ b/src/signals/engine.ts @@ -1352,16 +1352,16 @@ export function buildRoleContext(args: { const normalizedLogin = args.login.toLowerCase(); const [owner] = args.repoFullName.split("/"); const authoredAssociations = [ - ...(args.pullRequests ?? []).filter((pr) => pr.repoFullName === args.repoFullName && sameLogin(pr.authorLogin, args.login)).map((pr) => pr.authorAssociation), - ...(args.issues ?? []).filter((issue) => issue.repoFullName === args.repoFullName && sameLogin(issue.authorLogin, args.login)).map((issue) => issue.authorAssociation), + ...(args.pullRequests ?? []).filter((pr) => sameRepo(pr.repoFullName, args.repoFullName) && sameLogin(pr.authorLogin, args.login)).map((pr) => pr.authorAssociation), + ...(args.issues ?? []).filter((issue) => sameRepo(issue.repoFullName, args.repoFullName) && sameLogin(issue.authorLogin, args.login)).map((issue) => issue.authorAssociation), ].filter(Boolean) as string[]; const officialRepo = args.profile?.gittensor?.repositories.find((repo) => repo.repoFullName.toLowerCase() === args.repoFullName.toLowerCase()); const touchedByOfficial = Boolean(officialRepo && officialRepo.pullRequests + officialRepo.openIssues + officialRepo.closedIssues > 0); const touchedByCache = Boolean( args.profile?.registeredRepoActivity.reposTouched.some((repo) => repo.toLowerCase() === args.repoFullName.toLowerCase()) || - (args.pullRequests ?? []).some((pr) => pr.repoFullName === args.repoFullName && sameLogin(pr.authorLogin, args.login)) || + (args.pullRequests ?? []).some((pr) => sameRepo(pr.repoFullName, args.repoFullName) && sameLogin(pr.authorLogin, args.login)) || /* v8 ignore next -- Issue-authored cache fallback is defensive; PR and official contribution paths cover role detection behavior. */ - (args.issues ?? []).some((issue) => issue.repoFullName === args.repoFullName && sameLogin(issue.authorLogin, args.login)), + (args.issues ?? []).some((issue) => sameRepo(issue.repoFullName, args.repoFullName) && sameLogin(issue.authorLogin, args.login)), ); let role: ContributorRole = "unknown"; diff --git a/test/unit/signals-v2.test.ts b/test/unit/signals-v2.test.ts index d3247df0c6..b0fcc8164f 100644 --- a/test/unit/signals-v2.test.ts +++ b/test/unit/signals-v2.test.ts @@ -935,6 +935,16 @@ describe("v2 signal builders", () => { source: "unknown", normalContributorEvidenceAllowed: true, }); + // Maintainer association must survive a repoFullName casing mismatch between the + // canonical name (e.g. the official source's "Org/Project") and the cached PR's + // "org/project". Case-sensitive matching here would drop the association and + // wrongly mark the maintainer's repo as outside-contributor evidence. + expect(buildRoleContext({ login: "dev", repo: null, repoFullName: "Org/Project", pullRequests: [memberPr], issues: [] })).toMatchObject({ + role: "org_member", + maintainerLane: true, + source: "github_association", + association: "MEMBER", + }); }); it("branches repo fit recommendations across pursue, avoid, cleanup, unknown, and maintainer lanes", () => {