* 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>
The remote runner caught three families this branch caused. W028 moves the
generated health table to 35 rows / 32 rules, so gen-health-docs.test.cjs
is updated in both its assertion and its title - a title carrying the old
count is a test that lies about what it checks.
The three negative-proof rows no longer match a literal report substring.
They assert the typed signal instead: buildShadowReport reports
not_shadowed, renderShadowReport returns no lines, and stderr contains
none of those lines. That takes the new allow-test-rule annotations to
zero rather than lifting the ceiling to fit them.
health.md's growth is acknowledged at its existing key. A second fragment
on the same key is a duplicate-ack error, and the re-arm path only fires
on a reworded reason at the same key.
Refs #2873
gsd-test found two independent gaps around the newly-generated
health.md tables:
- emitted-attribution.test.cjs requires an acknowledgment for
health.md's 2271-byte growth (16-code hand-maintained table -> 34-row
generated table, this phase's explicit acceptance criterion). Adds
tests/emitted-drift-acks/3309-health-docs-generated.json. Removes
2573-state-head-freshness.json's now-inert health.md entry (that
fragment's growth already landed on origin/next, the diff base this
branch is compared against, so it has nothing left to acknowledge —
and the guard forbids two fragments naming the same path).
- runtime-converters.test.cjs's health.md content-consistency checks
asserted stale text from the old hand-written table: a regex that
false-positived on the new table's own unrelated W020 row (worktree
scan degradation, a different diagnostic than the W025 isolation
warning it was meant to detect), and anchors expecting the old
table's exact last row / footnote wording. Narrowed the regex to
require the literal use_worktrees config key, and updated the
anchors to the new table's real shape (I001/I010 as the last rows,
the new generated-table footnote).