From a1168cba134ca4df91e70f0d81b0b72ae1d9821b Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 18 Mar 2026 17:05:30 -0400 Subject: [PATCH] fix: replace blanket git clean .gsd/ with targeted runtime file removal (#1252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The git clean -fd .gsd/ added in #1239 was too aggressive — it could remove untracked milestone and planning files on projects where .gsd/ isn't fully gitignored (e.g., manage_gitignore: false). Replaced with explicit removal of only runtime state files: - STATE.md, completed-units.json, auto.lock, gsd.db - .gsd/runtime/ directory Milestone directories, DECISIONS.md, REQUIREMENTS.md, PROJECT.md and all other planning artifacts are never touched. Fixes #1250 --- src/resources/extensions/gsd/auto-worktree.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index b03341715..bf856593c 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -6,7 +6,7 @@ * manages create, enter, detect, and teardown for auto-mode worktrees. */ -import { existsSync, readFileSync, realpathSync, unlinkSync, statSync } from "node:fs"; +import { existsSync, readFileSync, realpathSync, unlinkSync, statSync, rmSync } from "node:fs"; import { isAbsolute, join, sep } from "node:path"; import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js"; import { execSync, execFileSync } from "node:child_process"; @@ -370,14 +370,18 @@ export function mergeMilestoneToMain( // squash merge can fail with "Your local changes would be overwritten" (#1127). autoCommitDirtyState(originalBasePath_); - // 3b. Remove untracked .gsd/ files that syncStateToProjectRoot copied. - // autoCommitDirtyState stages and commits everything (git add -A), but if - // the project root branch has no .gsd/ tracking (e.g., .gsd/ is gitignored), - // these files remain untracked and cause "untracked working tree files would - // be overwritten by merge" during squash-merge (#1237). + // 3b. Remove untracked .gsd/ runtime files that syncStateToProjectRoot copied. + // Only clean specific runtime files — NEVER touch milestones/, decisions, or + // other planning artifacts that represent user work (#1250). + const runtimeFilesToClean = ["STATE.md", "completed-units.json", "auto.lock", "gsd.db"]; + for (const f of runtimeFilesToClean) { + const p = join(originalBasePath_, ".gsd", f); + try { if (existsSync(p)) unlinkSync(p); } catch { /* non-fatal */ } + } try { - execFileSync("git", ["clean", "-fd", ".gsd/"], { cwd: originalBasePath_, stdio: "pipe" }); - } catch { /* non-fatal — clean failure shouldn't block merge attempt */ } + const runtimeDir = join(originalBasePath_, ".gsd", "runtime"); + if (existsSync(runtimeDir)) rmSync(runtimeDir, { recursive: true, force: true }); + } catch { /* non-fatal */ } // 4. Resolve integration branch — prefer milestone metadata, fall back to preferences / "main" const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};