Skip to content

Commit 1c7c771

Browse files
authored
fix(signals): scope contributor-open-pr-monitor to isInstalled, not isRegistered (#5691)
buildContributorOpenPrMonitor scoped a contributor's open-PR "next steps" packet to isRegistered repos. Every field it produces (classification, reasons, next steps, guidance) is generic PR-hygiene advice with zero gittensor-specific data (no reward-risk, no decision-pack fields), so per the product decision on #5025, it's now available for any self-host operator's installed repos regardless of gittensor-subnet opt-in status -- consistent with #5021/#5022/#5024's isRegistered->isInstalled fixes. Since no gittensor-specific fields exist on this endpoint today, no field-trimming was needed: gittensor stays a pure additive plugin here by not having added anything to this surface in the first place. Closes #5025
1 parent be9af47 commit 1c7c771

2 files changed

Lines changed: 28 additions & 4 deletions

File tree

src/signals/contributor-open-pr-monitor.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ export type ContributorOpenPrMonitor = {
5252

5353
export async function buildContributorOpenPrMonitor(env: Env, login: string): Promise<ContributorOpenPrMonitor> {
5454
const [pullRequests, repositories] = await Promise.all([listContributorPullRequests(env, login), listRepositories(env)]);
55-
const registered = new Set(repositories.filter((repo) => repo.isRegistered).map((repo) => repo.fullName.toLowerCase()));
55+
// #5025: scoped to isInstalled, not isRegistered -- every field this monitor produces (classification,
56+
// reasons, next steps) is generic PR-hygiene guidance with no gittensor-specific data (no reward-risk, no
57+
// decision-pack fields), so it's available to any self-host operator's installed repos regardless of
58+
// gittensor-subnet opt-in, consistent with #5021/#5022/#5024's isRegistered->isInstalled migration.
59+
const registered = new Set(repositories.filter((repo) => repo.isInstalled).map((repo) => repo.fullName.toLowerCase()));
5660
const openByContributor = pullRequests.filter(
5761
(pr) => pr.state === "open" && sameLogin(pr.authorLogin, login) && registered.has(pr.repoFullName.toLowerCase()),
5862
);

test/unit/contributor-open-pr-monitor.test.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,15 @@ describe("contributor open PR monitor", () => {
154154
expect(mapPendingClassToWorkClassification(nativeDraft, { changeRequestCount: 0, checkFailureCount: 0, duplicateProne: false, missingTests: false })).toBe("draft");
155155
});
156156

157-
it("builds contributor-wide monitor answer from registered repos only", async () => {
157+
it("builds contributor-wide monitor answer from installed repos only", async () => {
158158
const env = createTestEnv();
159159
vi.spyOn(repositories, "listRepositories").mockResolvedValue([
160160
{ fullName: "entrius/allways-ui", owner: "entrius", name: "allways-ui", isInstalled: true, isRegistered: true, isPrivate: false },
161-
{ fullName: "other/unregistered", owner: "other", name: "unregistered", isInstalled: true, isRegistered: false, isPrivate: true },
161+
{ fullName: "other/uninstalled", owner: "other", name: "uninstalled", isInstalled: false, isRegistered: true, isPrivate: true },
162162
] as Awaited<ReturnType<typeof repositories.listRepositories>>);
163163
vi.spyOn(repositories, "listContributorPullRequests").mockResolvedValue([
164164
pr({ number: 10 }),
165-
pr({ number: 11, repoFullName: "other/unregistered", authorLogin: "miner-a" }),
165+
pr({ number: 11, repoFullName: "other/uninstalled", authorLogin: "miner-a" }),
166166
]);
167167
vi.spyOn(repositories, "listPullRequests").mockResolvedValue([pr({ number: 10 }), pr({ number: 11 })]);
168168
vi.spyOn(repositories, "listPullRequestReviews").mockImplementation(async (_env, _repo, pullNumber) =>
@@ -174,6 +174,8 @@ describe("contributor open PR monitor", () => {
174174
{ repoFullName: "entrius/allways-ui", pullNumber: 10, path: "src/a.test.ts", additions: 5, deletions: 0, changes: 5, status: "added", payload: {} },
175175
]);
176176

177+
// #other/uninstalled is registered on the gittensor subnet but never installed on this self-host
178+
// instance -- it must NOT be covered, since the monitor is scoped to repos this instance operates on.
177179
const monitor = await buildContributorOpenPrMonitor(env, "miner-a");
178180
expect(monitor.openPrCount).toBe(1);
179181
expect(monitor.registeredRepoCount).toBe(1);
@@ -184,6 +186,24 @@ describe("contributor open PR monitor", () => {
184186
expect(monitor.guidance.length).toBeGreaterThan(0);
185187
});
186188

189+
it("#5025: covers an installed-but-not-subnet-registered repo, since the monitor's guidance is generic and unrelated to gittensor-subnet economics", async () => {
190+
const env = createTestEnv();
191+
vi.spyOn(repositories, "listRepositories").mockResolvedValue([
192+
{ fullName: "acme/installed-not-registered", owner: "acme", name: "installed-not-registered", isInstalled: true, isRegistered: false, isPrivate: false },
193+
] as Awaited<ReturnType<typeof repositories.listRepositories>>);
194+
vi.spyOn(repositories, "listContributorPullRequests").mockResolvedValue([pr({ number: 20, repoFullName: "acme/installed-not-registered", authorLogin: "miner-a" })]);
195+
vi.spyOn(repositories, "listPullRequests").mockResolvedValue([pr({ number: 20, repoFullName: "acme/installed-not-registered" })]);
196+
vi.spyOn(repositories, "listPullRequestReviews").mockResolvedValue([approvedReview(20)]);
197+
vi.spyOn(repositories, "listCheckSummaries").mockResolvedValue([]);
198+
vi.spyOn(repositories, "listPullRequestFiles").mockResolvedValue([]);
199+
200+
const monitor = await buildContributorOpenPrMonitor(env, "miner-a");
201+
202+
expect(monitor.openPrCount).toBe(1);
203+
expect(monitor.pullRequests).toHaveLength(1);
204+
expect(monitor.pullRequests[0]).toMatchObject({ number: 20, repoFullName: "acme/installed-not-registered" });
205+
});
206+
187207
it("loads signals and files with each PR casing in a case-variant repo group", async () => {
188208
const env = createTestEnv();
189209
vi.spyOn(repositories, "listRepositories").mockResolvedValue([

0 commit comments

Comments
 (0)