@zalom/plastic 1.0.3 → 1.1.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.
Files changed (98) hide show
  1. package/PLASTIC-reference.md +11 -11
  2. package/PLASTIC.md +7 -7
  3. package/README.md +47 -0
  4. package/agents/plastic-enforcer.md +3 -3
  5. package/agents/plastic-intent-curator.md +9 -10
  6. package/agents/plastic-spec-specialist.md +9 -6
  7. package/bin/plastic.js +2 -2
  8. package/package.json +1 -1
  9. package/scripts/dashboard.rb +1 -1
  10. package/scripts/doctor.rb +52 -4
  11. package/scripts/end-intent +330 -0
  12. package/scripts/hook-code-gate +1 -1
  13. package/scripts/hook-continue +1 -1
  14. package/scripts/hook-create-gate +2 -2
  15. package/scripts/hook-session-start +1 -1
  16. package/scripts/lib/bridge.rb +35 -3
  17. package/scripts/lib/installer_core.rb +8 -6
  18. package/scripts/lib/store_provisioning.rb +1 -1
  19. package/scripts/new-intent +1 -1
  20. package/scripts/{versions.rb → rollback.rb} +33 -41
  21. package/scripts/update.rb +1 -1
  22. package/skills/_active-intent-gate.md +1 -1
  23. package/skills/_decision-tables.md +50 -0
  24. package/skills/auto/SKILL.md +47 -64
  25. package/skills/auto/evals/evals.json +4 -4
  26. package/skills/auto/references/agent-architecture.md +2 -2
  27. package/skills/dashboard/SKILL.md +2 -2
  28. package/skills/doctor/SKILL.md +3 -3
  29. package/skills/install/SKILL.md +1 -1
  30. package/skills/{brainstorming → intent-brainstorming}/SKILL.md +43 -45
  31. package/skills/intent-brainstorming/evals/evals.json +67 -0
  32. package/skills/{brainstorming → intent-brainstorming}/references/design-principles.md +12 -17
  33. package/skills/{continuing → intent-continuing}/SKILL.md +3 -3
  34. package/skills/{continuing → intent-continuing}/evals/evals.json +24 -24
  35. package/skills/{creating-intent → intent-creating}/SKILL.md +3 -3
  36. package/skills/{creating-intent → intent-creating}/evals/evals.json +1 -1
  37. package/skills/{intent-discovery → intent-discovering}/SKILL.md +1 -1
  38. package/skills/intent-ending/SKILL.md +151 -0
  39. package/skills/intent-ending/evals/evals.json +74 -0
  40. package/skills/{executing-plan → intent-executing}/SKILL.md +75 -15
  41. package/skills/intent-executing/evals/evals.json +66 -0
  42. package/skills/{brainstorming-grill-me → intent-grilling}/SKILL.md +4 -4
  43. package/skills/{linking-intents → intent-linking}/SKILL.md +1 -1
  44. package/skills/{linking-intents → intent-linking}/evals/evals.json +1 -1
  45. package/skills/{lock → intent-locking}/SKILL.md +1 -1
  46. package/skills/{writing-plans → intent-planning}/SKILL.md +48 -20
  47. package/skills/intent-planning/evals/evals.json +66 -0
  48. package/skills/{writing-plans → intent-planning}/references/plan-format.md +31 -7
  49. package/skills/{research → intent-researching}/SKILL.md +2 -2
  50. package/skills/{research → intent-researching}/evals/evals.json +1 -1
  51. package/skills/{savepoint → intent-savepoint}/SKILL.md +1 -1
  52. package/skills/intent-speccing/SKILL.md +69 -0
  53. package/skills/intent-speccing/evals/evals.json +126 -0
  54. package/skills/intent-speccing/references/per-section-fill-rules.md +92 -0
  55. package/skills/intent-speccing/references/self-verify-checklist.md +38 -0
  56. package/skills/intent-starting/SKILL.md +2 -2
  57. package/skills/intent-starting/evals/evals.json +2 -2
  58. package/skills/{creating-project → project-creating}/SKILL.md +2 -2
  59. package/skills/releasing/SKILL.md +18 -14
  60. package/skills/{versions → rollback}/SKILL.md +30 -20
  61. package/skills/{creating-skills → skill-creating}/SKILL.md +2 -2
  62. package/skills/{creating-skills → skill-creating}/evals/evals.json +5 -5
  63. package/skills/{creating-skills → skill-creating}/references/progressive-disclosure.md +2 -2
  64. package/skills/{evaluating-skills → skill-evaluating}/SKILL.md +1 -1
  65. package/skills/{evaluating-skills → skill-evaluating}/evals/evals.json +3 -3
  66. package/skills/{intent-curator → store-curating}/SKILL.md +6 -7
  67. package/skills/{intent-curator → store-curating}/evals/evals.json +1 -1
  68. package/skills/{managing-index → store-indexing}/SKILL.md +6 -7
  69. package/skills/{managing-index → store-indexing}/evals/evals.json +1 -1
  70. package/skills/{add-project-store → store-provisioning}/SKILL.md +2 -2
  71. package/skills/tutorial/SKILL.md +65 -0
  72. package/skills/tutorial/evals/evals.json +186 -0
  73. package/skills/tutorial/references/track-1-guided.md +123 -0
  74. package/skills/tutorial/references/track-2-auto.md +95 -0
  75. package/skills/tutorial/references/track-3-projects-and-roadmaps.md +110 -0
  76. package/skills/update/SKILL.md +1 -1
  77. package/templates/agents.md +7 -7
  78. package/skills/brainstorming/evals/evals.json +0 -22
  79. /package/skills/{continuing → intent-continuing}/references/context-management.md +0 -0
  80. /package/skills/{creating-intent → intent-creating}/references/lifecycle.md +0 -0
  81. /package/skills/{creating-intent → intent-creating}/references/wikilinks.md +0 -0
  82. /package/skills/{executing-plan → intent-executing}/code-quality-reviewer-prompt.md +0 -0
  83. /package/skills/{executing-plan → intent-executing}/implementer-prompt.md +0 -0
  84. /package/skills/{executing-plan → intent-executing}/spec-reviewer-prompt.md +0 -0
  85. /package/skills/{linking-intents → intent-linking}/references/zettelkasten.md +0 -0
  86. /package/skills/{savepoint → intent-savepoint}/references/context-management.md +0 -0
  87. /package/skills/{creating-project → project-creating}/references/hubs-projects.md +0 -0
  88. /package/skills/{creating-project → project-creating}/references/project-scaffolding.md +0 -0
  89. /package/skills/{creating-skills → skill-creating}/references/agents.md +0 -0
  90. /package/skills/{creating-skills → skill-creating}/references/evals.md +0 -0
  91. /package/skills/{creating-skills → skill-creating}/references/hooks.md +0 -0
  92. /package/skills/{creating-skills → skill-creating}/references/scripts.md +0 -0
  93. /package/skills/{creating-skills → skill-creating}/references/skills.md +0 -0
  94. /package/skills/{creating-skills → skill-creating}/scripts/scaffold.rb +0 -0
  95. /package/skills/{evaluating-skills → skill-evaluating}/assets/eval-template.json +0 -0
  96. /package/skills/{evaluating-skills → skill-evaluating}/references/convention-checks.md +0 -0
  97. /package/skills/{evaluating-skills → skill-evaluating}/references/eval-methodology.md +0 -0
  98. /package/skills/{managing-index → store-indexing}/references/zettelkasten-linking.md +0 -0
