FN-5772: re-anchor nested task worktree paths
Fix executor worktree invariant handling by re-anchoring nested task worktree paths to the actual git top-level. - add nested worktree root detection that only re-anchors when the top-level is a registered worktree inside the configured worktrees directory - update executor liveness gating and verifyWorktreeInvariants to persist re-anchored task.worktree values and retry checks safely - emit a new run-audit git mutation (worktree:reanchored) and add reliability tests/docs coverage plus a patch changeset Files changed: .changeset/fn-5772-worktree-reanchor.md | 7 +++ docs/architecture.md | 4 +- packages/engine/src/__tests__/reliability-interactions/executor-liveness-gate.test.ts | 33 ++++++++++ packages/engine/src/__tests__/verify-worktree-invariants-missing.test.ts | 62 +++++++++++++++++- packages/engine/src/__tests__/worktree-reanchor-nested-root.test.ts | 73 ++++++++++++++++++++++ packages/engine/src/executor.ts | 54 ++++++++++++++-- packages/engine/src/run-audit.ts | 1 + packages/engine/src/worktree-pool.ts | 63 +++++++++++++++++++ 8 files changed, 290 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-5772 Fusion-Task-Lineage: 475de161-7bc1-4359-bb76-20c4c07196d9
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import "./executor-test-helpers.js";
|
||||
import { TaskExecutor } from "../executor.js";
|
||||
import { createMockStore, mockedExistsSync, resetExecutorMocks } from "./executor-test-helpers.js";
|
||||
import { createMockStore, mockedExecSync, mockedExistsSync, resetExecutorMocks } from "./executor-test-helpers.js";
|
||||
|
||||
describe("FN-009: verifyWorktreeInvariants with missing worktree directory", () => {
|
||||
let executor: TaskExecutor;
|
||||
@@ -70,6 +70,66 @@ describe("FN-009: verifyWorktreeInvariants with missing worktree directory", ()
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it("re-anchors nested task worktree to registered root and passes invariants", async () => {
|
||||
const task = {
|
||||
id: "FN-9004",
|
||||
title: "Test",
|
||||
description: "Test",
|
||||
column: "in-progress",
|
||||
worktree: "/repo/.worktrees/gentle-flame/packages/core",
|
||||
branch: "fusion/fn-9004",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
mockedExistsSync.mockReturnValue(true);
|
||||
mockedExecSync.mockImplementation((cmd: string) => {
|
||||
if (cmd.includes("rev-parse --show-toplevel")) return Buffer.from("/repo/.worktrees/gentle-flame\n");
|
||||
if (cmd.includes("worktree list --porcelain")) {
|
||||
return Buffer.from("worktree /repo\nbranch refs/heads/main\n\nworktree /repo/.worktrees/gentle-flame\nbranch refs/heads/fusion/fn-9004\n");
|
||||
}
|
||||
if (cmd.includes("rev-parse --abbrev-ref HEAD")) return Buffer.from("fusion/fn-9004\n");
|
||||
if (cmd.includes("rev-list --count")) return Buffer.from("1\n");
|
||||
return Buffer.from("");
|
||||
});
|
||||
|
||||
const result = await (executor as any).verifyWorktreeInvariants(task);
|
||||
|
||||
expect(result).toEqual({ ok: true });
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-9004", { worktree: "/repo/.worktrees/gentle-flame" });
|
||||
});
|
||||
|
||||
it("preserves wrong_toplevel for non-reanchorable mismatch", async () => {
|
||||
const task = {
|
||||
id: "FN-9005",
|
||||
title: "Test",
|
||||
description: "Test",
|
||||
column: "in-progress",
|
||||
worktree: "/repo/.worktrees/gentle-flame/packages/core",
|
||||
branch: "fusion/fn-9005",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
mockedExistsSync.mockReturnValue(true);
|
||||
mockedExecSync.mockImplementation((cmd: string) => {
|
||||
if (cmd.includes("rev-parse --show-toplevel")) return Buffer.from("/repo\n");
|
||||
return Buffer.from("");
|
||||
});
|
||||
|
||||
const result = await (executor as any).verifyWorktreeInvariants(task);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.reason).toBe("wrong_toplevel");
|
||||
expect(store.updateTask).not.toHaveBeenCalledWith("FN-9005", expect.objectContaining({ worktree: expect.any(String) }));
|
||||
});
|
||||
|
||||
it("preserves validation failure when worktree path is null", async () => {
|
||||
const task = {
|
||||
id: "FN-9003",
|
||||
|
||||
Reference in New Issue
Block a user