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) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-27 13:28:34 -07:00
parent 7657cc6d84
commit 63b44b819a
3 changed files with 58 additions and 5 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix PR-mode auto-merge failing with "error connecting to <branch>".
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.

View File

@@ -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");

View File

@@ -43,7 +43,7 @@ import type {
interface GitHubOperations {
findPrForBranch(params: { head: string; state?: "open" | "closed" | "all" }): Promise<PrInfo | null>;
createPr(params: { title: string; body: string; head: string; base?: string }): Promise<PrInfo>;
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<ReturnType<GitHubOperations["getPrMergeStatus"]>>;
try {
refreshedStatus = await github.getPrMergeStatus(mergeTarget.branch, branch, prInfo.number);
refreshedStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, prInfo.number);
} catch {
throw err;
}