planrails 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +69 -16
  2. package/PLANNER.md +255 -0
  3. package/README.md +103 -176
  4. package/bin/planrails.mjs +90 -5
  5. package/package.json +14 -48
  6. package/tools/check-plans.mjs +213 -0
  7. package/docs/PLANNING_GUIDE.md +0 -632
  8. package/src/hooks/_lib.mjs +0 -32
  9. package/src/hooks/guard-never-delete.sh +0 -29
  10. package/src/hooks/install.mjs +0 -179
  11. package/src/hooks/plan-pre-tool.mjs +0 -71
  12. package/src/hooks/plan-session-start.mjs +0 -46
  13. package/src/hooks/plan-stop.mjs +0 -60
  14. package/src/hooks/plan-subagent-start.mjs +0 -27
  15. package/src/hooks/postcompact-journal.mjs +0 -35
  16. package/src/hooks/precompact-journal.mjs +0 -128
  17. package/src/hooks/selftest.mjs +0 -128
  18. package/src/init.mjs +0 -161
  19. package/src/issue.mjs +0 -55
  20. package/src/plan/fixtures/README.md +0 -7
  21. package/src/plan/fixtures/broken-cli.mjs +0 -27
  22. package/src/plan/fixtures/broken-hooks-root/.claude/settings.json +0 -83
  23. package/src/plan/fixtures/broken-hooks-root/.project-management/plans/.gitkeep +0 -0
  24. package/src/plan/fixtures/broken-hooks-root/CLAUDE.md +0 -9
  25. package/src/plan/fixtures/broken-root/.project-management/plans/broken/PLAN.md +0 -4
  26. package/src/plan/fixtures/broken-root/.project-management/plans/broken/gates.json +0 -1
  27. package/src/plan/fixtures/broken-root/.project-management/plans/broken/rules.json +0 -1
  28. package/src/plan/fixtures/broken-root/.project-management/plans/broken/state.json +0 -67
  29. package/src/plan/fixtures/broken-root/CLAUDE.md +0 -3
  30. package/src/plan/fixtures/broken-trial.mjs +0 -25
  31. package/src/plan/lib/brief.mjs +0 -116
  32. package/src/plan/lib/claude-md.mjs +0 -68
  33. package/src/plan/lib/glob.mjs +0 -81
  34. package/src/plan/lib/judgment.mjs +0 -19
  35. package/src/plan/lib/paths.mjs +0 -65
  36. package/src/plan/lib/schema.mjs +0 -199
  37. package/src/plan/lib/store.mjs +0 -338
  38. package/src/plan/lib/time.mjs +0 -21
  39. package/src/plan/plan.mjs +0 -843
  40. package/src/plan/run.mjs +0 -88
  41. package/src/plan/skill/SKILL.md +0 -15
