From 77aceb4f69f27d84f8e68a664b0a741adfdd8203 Mon Sep 17 00:00:00 2001 From: oktofeesh1 <287075021+oktofeesh1@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:31:20 -0700 Subject: [PATCH] fix(signals): wire open-PR pressure scenarios into the branch scenario summary (#348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `simulateOpenPrPressure` (open-pr-pressure-scenarios.ts, #348) and the `renderPublicScenarioSummary` `options`/headline rendering for it were both implemented and tested, but the only production caller — buildLocalBranchAnalysis — never built or passed `pressureSimulation`. As a result `scenarioSummary.options` was ALWAYS empty in analyze-branch / preflight / local-diff, and the strategy guidance (open new work / wait / clean up first) never reached contributors. Build the simulation at the call site (roleContext is already in scope; derive queueHealth via buildCollisionReport + buildQueueHealth, and the contributor's own open-PR count) and pass it through. Pure/read-only and public-safe — the renderer already sanitizes every line. Test: local-branch.test.ts now asserts scenarioSummary.options is populated (3 ranked options, exactly one recommended, non-empty label + nextStep). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/signals/local-branch.ts | 16 +++++++++++++++ test/unit/local-branch.test.ts | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/signals/local-branch.ts b/src/signals/local-branch.ts index 5697e59cf3..5b6a40875a 100644 --- a/src/signals/local-branch.ts +++ b/src/signals/local-branch.ts @@ -4,8 +4,10 @@ import type { GittensorContributorSnapshot } from "../gittensor/api"; import type { BountyRecord, CheckSummaryRecord, IssueRecord, PullRequestRecord, RecentMergedPullRequestRecord, RepositoryRecord, ScoringModelSnapshotRecord } from "../types"; import { nowIso } from "../utils/json"; import { + buildCollisionReport, buildLaneAdvice, buildLocalDiffPreflightResult, + buildQueueHealth, buildRepoFitRecommendation, buildRoleContext, type ContributorOutcomeHistory, @@ -23,6 +25,7 @@ import { isPublicSafeText } from "./redaction"; import { deriveEligibilityPlan } from "../services/eligibility-plan"; import { scenarioInputFromLocalBranchMetadata } from "../scenarios/input-model"; import { renderPublicScenarioSummary, type PublicScenarioSummary, type ScenarioSummaryInput } from "../scenarios/scenario-summary"; +import { simulateOpenPrPressure } from "../services/open-pr-pressure-scenarios"; export type LocalBranchChangedFile = { path: string; @@ -366,6 +369,18 @@ export function buildLocalBranchAnalysis(args: { classified: [], } : undefined; + // Open-PR pressure strategy options (#348): the scenario summary renderer fills its strategy + // `options` (open new work / wait / clean up first) and headline from this simulation. Without + // passing it, scenarioSummary.options was always empty and the guidance never reached the miner. + const queuePressureSimulation = simulateOpenPrPressure({ + repoFullName: args.input.repoFullName, + generatedAt: nowIso(), + queueHealth: buildQueueHealth(args.repo, args.issues, args.pullRequests, buildCollisionReport(args.input.repoFullName, args.issues, args.pullRequests)), + roleContext, + contributorOpenPrCount: (args.contributorPullRequests ?? args.pullRequests).filter( + (pr) => pr.state === "open" && (pr.authorLogin ?? "").toLowerCase() === args.input.login.toLowerCase(), + ).length, + }); const scenarioSummary = renderPublicScenarioSummary({ repoFullName: args.input.repoFullName, generatedAt: nowIso(), @@ -373,6 +388,7 @@ export function buildLocalBranchAnalysis(args: { publicBlockers: scorePreview.blockedBy, scenarioInput: branchScenarioInput, pendingDetection: pendingDetectionForSummary, + pressureSimulation: queuePressureSimulation, }); return { login: args.input.login, diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts index f3370301d6..9d0e4a7a94 100644 --- a/test/unit/local-branch.test.ts +++ b/test/unit/local-branch.test.ts @@ -1604,6 +1604,43 @@ describe("local MCP git metadata collection", () => { expect(analysis.scenarioSummary.dataClassification.facts).toEqual(expect.arrayContaining(["Contributor", "Repository", "Branch"])); }); + it("wires open-PR pressure strategy options into scenarioSummary.options (#348)", () => { + const analysis = buildLocalBranchAnalysis({ + input: { + login: "oktofeesh1", + repoFullName: repo.fullName, + changedFiles: [{ path: "src/util.ts", additions: 30, deletions: 2, status: "modified" }], + localScorer: { mode: "external_command", sourceTokenScore: 40, totalTokenScore: 60, sourceLines: 38 }, + }, + repo, + issues: [{ repoFullName: repo.fullName, number: 9, title: "Improve util", state: "open", labels: [], linkedPrs: [] }], + pullRequests: [ + { repoFullName: repo.fullName, number: 4, title: "WIP util", state: "open", authorLogin: "oktofeesh1", labels: [], linkedIssues: [] }, + ], + // contributorPullRequests is preferred when present; the authorless PR exercises the null-author + // guard in the own-open-PR count and must not be miscounted as this contributor's work. + contributorPullRequests: [ + { repoFullName: repo.fullName, number: 4, title: "WIP util", state: "open", authorLogin: "oktofeesh1", labels: [], linkedIssues: [] }, + { repoFullName: repo.fullName, number: 5, title: "Authorless", state: "open", authorLogin: null, labels: [], linkedIssues: [] }, + ], + profile, + outcomeHistory, + scoringSnapshot, + scoringProfile, + }); + + // Before this fix the renderer never received the pressure simulation, so options was always []. + const options = analysis.scenarioSummary.options; + expect(options.length).toBe(3); + expect(options.map((option) => option.rank)).toEqual([1, 2, 3]); + expect(options.filter((option) => option.recommended)).toHaveLength(1); + expect(options[0]?.recommended).toBe(true); + for (const option of options) { + expect(option.label.length).toBeGreaterThan(0); + expect(option.nextStep.length).toBeGreaterThan(0); + } + }); + it("populates scenarioSummary.dataClassification with contributor and repo facts from branch metadata", () => { const analysis = buildLocalBranchAnalysis({ input: {