fix: copy mcp.json into auto-mode worktrees (#2791) (#3251)

Add mcp.json to ROOT_STATE_FILES and copyPlanningArtifacts so MCP
server configurations are available inside worktrees.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher 2026-03-30 15:51:37 -04:00 committed by GitHub
parent 01aea09f74
commit 01d7200e7b
2 changed files with 61 additions and 0 deletions

View file

@ -84,6 +84,7 @@ const ROOT_STATE_FILES = [
"QUEUE.md",
"completed-units.json",
"metrics.json",
"mcp.json",
// NOTE: project preferences are intentionally NOT in ROOT_STATE_FILES.
// Forward-sync (main → worktree) is handled explicitly in syncGsdStateToWorktree().
// Back-sync (worktree → main) must NEVER overwrite the project root's copy
@ -1078,6 +1079,7 @@ function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
"STATE.md",
"KNOWLEDGE.md",
"OVERRIDES.md",
"mcp.json",
]) {
safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
}

View file

@ -20,6 +20,7 @@ import {
enterAutoWorktree,
getAutoWorktreeOriginalBase,
getActiveAutoWorktreeContext,
syncGsdStateToWorktree,
} from "../../auto-worktree.ts";
// Note: execSync is used intentionally in tests for git operations with
@ -286,4 +287,62 @@ describe("auto-worktree lifecycle", () => {
teardownAutoWorktree(tempDir, "M004");
}
});
test("#2791: mcp.json copied into worktree via copyPlanningArtifacts", () => {
tempDir = createTempRepo();
const msDir = join(tempDir, ".gsd", "milestones", "M003");
mkdirSync(msDir, { recursive: true });
writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
run("git add .", tempDir);
run("git commit -m \"add milestone\"", tempDir);
// Create mcp.json in .gsd/ AFTER the commit (untracked, like real usage).
// copyPlanningArtifacts should copy it into the worktree's .gsd/.
writeFileSync(
join(tempDir, ".gsd", "mcp.json"),
JSON.stringify({ servers: { test: { command: "echo" } } }),
);
const wtPath = createAutoWorktree(tempDir, "M003");
try {
assert.ok(
existsSync(join(wtPath, ".gsd", "mcp.json")),
"mcp.json should be copied into worktree .gsd/ on creation",
);
} finally {
teardownAutoWorktree(tempDir, "M003");
}
});
test("#2791: mcp.json synced via syncGsdStateToWorktree (ROOT_STATE_FILES)", () => {
tempDir = createTempRepo();
const msDir = join(tempDir, ".gsd", "milestones", "M003");
mkdirSync(msDir, { recursive: true });
writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
run("git add .", tempDir);
run("git commit -m \"add milestone\"", tempDir);
// Create worktree first (no mcp.json yet)
const wtPath = createAutoWorktree(tempDir, "M003");
try {
// Now add mcp.json to the main .gsd/ after worktree was created
writeFileSync(
join(tempDir, ".gsd", "mcp.json"),
JSON.stringify({ servers: { test: { command: "echo" } } }),
);
// Sync should pick up the new mcp.json
const { synced } = syncGsdStateToWorktree(tempDir, wtPath);
assert.ok(synced.includes("mcp.json"), "mcp.json should be in the synced list");
assert.ok(
existsSync(join(wtPath, ".gsd", "mcp.json")),
"mcp.json should exist in worktree after sync",
);
} finally {
teardownAutoWorktree(tempDir, "M003");
}
});
});