refactor(pipeline): ignore specs/plans via project-local docs/.gitignore

Replace the global ~/.config/git/ignore mechanism with a committed,
per-project docs/.gitignore whose entries `specs/` and `plans/` are
relative to docs/. The ignore now travels with each repo instead of
living in the user's global excludes.

specify (Step 3) and planner (intro) create docs/.gitignore on demand
if a project has not set it up yet, so a spec/plan can never become
committable. conventions.md § File layout is the single source; the
onboarding fragment tells a new project to set it up.

The global ~/.config/git/ignore block is removed separately (it is
outside this repo).
This commit is contained in:
2026-07-02 11:53:59 +02:00
parent 2bc0cf025f
commit ea54be6535
4 changed files with 32 additions and 20 deletions
+11 -7
View File
@@ -28,13 +28,17 @@ elsewhere is the rare exception and states the override in its
`CLAUDE.md` project facts. `CLAUDE.md` project facts.
Both directories are **git-ignored** — their contents are transient Both directories are **git-ignored** — their contents are transient
working files, never committed (see § Lifecycle). The plugin ignores working files, never committed (see § Lifecycle). The ignore is
them **globally**: `docs/specs/` and `docs/plans/` live in the user's **project-local**: each project commits a `docs/.gitignore` whose two
global git excludes (`~/.config/git/ignore`, the XDG default that entries, `specs/` and `plans/`, are relative to `docs/` and so match
applies to every repo), next to the other plugin-wide ignores. One `docs/specs/` and `docs/plans/`. That `docs/.gitignore` is the one
entry set covers every project — no per-project `.gitignore` line is committed thing — it travels with the repo; the spec and plan files it
needed, though a project MAY add its own if it wants the ignore to covers never are. It is created once (whichever skill first writes a
travel with the repo. working file — `specify`, or `planner` on a direct tidy dispatch —
creates it if absent) and committed like any other repo file, after
which both directories are invisible to git for every future cycle. A
new project sets this up as part of adopting the plugin
(`../templates/CLAUDE.md.fragment`).
## Lifecycle ## Lifecycle
+5 -2
View File
@@ -17,8 +17,11 @@ into bites small enough that a subagent can execute one in
Plans live under `docs/plans` with the fixed name shape Plans live under `docs/plans` with the fixed name shape
`slug.md` — a short kebab-case topic slug, no numeric prefix (see `slug.md` — a short kebab-case topic slug, no numeric prefix (see
`docs/conventions.md` § Naming). `docs/plans` is git-ignored; the plan `docs/conventions.md` § Naming). `docs/plans` is git-ignored via the
is a working file, never committed. The header references the parent spec. The plan project's committed `docs/.gitignore`; on a direct tidy dispatch that
skipped `specify`, create that file first if it does not yet ignore
`plans/` (`docs/conventions.md` § File layout). The plan is a working
file, never committed. The header references the parent spec. The plan
decomposes work into tasks; each task is the unit of review decomposes work into tasks; each task is the unit of review
(spec-compliance and quality gates inside `implement`), not (spec-compliance and quality gates inside `implement`), not
the unit of commit. Commits are an orchestrator-only decision the unit of commit. Commits are an orchestrator-only decision
+5 -2
View File
@@ -270,8 +270,11 @@ it as a target, not verified fact.
Path: under `docs/specs`, named `slug.md` per the fixed naming Path: under `docs/specs`, named `slug.md` per the fixed naming
convention (see `docs/conventions.md` § Naming) — a short kebab-case convention (see `docs/conventions.md` § Naming) — a short kebab-case
topic slug, no numeric prefix. The directory is git-ignored; the spec is topic slug, no numeric prefix. The directory is git-ignored via the
created there and never `git add`'d. project's committed `docs/.gitignore`; if that file does not yet ignore
`specs/`, create it first (`docs/conventions.md` § File layout) so the
spec can never be committable. The spec is created under `docs/specs` and
never `git add`'d.
Structure: Structure:
+11 -9
View File
@@ -68,15 +68,17 @@ Two rules govern who touches git history and how:
## Spec/plan artefacts are ephemeral ## Spec/plan artefacts are ephemeral
`docs/specs/` and `docs/plans/` hold only the active cycle's spec and `docs/specs/` and `docs/plans/` hold only the active cycle's spec and
plan, and they are **git-ignored** — never committed (the plugin ignores plan, and they are **git-ignored** — never committed. This repo carries
them globally via `~/.config/git/ignore`; a project may add its own a committed `docs/.gitignore` with the two entries `specs/` and `plans/`
`.gitignore` entry instead). Each is a working file: present on disk for (relative to `docs/`); that ignore file is the only committed part — the
its own cycle, read across sessions from the working tree, and spec and plan files it covers never are. If `docs/.gitignore` is missing
shell-`rm`'d at cycle close by `audit` — never `git rm`'d, since it was when the first working file is written, the writing skill creates it.
never tracked, so no git-history copy survives. The durable record of a Each spec / plan is a working file: present on disk for its own cycle,
past cycle is the design ledger only, and no durable artefact cites a read across sessions from the working tree, and shell-`rm`'d at cycle
spec/plan by number or path (see `~/dev/skills/docs/conventions.md` close by `audit` — never `git rm`'d, since it was never tracked, so no
§ Lifecycle). git-history copy survives. The durable record of a past cycle is the
design ledger only, and no durable artefact cites a spec/plan by number
or path (see `~/dev/skills/docs/conventions.md` § Lifecycle).
## Design rationale ≠ implementation effort ## Design rationale ≠ implementation effort