@@ -20,30 +20,25 @@ steps).
20
20
 
21
21
  ## Process Flow (diagram)
22
22
 
23
- The Checklist above already states this ordered flow as numbered steps 1-8; this
23
+ The Checklist above already states this ordered flow as numbered steps 1-6; this
24
24
  diagram is the same flow in a visual form.
25
25
 
26
26
  ```dot
27
27
  digraph brainstorming {
28
28
  "Explore project context" [shape=box];
29
- "Ask clarifying questions" [shape=box];
29
+ "Grill in prose" [shape=box];
30
30
  "Propose 2-3 approaches" [shape=box];
31
31
  "Present design sections" [shape=box];
32
- "User approves design?" [shape=diamond];
33
- "Write spec" [shape=box];
34
- "Spec self-review\n(fix inline)" [shape=box];
35
- "User reviews spec?" [shape=diamond];
36
- "Invoke plastic-writing-plans" [shape=doublecircle];
37
-
38
- "Explore project context" -> "Ask clarifying questions";
39
- "Ask clarifying questions" -> "Propose 2-3 approaches";
32
+ "Owner rules?" [shape=diamond];
33
+ "Collect rulings\n(persist each immediately)" [shape=box];
34
+ "Invoke /plastic-intent-speccing" [shape=doublecircle];
35
+
36
+ "Explore project context" -> "Grill in prose";
37
+ "Grill in prose" -> "Propose 2-3 approaches";
40
38
  "Propose 2-3 approaches" -> "Present design sections";
41
- "Present design sections" -> "User approves design?";
42
- "User approves design?" -> "Present design sections" [label="no, revise"];
43
- "User approves design?" -> "Write spec" [label="yes"];
44
- "Write spec" -> "Spec self-review\n(fix inline)";
45
- "Spec self-review\n(fix inline)" -> "User reviews spec?";
46
- "User reviews spec?" -> "Write spec" [label="changes requested"];
47
- "User reviews spec?" -> "Invoke plastic-writing-plans" [label="approved"];
39
+ "Present design sections" -> "Owner rules?";
40
+ "Owner rules?" -> "Present design sections" [label="no, revise"];
41
+ "Owner rules?" -> "Collect rulings\n(persist each immediately)" [label="yes"];
42
+ "Collect rulings\n(persist each immediately)" -> "Invoke /plastic-intent-speccing";
48
43
  }
49
44
  ```
@@ -1,12 +1,12 @@
1
1
  ---
2
- name: plastic-continuing
3
- description: Use when the user says "continue", "resume", or "pick up where we left off", or when starting a new session. Continues work with the latest project context: lands on the right dashboard, then presents choices. Boot (health check, core context, version, statusline) is owned by the SessionStart hook, not this skill. Does not drive work autonomously (that is plastic-auto).
2
+ name: plastic-intent-continuing
3
+ description: 'Use when the user says "continue", "resume", or "pick up where we left off", or when starting a new session. Continues work with the latest project context: lands on the right dashboard, then presents choices. Boot (health check, core context, version, statusline) is owned by the SessionStart hook, not this skill. Does not drive work autonomously (that is plastic-auto).'
4
4
  user-invocable: true
5
5
  ---
6
6
 
7
7
  # Continuing
8
8
 
9
- `plastic-continuing` continues work. It presents the latest state via the dashboard and offers
9
+ `plastic-intent-continuing` continues work. It presents the latest state via the dashboard and offers
10
10
  choices, then stops. It does NOT execute work autonomously (that is `plastic-auto`) and does
11
11
  NOT render the dashboard itself (it only invokes it).
12
12
 
@@ -1,51 +1,51 @@
1
1
  {
2
- "skill_name": "plastic-continuing",
2
+ "skill_name": "plastic-intent-continuing",
3
3
  "notes": "Intent 36, updated for 36a (boot moved into hook-session-start; the skill is now work-continuation only). Scopes: description triggering (1-8) and behavior/convention compliance of the work-continuation skill (9-14). Triggering assertions follow the plastic-auto eval style (one subagent router per case). Behavior assertions are convention checks against the SKILL.md.",
4
4
  "results": {
5
5
  "triggering": { "cases": 8, "passed": 8, "run": "2026-06-16, one subagent per case" },
6
- "behavior": { "cases": 6, "passed": 6, "evidence": "convention checks against skills/continuing/SKILL.md after the intent-36 rewrite; doctor --core verified at 0.07s with 9 liveness checks and full doctor unchanged at 30 checks" }
6
+ "behavior": { "cases": 6, "passed": 6, "evidence": "convention checks against skills/intent-continuing/SKILL.md after the intent-36 rewrite; doctor --core verified at 0.07s with 9 liveness checks and full doctor unchanged at 30 checks" }
7
7
  },
8
8
  "evals": [
9
9
  {
10
10
  "id": 1, "scope": "triggering", "set": "train",
11
11
  "prompt": "continue",
12
- "expected_output": "Activates plastic-continuing (the bare 'continue' keyword is the documented trigger).",
12
+ "expected_output": "Activates plastic-intent-continuing (the bare 'continue' keyword is the documented trigger).",
13
13
  "files": [],
14
14
  "assertions": [
15
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
15
+ { "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
16
16
  ]
17
17
  },
18
18
  {
19
19
  "id": 2, "scope": "triggering", "set": "train",
20
20
  "prompt": "resume where we left off",
21
- "expected_output": "Activates plastic-continuing.",
21
+ "expected_output": "Activates plastic-intent-continuing.",
22
22
  "files": [],
23
23
  "assertions": [
24
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
24
+ { "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
25
25
  ]
26
26
  },
27
27
  {
28
28
  "id": 3, "scope": "triggering", "set": "validation",
29
29
  "prompt": "pick up where we left off in this project",
30
- "expected_output": "Activates plastic-continuing.",
30
+ "expected_output": "Activates plastic-intent-continuing.",
31
31
  "files": [],
32
32
  "assertions": [
33
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
33
+ { "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
34
34
  ]
35
35
  },
36
36
  {
37
37
  "id": 4, "scope": "triggering", "set": "train",
38
38
  "prompt": "boot plastic and show me where things stand",
39
- "expected_output": "Activates plastic-continuing (continuing work + presenting state is the skill's purpose; boot itself is hook-owned).",
39
+ "expected_output": "Activates plastic-intent-continuing (continuing work + presenting state is the skill's purpose; boot itself is hook-owned).",
40
40
  "files": [],
41
41
  "assertions": [
42
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
42
+ { "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
43
43
  ]
44
44
  },
45
45
  {
46
46
  "id": 5, "scope": "triggering", "set": "train",
47
47
  "prompt": "continue delivering this intent autonomously, don't ask me",
48
- "expected_output": "Does NOT settle on plastic-continuing for execution. Shares 'continue' but the autonomous-delivery intent routes to plastic-auto.",
48
+ "expected_output": "Does NOT settle on plastic-intent-continuing for execution. Shares 'continue' but the autonomous-delivery intent routes to plastic-auto.",
49
49
  "files": [],
50
50
  "assertions": [
51
51
  { "type": "code", "check": "router CHOICE == plastic-auto", "observed": "plastic-auto", "result": "pass" }
@@ -54,35 +54,35 @@
54
54
  {
55
55
  "id": 6, "scope": "triggering", "set": "validation",
56
56
  "prompt": "continue the for-loop to the next iteration in this function",
57
- "expected_output": "Does NOT activate plastic-continuing. Near-miss: shares 'continue' but is a code-editing task.",
57
+ "expected_output": "Does NOT activate plastic-intent-continuing. Near-miss: shares 'continue' but is a code-editing task.",
58
58
  "files": [],
59
59
  "assertions": [
60
- { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "none", "result": "pass" }
60
+ { "type": "code", "check": "router CHOICE != plastic-intent-continuing", "observed": "none", "result": "pass" }
61
61
  ]
62
62
  },
63
63
  {
64
64
  "id": 7, "scope": "triggering", "set": "train",
65
65
  "prompt": "resume the paused background download",
66
- "expected_output": "Does NOT activate plastic-continuing. Near-miss: shares 'resume' but is unrelated to Plastic sessions.",
66
+ "expected_output": "Does NOT activate plastic-intent-continuing. Near-miss: shares 'resume' but is unrelated to Plastic sessions.",
67
67
  "files": [],
68
68
  "assertions": [
69
- { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "none", "result": "pass" }
69
+ { "type": "code", "check": "router CHOICE != plastic-intent-continuing", "observed": "none", "result": "pass" }
70
70
  ]
71
71
  },
72
72
  {
73
73
  "id": 8, "scope": "triggering", "set": "validation",
74
74
  "prompt": "what's the dashboard look like right now",
75
- "expected_output": "May activate plastic-dashboard rather than plastic-continuing; an overview request without 'continue/resume' is a dashboard task.",
75
+ "expected_output": "May activate plastic-dashboard rather than plastic-intent-continuing; an overview request without 'continue/resume' is a dashboard task.",
76
76
  "files": [],
77
77
  "assertions": [
78
- { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "plastic-dashboard", "result": "pass" }
78
+ { "type": "code", "check": "router CHOICE != plastic-intent-continuing", "observed": "plastic-dashboard", "result": "pass" }
79
79
  ]
80
80
  },
81
81
  {
82
82
  "id": 9, "scope": "behavior", "set": "train",
83
83
  "prompt": "Does the skill defer boot to the SessionStart hook rather than running it itself?",
84
84
  "expected_output": "SKILL.md states boot (health check, core context, version banner, statusline) is owned by hook-session-start / plastic-statusline, not this skill.",
85
- "files": ["skills/continuing/SKILL.md"],
85
+ "files": ["skills/intent-continuing/SKILL.md"],
86
86
  "assertions": [
87
87
  { "type": "convention", "check": "SKILL.md says boot is owned by the SessionStart hook and the skill does not run it", "observed": "'Boot is not this skill's job.' paragraph naming hook-session-start and plastic-statusline", "result": "pass" }
88
88
  ]
@@ -91,7 +91,7 @@
91
91
  "id": 10, "scope": "behavior", "set": "train",
92
92
  "prompt": "Does the skill avoid running doctor / setting the statusline itself?",
93
93
  "expected_output": "SKILL.md does NOT instruct running doctor.rb --core or setting the statusline; it attributes those to the hooks.",
94
- "files": ["skills/continuing/SKILL.md"],
94
+ "files": ["skills/intent-continuing/SKILL.md"],
95
95
  "assertions": [
96
96
  { "type": "convention", "check": "no in-skill boot step invokes doctor.rb --core or sets the statusline; both are attributed to hooks", "observed": "doctor --core and statusline mentioned only as hook-owned in the 'Boot is not this skill's job' note", "result": "pass" }
97
97
  ]
@@ -100,7 +100,7 @@
100
100
  "id": 11, "scope": "behavior", "set": "train",
101
101
  "prompt": "Is dashboard selection project-aware?",
102
102
  "expected_output": "Project loaded -> `dashboard.rb project <slug>`; otherwise -> `dashboard.rb continue`. Skill only invokes, does not render.",
103
- "files": ["skills/continuing/SKILL.md"],
103
+ "files": ["skills/intent-continuing/SKILL.md"],
104
104
  "assertions": [
105
105
  { "type": "convention", "check": "both dashboard invocations present and gated on project detection", "observed": "'dashboard.rb project <slug>' and 'dashboard.rb continue' in the 'Continue (present the dashboard)' section", "result": "pass" }
106
106
  ]
@@ -109,7 +109,7 @@
109
109
  "id": 12, "scope": "behavior", "set": "validation",
110
110
  "prompt": "Is ledger-resume conditional and ledger-driven?",
111
111
  "expected_output": "Resume fires only when a specific intent is named; reads savepoint.md last line as stage, verifies the stage file, rebuilds on drift, derives next step from first unchecked checklist item.",
112
- "files": ["skills/continuing/SKILL.md"],
112
+ "files": ["skills/intent-continuing/SKILL.md"],
113
113
  "assertions": [
114
114
  { "type": "convention", "check": "Conditional Ledger-Resume section reads last ledger line, verifies stage file, calls rebuild_savepoint on drift, uses first unchecked checklist item", "observed": "all four present in 'Conditional Ledger-Resume'", "result": "pass" }
115
115
  ]
@@ -118,7 +118,7 @@
118
118
  "id": 13, "scope": "behavior", "set": "validation",
119
119
  "prompt": "Are the stale prose-savepoint fields gone?",
120
120
  "expected_output": "No 'In progress' / 'Blockers' fields read from a prose savepoint remain; the ledger model is used instead.",
121
- "files": ["skills/continuing/SKILL.md"],
121
+ "files": ["skills/intent-continuing/SKILL.md"],
122
122
  "assertions": [
123
123
  { "type": "convention", "check": "no prose-savepoint announce template (In progress / Blockers from prose)", "observed": "absent; announce uses Stage/Next step/Context/Drift derived from ledger + checklist", "result": "pass" }
124
124
  ]
@@ -127,7 +127,7 @@
127
127
  "id": 14, "scope": "behavior", "set": "validation",
128
128
  "prompt": "Is the 'Determine Store' local-store gap filled?",
129
129
  "expected_output": "Step 2 of Determine Store describes local/project-store detection (no empty step).",
130
- "files": ["skills/continuing/SKILL.md"],
130
+ "files": ["skills/intent-continuing/SKILL.md"],
131
131
  "assertions": [
132
132
  { "type": "convention", "check": "Determine Store step 2 documents local/project store detection via projects.yml + CWD match", "observed": "filled", "result": "pass" }
133
133
  ]
@@ -136,7 +136,7 @@
136
136
  "id": 15, "scope": "behavior", "set": "validation",
137
137
  "prompt": "QMD is present. The user says: continue the work on the statusline coloring (names the intent by description, not id).",
138
138
  "expected_output": "Before scanning the store with grep/Read to locate the named intent, runs `ruby ~/.plastic/scripts/qmd-sync search \"statusline coloring\"` to surface the candidate intent, then opens the authoritative intent file for the hit it resumes. No-op fallback to INDEX.md / file scan when QMD is absent.",
139
- "files": ["skills/continuing/SKILL.md"],
139
+ "files": ["skills/intent-continuing/SKILL.md"],
140
140
  "assertions": [
141
141
  { "type": "human", "check": "qmd-sync search is run before grep/Read when the intent is named by description; authoritative file opened for the hit", "observed": "Conditional Ledger-Resume carries the QMD-first step: run qmd-sync search before grep/Read, then open the authoritative file; no-op fallback when QMD is absent", "result": "pass" }
142
142
  ]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: plastic-creating-intent
2
+ name: plastic-intent-creating
3
3
  description: Use when new work begins, the user expresses a new goal, says "new intent", or no active intent exists for the current task. Creates intents in the global store (~/.plastic/store/) or in a project's store (~/.plastic/projects/{slug}/store/) depending on context.
4
4
  user-invocable: true
5
5
  ---
@@ -73,7 +73,7 @@ Having a "parent" in mind does NOT automatically mean branch. Choose by meaning:
73
73
  frontmatter edge and reproject. Links are decided by context influence (a `chain` edge has a high
74
74
  bar: the candidate's context must materially help deliver this intent), not by shared files or a
75
75
  similarity score. To gather candidates with their context and record an edge, use the
76
- `plastic-linking-intents` skill and `scripts/link-suggest`.
76
+ `plastic-intent-linking` skill and `scripts/link-suggest`.
77
77
 
78
78
  ### 3. Determine Intent Properties
79
79
 
@@ -126,7 +126,7 @@ cleanly, and do not work around the failure by hand-writing the files.
126
126
  ### 6. If Implementation Intent Spawns a Project
127
127
 
128
128
  When the user says "start building" or the plan calls for a new project, invoke the
129
- `plastic-creating-project` skill; it owns project directory creation, AGENTS.md
129
+ `plastic-project-creating` skill; it owns project directory creation, AGENTS.md
130
130
  population, projects.yml registration, store provisioning, and the auto-commit of
131
131
  both stores. Add `project-<slug>` to this intent's `tags` array either before
132
132
  invoking it or as part of that skill's handoff.
@@ -1,5 +1,5 @@
1
1
  {
2
- "skill_name": "plastic-creating-intent",
2
+ "skill_name": "plastic-intent-creating",
3
3
  "notes": "Intent 68. Scope: output-quality for the sources-vs-chain construction rules (D1/D2). Asserts the related-but-not-spawned case produces NO sources plus a predecessor chain link and a ## Links mirror, contrasted with the created-from case (true ascendant -> --sources set, reciprocal chain). The machine-checkable half lives in test/new_intent_test.rb (test_sources_path_gets_child_in_chain_frontmatter); this file documents the agent-facing scenario for skill evaluation and is NOT run by bin/test.",
4
4
  "evals": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: plastic-intent-discovery
2
+ name: plastic-intent-discovering
3
3
  description: >-
4
4
  What-stage context deposit at intent activation: run QMD discovery over the
5
5
  intent's chain/sources and related parked intents, and write findings to
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: plastic-intent-ending
3
+ description: >
4
+ Wrap, finish, close, or mark an intent Done, delivered or abandoned. Use
5
+ when completing or abandoning an intent, when a checklist reaches 100
6
+ percent and Exec is finished, or when asked to "wrap this up".
7
+ user-invocable: true
8
+ ---
9
+
10
+ # Intent Ending
11
+
12
+ The one procedure every terminal transition routes through. Auto mode, the
13
+ curator path, and releasing all call this skill (or its backing script,
14
+ `scripts/end-intent`) for the mechanical close instead of restating the same
15
+ prose three times. `abandoned` is the SAME procedure as `delivered`, not a
16
+ failure branch: only outcome.md content and the INDEX section differ.
17
+
18
+ ## The 8 steps (0-7)
19
+
20
+ | # | Step | Who does it |
21
+ |---|---|---|
22
+ | 0 | Precondition check | You, before touching outcome.md |
23
+ | 1 | outcome.md + intent-file `## Outcome` summary | `scripts/end-intent` |
24
+ | 2 | INDEX.md terminal move (Active -> Completed/Abandoned) | `scripts/end-intent` |
25
+ | 3 | savepoint `Done` bookend | `scripts/end-intent` |
26
+ | 4 | store auto-commit | `scripts/end-intent` |
27
+ | 5 | disarm (worktree + lock) | You, after end-intent exits 0 |
28
+ | 6 | QMD reindex, async, LAST | You |
29
+ | 7 | EM-to-CTO report | You |
30
+
31
+ Steps 1-4 are ONE callable script, not four separate one-liners: this is
32
+ exactly what the failure mode this intent fixes looked like (releasing hand
33
+ authored the close in prose and dropped the savepoint bookend for two real
34
+ deliveries). Never restate outcome/INDEX/savepoint prose inline again; call
35
+ `scripts/end-intent`.
36
+
37
+ ### Step 0. Precondition (the gate is section-blind, not selective)
38
+
39
+ `Bridge.check_gate` (scripts/lib/bridge.rb) is LIVE code already wired into
40
+ the write-time hook. Its outcome.md rule is a blind scan of the WHOLE
41
+ checklist.md: `content.scan(/^- \[ \]/)` counts every unchecked box, in
42
+ every section, with no awareness of which section a box lives in. ANY
43
+ unchecked box anywhere blocks the outcome.md write; there is no exemption
44
+ for orchestrator-owned or completion-tracking items.
45
+
46
+ 1. Read checklist.md. Tick every item as it is actually performed, including
47
+ an item that describes the close itself: running this very procedure IS
48
+ what that item describes, so tick it at the moment you begin the close,
49
+ before authoring outcome.md. By the time outcome.md is written,
50
+ checklist.md must read 100 percent checked; there is no other way past
51
+ the gate.
52
+ 2. Confirm every acceptance criterion in spec.md is verifiable (tests pass,
53
+ or the manual check described in its HOW line was actually run).
54
+ 3. If checklist.md still has an unchecked box, stop here. Finish it first
55
+ (tick it if the described work is actually done, or do the remaining
56
+ work); do not attempt outcome.md and fight the gate's deny.
57
+
58
+ ### Step 1-4. Run `scripts/end-intent`
59
+
60
+ First author outcome.md for real (never leave the scaffold placeholder in
61
+ place): copy `templates/outcome.md`, set the frontmatter to
62
+ `disposition: delivered` or `disposition: abandoned`, and fill `## Summary`,
63
+ `## Delivered`, `## Verification`, `## Follow-ups`. On abandon, `## Summary`
64
+ states the abandonment reason and the trail (see Pivot below). Also author
65
+ the rich INDEX entry note now (a short line in the store's existing
66
+ Completed/Abandoned convention: mode/tier, what shipped or why it was
67
+ abandoned, suite result, merge/spawn notes); content authoring stays with
68
+ you, `--index-note` only appends what you write.
69
+
70
+ Then call the script once:
71
+
72
+ ```bash
73
+ ruby ~/.plastic/scripts/end-intent \
74
+ --store <store_path> --id <intent_id> --disposition delivered|abandoned \
75
+ --outcome-summary "<one-line ## Outcome summary for the intent file>" \
76
+ --index-note "<rich Completed/Abandoned entry description>"
77
+ ```
78
+
79
+ This does all of steps 1-4 in order: guards outcome.md (refuses a missing,
80
+ still-placeholder, or wrong-disposition file with exit 2 and authors
81
+ nothing), stamps the intent file's `## Outcome` section, moves the INDEX.md
82
+ line from `## Active` to `## Completed` or `## Abandoned` (dated today,
83
+ idempotent) with the `--index-note` text appended after the date so the
84
+ entry stays rich, appends the savepoint `Done` bookend, and commits the
85
+ store repo. Omit `--index-note` for a thin id+date entry, add `--no-commit`
86
+ when a separate commit step already covers the store, and `--dry-run` to
87
+ preview with no writes. Exit 0 is success; exit 1 is a usage or resolution
88
+ failure; exit 2 is the outcome.md guard refusing (fix outcome.md and re-run,
89
+ nothing was written).
90
+
91
+ ### Step 5. Disarm (worktree + lock)
92
+
93
+ Two branches, decided by how this intent ships:
94
+
95
+ - **Shipped through a release** (the `plastic-releasing` flow reached this
96
+ close): merge the code branch back BEFORE removing the worktrees, via
97
+ `Worktree.finish(bridge_data, merge: true)`. Releasing's own workflow
98
+ already drives this; this skill's job here is only the outcome/INDEX/
99
+ savepoint/commit core above.
100
+ - **Auto mode or the curator path** (no release involved): plain remove,
101
+ branch survives for reclaim.
102
+ ```bash
103
+ ruby -r ~/.plastic/scripts/lib/bridge -e \
104
+ 'Bridge.disarm_auto(ENV["CLAUDE_CODE_SESSION_ID"], intent_id: "<ID>")'
105
+ ```
106
+ `disarm_auto` releases both worktrees, clears the `delivery.lock`, and only
107
+ then makes the bridge purge-eligible, in that order. Never leave an
108
+ orphaned worktree; run `git worktree prune` on a stale reference.
109
+
110
+ ### Step 6. QMD reindex, LAST
111
+
112
+ Only after step 5 has released the worktrees and cleared the lock, so the
113
+ index never references state about to disappear:
114
+
115
+ ```bash
116
+ ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async
117
+ ```
118
+
119
+ No-op when QMD is absent. Runs in the background so it never blocks the
120
+ turn.
121
+
122
+ ### Step 7. EM-to-CTO report
123
+
124
+ Brief the human like an engineering manager to a CTO: impact and risk first,
125
+ in plain language, the decision left to them (merge, release, or accept).
126
+ See `outcome.md` for the details; do not restate it verbatim.
127
+
128
+ ## Abandoned is the same procedure
129
+
130
+ `disposition: abandoned` runs the identical steps 0-7. The only differences
131
+ are outcome.md content (Summary states why this was abandoned, not what was
132
+ delivered) and the INDEX target section (`## Abandoned` instead of
133
+ `## Completed`). Never branch the mechanical steps by disposition; the
134
+ script already does that internally.
135
+
136
+ ## Mid-flight pivot
137
+
138
+ When the work that shipped differs from what spec.md or plan.md originally
139
+ called for, do not retcon those documents. Record the decision once in
140
+ `## Insights`, then let outcome.md carry the truth of what actually
141
+ happened, including the abandoned trail when part of the work was dropped
142
+ mid-flight. outcome.md is truth of delivery; spec and plan stay the
143
+ historical record of what was planned.
144
+
145
+ ## Routing
146
+
147
+ `plastic-releasing`, `plastic-auto`, the curator agent, `store-curating`, and
148
+ `store-indexing` all delegate their mechanical close to this skill (or call
149
+ `scripts/end-intent` directly for steps 1-4). None of them restate the
150
+ outcome/INDEX/savepoint prose inline any more; if you find one that does,
151
+ that surface has drifted and should route here instead.
@@ -0,0 +1,74 @@
1
+ {
2
+ "skill_name": "plastic-intent-ending",
3
+ "notes": "Intent 161. Scopes: description triggering (3-6) and behavior (1-2: the two mandatory D6 cases). Case B graduates into test/end_intent_test.rb (test_done_bookend_lands_once_and_is_idempotent) per evaluating-skills conventions.",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "behavior",
8
+ "set": "train",
9
+ "prompt": "checklist.md has one unchecked non-completion item. Try to complete the intent.",
10
+ "expected_output": "Defers to the existing Bridge.check_gate hard gate rather than writing outcome.md; tells the user to finish the checklist item first instead of fighting the deny.",
11
+ "files": [],
12
+ "assertions": [
13
+ { "type": "human", "check": "SKILL.md Step 0 states the gate blocks outcome.md while unchecked non-completion items remain, and instructs finishing the checklist rather than attempting the write", "result": "expect-pass" },
14
+ { "type": "code", "check": "Bridge.check_gate(intent_dir, 'outcome.md') returns a non-nil block reason when checklist.md has an unchecked '- [ ]' item (scripts/lib/bridge.rb)", "result": "pass" }
15
+ ]
16
+ },
17
+ {
18
+ "id": 2,
19
+ "scope": "behavior",
20
+ "set": "train",
21
+ "prompt": "Run the mechanical close (scripts/end-intent) for a delivered intent with a real outcome.md.",
22
+ "expected_output": "The savepoint Done bookend lands exactly once in savepoint.md, and a second run of the same command does not duplicate it (this is the regression the intent fixes: releasing used to skip this line entirely).",
23
+ "files": [],
24
+ "assertions": [
25
+ { "type": "human", "check": "SKILL.md Step 1-4 calls scripts/end-intent as one script instead of restating the outcome/INDEX/savepoint one-liners in prose", "result": "expect-pass" },
26
+ { "type": "code", "check": "test/end_intent_test.rb#test_done_bookend_lands_once_and_is_idempotent is green", "result": "pass" }
27
+ ]
28
+ },
29
+ {
30
+ "id": 3,
31
+ "scope": "triggering",
32
+ "set": "train",
33
+ "prompt": "Mark intent 87 done, it shipped.",
34
+ "expected_output": "Activates plastic-intent-ending to run the mechanical close (outcome.md, INDEX move, savepoint bookend, commit, disarm, reindex, report).",
35
+ "files": [],
36
+ "assertions": [
37
+ { "type": "code", "check": "router CHOICE == plastic-intent-ending", "result": "expect-pass" }
38
+ ]
39
+ },
40
+ {
41
+ "id": 4,
42
+ "scope": "triggering",
43
+ "set": "train",
44
+ "prompt": "This one's not worth finishing, abandon it and wrap this up.",
45
+ "expected_output": "Activates plastic-intent-ending (indirect trigger: 'wrap this up' names no lifecycle vocabulary directly). Abandoned runs the identical procedure, only outcome.md content and the INDEX section differ.",
46
+ "files": [],
47
+ "assertions": [
48
+ { "type": "code", "check": "router CHOICE == plastic-intent-ending", "result": "expect-pass" }
49
+ ]
50
+ },
51
+ {
52
+ "id": 5,
53
+ "scope": "triggering",
54
+ "set": "validation",
55
+ "prompt": "Start work on intent 87.",
56
+ "expected_output": "Does NOT activate plastic-intent-ending; this is a boarding request (plastic-intent-starting), the opposite end of the lifecycle from a close.",
57
+ "files": [],
58
+ "assertions": [
59
+ { "type": "code", "check": "router CHOICE != plastic-intent-ending", "result": "expect-pass" }
60
+ ]
61
+ },
62
+ {
63
+ "id": 6,
64
+ "scope": "triggering",
65
+ "set": "validation",
66
+ "prompt": "Cut a release and tag it v1.2.0.",
67
+ "expected_output": "Does NOT activate plastic-intent-ending directly; activates plastic-releasing, which internally calls scripts/end-intent for the mechanical close as one of its own steps.",
68
+ "files": [],
69
+ "assertions": [
70
+ { "type": "code", "check": "router CHOICE == plastic-releasing", "result": "expect-pass" }
71
+ ]
72
+ }
73
+ ]
74
+ }