package/CHANGELOG.md CHANGED
@@ -1,26 +1,79 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 — 2026-09-12
4
+
5
+ A careful post-release review, including an end-to-end test of the published
6
+ package, found one robustness gap in the checker and fixed it.
7
+
8
+ - The checker finds a task table by its columns (`id`, `status`, `proof`,
9
+ `evidence`), not by a `## Tasks` heading. So tasks under `## Phase 2 Tasks`,
10
+ `## Backlog`, any heading, or none at all are all gated — a phased plan is no
11
+ longer rejected with a confusing "no readable Tasks table". A table under a
12
+ literal `## Tasks` heading that is missing a column still names which one.
13
+ - Verified against the live 0.2.0 package: `npx planrails init` copies only into
14
+ `.project-management/`, leaves `package.json` and a pnpm lockfile untouched,
15
+ writes no `node_modules` and no `CLAUDE.md`, and the checker catches every
16
+ faked-"done" bypass. 33 tests.
17
+
18
+ ## 0.2.0 — 2026-09-12
19
+
20
+ Rebuilt as a planner prompt and one checker, with a two-command CLI that only
21
+ copies files. Backward compatibility with 0.1.x is intentionally dropped.
22
+
23
+ Why: 0.1.x was a heavy npm package, and its weight is what made it fragile. It
24
+ wrote a `package.json` and ran `npm install`, so it could not fit pnpm, bun,
25
+ non-Node or monorepo projects; it ran every gate through `bash -c`, so Windows
26
+ could not work; it resolved the project root by a rule the CLI and the installed
27
+ hooks could disagree on; and it carried ~2,500 lines and an 8,842-word guide to
28
+ plan a feature. A review found ten data-loss and silent-failure paths in that
29
+ surface. The planning idea never broke — the machinery around it did.
30
+
31
+ What it is now, same three rails:
32
+
33
+ - **`PLANNER.md`** — the prompt an agent reads to plan with you and then execute.
34
+ Self-contained: the reload line, proof-before-work, evidence-at-close, and the
35
+ plan and log templates are all in this one file.
36
+ - **`tools/check-plans.mjs`** — a small, dependency-free checker (any OS; Windows
37
+ in CI). It enforces the one machine-checkable rule: a task that claims to be
38
+ finished must name a proof and carry pasted evidence. It is built to catch a
39
+ faked "done": any status that is not an explicit not-done word counts as a
40
+ claim, and a pipe inside a proof command, a second task table, and a blank line
41
+ in the table are all handled. `--verify` re-runs each proof (trusted plans
42
+ only). `tools/check-plans.test.mjs`.
43
+ - **`bin/planrails.mjs`** — a safe CLI. `init` copies `PLANNER.md` and the checker
44
+ into a project's `.project-management/` and writes nothing else (no
45
+ `package.json`, no `npm install`, no hooks, no `CLAUDE.md` edits); `check` runs
46
+ the gate. `bin/planrails.test.mjs`.
47
+ - **`skill/SKILL.md`** — copy to `~/.claude/skills/plan/` for `/plan`.
48
+ - **`examples/weekly-digest/`** — a worked plan the checker validates in CI.
49
+
50
+ Removed: `src/plan/*` (the 843-line `plan.mjs`, `store`, `schema`, `paths`, …),
51
+ `src/hooks/*` (SessionStart/PreToolUse/Stop/journal hooks, replaced by the reload
52
+ line), the old `init` that wrote `package.json`, `src/issue.mjs`, the `run`
53
+ fresh-session runner, the release/trial/acceptance tooling, and the shipped
54
+ fixtures. The plan format changed from JSON (`state.json`, `gates.json`,
55
+ `rules.json`) to one markdown table a person can read and fix. Publishing stays on
56
+ GitHub Actions trusted publishing (`.github/workflows/publish.yml`).
57
+
58
+ ## 0.1.3 — 2026-09-12
59
+
60
+ - Releases published by GitHub Actions through npm trusted publishing (OIDC) on a `v*` tag push. `.github/workflows/publish.yml`. (Removed in 0.2.0.)
61
+ - The release checks understand CI. `test/release-check.test.mjs`. (Removed in 0.2.0.)
62
+ - `package-lock.json` committed; both workflows install with `npm ci`.
63
+
3
64
  ## 0.1.2 — 2026-09-12
4
65
 
5
- - `npx planrails update` now also rewrites the plans block in CLAUDE.md from the plans on disk, so a marker or wording written by an older version is replaced on upgrade. `test/claude-md.test.mjs`.
6
- - Release checks around `npm publish` (`scripts/release-check.mjs`, wired as `prepublishOnly` and `postpublish`): before uploading, refuse a version the registry already has, a missing CHANGELOG entry, a dirty tree or an unpushed HEAD; after uploading, wait until the registry serves the version. Why: a web-authenticated publish is staged and finalizes about a minute later; in that window a second publish fails with E409 and an install with ETARGET, which is how 0.1.1's release went. CONTRIBUTING.md § Release. `test/release-check.test.mjs`.
66
+ - `npx planrails update` also rewrote the plans block in CLAUDE.md from disk.
67
+ - Release checks around `npm publish` (`scripts/release-check.mjs`): refuse a staged/dirty/undocumented publish before, wait for the registry after.
7
68
 
8
69
  ## 0.1.1 — 2026-09-12
9
70
 
10
- Fixed, found while moving a real project from a vendored copy to the package:
11
-
12
- - `npx planrails init` on a project that did not yet hold the package wrote the npx cache's absolute path into every hook command (machine-local, and evicted by npm). Hook commands now name `$CLAUDE_PROJECT_DIR/node_modules/planrails/…` whenever the project holds its own copy, whichever copy runs the installer. `test/hooks-path.test.mjs` reproduces it with a copied package.
13
- - A CLAUDE.md plans block written by an older copy of the system (a different generator name in its marker) was not recognised, so `init` and `activate` added a second block beside it. Any `<!-- plans:begin` marker now counts as the block. `test/claude-md.test.mjs`.
14
- - Internal: `npm test` lists the test files by name (`scripts/run-tests.mjs`); Node 20 does not expand the glob the 0.1.0 script used, so its CI jobs failed.
71
+ - `npx planrails init` wrote portable hook commands (`$CLAUDE_PROJECT_DIR/…`) whenever the project held its own copy.
72
+ - Any `<!-- plans:begin` marker counted as the CLAUDE.md block, so `init` and `activate` stopped adding a second one.
73
+ - `npm test` listed test files by name for Node 20.
15
74
 
