import { describe, expect, it, vi } from "vitest"; import type { TaskDetail } from "@fusion/core"; import "./executor-test-helpers.js"; import { TaskExecutor } from "../executor.js"; import { createMockStore, resetExecutorMocks } from "./executor-test-helpers.js"; const now = "2026-06-23T00:00:00.000Z"; function task(overrides: Partial = {}): TaskDetail { return { id: "FN-GRAPH-REQUEUE", title: "Graph execute recovery", description: "Gate coverage for execute-node self-requeue preservation", column: "in-progress", dependencies: [], steps: [{ name: "Implement", status: "pending" }], currentStep: 0, log: [], branch: "fusion/fn-graph-requeue", baseBranch: "main", worktree: "/tmp/fusion-fn-graph-requeue", status: null, error: null, paused: false, userPaused: false, autoMerge: true, mergeRetries: 0, createdAt: now, updatedAt: now, ...overrides, } as TaskDetail; } describe("executor graph execute self-requeue gate", () => { it("preserves executor todo recovery when the live refetch is stale in-progress", async () => { resetExecutorMocks(); const store = createMockStore(); const live = task({ column: "in-progress" }); store.getTask.mockResolvedValue(live); store.getSettings.mockResolvedValue({ autoMerge: true, maxAutoMergeRetries: 3, maxConcurrent: 2, maxWorktrees: 4, pollIntervalMs: 15000, }); const executor = new TaskExecutor(store, "/tmp/test"); /* FNXC:WorkflowLifecycle 2026-06-23-23:03: The workflow cutover gate must directly cover the graph execute self-requeue guard. A stale live `in-progress` refetch after an inner executor moved the task to `todo` must not be parked in review or marked failed. */ (executor as any).graphRouting.add(live.id); (executor as any).markGraphExecuteSelfRequeued(live.id); try { await (executor as any).handleGraphFailure(live, { disposition: "failed", outcome: "failure", visitedNodeIds: ["execute"], context: { "node:execute:value": "recoverable" }, }); } finally { (executor as any).graphRouting.delete(live.id); } expect(store.logEntry).toHaveBeenCalledWith( live.id, expect.stringContaining("executor recovery preserved"), undefined, undefined, ); expect(store.moveTask).not.toHaveBeenCalledWith(live.id, "in-review", expect.anything()); expect(store.updateTask).not.toHaveBeenCalledWith( live.id, expect.objectContaining({ status: "failed" }), expect.anything(), ); }); });