From 63b44b819af8e178d594f9a2b2f04d3dac61441b Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 27 Jun 2026 13:28:34 -0700 Subject: [PATCH] FN-7133: fix PR merge status repository lookup Fix PR-mode auto-merge status checks to query GitHub with the project repository. - Resolve the current project owner/repo once from the task cwd before PR status checks. - Pass owner/repo/number to getPrMergeStatus for shared-group, task, and retry paths. - Cover repository resolution and PR status argument behavior in lifecycle tests. - Add a patch changeset for the published CLI fix. Files changed: .changeset/FN-7133-pr-merge-status-repo-args.md | 7 ++++ .../src/commands/__tests__/task-lifecycle.test.ts | 39 +++++++++++++++++++++- packages/cli/src/commands/task-lifecycle.ts | 17 +++++++--- 3 files changed, 58 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-7133 Fusion-Task-Lineage: 3f9bfd65-6950-40dc-9505-53140bd6a6a1 Co-authored-by: Fusion (runfusion.ai) --- .../FN-7133-pr-merge-status-repo-args.md | 7 ++++ .../commands/__tests__/task-lifecycle.test.ts | 39 ++++++++++++++++++- packages/cli/src/commands/task-lifecycle.ts | 17 ++++++-- 3 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 .changeset/FN-7133-pr-merge-status-repo-args.md diff --git a/.changeset/FN-7133-pr-merge-status-repo-args.md b/.changeset/FN-7133-pr-merge-status-repo-args.md new file mode 100644 index 0000000000..43395f3f17 --- /dev/null +++ b/.changeset/FN-7133-pr-merge-status-repo-args.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix PR-mode auto-merge failing with "error connecting to ". +category: fix +dev: processPullRequestMergeTask now resolves owner/repo via getCurrentRepo(cwd) and passes (owner, repo, number) to getPrMergeStatus at all three call sites (shared-group, per-task, retry); the local GitHubOperations interface param names corrected from base/head to owner/repo. FN-7133. diff --git a/packages/cli/src/commands/__tests__/task-lifecycle.test.ts b/packages/cli/src/commands/__tests__/task-lifecycle.test.ts index 5b715f2fa4..07131e10cb 100644 --- a/packages/cli/src/commands/__tests__/task-lifecycle.test.ts +++ b/packages/cli/src/commands/__tests__/task-lifecycle.test.ts @@ -36,6 +36,7 @@ vi.mock("@fusion/core", async () => { }; }); +import { getCurrentRepo } from "@fusion/core"; import { activeSessionRegistry } from "@fusion/engine"; import { cleanupMergedTaskArtifacts, @@ -153,6 +154,7 @@ describe("processPullRequestMergeTask", () => { beforeEach(() => { execMock.mockReset(); execFileCalls.length = 0; + vi.mocked(getCurrentRepo).mockReturnValue({ owner: "owner", repo: "repo" }); }); it("pushes the per-task branch to origin before creating a new PR", async () => { @@ -421,7 +423,7 @@ describe("processPullRequestMergeTask", () => { await processPullRequestMergeTask(store as never, "/repo", task.id, github as never, () => undefined); expect(github.createPr).not.toHaveBeenCalled(); - expect(github.getPrMergeStatus).toHaveBeenCalledWith("main", "fusion/groups/p-2", 22); + expect(github.getPrMergeStatus).toHaveBeenCalledWith("owner", "repo", 22); expect(store.updateBranchGroup).toHaveBeenCalledWith("BG-2", expect.objectContaining({ prNumber: 22, prUrl: "https://github.com/x/y/pull/22", @@ -429,6 +431,38 @@ describe("processPullRequestMergeTask", () => { })); }); + it("rejects before PR status checks when the project repository cannot be resolved", async () => { + vi.mocked(getCurrentRepo).mockReturnValueOnce(null); + const task: MockTask = { + id: "FN-7133", + title: "test", + description: "desc", + column: "in-review", + prInfo: { + number: 7133, + url: "https://github.com/x/y/pull/7133", + status: "open", + headBranch: "fusion/fn-7133", + baseBranch: "main", + }, + }; + const store = makeStore(task); + const github = { + findPrForBranch: vi.fn(), + createPr: vi.fn(), + getPrMergeStatus: vi.fn(), + mergePr: vi.fn(), + }; + + await expect( + processPullRequestMergeTask(store as never, "/repo", task.id, github as never, () => undefined), + ).rejects.toThrow("processPullRequestMergeTask: could not determine repository"); + + expect(github.getPrMergeStatus).not.toHaveBeenCalled(); + expect(github.findPrForBranch).not.toHaveBeenCalled(); + expect(github.createPr).not.toHaveBeenCalled(); + }); + it("finalizes branch group and member tasks when shared group PR is already merged", async () => { const taskA: MockTask = { id: "FN-9015", @@ -594,6 +628,7 @@ describe("processPullRequestMergeTask", () => { expect(github.createPr).toHaveBeenCalledWith(expect.objectContaining({ base: "main", })); + expect(github.getPrMergeStatus).toHaveBeenCalledWith("owner", "repo", 7); }); it("skips the push when an existing PR already covers the branch", async () => { @@ -966,6 +1001,8 @@ describe("processPullRequestMergeTask", () => { expect(result).toBe("merged"); expect(github.mergePr).toHaveBeenCalledWith({ number: 124, method: "squash" }); expect(github.getPrMergeStatus).toHaveBeenCalledTimes(2); + expect(github.getPrMergeStatus).toHaveBeenNthCalledWith(1, "owner", "repo", 124); + expect(github.getPrMergeStatus).toHaveBeenNthCalledWith(2, "owner", "repo", 124); expect(store.updatePrInfo).toHaveBeenLastCalledWith("FN-9104", expect.objectContaining({ status: "merged" })); expect(store.updateTask).toHaveBeenCalledWith("FN-9104", { status: null, mergeRetries: 0 }); expect(store.moveTask).toHaveBeenCalledWith("FN-9104", "done"); diff --git a/packages/cli/src/commands/task-lifecycle.ts b/packages/cli/src/commands/task-lifecycle.ts index 38224079ec..a4c9006121 100644 --- a/packages/cli/src/commands/task-lifecycle.ts +++ b/packages/cli/src/commands/task-lifecycle.ts @@ -43,7 +43,7 @@ import type { interface GitHubOperations { findPrForBranch(params: { head: string; state?: "open" | "closed" | "all" }): Promise; createPr(params: { title: string; body: string; head: string; base?: string }): Promise; - getPrMergeStatus(base?: string, head?: string, number?: number): Promise<{ + getPrMergeStatus(owner?: string, repo?: string, number?: number): Promise<{ prInfo: PrInfo; reviewDecision: string | null; checks: Array<{ name: string; required: boolean; state: string }>; @@ -667,6 +667,15 @@ export async function processPullRequestMergeTask( return "skipped"; } + /* + * FNXC:PrMergeAutoMerge 2026-06-27-13:14: + * FN-7133 requires PR-mode merge status to resolve owner/repo from the project cwd because multi-project daemons cannot rely on process cwd. Never pass branch names into gh pr view --repo; getPrMergeStatus forwards its first two args as the repository slug. + */ + const prRepo = getCurrentRepo(cwd); + if (!prRepo) { + throw new Error("processPullRequestMergeTask: could not determine repository"); + } + const branch = getTaskBranchName(task.id); const settings = await store.getSettings(); // `requirePrApproval` MOVED to workflow settings (U4): resolve the task's @@ -756,7 +765,7 @@ export async function processPullRequestMergeTask( prState: toBranchGroupPrState(groupPrInfo), }); - const mergeStatus = await github.getPrMergeStatus(projectDefaultBranch, branchGroup.branchName, groupPrInfo.number); + const mergeStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, groupPrInfo.number); const refreshedPrInfo: PrInfo = { ...groupPrInfo, ...mergeStatus.prInfo, @@ -858,7 +867,7 @@ export async function processPullRequestMergeTask( throw new Error(`Failed to create or resolve pull request for ${task.id}`); } - const mergeStatus = await github.getPrMergeStatus(mergeTarget.branch, branch, prInfo.number); + const mergeStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, prInfo.number); const refreshedPrInfo: PrInfo = { ...prInfo, ...mergeStatus.prInfo, @@ -904,7 +913,7 @@ export async function processPullRequestMergeTask( } catch (err: unknown) { let refreshedStatus: Awaited>; try { - refreshedStatus = await github.getPrMergeStatus(mergeTarget.branch, branch, prInfo.number); + refreshedStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, prInfo.number); } catch { throw err; }