fix(FN-5633): fail loudly when an executed, never-merged task has no branch

The AI merge path treated a missing task branch as a benign no-op. That's
correct when the task was never executed or already merged (branch cleaned up
on a re-process), but if the task WAS executed (a baseCommitSha was recorded)
and has no recorded merge, the branch should still exist — its work appears
lost. Now that case throws instead of silently marking the task done; the
benign cases still finalize as a no-op (reason "already-merged" / "no-branch").

Tests: executed+never-merged → throws; already-merged → no-op done;
never-executed → no-op done.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-28 19:28:57 -07:00
parent 29ac58f8d0
commit be0be507b3
2 changed files with 59 additions and 3 deletions

View File

@@ -185,6 +185,39 @@ describe("runAiMerge", () => {
expect(git(dir, "rev-parse main")).toBe(mainBefore);
});
it("fails loudly when an executed, never-merged task has no branch (possible lost work)", async () => {
const { dir } = initRepoWithBranch({ branch: "fusion/fn-1" });
// branch points at a ref that doesn't exist; task was executed (baseCommitSha) and never merged.
const { store } = makeStore(dir, { branch: "fusion/ghost", baseCommitSha: "0123456789abcdef" });
await expect(runAiMerge(store, dir, "FN-1", { manual: true }, {
mergeAgent: vi.fn(), reviewAgent: vi.fn(async () => "REVIEW_VERDICT: approve"),
})).rejects.toThrow(/work appears lost/);
expect(store.moveTask).not.toHaveBeenCalled();
});
it("finalizes as a no-op when an already-merged task's branch is gone (re-process)", async () => {
const { dir } = initRepoWithBranch({ branch: "fusion/fn-1" });
const { store } = makeStore(dir, { branch: "fusion/ghost", baseCommitSha: "0123456789abcdef", mergeDetails: { mergeConfirmed: true } });
const result = await runAiMerge(store, dir, "FN-1", { manual: true }, {
mergeAgent: vi.fn(), reviewAgent: vi.fn(),
});
expect(result.noOp).toBe(true);
expect(store.moveTask).toHaveBeenCalledWith("FN-1", "done");
});
it("finalizes as a no-op when a never-executed task has no branch", async () => {
const { dir } = initRepoWithBranch({ branch: "fusion/fn-1" });
const { store } = makeStore(dir, { branch: "fusion/ghost" }); // no baseCommitSha → never executed
const result = await runAiMerge(store, dir, "FN-1", { manual: true }, {
mergeAgent: vi.fn(), reviewAgent: vi.fn(),
});
expect(result.noOp).toBe(true);
expect(store.moveTask).toHaveBeenCalledWith("FN-1", "done");
});
it("throws a clear error when the task's target branch has no local ref", async () => {
const { dir } = initRepoWithBranch({ branch: "fusion/fn-1" });
const { store } = makeStore(dir, { baseBranch: "release/9.9" }); // never created locally