@zalom/plastic 1.14.0 → 2.0.0-alpha.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 (190) hide show
  1. package/PLASTIC.md +101 -294
  2. package/README.md +5 -6
  3. package/agents/plastic-advisor.md +18 -17
  4. package/agents/plastic-enforcer.md +57 -69
  5. package/agents/plastic-executor.md +34 -20
  6. package/agents/plastic-faux-advisor.md +18 -18
  7. package/config_asks.yml +38 -0
  8. package/hooks/capture +4 -0
  9. package/hooks/check-update +12 -3
  10. package/hooks/close +5 -0
  11. package/hooks/hooks.json +9 -49
  12. package/hooks/record +4 -0
  13. package/package.json +3 -2
  14. package/scripts/agent-report +8 -9
  15. package/scripts/append-ledger +195 -0
  16. package/scripts/codex-hook +101 -95
  17. package/scripts/dashboard.rb +4 -4
  18. package/scripts/doctor.rb +106 -43
  19. package/scripts/end-intent +77 -76
  20. package/scripts/exec-worktree +11 -20
  21. package/scripts/file-session-intent +61 -0
  22. package/scripts/hook-capture +242 -0
  23. package/scripts/hook-close +34 -0
  24. package/scripts/hook-record +149 -0
  25. package/scripts/hook-session-start +95 -12
  26. package/scripts/install.rb +8 -6
  27. package/scripts/lib/agent_models.rb +1 -7
  28. package/scripts/lib/arm.rb +283 -0
  29. package/scripts/lib/backfill_intent.rb +316 -0
  30. package/scripts/lib/bridge.rb +24 -1597
  31. package/scripts/lib/doctor_core.rb +17 -88
  32. package/scripts/lib/doctor_session_ledger.rb +106 -0
  33. package/scripts/lib/exec_worktree.rb +14 -60
  34. package/scripts/lib/harness_text.rb +5 -4
  35. package/scripts/lib/hook_registry.rb +44 -107
  36. package/scripts/lib/insights.rb +3 -4
  37. package/scripts/lib/installer_core.rb +19 -30
  38. package/scripts/lib/lock.rb +6 -26
  39. package/scripts/lib/outcome_guard.rb +7 -5
  40. package/scripts/lib/project_validator.rb +30 -1
  41. package/scripts/lib/roadmap_queue.rb +3 -3
  42. package/scripts/lib/roadmap_savepoint.rb +1 -1
  43. package/scripts/lib/rule_catalog.rb +6 -3
  44. package/scripts/lib/savepoint.rb +320 -0
  45. package/scripts/lib/scaffold_intent.rb +13 -179
  46. package/scripts/lib/session_backfill.rb +184 -0
  47. package/scripts/lib/session_close.rb +84 -0
  48. package/scripts/lib/session_git.rb +469 -0
  49. package/scripts/lib/session_ledger.rb +497 -0
  50. package/scripts/lib/store_discovery.rb +1 -1
  51. package/scripts/lib/store_provisioning.rb +1 -1
  52. package/scripts/maintenance-run +7 -7
  53. package/scripts/new-intent +63 -4
  54. package/scripts/plastic-lock +94 -41
  55. package/scripts/promote-session-item +150 -0
  56. package/scripts/rebuild-graph +1 -1
  57. package/scripts/roadmap-next +2 -2
  58. package/scripts/rollback.rb +42 -3
  59. package/scripts/scaffold-intent +37 -63
  60. package/scripts/session-commit +116 -0
  61. package/scripts/spawn-preamble +5 -5
  62. package/scripts/update.rb +7 -3
  63. package/skills/agent-advisor/SKILL.md +3 -3
  64. package/skills/agent-advisor/references/advisor-protocol.md +20 -20
  65. package/skills/auto/SKILL.md +218 -317
  66. package/skills/auto/evals/evals.json +10 -10
  67. package/skills/auto/references/agent-architecture.md +102 -90
  68. package/skills/auto/references/agent-report-contract.md +4 -21
  69. package/skills/auto/references/end-tail.md +47 -53
  70. package/skills/auto/references/human-report-contract.md +8 -8
  71. package/skills/conventions/SKILL.md +1 -3
  72. package/skills/conventions/references/lifecycle-and-savepoints.md +35 -1
  73. package/skills/conventions/references/locks-and-worktrees.md +47 -55
  74. package/skills/conventions/references/maintenance-and-revisions.md +12 -12
  75. package/skills/dashboard/SKILL.md +1 -1
  76. package/skills/direct/SKILL.md +66 -0
  77. package/skills/direct/references/request-signals.md +59 -0
  78. package/skills/doctor/SKILL.md +58 -11
  79. package/skills/doctor/report.md +3 -1
  80. package/skills/install/SKILL.md +11 -9
  81. package/skills/intent-continuing/SKILL.md +131 -89
  82. package/skills/intent-continuing/references/boarding-matrix.md +34 -0
  83. package/skills/intent-continuing/references/context-management.md +2 -2
  84. package/skills/intent-creating/SKILL.md +2 -2
  85. package/skills/intent-creating/references/lifecycle.md +1 -1
  86. package/skills/intent-ending/SKILL.md +28 -37
  87. package/skills/intent-ending/evals/evals.json +4 -4
  88. package/skills/intent-executing/SKILL.md +12 -49
  89. package/skills/intent-executing/plan-reviewer-prompt.md +39 -0
  90. package/skills/intent-speccing/SKILL.md +108 -51
  91. package/skills/intent-speccing/evals/evals.json +5 -5
  92. package/skills/intent-speccing/references/per-section-fill-rules.md +4 -4
  93. package/skills/intent-speccing/references/self-verify-checklist.md +1 -2
  94. package/skills/project-creating/SKILL.md +1 -1
  95. package/skills/releasing/SKILL.md +7 -8
  96. package/skills/tutorial/SKILL.md +2 -2
  97. package/skills/tutorial/references/track-1-guided.md +16 -18
  98. package/skills/tutorial/references/track-2-auto.md +12 -13
  99. package/skills/tutorial/references/track-3-projects-and-roadmaps.md +2 -2
  100. package/templates/agents.md +4 -7
  101. package/templates/project.yml +7 -0
  102. package/templates/roadmap.md +1 -1
  103. package/templates/savepoint.md +2 -2
  104. package/templates/session-intent.md +29 -0
  105. package/agents/plastic-brainstorming.md +0 -48
  106. package/agents/plastic-future-intent-researcher.md +0 -35
  107. package/agents/plastic-intent-curator.md +0 -47
  108. package/agents/plastic-intent-discovery.md +0 -43
  109. package/agents/plastic-planner.md +0 -56
  110. package/agents/plastic-spec-specialist.md +0 -53
  111. package/hooks/auto-arm +0 -5
  112. package/hooks/bash-gate +0 -3
  113. package/hooks/continue +0 -31
  114. package/hooks/edit-gates +0 -3
  115. package/hooks/future-intent-check +0 -25
  116. package/hooks/gate-check +0 -12
  117. package/hooks/power-tools +0 -8
  118. package/scripts/hook-auto-arm +0 -51
  119. package/scripts/hook-bash-gate +0 -78
  120. package/scripts/hook-code-gate +0 -38
  121. package/scripts/hook-continue +0 -48
  122. package/scripts/hook-create-gate +0 -51
  123. package/scripts/hook-edit-gates +0 -58
  124. package/scripts/hook-future-intent-check +0 -90
  125. package/scripts/hook-gate-check +0 -169
  126. package/scripts/hook-links-gate +0 -54
  127. package/scripts/hook-lock-gate +0 -47
  128. package/scripts/hook-power-tools +0 -38
  129. package/scripts/hook-savepoint-pre +0 -32
  130. package/scripts/lib/codex_edit_gates.rb +0 -138
  131. package/scripts/lib/edit_gates.rb +0 -398
  132. package/scripts/lib/links_gate.rb +0 -140
  133. package/scripts/lib/qmd_hook.rb +0 -41
  134. package/scripts/lib/spec_header.rb +0 -83
  135. package/scripts/lib/start_intent.rb +0 -296
  136. package/scripts/start-intent +0 -89
  137. package/skills/_active-intent-gate.md +0 -26
  138. package/skills/auto/references/tiers.md +0 -100
  139. package/skills/continuing/SKILL.md +0 -34
  140. package/skills/continuing/evals/evals.json +0 -91
  141. package/skills/conventions/references/gates-and-enforcement.md +0 -53
  142. package/skills/conventions/references/tiers-and-dispatch.md +0 -135
  143. package/skills/doctor/references/gates-stuck-detection.md +0 -43
  144. package/skills/intent-brainstorming/SKILL.md +0 -118
  145. package/skills/intent-brainstorming/evals/evals.json +0 -67
  146. package/skills/intent-continuing/evals/evals.json +0 -145
  147. package/skills/intent-discovering/SKILL.md +0 -56
  148. package/skills/intent-grilling/SKILL.md +0 -108
  149. package/skills/intent-linking/SKILL.md +0 -128
  150. package/skills/intent-linking/evals/evals.json +0 -22
  151. package/skills/intent-linking/references/zettelkasten.md +0 -45
  152. package/skills/intent-locking/SKILL.md +0 -64
  153. package/skills/intent-planning/SKILL.md +0 -145
  154. package/skills/intent-planning/evals/evals.json +0 -81
  155. package/skills/intent-planning/references/plan-format.md +0 -130
  156. package/skills/intent-researching/SKILL.md +0 -123
  157. package/skills/intent-researching/evals/evals.json +0 -22
  158. package/skills/intent-savepoint/SKILL.md +0 -83
  159. package/skills/intent-savepoint/references/context-management.md +0 -32
  160. package/skills/intent-starting/SKILL.md +0 -151
  161. package/skills/intent-starting/evals/evals.json +0 -117
  162. package/skills/intent-starting/references/boarding-matrix.md +0 -35
  163. package/skills/project-continuing/SKILL.md +0 -119
  164. package/skills/project-continuing/evals/evals.json +0 -100
  165. package/skills/roadmap-continuing/SKILL.md +0 -89
  166. package/skills/roadmap-continuing/evals/evals.json +0 -82
  167. package/skills/skill-creating/SKILL.md +0 -75
  168. package/skills/skill-creating/evals/evals.json +0 -108
  169. package/skills/skill-creating/references/agents.md +0 -168
  170. package/skills/skill-creating/references/defaults-first.md +0 -23
  171. package/skills/skill-creating/references/evals.md +0 -41
  172. package/skills/skill-creating/references/hooks.md +0 -251
  173. package/skills/skill-creating/references/progressive-disclosure.md +0 -176
  174. package/skills/skill-creating/references/scripts.md +0 -166
  175. package/skills/skill-creating/references/skills.md +0 -169
  176. package/skills/skill-creating/scripts/scaffold.rb +0 -313
  177. package/skills/skill-evaluating/SKILL.md +0 -141
  178. package/skills/skill-evaluating/assets/eval-template.json +0 -12
  179. package/skills/skill-evaluating/evals/evals.json +0 -75
  180. package/skills/skill-evaluating/references/convention-checks.md +0 -76
  181. package/skills/skill-evaluating/references/eval-methodology.md +0 -154
  182. package/skills/store-curating/SKILL.md +0 -64
  183. package/skills/store-curating/evals/evals.json +0 -38
  184. package/skills/store-indexing/SKILL.md +0 -93
  185. package/skills/store-indexing/evals/evals.json +0 -22
  186. package/skills/store-indexing/references/zettelkasten-linking.md +0 -32
  187. package/skills/store-provisioning/SKILL.md +0 -55
  188. /package/skills/{project-continuing → intent-continuing}/references/board-fill.md +0 -0
  189. /package/skills/{roadmap-continuing → intent-continuing}/references/liveness-ranking.md +0 -0
  190. /package/skills/{intent-brainstorming → intent-speccing}/references/design-principles.md +0 -0
