fix(#2843): findProjectRoot stops at git-repo boundaries, not just MAX_DEPTH (#2909)

* fix(#2843): findProjectRoot stops at git-repo boundaries, not just MAX_DEPTH

findProjectRoot's heuristic (3) used isInsideGitRepo(parent), which only checked
'does SOME .git exist between start and the ancestor' — it never verified the
.git was co-located with / bounded the trusted .planning/. A nested child repo
(own .git, no .planning) under an ancestor GSD project satisfied the check, so
resolution silently crossed into the ancestor project (wrong identity, exit 0).

Add nearestGitRoot(from, upTo) (fs-walk, no spawn) and use it in heuristics (3)
and (4): if the caller is inside its own nested repo whose root is strictly below
the candidate ancestor, do not return that ancestor. The plain-descendant (#1414),
co-located .git+.planning, and sub_repos/multiRepo cases are unchanged.

Regression test: a nested child .git under an ancestor .planning no longer
resolves to the ancestor; the co-located single-repo case still resolves.

* chore(#2843): backfill changeset PR 2909

---------

Co-authored-by: Test <test@example.com>
(cherry picked from commit 39dbe5e0f5)
This commit is contained in:
Tom Boucher
2026-07-31 02:30:50 -04:00
committed by sim
parent 3aabb0f441
commit e1b275766d
3 changed files with 90 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2909
---
**`findProjectRoot` no longer silently resolves to a parent project across a git-repo boundary** — when invoked from a nested git repository that has no `.planning/` of its own, resolution stays within the caller's repo (or falls back to the start directory) instead of crossing into an ancestor GSD project. The existing plain-descendant and co-located `.git`+`.planning` cases are unchanged.