fix(#1414): resolve project root from descendant via nearest ancestor .planning/ (Resolution Provenance P1) (#1423)

Add heuristic (4) to findProjectRoot: after heuristics (1)-(3) (sub_repos,
multiRepo, .git+parent-.planning/) are exhausted without a match, perform a
second bounded walk-up within FIND_PROJECT_ROOT_MAX_DEPTH to locate the
nearest ancestor directory containing a .planning/ subdirectory. Returns that
ancestor as the project root, so loadConfig finds the correct config instead
of falling through to defaults when gsd-tools is invoked from a plain
descendant subdirectory of a single-repo project (#1366 cwd-drift gap).

Ordering is load-bearing: the new walk runs AFTER the existing loop so
sub_repos workspaces (where a child sub-repo may have its own .planning/)
still resolve correctly to the parent workspace. The existing own-.planning/
guard (heuristic 0, #1362) and the depth bound (FIND_PROJECT_ROOT_MAX_DEPTH=10)
are both preserved unchanged.

Part of #1411

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-18 00:50:30 -04:00
committed by GitHub
parent 6f2547610e
commit d220ba6fd6
4 changed files with 292 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
---
type: Changed
pr: 1423
---
**`gsd-tools` now resolves the project root from a descendant subdirectory** — `findProjectRoot` walks up to the nearest ancestor directory containing `.planning/` so config loads correctly when invoked outside the project root; previously it fell through to defaults for plain descendant paths (cwd-drift gap #1366). Sub_repos, multiRepo, and `.git`-based heuristics retain priority. (Part of #1411, P1 / #1414)
<!-- docs-exempt: internal resolution heuristic in project-root.cts — no public docs surface for findProjectRoot; behavior change surfaced via config loading, not a user-visible API -->