@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
@@ -0,0 +1,184 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # SessionBackfill (intent 301): file a session day ledger. The four backfilled
5
+ # documents are a pure function of the day's checklist.md and savepoint.md, so
6
+ # every run regenerates them in full and a rerun is correct by construction.
7
+ # The carry step deduplicates against the target day before it appends and
8
+ # flips the source line after, so a crash at any point leaves a rerun correct.
9
+ # Every ledger write goes through SessionLedger. No environment reads here;
10
+ # the CLI passes every path in.
11
+
12
+ require "date"
13
+ require "fileutils"
14
+ require "time"
15
+ require_relative "session_ledger"
16
+
17
+ module SessionBackfill
18
+ module_function
19
+
20
+ DOCS = ["spec.md", "plan.md", "actions/ACTION_1.md", "outcome.md"].freeze
21
+ SAVEPOINT_RE = /\A(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)\s{2,}(\S+)(?:\s{2,}(.*))?\z/m.freeze
22
+ CARRIED_RE = / \(carried from \d{8}\)\z/.freeze
23
+ FILER_SESSION = "filer"
24
+ FILER_PROJECT = "global"
25
+ SUMMARY_CAP = 200
26
+
27
+ # --- reading ---------------------------------------------------------------
28
+
29
+ def parse_savepoint_line(line)
30
+ match = SAVEPOINT_RE.match(line.to_s.chomp.scrub)
31
+ return nil unless match
32
+
33
+ { timestamp: match[1], event: match[2], rest: match[3].to_s }
34
+ end
35
+
36
+ def read_items(dir)
37
+ path = File.join(dir, "checklist.md")
38
+ SessionLedger.read_locked(path).each_line.filter_map { |l| SessionLedger.parse_checklist_line(l) }
39
+ end
40
+
41
+ def read_savepoint_lines(dir)
42
+ path = File.join(dir, "savepoint.md")
43
+ SessionLedger.read_locked(path).each_line.map(&:chomp).reject(&:empty?)
44
+ end
45
+
46
+ # --- rendering, pure ---------------------------------------------------------
47
+
48
+ def format_item(item)
49
+ marker = SessionLedger::STATES.fetch(item[:state])
50
+ "- [#{marker}] [#{item[:session]}] [#{item[:project]}] #{item[:summary]}"
51
+ end
52
+
53
+ def section(title, lines)
54
+ body = lines.empty? ? "(none)" : lines.join("\n")
55
+ "## #{title}\n#{body}\n"
56
+ end
57
+
58
+ def render(dir, day:)
59
+ items = read_items(dir)
60
+ steps = read_savepoint_lines(dir)
61
+ requests = items.reject { |i| i[:state] == :dropped }.map { |i| "- [#{i[:session]}] [#{i[:project]}] #{i[:summary]}" }
62
+ done = items.select { |i| i[:state] == :done }.map { |i| format_item(i) }
63
+ carried = items.select { |i| i[:state] == :moved }.map { |i| format_item(i) }
64
+ promoted = items.select { |i| i[:state] == :promoted }.map { |i| format_item(i) }
65
+ disposition = done.empty? ? "abandoned" : "delivered"
66
+
67
+ {
68
+ "spec.md" => "# Spec: session ledger #{day}\n\n#{section('Requests', requests)}",
69
+ "plan.md" => "# Plan: session ledger #{day}\n\n#{section('Steps', steps.map { |s| "- #{s}" })}",
70
+ "actions/ACTION_1.md" => "# ACTION_1: session ledger #{day}\n\n#{section('Items', items.map { |i| format_item(i) })}",
71
+ "outcome.md" => "---\ndisposition: #{disposition}\n---\n# Outcome: session ledger #{day}\n\n" \
72
+ "#{section('Delivered', done)}\n#{section('Carried', carried)}\n#{section('Promoted', promoted)}",
73
+ }
74
+ end
75
+
76
+ # --- writing, atomic inside the day dir ----------------------------------------
77
+
78
+ def write_atomic(dir, relative, content)
79
+ target = File.join(dir, relative)
80
+ FileUtils.mkdir_p(File.dirname(target))
81
+ tmp = File.join(File.dirname(target), ".filing-#{File.basename(target)}")
82
+ File.write(tmp, content)
83
+ File.rename(tmp, target)
84
+ end
85
+
86
+ def write_docs(dir, docs)
87
+ docs.each { |relative, content| write_atomic(dir, relative, content) }
88
+ end
89
+
90
+ # --- closed marker ---------------------------------------------------------------
91
+
92
+ def closed_at(day_file)
93
+ return nil unless File.exist?(day_file)
94
+
95
+ frontmatter = File.read(day_file).split(/^---\s*$/, 3)[1].to_s
96
+ value = frontmatter[/^closed:\s*(\S+)/, 1]
97
+ value ? Time.iso8601(value) : nil
98
+ rescue ArgumentError
99
+ nil
100
+ end
101
+
102
+ def stamp_closed(day_file, now:)
103
+ stamp = "closed: #{now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}\n"
104
+ content = File.read(day_file)
105
+ updated =
106
+ if content =~ /^closed:.*\n/
107
+ content.sub(/^closed:.*\n/, stamp)
108
+ else
109
+ parts = content.split(/^(---\s*\n)/, 3)
110
+ # parts: ["", "---\n", frontmatter, "---\n", rest]
111
+ parts.length >= 4 ? parts[0] + parts[1] + parts[2] + stamp + parts[3] + parts[4].to_s : content + stamp
112
+ end
113
+ write_atomic(File.dirname(day_file), File.basename(day_file), updated)
114
+ end
115
+
116
+ # A day is closed when it carries a closed: stamp and its checklist has not
117
+ # been written since (integer-second comparison; a line appended within the
118
+ # closing second waits for the next line).
119
+ def closed?(store, day)
120
+ closed = closed_at(SessionLedger.day_file(store, day))
121
+ return false unless closed
122
+
123
+ checklist = SessionLedger.checklist_path(store, day)
124
+ return true unless File.exist?(checklist)
125
+
126
+ File.mtime(checklist).to_i <= closed.to_i
127
+ end
128
+
129
+ # --- carry -------------------------------------------------------------------------
130
+
131
+ def carried_summary(summary, from)
132
+ suffix = " (carried from #{from})"
133
+ base = SessionLedger.sanitize_summary(summary.sub(CARRIED_RE, ""))
134
+ room = SUMMARY_CAP - suffix.length
135
+ base = "#{base[0, room - 3]}..." if base.length > room
136
+ base + suffix
137
+ end
138
+
139
+ def note(store, day, text, now:)
140
+ SessionLedger.append_line(SessionLedger.savepoint_path(store, day),
141
+ SessionLedger.savepoint_line("Note", FILER_SESSION, FILER_PROJECT, text, now: now))
142
+ end
143
+
144
+ def carry(store:, from:, to:, templates:, author:, now: Time.now)
145
+ SessionLedger.open_day(store: store, day: to, templates: templates, author: author, now: now)
146
+ source = SessionLedger.checklist_path(store, from)
147
+ target = SessionLedger.checklist_path(store, to)
148
+ existing = read_items(SessionLedger.day_dir(store, to)).map { |i| [i[:session], i[:summary]] }
149
+ count = 0
150
+ read_items(SessionLedger.day_dir(store, from)).select { |i| i[:state] == :open }.each do |item|
151
+ summary = carried_summary(item[:summary], from)
152
+ unless existing.include?([item[:session], summary])
153
+ SessionLedger.append_line(target, SessionLedger.checklist_line(:open, item[:session], item[:project], summary),
154
+ header: SessionLedger.checklist_header(to))
155
+ existing << [item[:session], summary]
156
+ end
157
+ SessionLedger.set_state(source, from: :open, to: :moved, session: item[:session], match: item[:summary])
158
+ count += 1
159
+ end
160
+ return 0 if count.zero?
161
+
162
+ note(store, from, "carried #{count} items to #{to}", now: now)
163
+ note(store, to, "received #{count} items from #{from}", now: now)
164
+ count
165
+ end
166
+
167
+ # --- the filer -----------------------------------------------------------------------
168
+
169
+ # Returns :closed when the day is already filed and unchanged, else :filed.
170
+ def file_day(store:, day:, templates:, author:, carry_to: nil, now: Time.now)
171
+ SessionLedger.open_day(store: store, day: day, templates: templates, author: author, now: now)
172
+ return :closed if closed?(store, day)
173
+
174
+ dir = SessionLedger.day_dir(store, day)
175
+ checklist = SessionLedger.checklist_path(store, day)
176
+ dropped = SessionLedger.flip_all(checklist, from: :pending, to: :dropped)
177
+ note(store, day, "dropped #{dropped} pending lines at filing", now: now) if dropped.positive?
178
+ carry(store: store, from: day, to: carry_to, templates: templates, author: author, now: now) if carry_to
179
+ write_docs(dir, render(dir, day: day))
180
+ stamp = [Time.now, (File.exist?(checklist) ? File.mtime(checklist) : Time.at(0))].max
181
+ stamp_closed(SessionLedger.day_file(store, day), now: stamp)
182
+ :filed
183
+ end
184
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # SessionClose (intent 301): the per-session close, run by hooks/close at
5
+ # SessionEnd. Cheap, fail-open, and a no-op for the reasons that do not end a
6
+ # session (`clear`, `resume`). Every job runs in its own rescue so one failure
7
+ # never stops the next. No environment reads; the hook script passes the
8
+ # store, today's day id, and the spawner in.
9
+
10
+ require "fileutils"
11
+ require_relative "session_ledger"
12
+
13
+ module SessionClose
14
+ module_function
15
+
16
+ NOOP_REASONS = %w[clear resume].freeze
17
+
18
+ # The default spawner starts the day filer detached so a slow filing never
19
+ # blocks the harness shutdown (Codex kills a SessionEnd hook after 3 s).
20
+ def default_spawner(script)
21
+ lambda do |args|
22
+ pid = Process.spawn({ "RUBYOPT" => nil }, RbConfig.ruby, script, *args,
23
+ pgroup: true, in: File::NULL, out: File::NULL, err: File::NULL)
24
+ Process.detach(pid)
25
+ end
26
+ end
27
+
28
+ # Returns a small report hash; never raises.
29
+ def run(payload:, store:, today:, spawner:, now: Time.now)
30
+ report = { reason: nil, dropped: 0, removed_tmp: false, spawned: nil }
31
+ reason = payload.is_a?(Hash) ? payload["reason"].to_s : ""
32
+ report[:reason] = reason
33
+ return report if NOOP_REASONS.include?(reason)
34
+
35
+ session = SessionLedger.short_session_id(payload.is_a?(Hash) ? payload["session_id"] : nil, nil)
36
+ return report if session.empty?
37
+
38
+ pointer_day = safely { read_pointer_day(store, session) } || today
39
+ project = "global"
40
+
41
+ report[:dropped] = safely do
42
+ checklist = SessionLedger.checklist_path(store, pointer_day)
43
+ count = SessionLedger.flip_all(checklist, from: :pending, to: :dropped, session: session)
44
+ if count.positive?
45
+ SessionLedger.append_line(SessionLedger.savepoint_path(store, pointer_day),
46
+ SessionLedger.savepoint_line("Note", session, project,
47
+ "dropped #{count} pending lines at close", now: now))
48
+ end
49
+ count
50
+ end || 0
51
+
52
+ report[:removed_tmp] = safely do
53
+ dir = SessionLedger.session_tmp_dir(store, session)
54
+ FileUtils.rm_rf(dir) if Dir.exist?(dir)
55
+ true
56
+ end || false
57
+
58
+ if pointer_day < today
59
+ report[:spawned] = safely do
60
+ args = ["--day", pointer_day, "--carry-to", today, "--store", store]
61
+ spawner.call(args)
62
+ args
63
+ end
64
+ end
65
+ report
66
+ end
67
+
68
+ # The pointer names the intent this session records into; a day id means
69
+ # the day ledger. Anything else (an intent id) means an auto team owns the
70
+ # record and the close touches no day ledger.
71
+ def read_pointer_day(store, session)
72
+ path = SessionLedger.pointer_path(store, session)
73
+ return nil unless File.exist?(path)
74
+
75
+ value = File.read(path).strip
76
+ SessionLedger.valid_day_id?(value) ? value : nil
77
+ end
78
+
79
+ def safely
80
+ yield
81
+ rescue StandardError
82
+ nil
83
+ end
84
+ end