test(FN-4397): add loop reproduction for repeated branch-conflict emissions

Fusion-Task-Id: FN-4397
Fusion-Task-Lineage: 1ffac4f7-adf5-4115-b588-108d75eadde7
This commit is contained in:
Fusion
2026-05-13 15:50:59 -07:00
committed by gsxdsm
parent b0f01451d7
commit de7de9526a

View File

@@ -11,6 +11,7 @@ import { execSync } from "node:child_process";
import { findWorktreeUser, aiMergeTask } from "../merger.js";
import { WorktreePool } from "../worktree-pool.js";
import { BranchConflictError } from "../branch-conflicts.js";
import * as branchConflictModule from "../branch-conflicts.js";
import { generateWorktreeName, slugify } from "../worktree-names.js";
import type { Task, TaskDetail } from "@fusion/core";
import { SessionManager } from "@mariozechner/pi-coding-agent";
@@ -829,6 +830,38 @@ describe("TaskExecutor worktree recovery", () => {
expect(onError).toHaveBeenCalledWith(expect.objectContaining({ id: "FN-050" }), expect.any(BranchConflictError));
});
it("FN-4397 reproduces repeated branch-conflict recovery-required emissions for the same task", async () => {
const store = createMockStore();
const executor = new TaskExecutor(store, "/tmp/test");
const conflictError = new BranchConflictError({
branchName: "fusion/fn-050",
conflictingWorktreePath: "/tmp/test/.worktrees/green-sage",
existingTipSha: "abc123def456",
strandedCommits: [],
startPoint: "HEAD",
recommendedAction: "Reclaim the existing task branch/worktree or explicitly discard prior work before retrying.",
});
vi.spyOn(branchConflictModule, "inspectBranchConflict").mockResolvedValue({
kind: "live",
error: conflictError,
});
await (executor as any).handleBranchConflict(makeTask(), conflictError);
await (executor as any).handleBranchConflict(makeTask(), conflictError);
await (executor as any).handleBranchConflict(makeTask(), conflictError);
expect(store.appendAgentLog).toHaveBeenCalledTimes(3);
expect(store.appendAgentLog).toHaveBeenNthCalledWith(
1,
"FN-050",
"Branch conflict recovery required",
"tool_error",
expect.any(String),
"executor",
);
});
it("falls back to default base and clears task.executionStartBranch when the configured base ref is missing (FN-2165)", async () => {
const store = createMockStore();