feat(FN-4410): complete Step 1 — dispatch reclaim telemetry wiring

Fusion-Task-Id: FN-4410
Fusion-Task-Lineage: 306d4fe0-d024-49dd-86b4-fb98d4ceff59
This commit is contained in:
Fusion
2026-05-14 03:01:24 -07:00
committed by gsxdsm
parent d54c80f40f
commit 0570ec3891
3 changed files with 70 additions and 1 deletions

View File

@@ -1859,6 +1859,46 @@ describe("TaskExecutor dependency-based worktree creation", () => {
);
});
it("records branch:auto-reclaim run-audit event when pooled prepare returns reclaimed worktree", async () => {
const pool = new WorktreePool();
pool.release("/tmp/test/.worktrees/idle-wt");
mockedExistsSync.mockImplementation((p) => p === "/tmp/test/.worktrees/idle-wt" || p === "/tmp/test/.worktrees/live-wt");
vi.spyOn(pool, "prepareForTask").mockResolvedValue({
branch: "fusion/fn-066",
worktreePath: "/tmp/test/.worktrees/live-wt",
reclaimed: true,
existingTipSha: "abc123def456",
strandedCommitCount: 2,
});
const store = createMockStore();
store.recordRunAuditEvent = vi.fn().mockResolvedValue(undefined);
store.getSettings.mockResolvedValue({
maxConcurrent: 2,
maxWorktrees: 4,
pollIntervalMs: 15000,
groupOverlappingFiles: false,
autoMerge: false,
recycleWorktrees: true,
});
const executor = new TaskExecutor(store, "/tmp/test", { pool });
await executor.execute(makeTask({ id: "FN-066", worktree: null, branch: null }));
expect(store.recordRunAuditEvent).toHaveBeenCalledWith(expect.objectContaining({
mutationType: "branch:auto-reclaim",
target: "fusion/fn-066",
metadata: expect.objectContaining({
taskId: "FN-066",
trigger: "dispatch-preflight",
worktreePath: "/tmp/test/.worktrees/live-wt",
existingTipSha: "abc123def456",
strandedCommitCount: 2,
}),
}));
});
it("stores suffixed branch name when pool returns a different name", async () => {
const pool = new WorktreePool();
pool.release("/tmp/test/.worktrees/idle-wt");

View File

@@ -41,6 +41,7 @@ describe("acquireTaskWorktree", () => {
it("acquires from pool when enabled", async () => {
const prepareForTask = vi.fn().mockResolvedValue({ branch: "fusion/fn-1", worktreePath: "/tmp/pooled", reclaimed: false });
const release = vi.fn();
const result = await acquireTaskWorktree({
task,
rootDir: process.cwd(),
@@ -49,10 +50,11 @@ describe("acquireTaskWorktree", () => {
pool: {
acquire: () => "/tmp/pooled",
prepareForTask,
release: vi.fn(),
release,
} as any,
createWorktree: vi.fn(),
});
expect(release).not.toHaveBeenCalled();
expect(result.source).toBe("pool");
expect(prepareForTask).toHaveBeenCalledWith(
"/tmp/pooled",
@@ -63,6 +65,30 @@ describe("acquireTaskWorktree", () => {
expect(store.updateTask).toHaveBeenCalledWith("FN-1", { worktree: "/tmp/pooled", branch: "fusion/fn-1" });
});
it("releases acquired pooled worktree when prepareForTask returns reclaimed path", async () => {
const release = vi.fn();
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/live-existing",
reclaimed: true,
existingTipSha: "abc123",
strandedCommitCount: 2,
}),
release,
} as any,
createWorktree: vi.fn(),
});
expect(release).toHaveBeenCalledWith("/tmp/pooled");
});
it("creates fresh when pool disabled", async () => {
const createWorktree = vi.fn().mockResolvedValue({ path: "/tmp/new", branch: "fusion/fn-1" });
const result = await acquireTaskWorktree({

View File

@@ -154,6 +154,9 @@ export async function acquireTaskWorktree(opts: AcquireTaskWorktreeOptions): Pro
const prepared = typeof preparedRaw === "string"
? { branch: preparedRaw, worktreePath: pooled, reclaimed: false as const }
: preparedRaw;
if (prepared.reclaimed && prepared.worktreePath !== pooled) {
pool.release(pooled);
}
worktreePath = prepared.worktreePath;
branch = prepared.branch;
acquiredFromPool = true;