@@ -1,296 +0,0 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
-
4
- require_relative "bridge"
5
- require_relative "lock"
6
- require_relative "intent_validator"
7
- require_relative "spec_header"
8
- require_relative "outcome_guard"
9
- require_relative "scaffold_intent"
10
-
11
- # StartIntent - all logic for `scripts/start-intent` (intent 213, group 2). Boards a
12
- # session onto an intent: arm the delivery lock, then print a read-only resume-station
13
- # report. This is the highest-risk surface in this action set because it composes lock
14
- # arbitration; the rule for that surface is compose the existing primitives, add nothing
15
- # (spec D6).
16
- #
17
- # NEVER REIMPLEMENTS LOCK ARBITRATION. Arming is `Bridge.arm_auto` or `Bridge.arm_guided`
18
- # and nothing else acquires anything: no new arming logic, no retry loop, no takeover path.
19
- # This module NEVER calls the Lock module's release or takeover operations. That unguarded
20
- # read-modify-write gap is parked intent 254's territory; start-intent reports and refuses,
21
- # it never repairs a lock. Read-only lock inspection (`Lock.path`, `Lock.read`,
22
- # `Lock.fresh?`, `Lock.authorized?`) is expected and used below.
23
- #
24
- # DETERMINISM CRITERION: every value this module prints is read from already-committed
25
- # files or from `Bridge.arm_*`'s own return value. No model inference, no free-text
26
- # generation.
27
- #
28
- # Pure and dependency-injected: never calls `exit` or `abort`, never reads `ARGV` or `ENV`
29
- # directly. Seams: `armer:` (a lambda dispatching on mode to the two real Bridge methods),
30
- # plus session/harness/thread values passed in as arguments rather than read from ENV here.
31
- # Reading ENV for harness detection happens in the CLI (`scripts/start-intent`) and is
32
- # passed down.
33
- module StartIntent
34
- module_function
35
-
36
- EXIT_OK = 0
37
- EXIT_USAGE = 1
38
- EXIT_HELD = 3
39
- EXIT_UNRESOLVED = 4
40
-
41
- # --- Step 0: resolve inputs -------------------------------------------------------
42
-
43
- # `name:` for the arm call, deterministically: the intent file frontmatter's `intent:`
44
- # field, or the directory basename when the frontmatter is unreadable or blank. Never
45
- # invents a name.
46
- def resolve_name(intent_dir)
47
- fm = IntentValidator.parse_frontmatter(Bridge.intent_file(intent_dir))
48
- name = fm.is_a?(Hash) ? fm["intent"] : nil
49
- Bridge.blank?(name) ? File.basename(intent_dir) : name
50
- end
51
-
52
- # Session resolution order: explicit --session, else CLAUDE_CODE_SESSION_ID (passed in
53
- # as env_session, never read from ENV here), else nil. Deliberately NO fallback to the
54
- # lock's own recorded owner (unlike end-intent's resolve_end_session): arming as an
55
- # owner you cannot prove you are is wrong.
56
- def resolve_session(explicit, env_session)
57
- return explicit.to_s.strip unless Bridge.blank?(explicit)
58
- return env_session.to_s.strip unless Bridge.blank?(env_session)
59
- nil
60
- end
61
-
62
- # Harness/thread detection, mirroring skills/auto/SKILL.md's arm-the-lifecycle-gate
63
- # snippet exactly: a non-empty CODEX_THREAD_ID means codex (thread = that id); else a
64
- # non-empty CLAUDE_CODE_SESSION_ID means claude (thread = nil); else both nil. Pure:
65
- # takes the raw ENV strings as arguments, never reads ENV itself.
66
- def resolve_harness(codex_thread_id, claude_session_id)
67
- codex = codex_thread_id.to_s.strip
68
- claude = claude_session_id.to_s.strip
69
- return { harness: "codex", thread: codex } unless codex.empty?
70
- return { harness: "claude", thread: nil } unless claude.empty?
71
- { harness: nil, thread: nil }
72
- end
73
-
74
- # --- Step 1: pre-flight lock read (read-only, before arming) ---------------------
75
- #
76
- # Mirrors the READ-ONLY half of scripts/end-intent's preflight_lock_verdict, minus its
77
- # takeover branch (start-intent never takes over). Returns [verdict, lock_or_nil]:
78
- # :proceed no lock, or this session already owns/delegates it
79
- # :corrupt a lock file exists but Lock.read cannot parse it (exit 4, unconditional;
80
- # unlike end-intent, freshness is never consulted for a corrupt lock,
81
- # because there is no repair path here to reach for a stale one)
82
- # :no_session a lock file exists and no session identity resolved from any source
83
- # (exit 4)
84
- # :held a FRESH foreign lock (exit 4... exit 3, before ever calling the armer)
85
- # :stale a STALE foreign lock: fall through to arming, which arbitrates it (and
86
- # raises Bridge::LockHeldError with the reclaim hint) itself
87
- def preflight_verdict(intent_dir, session)
88
- return [:proceed, nil] unless File.exist?(Lock.path(intent_dir))
89
-
90
- lock = Lock.read(intent_dir)
91
- return [:corrupt, nil] if lock.nil?
92
- return [:no_session, lock] if Bridge.blank?(session)
93
- return [:proceed, lock] if Lock.authorized?(lock, session)
94
- return [:held, lock] if Lock.fresh?(intent_dir)
95
- [:stale, lock]
96
- end
97
-
98
- def corrupt_message(intent_dir)
99
- "start-intent: the delivery lock at #{Lock.path(intent_dir)} exists but its content " \
100
- "will not parse (corrupt); refusing to arm since another session may still be " \
101
- "heartbeating it. Run /plastic-doctor check the lock status, or plastic-lock fix " \
102
- "once it is confirmed safe."
103
- end
104
-
105
- def no_session_message(intent_dir)
106
- "start-intent: a delivery lock exists at #{Lock.path(intent_dir)} but no session " \
107
- "identity could be resolved (--session or CLAUDE_CODE_SESSION_ID are both blank); " \
108
- "refusing rather than guessing. Pass --session explicitly."
109
- end
110
-
111
- def held_message(intent_dir, lock, harness:)
112
- owner = lock.is_a?(Hash) ? lock["owner_session"] : nil
113
- "start-intent: delivery lock for #{File.basename(intent_dir)} is held by a live " \
114
- "session (#{owner}); run #{Bridge.skill_ref('plastic-doctor', harness: harness)} " \
115
- "check the lock status"
116
- end
117
-
118
- # --- Step 2: arm -------------------------------------------------------------------
119
-
120
- # Default arm seam: dispatches on mode to the two real Bridge methods. Nothing else
121
- # acquires anything.
122
- def default_armer
123
- lambda do |mode, session:, intent_id:, intent_dir:, store:, name:, harness:, agent:,
124
- model:, thread:|
125
- case mode
126
- when "auto"
127
- Bridge.arm_auto(session, intent_id: intent_id, intent_dir: intent_dir, store: store,
128
- name: name, harness: harness, agent: agent, model: model, thread: thread)
129
- when "guided"
130
- Bridge.arm_guided(session, intent_id: intent_id, intent_dir: intent_dir, store: store,
131
- name: name, harness: harness, agent: agent, model: model, thread: thread)
132
- end
133
- end
134
- end
135
-
136
- # --- Step 3: the resume-station report (read-only, after arming) -----------------
137
-
138
- # Count-only helper for the printed "actions/: <n> real action file(s)" line. The
139
- # STATION gate (whether How is delivered) is decided exclusively by
140
- # Bridge.has_real_action?, called separately in build_report; this exists only to render
141
- # a number and must never be used for any pass/fail decision.
142
- def real_action_count(intent_dir)
143
- Dir.glob("#{intent_dir}/actions/*.md").count do |f|
144
- File.file?(f) && File.size(f) > 0 && Bridge.stage_file_present?(f)
145
- end
146
- rescue StandardError
147
- 0
148
- end
149
-
150
- # Classify outcome.md into one of three states, per the spec's outcome.md nuance:
151
- # `scaffold-intent outcome` writes a real (non-sentinel) outcome.md while leaving the
152
- # frontmatter disposition at the template placeholder `delivered|abandoned`, so a naive
153
- # sentinel check would misreport that as Done. Returns [state, reason]:
154
- # "not_started" the sentinel is still present (or the file is absent), reason nil
155
- # "authored" sentinel gone AND OutcomeGuard.reason(intent_dir, disposition) is nil
156
- # for EITHER "delivered" or "abandoned" (BLOCKING 3 fix: a fully-authored
157
- # abandoned outcome.md is authored too; checking "delivered" only used to
158
- # misreport it as "scaffolded, not authored" and point a resuming session
159
- # back into Exec)
160
- # "scaffolded" sentinel gone AND OutcomeGuard.reason(...) returns a reason string for
161
- # BOTH dispositions
162
- # OutcomeGuard.reason is the single existing definition of "is outcome.md real for this
163
- # close"; this never re-derives that definition.
164
- def classify_outcome(intent_dir)
165
- outcome_path = File.join(intent_dir, "outcome.md")
166
- return ["not_started", nil] unless Bridge.stage_file_present?(outcome_path)
167
-
168
- delivered_reason = OutcomeGuard.reason(intent_dir, "delivered")
169
- return ["authored", nil] if delivered_reason.nil?
170
-
171
- abandoned_reason = OutcomeGuard.reason(intent_dir, "abandoned")
172
- return ["authored", nil] if abandoned_reason.nil?
173
-
174
- # Neither disposition's guard passed: report the "delivered" reason, since it is the
175
- # more common close path and OutcomeGuard.reason's message already names whatever
176
- # disposition is actually recorded in the frontmatter (or that the file is missing or
177
- # still the scaffold placeholder), so it stays informative either way.
178
- ["scaffolded", delivered_reason]
179
- end
180
-
181
- def settled_text(header)
182
- return "no" unless header[:settled]
183
- header[:settled_reason] ? "yes (#{header[:settled_reason]})" : "yes"
184
- end
185
-
186
- # Build the resume-station report. Every value comes from already-committed files
187
- # (Bridge.stage_file_present?, Bridge.has_real_action?, SpecHeader, OutcomeGuard) or from
188
- # `data`, the Hash Bridge.arm_* itself returned. No model inference, no free text beyond
189
- # the fixed shape below.
190
- def build_report(intent_dir:, mode:, data:)
191
- spec_path = File.join(intent_dir, "spec.md")
192
- plan_path = File.join(intent_dir, "plan.md")
193
- checklist_path = File.join(intent_dir, "checklist.md")
194
-
195
- spec_delivered = Bridge.stage_file_present?(spec_path)
196
- plan_delivered = Bridge.stage_file_present?(plan_path)
197
- checklist_delivered = Bridge.stage_file_present?(checklist_path)
198
- has_action = Bridge.has_real_action?(intent_dir)
199
- how_triple = plan_delivered && checklist_delivered && has_action
200
-
201
- outcome_state, outcome_reason = classify_outcome(intent_dir)
202
-
203
- resume_at =
204
- if !spec_delivered
205
- "Why"
206
- elsif !how_triple
207
- "How"
208
- elsif outcome_state != "authored"
209
- "Exec"
210
- else
211
- "Done"
212
- end
213
-
214
- header = SpecHeader.parse_file(spec_path)
215
- tier = header[:tier] || "unknown"
216
-
217
- worktree_code = data.dig("worktree", "code")
218
- worktree_line = Bridge.blank?(worktree_code) ? "not provisioned" : worktree_code
219
-
220
- outcome_line =
221
- case outcome_state
222
- when "authored" then "authored"
223
- when "scaffolded" then "scaffolded, not authored (#{outcome_reason})"
224
- else "not started"
225
- end
226
-
227
- lines = []
228
- lines << "start-intent: #{File.basename(intent_dir)}"
229
- lines << " mode: #{mode}"
230
- lines << " session: #{data['session']}"
231
- lines << " lock: held by #{data.dig('lock', 'owner_session')} (ours)"
232
- lines << " worktree: #{worktree_line}"
233
- lines << " tier: #{tier}"
234
- lines << " settled: #{settled_text(header)}"
235
- lines << " spec.md: #{spec_delivered ? 'delivered' : 'not started'}"
236
- lines << " plan.md: #{plan_delivered ? 'delivered' : 'not started'}"
237
- lines << " checklist.md: #{checklist_delivered ? 'delivered' : 'not started'}"
238
- lines << " actions/: #{real_action_count(intent_dir)} real action file(s)"
239
- lines << " outcome.md: #{outcome_line}"
240
- lines << " resume at: #{resume_at}"
241
- lines.join("\n")
242
- end
243
-
244
- # --- entry point --------------------------------------------------------------------
245
-
246
- def usage_result(message)
247
- { exit_code: EXIT_USAGE, stderr: [message], stdout: [] }
248
- end
249
-
250
- def deny_result(code, message)
251
- { exit_code: code, stderr: [message], stdout: [] }
252
- end
253
-
254
- def run(store:, id:, mode:, session:, env_session:, harness:, thread:, armer: default_armer)
255
- return usage_result("--store is required") if Bridge.blank?(store)
256
- return usage_result("--id is required") if Bridge.blank?(id)
257
- return usage_result("--mode is required") if Bridge.blank?(mode)
258
- unless %w[auto guided].include?(mode)
259
- return usage_result("--mode must be one of auto, guided (got #{mode.inspect})")
260
- end
261
-
262
- store_abs = ScaffoldIntent.expand(store)
263
- return usage_result("store dir does not exist: #{store_abs}") unless Dir.exist?(store_abs)
264
-
265
- intent_dir, resolve_err = ScaffoldIntent.resolve_intent_dir(store_abs, id)
266
- return usage_result(resolve_err) if intent_dir.nil?
267
-
268
- name = resolve_name(intent_dir)
269
- key_session = resolve_session(session, env_session)
270
-
271
- verdict, lock = preflight_verdict(intent_dir, key_session)
272
- case verdict
273
- when :corrupt
274
- return deny_result(EXIT_UNRESOLVED, corrupt_message(intent_dir))
275
- when :no_session
276
- return deny_result(EXIT_UNRESOLVED, no_session_message(intent_dir))
277
- when :held
278
- return deny_result(EXIT_HELD, held_message(intent_dir, lock, harness: harness))
279
- end
280
- # :proceed and :stale both fall through to arming; Bridge.arm_* arbitrates :stale
281
- # itself (and raises Bridge::LockHeldError with the reclaim hint) via its own
282
- # up-to-date Lock.acquire call.
283
-
284
- data =
285
- begin
286
- armer.call(mode, session: key_session, intent_id: id, intent_dir: intent_dir,
287
- store: store_abs, name: name, harness: harness, agent: nil, model: nil,
288
- thread: thread)
289
- rescue Bridge::LockHeldError => e
290
- return deny_result(EXIT_HELD, e.message)
291
- end
292
-
293
- report = build_report(intent_dir: intent_dir, mode: mode, data: data)
294
- { exit_code: EXIT_OK, stderr: [], stdout: [report] }
295
- end
296
- end
@@ -1,89 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
- # frozen_string_literal: true
4
-
5
- # start-intent (intent 213, group 2) - board a session onto an intent: arm the delivery
6
- # lock, then print a read-only resume-station report.
7
- #
8
- # Usage:
9
- # start-intent --store <path> --id <intent_id> --mode auto|guided [--session <session_id>]
10
- #
11
- # --store, --id, and --mode are required. --session is optional and defaults to
12
- # CLAUDE_CODE_SESSION_ID, matching end-intent's resolution order. No other flags exist.
13
- #
14
- # Steps performed:
15
- # 0. Resolve inputs: the intent dir, the intent name from frontmatter (falling back to
16
- # the dir basename), the calling session (--session, else CLAUDE_CODE_SESSION_ID,
17
- # else nil; deliberately no lock-owner fallback), and harness/thread from
18
- # CODEX_THREAD_ID / CLAUDE_CODE_SESSION_ID (mirroring skills/auto/SKILL.md's arming
19
- # snippet).
20
- # 1. Pre-flight lock read (read-only): refuse a fresh foreign lock, an unparseable
21
- # lock, or a lock present with no resolvable session identity. A stale foreign lock
22
- # falls through to arming, which arbitrates it itself.
23
- # 2. Arm: Bridge.arm_auto (--mode auto) or Bridge.arm_guided (--mode guided). Nothing
24
- # else acquires anything.
25
- # 3. Print the resume-station report (read-only): which lifecycle files are real, and
26
- # where the intent should resume (Why/How/Exec/Done).
27
- #
28
- # This script never releases and never takes over a lock: it never calls the Lock
29
- # module's release or takeover operations (parked intent 254's territory). A held or
30
- # corrupt lock is reported and refused, never repaired.
31
- #
32
- # Exit codes:
33
- # 0 armed and resume-station printed
34
- # 1 usage or path-resolution failure
35
- # 3 lock held by another live session (deny, mirrors end-intent's pre-flight refusal)
36
- # 4 unparseable lock, or no session identity with a lock present
37
- #
38
- # There is deliberately no exit 2 in this script's contract.
39
-
40
- require_relative "lib/start_intent"
41
-
42
- def parse_args(argv)
43
- opts = { store: nil, id: nil, mode: nil, session: nil }
44
- i = 0
45
- while i < argv.length
46
- arg = argv[i]
47
- case arg
48
- when "--store" then opts[:store] = argv[i += 1]
49
- when "--id" then opts[:id] = argv[i += 1]
50
- when "--mode" then opts[:mode] = argv[i += 1]
51
- when "--session" then opts[:session] = argv[i += 1]
52
- else
53
- usage_abort("unknown argument #{arg.inspect}")
54
- end
55
- i += 1
56
- end
57
- opts
58
- end
59
-
60
- def usage
61
- "usage: start-intent --store <store_path> --id <intent_id> --mode auto|guided " \
62
- "[--session <session_id>]"
63
- end
64
-
65
- def usage_abort(message)
66
- warn "start-intent: #{message}"
67
- warn usage
68
- exit 1
69
- end
70
-
71
- def main(argv)
72
- opts = parse_args(argv)
73
-
74
- codex_thread_id = ENV["CODEX_THREAD_ID"]
75
- claude_session_id = ENV["CLAUDE_CODE_SESSION_ID"]
76
- detected = StartIntent.resolve_harness(codex_thread_id, claude_session_id)
77
-
78
- result = StartIntent.run(
79
- store: opts[:store], id: opts[:id], mode: opts[:mode], session: opts[:session],
80
- env_session: claude_session_id, harness: detected[:harness], thread: detected[:thread]
81
- )
82
-
83
- Array(result[:stderr]).each { |line| warn line }
84
- warn usage if result[:exit_code] == StartIntent::EXIT_USAGE
85
- Array(result[:stdout]).each { |line| puts line }
86
- exit result[:exit_code]
87
- end
88
-
89
- main(ARGV) if $PROGRAM_NAME == __FILE__
@@ -1,26 +0,0 @@
1
- ## Active Intent Gate
2
-
3
- Before proceeding, resolve the active intent:
4
-
5
- 1. **Detect store:**
6
- - Read `~/.plastic/projects.yml`
7
- - Match CWD against registered project paths
8
- - If match → project store at `~/.plastic/projects/{slug}/store/`
9
- - If no match → global store at `~/.plastic/store/`
10
-
11
- 2. **Find active intent:**
12
- - Read `INDEX.md` from the detected store
13
- - Look under `## Active` for intent entries
14
- - If exactly one active intent → use it
15
- - If multiple active intents → ask user which one
16
- - If no active intent → refuse: "No active intent. Create one first with /plastic-intent-creating"
17
-
18
- 3. **Resolve paths:**
19
- - Intent directory: `{store}/store/{id}--{slug}/`
20
- - Spec: `{intent_dir}/spec.md`
21
- - Plan: `{intent_dir}/plan.md`
22
- - Checklist: `{intent_dir}/checklist.md`
23
- - Resources: `{intent_dir}/resources/`
24
- - Outcome: `{intent_dir}/outcome.md`
25
-
26
- All lifecycle artifacts MUST be written to the intent directory. Never write to `docs/superpowers/specs/`, `docs/superpowers/plans/`, or any other external path.
@@ -1,100 +0,0 @@
1
- # Tiers — Extended Walkthrough
2
-
3
- This is the detail behind `## Tiers` in SKILL.md. The five points there (sizing rule, two
4
- levers, per-tier topology, never-cut list, tier record) are the authoritative contract;
5
- this file only expands on them with examples and rationale.
6
-
7
- ## Why sizing exists
8
-
9
- Auto mode used to run every intent through the same full-ceremony team: brainstorming,
10
- spec-specialist, planner, executor, and an independent reviewer, each a separate agent
11
- booting a fresh context. For a large or novel intent that ceremony earns its cost. For a
12
- small intent (one file, one mechanism, an hour of real work) the ceremony dominates:
13
- overhead runs about 3 to 4 times the actual work time, roughly flat regardless of size, so
14
- it hurts small intents the most. Sizing at Why and matching the topology to that size drops
15
- small-intent ceremony toward about 1.5x work time, without touching structure, gates, or
16
- the savepoint ledger.
17
-
18
- ## Sizing rule, worked examples
19
-
20
- - S: fixing one script's argument parsing, adding one skill section, a single bug fix
21
- confined to one file. Hours of work.
22
- - M: adding a new agent role file end to end, a subsystem with a handful of touched files
23
- and one clear seam. About a day.
24
- - L: cross-cutting prose or code that spans the skill contract plus multiple agent role
25
- files (like this intent, 130), or any genuinely novel design with no established pattern
26
- to follow.
27
-
28
- ## S/M collapsed topology, in detail
29
-
30
- One thinker agent boots ONCE and stays in a single context for two stations:
31
-
32
- 1. Station 1 — writes `spec.md` (collapsed sections allowed, one line each is valid).
33
- 2. Station 2 — writes `plan.md` + `checklist.md` + at least one real action file in the
34
- SAME context (no reboot). At S/M the thinker consolidates the whole delivery into one
35
- `actions/ACTION_1.md` (rather than one file per task); `actions/` is populated at every
36
- tier, and a `.gitkeep`-only or empty `actions/` fails the How gate.
37
-
38
- Then a sonnet executor (a fresh dispatch, this is the one topology split that always
39
- happens) implements from plan.md + checklist.md, checks off items, appends `## Insights`,
40
- and drives the suite green. At S/M that is ONE executor dispatch for the whole consolidated
41
- action: no per-task implementer, and no per-task spec review or quality review. L keeps the
42
- per-task loop. The gate lives in `plastic-intent-executing`'s Subagent-Driven workflow,
43
- which reads the `Tier:` line stamped at the top of spec.md, the authoritative record. The
44
- arithmetic: an S plan with N tasks costs 3N plus 1 agent boots under the L shape, and 2
45
- boots under this one.
46
-
47
- The independent reviewer still runs at the final gate for S/M, in its own fresh context,
48
- never the maker. This is on the never-cut list; it does not collapse.
49
-
50
- S sends ONE mid-flight owner briefing instead of four. Only the How briefing fires, at the
51
- point the plan is ready and before any code is written, and it folds in what the What and
52
- Why briefings would have said; the Exec briefing folds into the final owner report at End.
53
- M and L send all four. The briefing calls live in `plastic-auto`'s stage sections, and the
54
- depth note lives in `references/human-report-contract.md`.
55
-
56
- S may skip the QMD discovery deposit (normally a `plastic-intent-discovery` pass before
57
- Why) when the intent's `chain` and `sources` are both empty in frontmatter. With no graph
58
- edges there is nothing to discover, so the deposit is pure overhead; a one-line context
59
- note ("no chain/sources, discovery skipped") takes its place, written to
60
- `resources/discovery--<slug>.md`. Three places implement the skip: the dispatch site in
61
- `plastic-intent-starting` step 4, the precondition in `plastic-intent-discovering`, and the
62
- same precondition in the `plastic-intent-discovery` agent file. The skip needs a size already
63
- on record (a stamped `Tier: S` line in spec.md), and sizing happens at Why, so a first
64
- activation usually runs the full pass.
65
-
66
- ## L topology, unchanged
67
-
68
- L keeps today's full multi-agent team as described in `## Team Spin-Up`: brainstorming,
69
- spec-specialist, planner, executor, each a separate agent in its own fresh context, plus
70
- the independent reviewer at the final gate. Cross-cutting or novel work benefits from the
71
- separate perspectives and the handoff discipline; the ceremony is not waste at this size.
72
-
73
- ## Same-structure invariant, why it is non-negotiable
74
-
75
- The file set, stage order, gates, and savepoint ledger never change by tier. Renaming or
76
- skipping files to save time would require new gate logic per tier and would break state
77
- derivability (the gates and the savepoint rebuild depend on a fixed file set at fixed
78
- paths). So the only two levers are content depth and agent topology; structure is the
79
- constant that keeps every tier auditable the same way.
80
-
81
- ## Tier record, mechanics
82
-
83
- The tier is recorded as a `Tier: S|M|L` line at the very top of spec.md, above the `#
84
- Spec:` heading. It is convention-only: the orchestrator reads it to pick topology, and
85
- nothing else depends on it. No frontmatter schema change, no new file, no doctor rule, no
86
- gate check. If a later intent wants doctor or a gate to validate the line, that is a
87
- separate, explicit follow-up; this system deliberately adds no new operational surface.
88
-
89
- Settledness is a separate line, `Settled: yes (<reason>)`, directly beneath the `Tier:` line
90
- and above the `# Spec:` heading. It records that a design has already done its thinking. An
91
- absent line means not settled; there is no `Settled: no` form. Settledness and scope are
92
- independent, so a large intent can be settled without becoming a smaller tier.
93
- `scripts/lib/spec_header.rb` is the only parser of both lines.
94
-
95
- ## Never-cut list, the safety floor
96
-
97
- At any tier or mode: the independent reviewer (separate agent, fresh context, never the
98
- maker), `outcome.md` as the truth of delivery, the delivery lock, worktree isolation,
99
- intent creation via skill, INDEX as status truth, the QMD reindex at End. These are
100
- predictability and safety guarantees, not ceremony, and lightness never touches them.
@@ -1,34 +0,0 @@
1
- ---
2
- name: plastic-continuing
3
- description: >-
4
- Use when the user says "continue", "resume", or "pick up where we left off", starts a new
5
- session and wants to keep going, asks "what should I work on", or asks a where-was-I question
6
- that never says "continue" (for example "where was I"). This is the front door for resuming
7
- work: it dispatches to plastic-intent-continuing (a specific intent named to resume),
8
- plastic-project-continuing (the default, general board landing), or
9
- plastic-roadmap-continuing (a roadmap or delivery batch named to resume).
10
- user-invocable: true
11
- ---
12
-
13
- # Continuing - the front door
14
-
15
- `plastic-continuing` is the front door for resuming work (ruling 96: continuing routes,
16
- starting does the lock plus resume plus work). It routes to exactly one of three skills and
17
- does nothing else: no ledger-resume, no dashboard render, no roadmap read happens here.
18
-
19
- ## Route
20
-
21
- | Args / context | Route to |
22
- |---|---|
23
- | `--intent {id}`, or the user names one specific intent (by id or description) to resume | `plastic-intent-continuing` (intent route) |
24
- | `--roadmap {slug}`, or the user asks to continue/resume a roadmap or delivery batch | `plastic-roadmap-continuing` (roadmap route) |
25
- | bare "continue" / "resume" / no further target (default) | `plastic-project-continuing` (project route) |
26
-
27
- ## Announce, then hand off
28
-
29
- State the chosen route in one line before delegating (this is the router's own "present state
30
- before any ask" - the router asks nothing itself). Example: "Routing to the project board (no
31
- specific intent or roadmap named)."
32
-
33
- Then hand off to the chosen skill. Do not inline any of the routed skill's own work here -
34
- that belongs to the routes.
@@ -1,91 +0,0 @@
1
- {
2
- "skill_name": "plastic-continuing",
3
- "notes": "Intent 158a1. New bare front-door router restored per intent 96 (D8). Scopes: description triggering (1-5) and routing behavior (6-9), the latter checked as convention against SKILL.md's routing table.",
4
- "results": {
5
- "triggering": { "cases": 5, "passed": 5, "run": "2026-07-10, one subagent per case" },
6
- "behavior": { "cases": 4, "passed": 4, "evidence": "convention checks against skills/continuing/SKILL.md's routing table" }
7
- },
8
- "evals": [
9
- {
10
- "id": 1, "scope": "triggering", "set": "train",
11
- "prompt": "continue",
12
- "expected_output": "Activates plastic-continuing (the bare 'continue' keyword is the router's documented trigger).",
13
- "files": [],
14
- "assertions": [
15
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
16
- ]
17
- },
18
- {
19
- "id": 2, "scope": "triggering", "set": "train",
20
- "prompt": "resume where we left off",
21
- "expected_output": "Activates plastic-continuing.",
22
- "files": [],
23
- "assertions": [
24
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
25
- ]
26
- },
27
- {
28
- "id": 3, "scope": "triggering", "set": "validation",
29
- "prompt": "pick up where we left off",
30
- "expected_output": "Activates plastic-continuing.",
31
- "files": [],
32
- "assertions": [
33
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
34
- ]
35
- },
36
- {
37
- "id": 4, "scope": "triggering", "set": "validation",
38
- "prompt": "where was I",
39
- "expected_output": "Activates plastic-continuing (indirect trigger: a continue request that never says 'continue').",
40
- "files": [],
41
- "assertions": [
42
- { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
43
- ]
44
- },
45
- {
46
- "id": 5, "scope": "triggering", "set": "validation",
47
- "prompt": "continue the for-loop in this function",
48
- "expected_output": "Does NOT activate plastic-continuing. Near-miss: shares 'continue' but is a code-editing task.",
49
- "files": [],
50
- "assertions": [
51
- { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "none", "result": "pass" }
52
- ]
53
- },
54
- {
55
- "id": 6, "scope": "behavior", "set": "train",
56
- "prompt": "continue",
57
- "expected_output": "Bare 'continue' with no further target dispatches to the project route (plastic-project-continuing), the routing table's default.",
58
- "files": ["skills/continuing/SKILL.md"],
59
- "assertions": [
60
- { "type": "convention", "check": "routing table's default row maps bare continue/resume/no target to plastic-project-continuing", "observed": "present", "result": "pass" }
61
- ]
62
- },
63
- {
64
- "id": 7, "scope": "behavior", "set": "train",
65
- "prompt": "continue --intent 4a",
66
- "expected_output": "Dispatches to the intent route (plastic-intent-continuing).",
67
- "files": ["skills/continuing/SKILL.md"],
68
- "assertions": [
69
- { "type": "convention", "check": "routing table maps --intent {id} to plastic-intent-continuing", "observed": "present", "result": "pass" }
70
- ]
71
- },
72
- {
73
- "id": 8, "scope": "behavior", "set": "validation",
74
- "prompt": "resume the statusline-coloring intent",
75
- "expected_output": "Dispatches to the intent route (plastic-intent-continuing): a specific intent named by description.",
76
- "files": ["skills/continuing/SKILL.md"],
77
- "assertions": [
78
- { "type": "convention", "check": "routing table maps a named specific intent (by id or description) to plastic-intent-continuing", "observed": "present", "result": "pass" }
79
- ]
80
- },
81
- {
82
- "id": 9, "scope": "behavior", "set": "validation",
83
- "prompt": "continue the consistency-dividend roadmap",
84
- "expected_output": "Dispatches to the roadmap route (plastic-roadmap-continuing).",
85
- "files": ["skills/continuing/SKILL.md"],
86
- "assertions": [
87
- { "type": "convention", "check": "routing table maps a named roadmap/delivery batch to plastic-roadmap-continuing", "observed": "present", "result": "pass" }
88
- ]
89
- }
90
- ]
91
- }