@zalom/plastic 2.0.1 → 2.0.3

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 (50) hide show
  1. package/PLASTIC.md +2 -1
  2. package/README.md +40 -33
  3. package/agents/plastic-enforcer.md +13 -11
  4. package/agents/plastic-executor.md +2 -1
  5. package/agents/plastic-primary-advisor.md +2 -2
  6. package/agents/plastic-secondary-advisor.md +2 -2
  7. package/bin/plastic +2 -0
  8. package/docs/help/agent-architecture.md +9 -8
  9. package/docs/help/agent-report-contract.md +6 -4
  10. package/docs/help/completion-and-done.md +20 -4
  11. package/docs/help/human-report-contract.md +23 -20
  12. package/docs/help/knowledge-graph.md +2 -2
  13. package/docs/help/lifecycle-and-savepoints.md +9 -5
  14. package/docs/help/locks-and-worktrees.md +4 -3
  15. package/docs/help/maintenance-and-revisions.md +2 -2
  16. package/docs/help/roadmaps.md +16 -11
  17. package/docs/help/track-1-guided.md +48 -30
  18. package/docs/help/track-2-auto.md +35 -10
  19. package/docs/help/track-3-projects-and-roadmaps.md +10 -7
  20. package/docs/help/tutorial.md +421 -0
  21. package/package.json +1 -1
  22. package/scripts/end-intent +312 -69
  23. package/scripts/lib/arm.rb +28 -10
  24. package/scripts/lib/cli/commands/auto_lock.rb +17 -5
  25. package/scripts/lib/cli/commands/auto_take.rb +38 -1
  26. package/scripts/lib/cli/commands/doctor.rb +2 -2
  27. package/scripts/lib/cli/commands/intent_command.rb +6 -1
  28. package/scripts/lib/cli/commands/intent_end.rb +15 -2
  29. package/scripts/lib/cli/commands/intent_step.rb +8 -0
  30. package/scripts/lib/cli/commands/project_links.rb +5 -1
  31. package/scripts/lib/cli/commands/project_new.rb +24 -1
  32. package/scripts/lib/cli/commands/render.rb +3 -1
  33. package/scripts/lib/cli/commands/roadmap_next.rb +10 -3
  34. package/scripts/lib/cli/commands/roadmap_show.rb +21 -1
  35. package/scripts/lib/cli/commands/session_commit.rb +1 -1
  36. package/scripts/lib/cli/commands/sync.rb +10 -1
  37. package/scripts/lib/hook_replay.rb +51 -45
  38. package/scripts/lib/installer_core.rb +12 -0
  39. package/scripts/lib/node_input.rb +13 -5
  40. package/scripts/lib/revisions_writer.rb +1 -2
  41. package/scripts/lib/roadmap_queue.rb +12 -4
  42. package/scripts/lib/runner_dispatch.rb +8 -7
  43. package/scripts/lib/session_git.rb +42 -1
  44. package/scripts/lib/untouched_scaffold.rb +51 -0
  45. package/scripts/plastic-lock +24 -28
  46. package/scripts/project-links +6 -21
  47. package/scripts/roadmap-graph +10 -4
  48. package/scripts/rollback.rb +5 -1
  49. package/scripts/update.rb +7 -1
  50. package/skills/_decision-tables.md +3 -3
@@ -0,0 +1,421 @@
1
+ # Tutorial: from a new intent to merged, delivered code
2
+
3
+ ## What you will do
4
+
5
+ This tutorial takes one small change to a Ruby project from a new intent to a delivered
6
+ close. You create the intent, record a ruling, write the spec, plan, and checklist, make the
7
+ change on a branch with a failing test first, merge the branch, and close the intent as
8
+ delivered. At the end, the intent sits under `## Completed` with the `outcome.md` you wrote and
9
+ a verification that passed.
10
+
11
+ The tutorial uses the checklist path, which needs no hooks, no lock, and no agent team. Every
12
+ command here was run in a disposable `HOME` and `PLASTIC_HOME`. The output blocks come from
13
+ that run, and some are shortened. Paths, times, commit hashes, and lock names differ on your
14
+ machine: `/home/you` stands for your home directory.
15
+
16
+ Plastic records the work. It does not write the spec, the code, or the Git history for you.
17
+ Each step says who does it:
18
+
19
+ - **Plastic**: a `plastic` command.
20
+ - **You or your agent**: writing a file, running tests, or running Git.
21
+
22
+ ## Before you start
23
+
24
+ - Plastic is installed. `plastic version` prints a version.
25
+ - Ruby and Git are on your `PATH`.
26
+ - To try the tutorial without touching your real stores, open a new shell, point `HOME` and
27
+ `PLASTIC_HOME` at a scratch directory, and install there first. Type `exit` at the end to
28
+ return to your normal `HOME`:
29
+
30
+ ```sh
31
+ bash
32
+ export HOME=/tmp/plastic-tutorial/home
33
+ export PLASTIC_HOME="$HOME/.plastic"
34
+ export PATH="$PLASTIC_HOME/bin:$PATH"
35
+ mkdir -p "$HOME/.claude"
36
+ npx -y @zalom/plastic install --claude
37
+ hash -r
38
+ command -v plastic
39
+ ```
40
+
41
+ The installer needs an existing `~/.claude` directory. It stops with `.claude not found`
42
+ otherwise. It places the `plastic` command at `$PLASTIC_HOME/bin/plastic`, and
43
+ `command -v plastic` must print that path. If it prints another path, the commands below
44
+ would run your normal installation.
45
+ - In that shell, check that Ruby can load Minitest: `ruby -e 'require "minitest"'`. Some Ruby
46
+ builds do not include it, and gems installed under your normal `HOME` may not be found from
47
+ the scratch one. Run `gem install minitest` if the check fails.
48
+
49
+ ## 1. Create a small Ruby project
50
+
51
+ **You.** Create a Git repository named `greeter` with one method and one test:
52
+
53
+ ```ruby
54
+ # lib/greeter.rb
55
+ # frozen_string_literal: true
56
+
57
+ module Greeter
58
+ def self.greet(name)
59
+ "Hello, #{name}!"
60
+ end
61
+ end
62
+ ```
63
+
64
+ ```ruby
65
+ # test/greeter_test.rb
66
+ # frozen_string_literal: true
67
+
68
+ require "minitest/autorun"
69
+ require "greeter"
70
+
71
+ class GreeterTest < Minitest::Test
72
+ def test_greets_by_name
73
+ assert_equal "Hello, Ada!", Greeter.greet("Ada")
74
+ end
75
+ end
76
+ ```
77
+
78
+ Add an `AGENTS.md` file at the repository root. `plastic project new` refuses to register a
79
+ repository without one. A line or two describing the project is enough.
80
+
81
+ Commit everything on `main`. A scratch `HOME` has no Git identity, so set one for this
82
+ repository first, with your own name and email:
83
+
84
+ ```sh
85
+ git init -b main
86
+ git config user.name "Your Name"
87
+ git config user.email "you@example.com"
88
+ git add .
89
+ git commit -m "chore: greeter"
90
+ ```
91
+
92
+ ## 2. Register the project
93
+
94
+ **Plastic.** From inside the repository, run:
95
+
96
+ ```sh
97
+ plastic project new greeter --path "$PWD"
98
+ plastic project links
99
+ ```
100
+
101
+ `project new` creates the project store at `~/.plastic/stores/greeter/` and records the
102
+ repository path in `~/.plastic/projects.yml`. Every later command run inside this repository
103
+ resolves to the `greeter` store. From anywhere else, add `--project greeter`.
104
+
105
+ ## 3. Create the intent (What)
106
+
107
+ **Plastic.** Run:
108
+
109
+ ```sh
110
+ plastic intent new "Let Greeter.greet take an optional greeting word" --slug custom-greeting
111
+ ```
112
+
113
+ The output names the new intent directory and the next step:
114
+
115
+ ```text
116
+ /home/you/.plastic/stores/greeter/store/1--custom-greeting
117
+ next: plastic intent spec 1 --project greeter
118
+ because: a new intent has no specification yet
119
+ ```
120
+
121
+ The directory holds the intent file `1--custom-greeting.md`, placeholder files `spec.md`,
122
+ `plan.md`, `checklist.md`, and `outcome.md`, and empty `actions/` and `resources/` folders.
123
+ The intent is listed under `## Active` in `~/.plastic/stores/greeter/INDEX.md`.
124
+
125
+ ## 4. Record the rulings (Why)
126
+
127
+ **Plastic.** Run `plastic intent spec 1`. It prints the intent's state screen and the rules the
128
+ speccing conversation follows: one question at a time, two or three approaches with a
129
+ recommendation, and every ruling recorded the moment it lands.
130
+
131
+ **You and your agent** hold that conversation. When a ruling lands, record it:
132
+
133
+ ```sh
134
+ plastic intent rule 1 "Keep the default greeting Hello so existing callers are unchanged"
135
+ ```
136
+
137
+ The command prints one `appended:` line. The ruling is appended to the intent file's
138
+ `## Insights` section, stamped with the time, the `Why` stage, and the `human` author, followed
139
+ by the ruling text.
140
+
141
+ `intent rule` writes only to `## Insights`. If you keep a `### Decisions` list in the intent
142
+ file, write it yourself.
143
+
144
+ ## 5. Write the spec, the plan, and the checklist (How)
145
+
146
+ **You or your agent** write three files in the intent directory. No command writes them. Each
147
+ file replaces the placeholder that `intent new` created.
148
+
149
+ `spec.md` states the accepted scope:
150
+
151
+ ```markdown
152
+ # Spec: Optional greeting word
153
+
154
+ ## Problem
155
+ `Greeter.greet` always says "Hello". Callers cannot choose another greeting.
156
+
157
+ ## Goals
158
+ - `Greeter.greet("Ada", greeting: "Hi")` returns "Hi, Ada!".
159
+
160
+ ## Non-Goals
161
+ - Translation.
162
+
163
+ ## Approach
164
+ Add a keyword argument with the default "Hello".
165
+
166
+ ## Decisions
167
+ - The default stays "Hello", so existing callers are unchanged.
168
+
169
+ ## Acceptance Criteria
170
+ - [ ] The new test passes and the old test still passes.
171
+ ```
172
+
173
+ `plan.md` follows `templates/plan.md`: a `## Goal`, the `## Steps`, and `## Notes`.
174
+ `checklist.md` follows `templates/checklist.md`, with one checkbox for each piece of work:
175
+
176
+ ```markdown
177
+ # Checklist: Optional greeting word
178
+
179
+ ## In Progress
180
+ - [ ] Add a failing test for the greeting keyword
181
+ - [ ] Add the greeting keyword to Greeter.greet
182
+
183
+ ## Completed
184
+ (move items here when done)
185
+ ```
186
+
187
+ **Plastic.** `plastic intent show 1` now reads the checklist and points at execution:
188
+
189
+ ```text
190
+ | S1 | open | Add a failing test for the greeting keyword |
191
+ | S2 | open | Add the greeting keyword to Greeter.greet |
192
+ next: plastic intent step 1 --project greeter
193
+ because: the checklist has unfinished work
194
+ ```
195
+
196
+ Until `spec.md` is written, the same screen points back at `plastic intent spec 1`. Until
197
+ `plan.md` and `checklist.md` are written, it says so in the `because:` line.
198
+
199
+ ## 6. Do the work (Exec)
200
+
201
+ **Plastic.** `plastic intent step 1` prints the next unchecked item. On a checklist intent, it
202
+ does not run anything:
203
+
204
+ ```text
205
+ work Add a failing test for the greeting keyword
206
+ checklist /home/you/.plastic/stores/greeter/store/1--custom-greeting/checklist.md
207
+
208
+ next: none
209
+ because: perform this checklist item, record its verification, then run plastic intent show 1 --project greeter
210
+ ```
211
+
212
+ **You or your agent** do the item on a branch. The branch name `plastic/1--custom-greeting`
213
+ matches the name `plastic auto take` would provision, so the close can find it:
214
+
215
+ ```sh
216
+ git switch -c plastic/1--custom-greeting
217
+ ```
218
+
219
+ Add the new test to `test/greeter_test.rb`:
220
+
221
+ ```ruby
222
+ def test_takes_a_greeting_word
223
+ assert_equal "Hi, Ada!", Greeter.greet("Ada", greeting: "Hi")
224
+ end
225
+ ```
226
+
227
+ Run the tests and watch the new one fail:
228
+
229
+ ```sh
230
+ ruby -Ilib test/greeter_test.rb
231
+ ```
232
+
233
+ ```text
234
+ ArgumentError: wrong number of arguments (given 2, expected 1)
235
+ 2 runs, 1 assertions, 0 failures, 1 errors, 0 skips
236
+ ```
237
+
238
+ Commit the red test. Then tick the item in `checklist.md` in the intent directory: change
239
+ `- [ ]` to `- [x]`.
240
+
241
+ Run `plastic intent step 1` again for the second item. Change `lib/greeter.rb`:
242
+
243
+ ```ruby
244
+ module Greeter
245
+ def self.greet(name, greeting: "Hello")
246
+ "#{greeting}, #{name}!"
247
+ end
248
+ end
249
+ ```
250
+
251
+ Run the tests again. Both pass:
252
+
253
+ ```text
254
+ 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
255
+ ```
256
+
257
+ Commit, and tick the second item. `plastic intent show 1` now points at
258
+ `plastic intent verify 1`, because every checklist item is complete.
259
+
260
+ To keep the commit on the record, add a note to the savepoint. Replace `<sha>` with the commit
261
+ hash:
262
+
263
+ ```sh
264
+ plastic intent note 1 "<sha> greeting keyword, tests green" --kind Commit
265
+ ```
266
+
267
+ ## 7. Write the outcome and verify
268
+
269
+ **You or your agent** replace the `outcome.md` placeholder in the intent directory. The shape
270
+ follows `templates/outcome.md`. The `disposition` must match the close you plan, and each
271
+ `## Delivered` row names one checklist step:
272
+
273
+ ```markdown
274
+ ---
275
+ disposition: delivered
276
+ ---
277
+ # Outcome: Let Greeter.greet take an optional greeting word
278
+
279
+ ## Summary
280
+ Greeter.greet takes an optional greeting word; the default stays Hello.
281
+
282
+ ## Delivered
283
+ | Row | What |
284
+ | --- | --- |
285
+ | S1 | A test for the greeting keyword |
286
+ | S2 | The greeting keyword on Greeter.greet |
287
+
288
+ ## Verification
289
+ - The new test passes and the old test still passes: `ruby -Ilib test/greeter_test.rb` printed 2 runs, 0 failures.
290
+
291
+ ## Needs you
292
+ None
293
+
294
+ ## Follow-ups
295
+ None
296
+ ```
297
+
298
+ **Plastic.** Record what you verified as a report note:
299
+
300
+ ```sh
301
+ plastic intent note 1 "Both greeter tests pass on the branch; outcome.md written" --kind Report
302
+ ```
303
+
304
+ `--kind` takes `Review`, `Commit`, or `Report`. Without it, the note is a `Report`.
305
+
306
+ Then run `plastic intent verify 1`. It runs the per-intent doctor check, the em-dash guard,
307
+ and a diffstat of the code branch against `main`. Every check passes, and the command exits 0:
308
+
309
+ ```text
310
+ doctor: pass
311
+ em-dash guard: pass (0 violations)
312
+ diffstat against main:
313
+ lib/greeter.rb | 4 ++--
314
+ test/greeter_test.rb | 4 ++++
315
+ 2 files changed, 6 insertions(+), 2 deletions(-)
316
+ report lines:
317
+ 2026-09-23T11:50:28Z Report Both greeter tests pass on the branch; outcome.md written
318
+ next: plastic intent end 1 --delivered --summary "TEXT" --project greeter
319
+ because: a clean verify is what makes the close trustworthy
320
+ ```
321
+
322
+ If a check fails, fix what it names and run `plastic intent verify 1` again. Do not close the
323
+ intent while verify fails.
324
+
325
+ ## 8. Merge the code (Git)
326
+
327
+ Plastic does not merge. Its delivered close refuses code that is not merged.
328
+
329
+ **Plastic.** Try the close while the repository is still on the code branch:
330
+
331
+ ```sh
332
+ plastic intent end 1 --delivered --summary "Greeter.greet takes an optional greeting word; the default stays Hello."
333
+ ```
334
+
335
+ It exits 1 and writes nothing:
336
+
337
+ ```text
338
+ end-intent: refusing a delivered close: the repo checkout /home/you/greeter is on the code branch plastic/1--custom-greeting itself, so the code is not merged anywhere. Check out the branch you release from, merge plastic/1--custom-greeting into it, then run the close again
339
+ end-intent: nothing was merged, written, or released
340
+ ```
341
+
342
+ **You.** Merge the branch and run the tests on `main`:
343
+
344
+ ```sh
345
+ git switch main
346
+ git merge --no-ff plastic/1--custom-greeting
347
+ ruby -Ilib test/greeter_test.rb
348
+ ```
349
+
350
+ ## 9. Close the intent as delivered
351
+
352
+ **Plastic.** Preview the close first:
353
+
354
+ ```sh
355
+ plastic intent end 1 --delivered --summary "Greeter.greet takes an optional greeting word; the default stays Hello." --dry-run
356
+ ```
357
+
358
+ The dry run lists what the close would do: move the `INDEX.md` entry to `## Completed`, append
359
+ the terminal savepoint line, commit the store repository, and release the lock and the code
360
+ worktree. It ends with:
361
+
362
+ ```text
363
+ next: none
364
+ because: the dry run wrote nothing and found nothing that would refuse the close
365
+ ```
366
+
367
+ Run the same command without `--dry-run`. The close fills the records that are still
368
+ placeholders from the record itself. Here that is the action file:
369
+
370
+ ```text
371
+ end-intent: backfilled actions/ACTION_1.md from the record
372
+ ```
373
+
374
+ `plastic intent show 1` now shows both steps done:
375
+
376
+ ```text
377
+ next: none
378
+ because: the intent is completed
379
+ ```
380
+
381
+ `outcome.md` keeps what you wrote. The close writes the `--summary` text into the intent file's
382
+ `## Outcome` section.
383
+
384
+ ## The same change in auto mode
385
+
386
+ In auto mode, an agent team does steps 4 to 9. The commands below set up and inspect that
387
+ run. They do not run the team; your harness does that.
388
+
389
+ 1. `plastic auto take ID` takes the delivery lock and provisions the code worktree at
390
+ `<repo>/.claude/worktrees/ID--slug` on branch `plastic/ID--slug`.
391
+
392
+ ```text
393
+ intent 2--shout
394
+ lock acquired by auto-9184f6c4fa, auto mode
395
+ worktree /home/you/greeter/.claude/worktrees/2--shout
396
+ ```
397
+
398
+ Run from inside a conversation session, `auto take` refuses with exit 3 unless you pass
399
+ `--allow-inline`. Exit 3 means the step belongs to the owner: stop and report it.
400
+
401
+ 2. `plastic auto brief ID` prints the preamble the dispatched agent reads first.
402
+ 3. `plastic auto lock status ID` shows who holds the lock and where the worktree is.
403
+ 4. `plastic auto report ID` prints the report contract and the review rules the lead follows.
404
+ 5. The close is the same `plastic intent end` as in step 9, with the same merge check.
405
+
406
+ `plastic intent end ID --delivered` refuses an intent that nobody worked on. The spec, plan,
407
+ checklist, and outcome are still placeholders, and the worktree has no changes. Close it with
408
+ `--abandoned` instead:
409
+
410
+ ```sh
411
+ plastic intent end 2 --abandoned --summary "Probe of the auto contract only."
412
+ ```
413
+
414
+ The abandoned close also removes the code worktree.
415
+
416
+ ## Where to go next
417
+
418
+ - `plastic help track-1-guided`: the same cycle for a graph intent, driven one node at a time.
419
+ - `plastic help track-2-auto`: how the auto team walks the record.
420
+ - `plastic help completion-and-done`: what the close checks and writes.
421
+ - `plastic help locks-and-worktrees`: the delivery lock and the code worktree.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {