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:
gsxdsm
2026-05-31 08:25:03 -07:00
parent 5b4eecb5d4
commit b154844286
8 changed files with 290 additions and 7 deletions

View File

@@ -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",