fix: include preferences.md in worktree sync and initial seed
preferences.md was missing from both ROOT_STATE_FILES (used by syncGsdStateToWorktree and syncWorktreeStateBack) and the copyPlanningArtifacts file list (initial worktree seed). Because .gsd/ is gitignored, worktrees start with an empty .gsd/ directory — the bootstrap is the only opportunity to carry config over. Without preferences.md, post_unit_hooks, skill rules, custom instructions, and all other preference-driven config silently stop working once auto-mode enters a worktree. Closes #2684
This commit is contained in:
parent
913984c26e
commit
fa2bb7e8a8
2 changed files with 87 additions and 0 deletions
|
|
@ -82,6 +82,8 @@ const ROOT_STATE_FILES = [
|
|||
"QUEUE.md",
|
||||
"completed-units.json",
|
||||
"metrics.json",
|
||||
"preferences.md", // #2684: without this, post_unit_hooks and all preference-driven
|
||||
// config silently stop working inside worktrees.
|
||||
] as const;
|
||||
|
||||
/**
|
||||
|
|
@ -946,6 +948,8 @@ function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
|
|||
"STATE.md",
|
||||
"KNOWLEDGE.md",
|
||||
"OVERRIDES.md",
|
||||
"preferences.md", // #2684: must be seeded so post_unit_hooks and
|
||||
// preference-driven config work inside worktrees.
|
||||
]) {
|
||||
safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Regression tests for #2684: preferences.md must be included in both
|
||||
* ROOT_STATE_FILES (sync) and copyPlanningArtifacts (initial seed).
|
||||
*
|
||||
* Without this, post_unit_hooks and all preference-driven config silently
|
||||
* stop working inside auto-mode worktrees.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
|
||||
test("#2684: ROOT_STATE_FILES includes preferences.md", () => {
|
||||
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
||||
const src = readFileSync(srcPath, "utf-8");
|
||||
|
||||
const constIdx = src.indexOf("ROOT_STATE_FILES");
|
||||
assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
|
||||
|
||||
const arrayStart = src.indexOf("[", constIdx);
|
||||
const arrayEnd = src.indexOf("]", arrayStart);
|
||||
const block = src.slice(arrayStart, arrayEnd);
|
||||
|
||||
assert.ok(
|
||||
block.includes("preferences.md"),
|
||||
"preferences.md should be in ROOT_STATE_FILES list",
|
||||
);
|
||||
});
|
||||
|
||||
test("#2684: copyPlanningArtifacts file list includes preferences.md", () => {
|
||||
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
||||
const src = readFileSync(srcPath, "utf-8");
|
||||
|
||||
// Find the copyPlanningArtifacts function body
|
||||
const fnIdx = src.indexOf("function copyPlanningArtifacts");
|
||||
assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
|
||||
|
||||
// Extract function body (up to the next top-level function)
|
||||
const fnBody = src.slice(fnIdx, fnIdx + 1500);
|
||||
|
||||
assert.ok(
|
||||
fnBody.includes('"preferences.md"'),
|
||||
"preferences.md should be in copyPlanningArtifacts file list",
|
||||
);
|
||||
});
|
||||
|
||||
test("#2684: syncGsdStateToWorktree copies preferences.md", async () => {
|
||||
// Functional test: create a mock source and destination, call the sync
|
||||
const srcBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-src-"));
|
||||
const dstBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-dst-"));
|
||||
const srcGsd = join(srcBase, ".gsd");
|
||||
const dstGsd = join(dstBase, ".gsd");
|
||||
mkdirSync(srcGsd, { recursive: true });
|
||||
mkdirSync(dstGsd, { recursive: true });
|
||||
|
||||
try {
|
||||
// Write a preferences.md in source
|
||||
writeFileSync(
|
||||
join(srcGsd, "preferences.md"),
|
||||
"---\nversion: 1\n---\n\npost_unit_hooks:\n - name: notify\n command: echo done\n",
|
||||
);
|
||||
|
||||
// Import and call syncGsdStateToWorktree
|
||||
const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
|
||||
syncGsdStateToWorktree(srcBase, dstBase);
|
||||
|
||||
// Verify preferences.md was copied
|
||||
assert.ok(
|
||||
existsSync(join(dstGsd, "preferences.md")),
|
||||
"preferences.md should be copied to worktree",
|
||||
);
|
||||
|
||||
const content = readFileSync(join(dstGsd, "preferences.md"), "utf-8");
|
||||
assert.ok(
|
||||
content.includes("post_unit_hooks"),
|
||||
"copied preferences.md should contain the hooks config",
|
||||
);
|
||||
} finally {
|
||||
rmSync(srcBase, { recursive: true, force: true });
|
||||
rmSync(dstBase, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue