planrails 0.4.0 → 0.5.0
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.
- package/CHANGELOG.md +50 -0
- package/PLANNER.md +72 -8
- package/README.md +35 -9
- package/bin/planrails.mjs +1 -1
- package/package.json +1 -1
- package/tools/check-plans.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 — 2026-09-13
|
|
4
|
+
|
|
5
|
+
One method change, paid for by a real collision. The reload line loads an active
|
|
6
|
+
plan into every session opened in a checkout, and on 2026-09-13 a second session
|
|
7
|
+
in one repo read the reloaded plan, was told "continue", committed, and was
|
|
8
|
+
starting the next task while the first session was mid-edit on the same files and
|
|
9
|
+
the same PLAN.md. The checker is unchanged apart from its stamp; `init` still
|
|
10
|
+
writes only its two files.
|
|
11
|
+
|
|
12
|
+
- **A plan names the session working it.** NOW gains a fourth line,
|
|
13
|
+
`session: <name> · <first 8 characters of the session id> · since <date>`, or
|
|
14
|
+
`session: none` — written when the plan is written, when a task goes `doing`,
|
|
15
|
+
and on a takeover; cleared at close. It is a lead, not a lock: whether that
|
|
16
|
+
session is alive is decided by the live listing (`claude agents --json`, which
|
|
17
|
+
is documented and lists headless runs too; `ListAgents` inside Claude Code), so
|
|
18
|
+
a stale line from a closed terminal blocks nobody.
|
|
19
|
+
- **The plan's block runs the check.** "How to work this plan" now tells a
|
|
20
|
+
session, before any task goes `doing` and even when the person asked it to
|
|
21
|
+
continue: re-read NOW from disk, list the live sessions, run
|
|
22
|
+
`git status --short`, and — if the line names a live session that is not you,
|
|
23
|
+
or says `none` while a peer in the checkout is busy or plan files are dirty —
|
|
24
|
+
stop, report and ask before writing to the plan, its files, or a commit.
|
|
25
|
+
Several reloaded plans: work only the one assigned in this session, and say
|
|
26
|
+
which.
|
|
27
|
+
- **One plan per session, one session per plan** replaces "one active plan per
|
|
28
|
+
repo". Several plans may be active, each with its own reload line and one
|
|
29
|
+
holder; keep them few, since every session reloads all of them. The "Active
|
|
30
|
+
plans" heading in `CLAUDE.md` carries one line saying so.
|
|
31
|
+
- **PLANNER.md** §3 has the rule and "name the session"; §4 the check, the listing
|
|
32
|
+
and the decision rules (busy, idle, waiting, stale, a plan with no line, a
|
|
33
|
+
worktree, a sub-agent), and how to stop and ask; §5 clears the line at close;
|
|
34
|
+
"Why it is shaped this way" records the incident.
|
|
35
|
+
- **Existing plans: update by hand.** `init` never touches a plan. Add the
|
|
36
|
+
`session:` line to NOW, change "NOW is three lines" to four, and append the
|
|
37
|
+
block's last paragraph from the template. A plan without them still passes the
|
|
38
|
+
checker, which ignores the line; a test pins that.
|
|
39
|
+
- Field-tested with headless sessions in a scratch project: told only "Continue
|
|
40
|
+
the active plan.", a second session stopped, reported and asked while the
|
|
41
|
+
holder was live (idle with a clean tree; busy with a dirty PLAN.md and no
|
|
42
|
+
line), and wrote nothing; took over a stale claim and finished the plan by the
|
|
43
|
+
loop; and, told to continue the second of two plans, wrote its claim on that
|
|
44
|
+
one and left the held plan byte-identical. One wording gap found and fixed: the
|
|
45
|
+
id is the session id, not the ref `ListAgents` prints. 73 tests.
|
|
46
|
+
|
|
47
|
+
## 0.4.1 — 2026-09-13
|
|
48
|
+
|
|
49
|
+
- The Claude Code skill is `/planrails`, not `/plan`, because Claude Code has a
|
|
50
|
+
`/plan` of its own. Install it at `~/.claude/skills/planrails/`; the README and
|
|
51
|
+
the sentence `init` prints say so. Nothing else changed.
|
|
52
|
+
|
|
3
53
|
## 0.4.0 — 2026-09-13
|
|
4
54
|
|
|
5
55
|
A review of 0.3.0 against a real multi-session plan, the Claude Code docs, and
|
package/PLANNER.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- planrails 0.
|
|
1
|
+
<!-- planrails 0.5.0 -->
|
|
2
2
|
# The Planner
|
|
3
3
|
|
|
4
4
|
You are about to plan a piece of work with a person, then help execute it so the
|
|
@@ -125,10 +125,26 @@ Pick a short kebab-case `<id>` (`weekly-digest`). Then:
|
|
|
125
125
|
Put it on its own line, outside any code block — an `@` import wrapped in
|
|
126
126
|
backticks does not load. Claude Code re-reads that file, and everything it
|
|
127
127
|
imports, at every session start and after every compaction, so the plan comes
|
|
128
|
-
back on its own.
|
|
129
|
-
|
|
128
|
+
back on its own. Under the same heading, one line says how the plans are held:
|
|
129
|
+
*Each plan below is held by one session — see its NOW `session:` line. Work one
|
|
130
|
+
only when asked in this session, after the check in its block.* (For a tool that
|
|
130
131
|
does not do `@`-imports, put the plan's path in `AGENTS.md` and open it by hand
|
|
131
132
|
at the start of each session.)
|
|
133
|
+
- **One plan per session, one session per plan.** A repo may hold several active
|
|
134
|
+
plans, each with its own reload line and each held by one session, named on its
|
|
135
|
+
NOW `session:` line. Every session reloads all of them, so keep them few; a plan
|
|
136
|
+
nobody is working is paused (`status: paused`, its line in backticks). A session
|
|
137
|
+
works only the plan the person in that session assigned; the other reloaded
|
|
138
|
+
plans are context, not work orders. Naming the session after the plan
|
|
139
|
+
(`claude -n <id>`) shows the holder in every listing and in the terminal title;
|
|
140
|
+
Claude Code gives a second live session with that name a variant suffix.
|
|
141
|
+
- **Name the session.** NOW's `session:` line names the session that will execute
|
|
142
|
+
the plan: yours, if that is you. In Claude Code the name is what `ListAgents`
|
|
143
|
+
prints after "This session is", and the id is the first eight characters of
|
|
144
|
+
`$CLAUDE_CODE_SESSION_ID` — the `sessionId` that `claude agents --json` prints,
|
|
145
|
+
not the bracketed ref `ListAgents` shows. The id survives a resume when the name
|
|
146
|
+
may not. Elsewhere, write anything a reader can tell apart (`codex@mbp tty003`);
|
|
147
|
+
`none` when handing the plan to someone else.
|
|
132
148
|
- **Wire the checker, if the project runs Node and has a check command.**
|
|
133
149
|
`npx planrails init` already put it at `.project-management/planrails/check-plans.mjs`;
|
|
134
150
|
if you did not run init, copy `tools/check-plans.mjs` from
|
|
@@ -154,9 +170,46 @@ for — read them and you skip the struggle instead of repeating it. After a
|
|
|
154
170
|
compaction, also run `git status --short`: it is the journal of in-flight work
|
|
155
171
|
that NOW may not mention yet.
|
|
156
172
|
|
|
173
|
+
**Find out who holds the plan before you touch it.** The reload line puts every
|
|
174
|
+
active plan into every session opened in this checkout; a plan in your context is
|
|
175
|
+
not a work order. At session start, read NOW's `session:` line and say in one line
|
|
176
|
+
which plan you are on, or none. Before any task goes `doing` — even when the
|
|
177
|
+
person just asked you to continue; they can forget which window holds the plan —
|
|
178
|
+
re-read that line from disk (your reloaded copy may be older than the file), list
|
|
179
|
+
the live sessions, and run `git status --short`. `claude agents --json` lists every
|
|
180
|
+
live session on the machine: `name`, `sessionId`, `cwd`, `pid`, `status` (`busy`,
|
|
181
|
+
`idle`, or `waiting` for the person) and `startedAt`; `--cwd <dir>` keeps those
|
|
182
|
+
started under a directory, and a headless `claude -p` run is listed too. In Claude
|
|
183
|
+
Code, `ListAgents` names you and the peers, but its bracketed ref is not the
|
|
184
|
+
session id; the id is `$CLAUDE_CODE_SESSION_ID`. An entry changes on status, not
|
|
185
|
+
on edits: last activity is the plan's `updated:` stamp or its transcript's mtime
|
|
186
|
+
under `~/.claude/projects/`. Then decide:
|
|
187
|
+
|
|
188
|
+
- The line names you: go on.
|
|
189
|
+
- It names a live session that is not you: stop. Busy is mid-task; idle is between
|
|
190
|
+
turns and still the holder; waiting means it needs the person — tell them.
|
|
191
|
+
- It names a session the listing lacks: say so. No other live session in this
|
|
192
|
+
checkout: take over, write your claim. One is: stop; the id tells whether it is
|
|
193
|
+
that conversation resumed under a new name.
|
|
194
|
+
- `none`, or no line (an older plan): a busy session in this checkout, or plan
|
|
195
|
+
files dirty in `git status`: stop. Otherwise say what you saw in one line, write
|
|
196
|
+
your claim, go on.
|
|
197
|
+
- A worktree is another checkout with its own copy of the plan: a merge concern
|
|
198
|
+
later, not a clobber now. A sub-agent you briefed never runs this check; it
|
|
199
|
+
never writes the plan.
|
|
200
|
+
|
|
201
|
+
Stopping means: report the session's name, status and start, what NOW says and
|
|
202
|
+
what `git status` shows, then ask — `AskUserQuestion` in Claude Code; a `claude -p`
|
|
203
|
+
run ends its turn with the question — whether to leave the plan to that session,
|
|
204
|
+
take it over here once they have stopped it, or work something else. Write nothing
|
|
205
|
+
to the plan, its files, or a commit until they answer. Stage commits by path;
|
|
206
|
+
another session's in-flight edits are not yours to sweep up.
|
|
207
|
+
|
|
157
208
|
The loop for each task:
|
|
158
209
|
|
|
159
|
-
1. **
|
|
210
|
+
1. **Check who holds the plan** (above), then **set it doing.** Change the status
|
|
211
|
+
cell to `doing`. Update **NOW**, and put your session on its `session:` line
|
|
212
|
+
(`since` from `date` on a takeover; unchanged when it already names you).
|
|
160
213
|
2. **Do the work.** Fix the cause, not the symptom. The simplest change that
|
|
161
214
|
works, end to end.
|
|
162
215
|
3. **Run the proof.** Right now, not from memory. Copy the exit code and the last
|
|
@@ -225,8 +278,8 @@ the task `blocked` with the reason, and stop. Do not hand-fix state to look done
|
|
|
225
278
|
defects the self-tests had passed.
|
|
226
279
|
3. **Update the docs** the work changed — in the same step, per the project's
|
|
227
280
|
documentation guide if it has one.
|
|
228
|
-
4. **Retire the plan.** Set its header to `status: done
|
|
229
|
-
line in backticks, or delete it. Do not move a bare `@` line under a
|
|
281
|
+
4. **Retire the plan.** Set its header to `status: done` and NOW's `session:` to
|
|
282
|
+
`none`, then wrap its reload line in backticks, or delete it. Do not move a bare `@` line under a
|
|
230
283
|
"Finished" heading: it still imports, and every retired plan would reload
|
|
231
284
|
forever. The status comes first: the checker holds an `active` plan to its
|
|
232
285
|
reload line. The plan files stay on disk; they are the record.
|
|
@@ -261,12 +314,20 @@ Each line here was paid for by a real failure in earlier planning systems:
|
|
|
261
314
|
repeated. The same struggle coming back in a new chat is the exact failure this
|
|
262
315
|
fixes.
|
|
263
316
|
- **History stays out of the plan.** One plan grew a 16,000-word progress section,
|
|
264
|
-
stamped two hours behind its own log. NOW is
|
|
317
|
+
stamped two hours behind its own log. NOW is four lines; LOG.md is the history.
|
|
265
318
|
- **Sub-agent findings are leads** because four spot-checked findings were each
|
|
266
319
|
right in direction and wrong in number, and a wrong number becomes a wrong plan.
|
|
267
320
|
Delegation is method, not machinery: a brief of one unit and nothing else worked.
|
|
268
321
|
- **Repo-relative paths** because absolute paths break on the next machine, and a
|
|
269
322
|
path a reader cannot open is worse than none.
|
|
323
|
+
- **The `session:` line and the check before `doing`** exist because the reload
|
|
324
|
+
line loads a plan into every session opened in a checkout, not only the one
|
|
325
|
+
working it. On 2026-09-13 in edodo-video, a second session, opened for a
|
|
326
|
+
planrails upgrade, read the reloaded plan, was told "continue with T5",
|
|
327
|
+
committed, and was starting T5b while the first session was mid-edit on the
|
|
328
|
+
same files and the same PLAN.md; the owner interrupted. The person had asked,
|
|
329
|
+
so the check runs even then. The claim is a lead and the live listing decides,
|
|
330
|
+
so a stale line from a closed terminal blocks nobody.
|
|
270
331
|
- **Nothing the method needs is installed.** `npx planrails init` only copies two
|
|
271
332
|
files, and you can copy them by hand instead. The old version was a heavy
|
|
272
333
|
package, and that is what broke on the projects that were not npm — Python,
|
|
@@ -295,6 +356,7 @@ status: active · opened <YYYY-MM-DD> · id: <kebab-id>
|
|
|
295
356
|
RESUME: <T2 — the one thing to do next, with the file; written for a stranger>
|
|
296
357
|
NEXT: <T3 · T4 · …>
|
|
297
358
|
updated: <YYYY-MM-DD HH:MM, from `date`>
|
|
359
|
+
session: <none, or the session working this plan: name · the first 8 characters of its session id · since YYYY-MM-DD HH:MM>
|
|
298
360
|
|
|
299
361
|
## How to work this plan
|
|
300
362
|
Read NOW, then Rules and Learnings; do not re-read Context. One task at a time:
|
|
@@ -304,7 +366,9 @@ Read NOW, then Rules and Learnings; do not re-read Context. One task at a time:
|
|
|
304
366
|
4. `done` only if N is 0. Point NOW at the next task; append an entry to LOG.md: did, files, proof, next, learned.
|
|
305
367
|
5. If it fought back, add a Learning: the trap, then the rule. A verified fact goes in Context, a choice in Decisions.
|
|
306
368
|
|
|
307
|
-
NOW is
|
|
369
|
+
NOW is four lines for a stranger; on a long task note the sub-step, and update it before any turn ends. Blocked: say so in NOW, reason in the evidence cell. A task you will not do is `dropped`; its row stays. After a compaction, `git status --short` shows the in-flight work. Edit this file with the editor or a quoted heredoc; an unquoted shell string eats backticks. A self-contained task may go to a sub-agent briefed with its row, Rules, Decisions, Learnings and Context; it writes to a named file and reports a few lines, which are leads; you run the proof before pasting evidence. If the project runs Node, `node .project-management/planrails/check-plans.mjs` must pass. Full method: `.project-management/planrails/PLANNER.md` §4.
|
|
370
|
+
|
|
371
|
+
The `session:` line names the one session working this plan. Before any task goes `doing`, even when asked to continue: re-read NOW from disk, run `claude agents --json` (in Claude Code, `ListAgents` names you) and `git status --short`. If the line names a live session that is not you, or says `none` while another session in this checkout is busy or plan files are dirty: stop, report what you found, and ask before writing to the plan, its files, or a commit. A named session the listing lacks is stale: say so, take over. Other reloaded plans are context; work only the one assigned in this session, and say which.
|
|
308
372
|
|
|
309
373
|
## Goal
|
|
310
374
|
<4–5 sentences: what we are building and why. What is true when it ships. A mermaid diagram only if the architecture is non-trivial.>
|
package/README.md
CHANGED
|
@@ -9,8 +9,10 @@ work that is not. planrails fixes that with three plain rules and almost no code
|
|
|
9
9
|
|
|
10
10
|
1. **The plan reloads itself.** One line in your project's root `CLAUDE.md`
|
|
11
11
|
re-opens the plan after every compaction, and the plan carries its own
|
|
12
|
-
operating loop
|
|
13
|
-
|
|
12
|
+
operating loop and the name of the session working it, so a session that
|
|
13
|
+
never saw the planner prompt still works it correctly — and a second session
|
|
14
|
+
in the same checkout checks for the first before it touches the plan. (Claude
|
|
15
|
+
Code reloads it for you; another agent opens it by hand.)
|
|
14
16
|
2. **Every task names its proof before the work starts** — the command that will
|
|
15
17
|
show it is done. No command, no way to fake it later.
|
|
16
18
|
3. **A task is done only when its proof was run and its exit code pasted in.** An
|
|
@@ -92,11 +94,12 @@ The checker needs Node to run. If your project has no Node at all, skip it — t
|
|
|
92
94
|
plan still works, and your agent enforces the gate the way `PLANNER.md` says. The
|
|
93
95
|
planner method does not depend on any language.
|
|
94
96
|
|
|
95
|
-
### As a `/
|
|
97
|
+
### As a `/planrails` command in Claude Code
|
|
96
98
|
|
|
97
|
-
Copy [`skill/`](skill) to `~/.claude/skills/
|
|
98
|
-
flow in any project that has run `npx planrails init` — the skill reads
|
|
99
|
-
project's own copy of `PLANNER.md`, so every project follows the version it
|
|
99
|
+
Copy [`skill/`](skill) to `~/.claude/skills/planrails/`. Then `/planrails` starts
|
|
100
|
+
the same flow in any project that has run `npx planrails init` — the skill reads
|
|
101
|
+
the project's own copy of `PLANNER.md`, so every project follows the version it
|
|
102
|
+
has. (It is not called `/plan`, because Claude Code has a `/plan` of its own.)
|
|
100
103
|
|
|
101
104
|
## How a plan works
|
|
102
105
|
|
|
@@ -119,9 +122,10 @@ status: active · opened 2026-09-12 · id: weekly-digest
|
|
|
119
122
|
RESUME: T2 — render the digest through the email seam (lib/digest/render.ts)
|
|
120
123
|
NEXT: T3 schedule the Monday send · T4 docs
|
|
121
124
|
updated: 2026-09-12 14:20
|
|
125
|
+
session: app-3f · 7c1d2e9a · since 2026-09-12 14:05
|
|
122
126
|
|
|
123
127
|
## How to work this plan
|
|
124
|
-
(the loop a session follows: set doing, run the proof, paste exit N, done on 0, update NOW, log it)
|
|
128
|
+
(the loop a session follows: set doing, run the proof, paste exit N, done on 0, update NOW, log it — and, before any doing, the check that no other live session holds the plan)
|
|
125
129
|
|
|
126
130
|
## Tasks
|
|
127
131
|
| id | task | status | proof | evidence |
|
|
@@ -176,6 +180,27 @@ trusted pipeline.
|
|
|
176
180
|
- **Decide whether to commit `.project-management/`.** Committing it shares plans
|
|
177
181
|
and lets CI run the checker. If your repo gitignores it, the checker still runs
|
|
178
182
|
locally and the plan still reloads for whoever has the files.
|
|
183
|
+
- **Two machines, one plan.** The `session:` line names a session on the machine
|
|
184
|
+
that wrote it; another machine cannot list it. There, git is the record: pull
|
|
185
|
+
before you take a task, and read a `doing` row with a fresh `updated:` stamp as
|
|
186
|
+
someone's in-flight work.
|
|
187
|
+
|
|
188
|
+
## Several sessions in one checkout
|
|
189
|
+
|
|
190
|
+
The reload line puts an active plan into every Claude Code session opened in that
|
|
191
|
+
directory, so a second session can read the plan and start the task the first one
|
|
192
|
+
is on. A plan's NOW names the session working it
|
|
193
|
+
(`session: app-3f · 7c1d2e9a · since 2026-09-12 14:05`), and the plan's block tells
|
|
194
|
+
every session, before it sets a task `doing`, to re-read that line, list the live
|
|
195
|
+
sessions (`claude agents --json`; `ListAgents` inside Claude Code) and run
|
|
196
|
+
`git status --short`, and to stop and ask when another live session holds the plan
|
|
197
|
+
— even when you asked it to continue, because you can forget which window owns
|
|
198
|
+
it. One plan per session, one session per plan; the other reloaded plans are
|
|
199
|
+
context. The claim is a lead, not a lock: a line left by a closed terminal blocks
|
|
200
|
+
nobody, and the checker ignores the line. Field-tested with headless sessions: a
|
|
201
|
+
second session told "Continue the active plan." stopped and asked while the first
|
|
202
|
+
was live, took over when it was gone, and touched only the plan it was given when
|
|
203
|
+
two were active.
|
|
179
204
|
|
|
180
205
|
## History
|
|
181
206
|
|
|
@@ -187,7 +212,8 @@ rails, as a prompt plus one checker, with a two-command CLI that only copies
|
|
|
187
212
|
files. 0.3.0 makes learnings a reloaded part of every plan and groups the two
|
|
188
213
|
installed files under `.project-management/planrails/`. 0.4.0 makes the plan
|
|
189
214
|
carry its own loop, makes the checker demand `exit 0` and check the reload line,
|
|
190
|
-
and teaches the executor to brief sub-agents from the plan.
|
|
191
|
-
|
|
215
|
+
and teaches the executor to brief sub-agents from the plan. 0.5.0 names the
|
|
216
|
+
session working a plan and has every session check for a live holder before it
|
|
217
|
+
touches the plan. See [`CHANGELOG.md`](CHANGELOG.md).
|
|
192
218
|
|
|
193
219
|
MIT.
|
package/bin/planrails.mjs
CHANGED
|
@@ -83,7 +83,7 @@ function init(args) {
|
|
|
83
83
|
console.log(`
|
|
84
84
|
Next:
|
|
85
85
|
1. Tell your agent: Follow .project-management/planrails/PLANNER.md and tell me when you are ready to plan <the feature> with me.
|
|
86
|
-
(Or, in Claude Code, use /
|
|
86
|
+
(Or, in Claude Code, use /planrails if you installed the skill.)
|
|
87
87
|
2. Add to the command you run before every commit:
|
|
88
88
|
node .project-management/planrails/check-plans.mjs
|
|
89
89
|
3. When the agent writes a plan, it adds one line to your CLAUDE.md so the plan
|
package/package.json
CHANGED
package/tools/check-plans.mjs
CHANGED