From 523e910f2148d578aa4610810ab1262f59dfb27c Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Thu, 26 Mar 2026 23:57:17 +0100 Subject: [PATCH] fix: remove preferences.md from ROOT_STATE_FILES to prevent back-sync overwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit preferences.md was in ROOT_STATE_FILES which caused syncWorktreeStateBack() to overwrite the project root's authoritative copy with the worktree's stale copy. The forward-sync (main → worktree) is already handled separately in syncGsdStateToWorktree() as additive-only. Fixes the failing CI test: worktree-preferences-sync.test.ts:107 '#2684: syncWorktreeStateBack does NOT overwrite project root preferences.md' Also updates preferences-worktree-sync.test.ts to assert preferences.md is NOT in ROOT_STATE_FILES (it must be handled separately). --- src/resources/extensions/gsd/auto-worktree.ts | 6 ++++-- .../gsd/tests/preferences-worktree-sync.test.ts | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index 0b9d28bda..037bb516f 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -82,8 +82,10 @@ 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. + // NOTE: preferences.md is 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 + // because the project root is authoritative for preferences (#2684). ] as const; /** diff --git a/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts b/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts index 776c28e70..04a0fbd0f 100644 --- a/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +++ b/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts @@ -11,7 +11,7 @@ import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync import { join } from "node:path"; import { tmpdir } from "node:os"; -test("#2684: ROOT_STATE_FILES includes preferences.md", () => { +test("#2684: preferences.md is NOT in ROOT_STATE_FILES (forward-only sync)", () => { const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts"); const src = readFileSync(srcPath, "utf-8"); @@ -19,12 +19,20 @@ test("#2684: ROOT_STATE_FILES includes preferences.md", () => { assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists"); const arrayStart = src.indexOf("[", constIdx); - const arrayEnd = src.indexOf("]", arrayStart); + const arrayEnd = src.indexOf("] as const", arrayStart); const block = src.slice(arrayStart, arrayEnd); + // preferences.md must NOT be in ROOT_STATE_FILES — it is handled separately + // in syncGsdStateToWorktree() (forward-only, additive). Including it in + // ROOT_STATE_FILES would cause syncWorktreeStateBack() to overwrite the + // authoritative project root copy (#2684). + const entries = block.split("\n") + .map(l => l.trim()) + .filter(l => l.startsWith('"') && l.includes(".md")); + const hasPrefs = entries.some(l => l.includes("preferences.md")); assert.ok( - block.includes("preferences.md"), - "preferences.md should be in ROOT_STATE_FILES list", + !hasPrefs, + "preferences.md must NOT be in ROOT_STATE_FILES (back-sync would overwrite root)", ); });