FN-5851: fix goal tool store resolution

Resolve goal tool lookups to the canonical project store from Fusion worktree directories.

- recognize both legacy .worktrees and .fusion/worktrees paths when resolving the project root for pi extensions
- add CLI coverage proving fn_goal_list and fn_goal_show return dashboard-created goals from a Fusion worktree cwd
- extend core worktree-resolution tests and keep the branch conflict recovery test worktree path isolated in tmpdir
- add a patch changeset for the published CLI fix

Files changed:
 .changeset/fn-5851-goal-store-resolution.md        |   5 +
 packages/cli/src/__tests__/goal-store-resolution.test.ts    | 104 +++++++++++++++++++++
 packages/core/src/__tests__/pi-extensions.test.ts  |  14 ++-
 packages/core/src/pi-extensions.ts                 |  12 ++-
 packages/engine/src/__tests__/branch-conflicts-recovery.test.ts    |   2 +-
 5 files changed, 130 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-5851

Fusion-Task-Lineage: ec4c8239-b953-4df7-9f72-02e17e4b0851
This commit is contained in:
gsxdsm
2026-06-01 23:39:20 -07:00
parent c60dae1cb9
commit 7d1708f023
5 changed files with 130 additions and 7 deletions

View File

@@ -8,11 +8,15 @@ describe("getProjectRootFromWorktree", () => {
it("detects POSIX worktree paths", () => {
expect(getProjectRootFromWorktree("/repo/.worktrees/fn-001")).toBe("/repo");
expect(getProjectRootFromWorktree("/repo/.worktrees/fn-001/src/file.ts")).toBe("/repo");
expect(getProjectRootFromWorktree("/repo/.fusion/worktrees/fn-001")).toBe("/repo");
expect(getProjectRootFromWorktree("/repo/.fusion/worktrees/fn-001/src/file.ts")).toBe("/repo");
});
it("detects Windows worktree paths", () => {
expect(getProjectRootFromWorktree("C:\\repo\\.worktrees\\fn-001")).toBe("C:\\repo");
expect(getProjectRootFromWorktree("C:\\repo\\.worktrees\\fn-001\\src\\file.ts")).toBe("C:\\repo");
expect(getProjectRootFromWorktree("C:\\repo\\.fusion\\worktrees\\fn-001")).toBe("C:\\repo");
expect(getProjectRootFromWorktree("C:\\repo\\.fusion\\worktrees\\fn-001\\src\\file.ts")).toBe("C:\\repo");
});
it("supports configured candidate worktrees dir paths", () => {
@@ -43,10 +47,14 @@ describe("resolvePiExtensionProjectRoot", () => {
try {
mkdirSync(join(root, ".fusion"), { recursive: true });
mkdirSync(join(root, ".worktrees", "feature", ".fusion"), { recursive: true });
const cwd = join(root, ".worktrees", "feature", "sub");
mkdirSync(cwd, { recursive: true });
mkdirSync(join(root, ".fusion", "worktrees", "feature", ".fusion"), { recursive: true });
const legacyCwd = join(root, ".worktrees", "feature", "sub");
const fusionCwd = join(root, ".fusion", "worktrees", "feature", "sub");
mkdirSync(legacyCwd, { recursive: true });
mkdirSync(fusionCwd, { recursive: true });
expect(resolvePiExtensionProjectRoot(cwd)).toBe(root);
expect(resolvePiExtensionProjectRoot(legacyCwd)).toBe(root);
expect(resolvePiExtensionProjectRoot(fusionCwd)).toBe(root);
} finally {
rmSync(root, { recursive: true, force: true });
}

View File

@@ -40,9 +40,15 @@ export function getProjectRootFromWorktree(
cwd: string,
opts?: { worktreesDirCandidates?: string[] },
): string | null {
const legacyMatch = cwd.match(/^(.+?)[\\/]\.worktrees[\\/][^\\/]+(?:[\\/]|$)/);
if (legacyMatch) {
return legacyMatch[1]!;
const knownWorktreePatterns = [
/^(.+?)[\\/]\.worktrees[\\/][^\\/]+(?:[\\/]|$)/,
/^(.+?)[\\/]\.fusion[\\/]worktrees[\\/][^\\/]+(?:[\\/]|$)/,
];
for (const pattern of knownWorktreePatterns) {
const match = cwd.match(pattern);
if (match) {
return match[1]!;
}
}
for (const candidate of opts?.worktreesDirCandidates ?? []) {