16
75
  ## 0.1.0 — 2026-09-12
17
76
 
18
- First release, extracted from the project it was built in after a day of use.
19
-
20
- - `planrails init` sets a new or an existing project up, idempotently; `update`, `uninstall`, `doctor`.
21
- - Plans as files: PLAN.md, state.json, gates.json, rules.json, append-only log / learnings / decisions / gate-runs.
22
- - A task is done only through `task done`: it runs the gate, records the run, and needs every condition of done answered (C1–C7 by default, more per plan or per task).
23
- - Gates declare what they do not check and a known-fail case; `gate verify` proves each can fail; a gate edited after verification cannot close a task.
24
- - Hooks for Claude Code: brief at SessionStart (also after compaction), rules at the tool call, a Stop reminder to log, a subagent brief, a pre-compaction journal. Portable settings via `$CLAUDE_PROJECT_DIR`.
25
- - `run`: one fresh `claude -p` session per task.
26
- - `issue`: a prefilled GitHub issue with the environment.
77
+ First release, extracted from the project it was built in. An `init`/`update`/
78
+ `uninstall`/`doctor` CLI, plans as JSON files, `task done` with gates and C1–C7
79
+ conditions, Claude Code hooks, a fresh-session `run`, and an `issue` command.
package/PLANNER.md ADDED
@@ -0,0 +1,255 @@
1
+ # The Planner
2
+
3
+ You are about to plan a piece of work with a person, then help execute it so the
4
+ work survives a lost session and "done" always means done.
5
+
6
+ This file is the whole method. You, the model, do the setup by following the
7
+ steps below: make the folders, write the plan, wire the reload line, and — if the
8
+ project runs Node — copy in one small checker.
9
+
10
+ **How a person starts you.** They say something like *"Familiarise yourself with
11
+ these instructions and tell me when you are ready to plan the next feature with
12
+ me."* When they do, run **§1 Get ready**, give the eight-line report, and stop.
13
+ Do not start planning until they answer.
14
+
15
+ ---
16
+
17
+ ## What you are making
18
+
19
+ Two files per plan, under `.project-management/plans/<id>/`:
20
+
21
+ - **PLAN.md** — the map and the tracker. What we are building, the tasks, and the
22
+ proof each task is done. A fresh session reads this and knows the whole state.
23
+ - **LOG.md** — append-only history. One dated line per landed piece of work.
24
+
25
+ The plan is written for an **executor who never saw this prompt** — maybe a fresh
26
+ session of you after the chat is gone. So everything the executor needs goes in
27
+ the plan. If it is not in the plan, it does not exist.
28
+
29
+ Three rules make the plan trustworthy. They are the whole point of this system:
30
+
31
+ 1. **The reload line.** One line in the project's always-loaded file re-opens the
32
+ plan after every compaction, so the state is never lost.
33
+ 2. **Proof before work.** Every task names the command that will prove it done
34
+ *before* the work starts. No command, no way to fake it later.
35
+ 3. **Evidence at close.** A task is done only when its proof was run and its exit
36
+ code and last line are pasted into the plan. An empty evidence cell is not
37
+ done, whatever the status column says.
38
+
39
+ ---
40
+
41
+ ## §1 Get ready
42
+
43
+ Do this before you ask the person anything. Read the project; do not make them
44
+ tell you what the repo already says.
45
+
46
+ 1. **Read the always-loaded file** — `CLAUDE.md`, else `AGENTS.md`, else
47
+ `README.md`. Find the stack, the package manager, the **check command** (the
48
+ one run before every commit), and the **test command**.
49
+ 2. **List `docs/`** and read the ones this feature touches. Use sub-agents for
50
+ long files so your own context stays clear. Record each relevant doc's path
51
+ and one line of what it holds.
52
+ 3. **Read `.project-management/`** — existing plans and prose. Note the active one.
53
+ 4. **Read the last ~20 commits** (`git log --oneline -20`) for how the code moves.
54
+
55
+ **A sub-agent's finding is a lead, not a fact.** If it names a file and line, you
56
+ can check it. If it is only a number, re-derive it yourself or drop it. A wrong
57
+ number in a plan becomes a wrong decision later.
58
+
59
+ Then **report ready in eight lines** and stop:
60
+
61
+ ```
62
+ READY — <project name>
63
+ Stack: <language, framework, package manager>
64
+ Always-on: <CLAUDE.md | AGENTS.md | README.md>
65
+ Check: <the check command> Test: <the test command>
66
+ Plans: <existing plan ids; which is active, or "none">
67
+ Docs: <relevant doc paths, or "none">
68
+ Touches: <the folders this feature will change>
69
+ Questions: <up to 4 things the repo did not answer, or "none">
70
+ ```
71
+
72
+ ---
73
+
74
+ ## §2 Interview
75
+
76
+ Ask only what the repo did not answer. Four questions at most:
77
+
78
+ - **Goal** — one sentence. What is true when this is shipped.
79
+ - **Done when** — as *commands and checks*, not adjectives. "A digest email
80
+ arrives for a test user with five posts", not "digests work well".
81
+ - **Must not change** — the rails, the data, the public shape you must not break.
82
+ - **Touches** — what existing code and docs this sits next to.
83
+
84
+ If the person reaffirms something after you raise a concern, that is their
85
+ decision. Note it and move on.
86
+
87
+ ---
88
+
89
+ ## §3 Write the plan
90
+
91
+ Create `.project-management/plans/<id>/PLAN.md` and `LOG.md` from the two
92
+ templates at the end of this file (§ Template — PLAN.md, § Template — LOG.md).
93
+ Pick a short kebab-case `<id>` (`weekly-digest`). Then:
94
+
95
+ - **Name the proof for every task before any work.** The proof is the command
96
+ that shows the task is done: a test, a build, a check. A task that no command
97
+ can prove is proven by the person's word — write `owner` in the proof column
98
+ and record their words and the date in evidence when they give it.
99
+ - **Use repo-relative paths** (`lib/digest/query.ts`), never absolute ones. They
100
+ are clickable and they survive a move to another machine.
101
+ - **Keep PLAN.md under ~1,500 words.** History goes in LOG.md, not here.
102
+ - **Add the reload line.** In the always-loaded file, under a short "Active plans"
103
+ spot, add:
104
+ ```
105
+ @.project-management/plans/<id>/PLAN.md
106
+ ```
107
+ Put it on its own line, outside any code block — an `@` import wrapped in
108
+ backticks does not load. Claude Code re-reads that file, and everything it
109
+ imports, at every session start and after every compaction, so the plan comes
110
+ back on its own. (For a tool that does not do `@`-imports, put the plan's path
111
+ in `AGENTS.md` and open it by hand at the start of each session.)
112
+ - **Wire the checker, if the project runs Node and has a check command.**
113
+ `npx planrails init` already put it at `.project-management/check-plans.mjs`; if
114
+ you did not run init, copy this repo's `tools/check-plans.mjs` there. Add
115
+ `node .project-management/check-plans.mjs` to the check command. Now the build
116
+ fails if a task is marked done with no evidence. If the project is not Node,
117
+ skip this; the plan still works, and you enforce the gate yourself.
118
+
119
+ ---
120
+
121
+ ## §4 Execute — one task at a time
122
+
123
+ The loop for each task:
124
+
125
+ 1. **Set it doing.** Change the status cell to `doing`. Update **NOW**.
126
+ 2. **Do the work.**
127
+ 3. **Run the proof.** Right now, not from memory. Copy the exit code and the last
128
+ line of output.
129
+ 4. **Paste the evidence.** Into the task's evidence cell:
130
+ `2026-09-12 14:20 · exit 0 · "6 passed"`.
131
+ 5. **Set it done.** Only now. Update **NOW** to point at the next task.
132
+ 6. **Append one line to LOG.md** — what landed, what is next, anything learned,
133
+ any decision made.
134
+
135
+ **Update NOW before you end any turn.** NOW is the first thing a fresh session
136
+ reads. If it is stale, the next session repeats your work or starts in the wrong
137
+ place.
138
+
139
+ **Your judgement outranks the gate.** The gate is a proposal from a script that
140
+ cannot see the screen. You can.
141
+
142
+ - Gate red, work right: never make the gate pass. Fix the gate, or block the task
143
+ for the owner. Say what you saw.
144
+ - Gate green, work wrong: refuse to close it. Say what the gate cannot see.
145
+ - A green check is not proof the work is good. It is proof one command exited 0.
146
+
147
+ **Never edit a plan the way a script would.** If something crashes, log it, set
148
+ the task `blocked` with the reason, and stop. Do not hand-fix state to look done.
149
+
150
+ ---
151
+
152
+ ## §5 Close
153
+
154
+ 1. **Re-run every proof.** A plan closes on what the checks say now, not on their
155
+ last recorded run. Paste fresh evidence.
156
+ 2. **Update the docs** the work changed — in the same step, per the project's
157
+ documentation guide if it has one.
158
+ 3. **Retire the plan.** Move its reload line out of "Active plans" into a
159
+ "Finished" list (or delete the line). The plan files stay on disk; they are the
160
+ record.
161
+
162
+ ---
163
+
164
+ ## Why it is shaped this way
165
+
166
+ Each line here was paid for by a real failure in earlier planning systems:
167
+
168
+ - **The reload line** replaces a whole SessionStart hook. Re-reading the
169
+ always-loaded file after compaction is something the tool already does.
170
+ - **Proof before work** exists because a checkbox lies. One project marked a phase
171
+ "done" three times while it was not; nothing in a status column could catch it.
172
+ A named command that must be run and pasted can.
173
+ - **Evidence at close** is the one machine-checkable rail worth keeping. The
174
+ checker enforces exactly this and nothing else.
175
+ - **Sub-agent findings are leads** because four spot-checked findings were each
176
+ right in direction and wrong in number, and a wrong number becomes a wrong plan.
177
+ - **Repo-relative paths** because absolute paths break on the next machine, and a
178
+ path a reader cannot open is worse than none.
179
+ - **Nothing the method needs is installed.** `npx planrails init` only copies two
180
+ files, and you can copy them by hand instead. The old version was a heavy
181
+ package, and that is what broke on the projects that were not npm — Python,
182
+ pnpm, bun, monorepos, Windows. A prompt and a copied script work everywhere.
183
+
184
+ Keep it this simple. If you are tempted to add a config file, a second script, or
185
+ a fourth rule, you are rebuilding the thing this replaced.
186
+
187
+ ---
188
+
189
+ ## Template — PLAN.md
190
+
191
+ Create `.project-management/plans/<id>/PLAN.md` with this shape. Keep the column
192
+ names exactly as shown. The checker reads `id`, `status`, `proof` and `evidence`
193
+ by name, in any order; `task` is there for you to read.
194
+
195
+ ````markdown
196
+ # <Feature> — plan
197
+
198
+ status: active · opened <YYYY-MM-DD> · id: <kebab-id>
199
+
200
+ ## NOW
201
+ RESUME: <the one thing to do next, with the file — e.g. "T2: render the digest (lib/digest/render.ts)">
202
+ NEXT: <T3 · T4 · …>
203
+ updated: <YYYY-MM-DD HH:MM>
204
+
205
+ ## Goal
206
+ <4–5 sentences: what we are building and why. What is true when it ships.>
207
+ <!-- If the architecture is non-trivial, add a mermaid diagram here. Skip it for a simple change. -->
208
+
209
+ ## Done when
210
+ - <a command or check that must pass — e.g. `npm run check` exits 0>
211
+ - <a real-world check — e.g. a digest email arrives for a test user with 5 posts>
212
+
213
+ ## Must not change
214
+ - <the rails, data, or public shape this work must not break>
215
+
216
+ ## Tasks
217
+ <!-- status: todo | doing | done | blocked. proof: a `command` in backticks, or `owner`
218
+ for a task only the owner's word can close. evidence: pasted after the proof runs —
219
+ required for a done task. -->
220
+ | id | task | status | proof | evidence |
221
+ |----|------|--------|-------|----------|
222
+ | T1 | <what to do> (<path>) | todo | `<command that proves it>` | |
223
+ | T2 | <what to do> (<path>) | todo | `<command>` | |
224
+ | T3 | update the docs this work changed | todo | owner | |
225
+
226
+ ## Rules for this plan
227
+ <!-- Short. These stay in the executor's context the whole time. Max ~15 lines. -->
228
+ - <a rule that governs this area — e.g. "every email goes through lib/email, never a bare send">
229
+
230
+ ## Decisions
231
+ | date | decision | why |
232
+ |------|----------|-----|
233
+ | <YYYY-MM-DD> | <what was chosen> | <the reason and what it rules out> |
234
+
235
+ ## Context (read during planning — do not re-read)
236
+ - <path> — <one line of what it holds>
237
+ ````
238
+
239
+ ## Template — LOG.md
240
+
241
+ Create `.project-management/plans/<id>/LOG.md` beside it. Append one entry per
242
+ landed piece of work; never rewrite an old one.
243
+
244
+ ````markdown
245
+ # <Feature> — log
246
+
247
+ Append-only. One entry per landed piece of work, newest at the bottom.
248
+
249
+ ## <YYYY-MM-DD HH:MM> — <task id>: <short title>
250
+ - did: <what landed, 1–2 sentences>
251
+ - files: <repo-relative paths touched>
252
+ - proof: <the command and its result — e.g. `npx vitest run tests/digest.test.ts` → exit 0, "6 passed">
253
+ - next: <what comes next; where you stopped if you paused>
254
+ - learned / decided: <anything a second reader needs; omit if nothing>
255
+ ````