feat(FN-4917): complete Step 3 — gate pooled and resume worktree validity

Fusion-Task-Id: FN-4917
Fusion-Task-Lineage: 1422a545-f4b5-4e1f-a347-8be2a4202f45
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 11:05:27 -07:00
committed by gsxdsm
parent 2cb141a511
commit 2bc76cf00f
2 changed files with 135 additions and 42 deletions

View File

@@ -1,10 +1,15 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { promisify } from "node:util";
import { acquireTaskWorktree } from "../worktree-acquisition.js";
import { classifyTaskWorktree } from "../worktree-pool.js";
vi.mock("../worktree-pool.js", async () => {
const actual = await vi.importActual<any>("../worktree-pool.js");
return { ...actual, isUsableTaskWorktree: vi.fn().mockResolvedValue(true) };
return {
...actual,
classifyTaskWorktree: vi.fn().mockResolvedValue({ ok: true }),
isInsideWorktreesDir: vi.fn().mockReturnValue(true),
};
});
vi.mock("../worktree-db-hydrate.js", () => ({
@@ -90,6 +95,57 @@ describe("acquireTaskWorktree", () => {
expect(release).toHaveBeenCalledWith("/tmp/pooled");
});
it("falls through to fresh creation when pooled worktree is incomplete and emits detection audit", async () => {
vi.mocked(classifyTaskWorktree).mockResolvedValueOnce({ ok: false, classification: "incomplete", reason: "missing or invalid .git metadata" });
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
const auditGit = vi.fn().mockResolvedValue(undefined);
const remove = vi.fn().mockResolvedValue(undefined);
const result = await acquireTaskWorktree({
task,
rootDir: process.cwd(),
store,
settings: { recycleWorktrees: true } as any,
pool: {
acquire: () => "/tmp/pooled",
prepareForTask: vi.fn().mockResolvedValue({ branch: "fusion/fn-1", worktreePath: "/tmp/pooled", reclaimed: false }),
release: vi.fn(),
} as any,
createWorktree,
audit: { git: auditGit } as any,
backend: { kind: "native", create: vi.fn(), remove } as any,
});
expect(result.source).toBe("fresh");
expect(auditGit).toHaveBeenCalledWith(expect.objectContaining({
type: "worktree:incomplete-detected",
metadata: expect.objectContaining({ classification: "incomplete", source: "pool-acquire" }),
}));
expect(store.logEntry).toHaveBeenCalledWith("FN-1", expect.stringContaining("Pool returned incomplete worktree"), undefined, undefined);
expect(store.logEntry).not.toHaveBeenCalledWith("FN-1", expect.stringMatching(/Refusing to start coding agent/), expect.anything(), expect.anything());
});
it("emits resume detection audit and clears session file when assigned worktree is unregistered", async () => {
vi.mocked(classifyTaskWorktree).mockResolvedValueOnce({ ok: false, classification: "unregistered", reason: "not registered in git worktree list" });
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
const auditGit = vi.fn().mockResolvedValue(undefined);
await acquireTaskWorktree({
task: { ...task, worktree: process.cwd(), branch: "fusion/fn-1", sessionFile: "/tmp/session.json" },
rootDir: process.cwd(),
store,
settings: {} as any,
createWorktree,
audit: { git: auditGit } as any,
});
expect(auditGit).toHaveBeenCalledWith(expect.objectContaining({
type: "worktree:incomplete-detected",
metadata: expect.objectContaining({ classification: "unregistered", source: "resume" }),
}));
expect(store.updateTask).toHaveBeenCalledWith("FN-1", { worktree: null, branch: null, sessionFile: null });
});
it("creates fresh when pool disabled", async () => {
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
const result = await acquireTaskWorktree({