* feat(#3586): warn when .planning/ is gitignored but still tracked git ignore rules have no effect on files git already tracks, so a project that committed .planning/ before ignoring it keeps staging those files -- while commit_docs correctly resolves to false, which is exactly what makes the contradiction invisible. The probe lives in the SNAPSHOT BUILDER, not the rule: Rule.check may perform no ambient I/O (ADR-3180 8.1 rule 1, enforced by lint-planning-snapshot-bypass). buildPlanningTrackedField follows buildWorktreeHealthField's precedent -- injected execGit, bounded, degrading to UNREADABLE with a typed reason rather than throwing. W024 went inline instead only because no snapshot field carried its fact; that precondition does not apply here. W029 fires only on COMPLETE scope with ignored and tracked both true, so a degraded probe yields neither a finding nor a false all-clear, and the default project (tracked, not ignored) stays silent. The remedy is ADVISE-only -- --repair never untracks anything. * docs(#3586): document W029 and correct the health rule count CONFIGURATION.md documented the gitignore auto-detect without the caveat that ignore rules do not affect already-tracked files -- the very gap W029 exists to surface. Adds the caveat, the warning, its remedy, and why --repair will not act on it. CONTEXT.md's rule count was stale at 31 before this change (actual 32 through W028); corrected to 33 and pointed at the two other places the count is locked, so the next editor updates all three together. * fix(#3586): treat ls-files overflow as tracked, add CLI-level W029 tests Review findings. Security (minor, confirmed): execGit sets no maxBuffer, so Node's 1MB default applies to git ls-files. A .planning/ tree large enough to overflow it failed into git_list_failed and silenced W029 -- a false negative in exactly the large-history case most likely to have the real bug. Overflow is now treated as PROOF of tracking (the output was non-empty by definition) and resolves to tracked:true, scope COMPLETE, reason ok_truncated. Spec (major): test-matrix rows C1 and C2 were never implemented -- there was no CLI-level integration test at all, only rule-level ones. Both now drive the real validate-health dispatch and confirm W029 is reachable end-to-end. Known limit documented, not papered over: a deliberate git add -f under an otherwise-ignored .planning/ raises the same signal as the accidental case. There is no reliable way to tell them apart, the finding is advisory-only, and a heuristic that cannot actually distinguish them would be worse than the honest caveat. * test(#3586): update frozen health-doc counts and acknowledge health.md growth The remote matrix caught three gates that lint:ci does not cover. gen-health-docs.test.cjs froze a 35-row / 32-rule assertion; W029 makes it 36/33. Updated both the assertion and the test NAME, which embeds the counts -- a stale name is a lie even when the assertion passes. The second reported failure was the same assertion surfacing at describe-rollup granularity, not a distinct bug. emitted-attribution's growth arm needed an ack for the generated health.md. health.md was already named in 3309-health-docs-generated.json, and two ack sources naming one path is a hard error -- so a new fragment was not an option. That fragment's own history shows the pattern: #3309 created it, #2873 amended it in place for W028. Amended again for W029, with a note recording why this one file is amended rather than joined by a sibling. * docs(#3586): add the private-planning how-to and fix a wrong link docs/CONFIGURATION.md pointed 'Configure private planning' at how-to/configure-model-profiles.md -- an unrelated page -- and no private-planning how-to existed at all. Found while editing that section. The how-to test genuinely fires here: going private is four steps and crosses planning.search_gitignored, a setting owned by another concern, so a reference table structurally cannot carry it. The new page walks the whole sequence and leads with the step people miss -- .gitignore does not untrack what git already tracks -- which is the exact state W029 now detects. Also corrects 'artefacts' to 'artifacts' (repo house style is American). * chore(#3586): backfill changeset pr number to 3598 --------- Co-authored-by: sim <sim@local>
3.4 KiB
Keep planning docs out of a shared repo
You are working in a team repo and want GSD's .planning/ artifacts — PLAN.md, SUMMARY.md,
ROADMAP.md, STATE.md — to stay on your machine instead of appearing in commits your teammates read.
This takes four steps, and the third is the one people miss.
1. Turn off doc commits
gsd-tools config-set planning.commit_docs false
gsd-tools query commit now returns a skipped envelope instead of committing, and every workflow
that writes a planning artifact honors it.
2. Ignore the directory
Add to .gitignore:
.planning/
This is also enough on its own: when .planning/ is gitignored and config.json sets no explicit
value, GSD auto-resolves commit_docs to false. Setting it explicitly in step 1 is clearer, and
it survives someone later editing .gitignore.
3. Untrack what git is already tracking
This is the step that catches people out. .gitignore only stops git picking up new files.
It has no effect on files already committed — git keeps tracking those, so git add -A keeps
staging them even though steps 1 and 2 are both done.
Because GSD's default is commit_docs: true, most existing projects already have .planning/
in history, which makes this the common case rather than an edge case.
git rm -r --cached .planning/
git commit -m "chore: stop tracking planning docs"
--cached removes the files from the index only — your files on disk are untouched.
To check whether this applies to you before running it:
git ls-files .planning
Any output means git is still tracking those paths.
4. Keep search working
With .planning/ ignored, tools that respect .gitignore stop searching it — including GSD's own
broad searches, which is rarely what you want, since the planning docs are exactly what you want an
agent to read.
gsd-tools config-set planning.search_gitignored true
This adds --no-ignore to broad searches so .planning/ is still found locally.
Verify
gsd-tools validate health
A clean result means you are done. If you skipped step 3, you will see:
W029 .planning/ is gitignored but N file(s) are still tracked by git
Fix: git rm -r --cached .planning/ && git commit -m "chore: stop tracking planning docs"
W029 is advisory. GSD will not untrack files for you, and --repair deliberately does not act on
it — removing files from the index is destructive and the timing is yours.
Notes
- A deliberate force-add also raises
W029. If you intentionally keep one file tracked under an otherwise-ignored.planning/(git add -f .planning/decisions.md), the warning still appears. There is no reliable way to tell an intentional force-add from the accidental case, so the warning is expected there too. - Teammates who have already pulled the tracked files will see them deleted by your step-3 commit. That is the intended effect — the files leave the repo, not their working copies of your branch — but say so in the commit message or PR description so it is not a surprise.
- Per-phase control (committing docs for an architecture phase while keeping execution phases
local) is tracked separately;
commit_docsis currently project-wide.