* 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>
This commit is contained in:
@@ -505,6 +505,31 @@ If none match, the starting directory is returned unchanged. Explicit `--project
|
||||
|
||||
If `.planning/` is in `.gitignore`, `commit_docs` is automatically `false` regardless of config.json. This prevents git errors.
|
||||
|
||||
#### Caveat: `.gitignore` does not affect files git already tracks
|
||||
|
||||
Adding `.planning/` to `.gitignore` stops git from picking up **new** files there. It has no effect
|
||||
on files already committed — git keeps tracking those, so `git add -A` keeps staging them even
|
||||
though `commit_docs` now resolves to `false`. Because GSD's default is `commit_docs: true`, most
|
||||
existing projects have already committed `.planning/`, which makes this the common case rather than
|
||||
the edge case.
|
||||
|
||||
`/gsd-health` reports this contradiction as **`W029`**:
|
||||
|
||||
```
|
||||
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"
|
||||
```
|
||||
|
||||
The warning is advisory. GSD never untracks files for you — `--repair` deliberately will not act on
|
||||
`W029`, because removing files from the index is destructive and the timing is yours to choose.
|
||||
|
||||
Once you run the `git rm -r --cached` above, `.planning/` is untracked, the ignore rule takes full
|
||||
effect, and the warning clears.
|
||||
|
||||
Note: a file deliberately force-added under an otherwise-ignored `.planning/` (`git add -f
|
||||
.planning/keep.md`) triggers this same warning — there is no reliable way to distinguish an
|
||||
intentional force-add from the accidental case above, so `W029` is expected in that situation too.
|
||||
|
||||
---
|
||||
|
||||
## Hook Settings
|
||||
@@ -523,7 +548,7 @@ The prompt injection guard hook (`gsd-prompt-guard.js`) is always active and can
|
||||
|
||||
### Private Planning Setup
|
||||
|
||||
When `planning.commit_docs` is `false` and `.planning/` is listed in `.gitignore`, GSD treats planning artefacts as local-only. `planning.search_gitignored: true` ensures broad searches still include the `.planning/` directory in this configuration. See [Configure private planning](how-to/configure-model-profiles.md) for setup steps.
|
||||
When `planning.commit_docs` is `false` and `.planning/` is listed in `.gitignore`, GSD treats planning artifacts as local-only. `planning.search_gitignored: true` ensures broad searches still include the `.planning/` directory in this configuration. See [Keep planning docs out of a shared repo](how-to/keep-planning-docs-private.md) for the full setup, including untracking files git is already tracking.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md)
|
||||
- [Resolve a contract-drift finding](how-to/resolve-contract-drift-findings.md) — bring an agent's completion contract, read-tag gate, or deleted-file test reference back into agreement with the registry
|
||||
- [Resolve unreachable-guard findings](how-to/resolve-unreachable-guard-findings.md) — fix shell guards whose fallback arm cannot run, and tell "nothing to report" apart from "could not look"
|
||||
- [Resolve an ESLint glob-coverage finding](how-to/resolve-eslint-coverage-findings.md) — bring a source file that matches no lint rule under coverage, or record a reasoned exemption
|
||||
- [Keep planning docs out of a shared repo](how-to/keep-planning-docs-private.md) — make `.planning/` local-only, including untracking files git already tracks (the step `.gitignore` alone cannot do)
|
||||
- [Plan a phase](how-to/plan-a-phase.md) — run research, decompose work, and verify plan quality
|
||||
- [Execute a phase](how-to/execute-a-phase.md) — run plans in parallel waves with fresh-context subagents
|
||||
- [Verify and ship](how-to/verify-and-ship.md) — walk through completed work, diagnose failures, and create the PR
|
||||
|
||||
96
docs/how-to/keep-planning-docs-private.md
Normal file
96
docs/how-to/keep-planning-docs-private.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```bash
|
||||
gsd-tools config-set planning.search_gitignored true
|
||||
```
|
||||
|
||||
This adds `--no-ignore` to broad searches so `.planning/` is still found locally.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
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_docs` is currently project-wide.
|
||||
|
||||
## Related
|
||||
|
||||
- [Configuration reference — `planning.commit_docs`](../CONFIGURATION.md#planning-settings)
|
||||
- [Configuration reference — auto-detection and the tracked-files caveat](../CONFIGURATION.md#auto-detection)
|
||||
Reference in New Issue
Block a user