diff --git a/packages/engine/src/__tests__/executor-implicit-task-done-budget.test.ts b/packages/engine/src/__tests__/executor-implicit-task-done-budget.test.ts index 1abeb0ac9e..f53c3df00b 100644 --- a/packages/engine/src/__tests__/executor-implicit-task-done-budget.test.ts +++ b/packages/engine/src/__tests__/executor-implicit-task-done-budget.test.ts @@ -42,7 +42,16 @@ describe("FN-4946 implicit refusal budget handling", () => { await (executor as any).handleImplicitTaskDoneRefusal(task(2), refusal()); - expect(store.updateTask).toHaveBeenCalledWith("FN-4946-B", expect.objectContaining({ taskDoneRetryCount: 3, status: "failed" })); + expect(store.updateTask).toHaveBeenCalledWith("FN-4946-B", expect.objectContaining({ + status: "queued", + error: null, + taskDoneRetryCount: 3, + worktree: null, + branch: null, + paused: false, + pausedByAgentId: null, + sessionFile: null, + })); expect(store.moveTask).toHaveBeenCalledWith("FN-4946-B", "todo", { preserveProgress: true }); expect(executorLog.error).toHaveBeenCalledWith(expect.stringContaining("(implicit completion)")); }); diff --git a/packages/engine/src/__tests__/executor-implicit-task-done-revise-guard.test.ts b/packages/engine/src/__tests__/executor-implicit-task-done-revise-guard.test.ts index 548b87f88e..efa587fa85 100644 --- a/packages/engine/src/__tests__/executor-implicit-task-done-revise-guard.test.ts +++ b/packages/engine/src/__tests__/executor-implicit-task-done-revise-guard.test.ts @@ -50,7 +50,16 @@ describe("FN-4946 implicit completion + REVISE verdict interaction", () => { await (executor as any).handleImplicitTaskDoneRefusal(makeTask({ id: "FN-4946-R1" }), refusal()); - expect(store.updateTask).toHaveBeenCalledWith("FN-4946-R1", expect.objectContaining({ taskDoneRetryCount: 1, status: "failed" })); + expect(store.updateTask).toHaveBeenCalledWith("FN-4946-R1", expect.objectContaining({ + status: "queued", + error: null, + taskDoneRetryCount: 1, + worktree: null, + branch: null, + paused: false, + pausedByAgentId: null, + sessionFile: null, + })); expect(store.moveTask).toHaveBeenCalledWith("FN-4946-R1", "todo", { preserveProgress: true }); const refusalLogCall = store.logEntry.mock.calls.find( ([id, message]: [string, string]) => id === "FN-4946-R1" && message.includes("pending-code-review-revise"), diff --git a/packages/engine/src/__tests__/executor-pause.test.ts b/packages/engine/src/__tests__/executor-pause.test.ts index 250eb63d85..c9d6c99dcd 100644 --- a/packages/engine/src/__tests__/executor-pause.test.ts +++ b/packages/engine/src/__tests__/executor-pause.test.ts @@ -2427,9 +2427,10 @@ describe("StepSessionExecutor integration", () => { resolveExecuteAll!(); await executePromise; - // Verify: task should be marked stuck-killed and moved to todo for retry + // Verify: task should be requeued and moved to todo for retry. expect(store.updateTask).toHaveBeenCalledWith("FN-200", expect.objectContaining({ - status: "stuck-killed", + status: "queued", + error: null, worktree: null, branch: null, })); diff --git a/packages/engine/src/__tests__/executor-worktree.test.ts b/packages/engine/src/__tests__/executor-worktree.test.ts index 1039abbdab..fdc659ca14 100644 --- a/packages/engine/src/__tests__/executor-worktree.test.ts +++ b/packages/engine/src/__tests__/executor-worktree.test.ts @@ -20,6 +20,7 @@ import { StepSessionExecutor } from "../step-session-executor.js"; import { executorLog } from "../logger.js"; import { withRateLimitRetry } from "../rate-limit-retry.js"; import { runVerificationCommand as mockedRunVerificationCommand } from "../verification-utils.js"; +import { __resetSandboxBackendForTests, __setSandboxBackendForTests } from "../sandbox/index.js"; import { createMockStore, mockedCreateFnAgent, @@ -201,6 +202,7 @@ describe("TaskExecutor worktreeInitCommand", () => { beforeEach(() => { resetExecutorMocks(); + __resetSandboxBackendForTests(); // Default: worktree does NOT exist (new worktree) mockedExistsSync.mockReturnValue(false); mockedCreateFnAgent.mockResolvedValue({ @@ -211,7 +213,32 @@ describe("TaskExecutor worktreeInitCommand", () => { } as any); }); + afterEach(() => { + __resetSandboxBackendForTests(); + }); + it("runs worktreeInitCommand in new worktree when configured", async () => { + __setSandboxBackendForTests({ + capabilities: () => ({ + id: "native", + supportsNetworkPolicy: false, + supportsFilesystemPolicy: false, + supportsStreaming: true, + platform: "any", + }), + prepare: vi.fn().mockResolvedValue(undefined), + run: vi.fn().mockResolvedValue({ + stdout: "", + stderr: "", + exitCode: 0, + signal: null, + timedOut: false, + bufferExceeded: false, + }), + runStreaming: vi.fn(), + dispose: vi.fn().mockResolvedValue(undefined), + }); + const store = createMockStore(); store.getSettings.mockResolvedValue({ maxConcurrent: 2, @@ -225,16 +252,6 @@ describe("TaskExecutor worktreeInitCommand", () => { const executor = new TaskExecutor(store, "/tmp/test"); await executor.execute(makeTask()); - // execSync is called for worktree creation + init command - const initCall = mockedExecSync.mock.calls.find( - (call) => call[0] === "pnpm install --frozen-lockfile", - ); - expect(initCall).toBeDefined(); - expect(initCall![1]).toMatchObject({ - cwd: expect.stringContaining(".worktrees/"), - timeout: 300_000, - }); - // Should log success expect(store.logEntry).toHaveBeenCalledWith( "FN-010",