@zalom/plastic 1.14.1 → 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 (189) 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/close +5 -0
  10. package/hooks/hooks.json +9 -49
  11. package/hooks/record +4 -0
  12. package/package.json +3 -2
  13. package/scripts/agent-report +8 -9
  14. package/scripts/append-ledger +195 -0
  15. package/scripts/codex-hook +78 -90
  16. package/scripts/dashboard.rb +4 -4
  17. package/scripts/doctor.rb +106 -43
  18. package/scripts/end-intent +77 -76
  19. package/scripts/exec-worktree +11 -20
  20. package/scripts/file-session-intent +61 -0
  21. package/scripts/hook-capture +242 -0
  22. package/scripts/hook-close +34 -0
  23. package/scripts/hook-record +149 -0
  24. package/scripts/hook-session-start +95 -12
  25. package/scripts/install.rb +8 -6
  26. package/scripts/lib/agent_models.rb +1 -7
  27. package/scripts/lib/arm.rb +283 -0
  28. package/scripts/lib/backfill_intent.rb +316 -0
  29. package/scripts/lib/bridge.rb +24 -1597
  30. package/scripts/lib/doctor_core.rb +17 -88
  31. package/scripts/lib/doctor_session_ledger.rb +106 -0
  32. package/scripts/lib/exec_worktree.rb +14 -60
  33. package/scripts/lib/harness_text.rb +5 -4
  34. package/scripts/lib/hook_registry.rb +44 -107
  35. package/scripts/lib/insights.rb +3 -4
  36. package/scripts/lib/installer_core.rb +19 -30
  37. package/scripts/lib/lock.rb +6 -26
  38. package/scripts/lib/outcome_guard.rb +7 -5
  39. package/scripts/lib/project_validator.rb +30 -1
  40. package/scripts/lib/roadmap_queue.rb +3 -3
  41. package/scripts/lib/roadmap_savepoint.rb +1 -1
  42. package/scripts/lib/rule_catalog.rb +6 -3
  43. package/scripts/lib/savepoint.rb +320 -0
  44. package/scripts/lib/scaffold_intent.rb +13 -179
  45. package/scripts/lib/session_backfill.rb +184 -0
  46. package/scripts/lib/session_close.rb +84 -0
  47. package/scripts/lib/session_git.rb +469 -0
  48. package/scripts/lib/session_ledger.rb +497 -0
  49. package/scripts/lib/store_discovery.rb +1 -1
  50. package/scripts/lib/store_provisioning.rb +1 -1
  51. package/scripts/maintenance-run +7 -7
  52. package/scripts/new-intent +63 -4
  53. package/scripts/plastic-lock +94 -41
  54. package/scripts/promote-session-item +150 -0
  55. package/scripts/rebuild-graph +1 -1
  56. package/scripts/roadmap-next +2 -2
  57. package/scripts/rollback.rb +42 -3
  58. package/scripts/scaffold-intent +37 -63
  59. package/scripts/session-commit +116 -0
  60. package/scripts/spawn-preamble +5 -5
  61. package/scripts/update.rb +7 -3
  62. package/skills/agent-advisor/SKILL.md +3 -3
  63. package/skills/agent-advisor/references/advisor-protocol.md +20 -20
  64. package/skills/auto/SKILL.md +218 -317
  65. package/skills/auto/evals/evals.json +10 -10
  66. package/skills/auto/references/agent-architecture.md +102 -90
  67. package/skills/auto/references/agent-report-contract.md +4 -21
  68. package/skills/auto/references/end-tail.md +47 -53
  69. package/skills/auto/references/human-report-contract.md +8 -8
  70. package/skills/conventions/SKILL.md +1 -3
  71. package/skills/conventions/references/lifecycle-and-savepoints.md +35 -1
  72. package/skills/conventions/references/locks-and-worktrees.md +47 -55
  73. package/skills/conventions/references/maintenance-and-revisions.md +12 -12
  74. package/skills/dashboard/SKILL.md +1 -1
  75. package/skills/direct/SKILL.md +66 -0
  76. package/skills/direct/references/request-signals.md +59 -0
  77. package/skills/doctor/SKILL.md +58 -11
  78. package/skills/doctor/report.md +3 -1
  79. package/skills/install/SKILL.md +11 -9
  80. package/skills/intent-continuing/SKILL.md +131 -89
  81. package/skills/intent-continuing/references/boarding-matrix.md +34 -0
  82. package/skills/intent-continuing/references/context-management.md +2 -2
  83. package/skills/intent-creating/SKILL.md +2 -2
  84. package/skills/intent-creating/references/lifecycle.md +1 -1
  85. package/skills/intent-ending/SKILL.md +28 -37
  86. package/skills/intent-ending/evals/evals.json +4 -4
  87. package/skills/intent-executing/SKILL.md +12 -49
  88. package/skills/intent-executing/plan-reviewer-prompt.md +39 -0
  89. package/skills/intent-speccing/SKILL.md +108 -51
  90. package/skills/intent-speccing/evals/evals.json +5 -5
  91. package/skills/intent-speccing/references/per-section-fill-rules.md +4 -4
  92. package/skills/intent-speccing/references/self-verify-checklist.md +1 -2
  93. package/skills/project-creating/SKILL.md +1 -1
  94. package/skills/releasing/SKILL.md +7 -8
  95. package/skills/tutorial/SKILL.md +2 -2
  96. package/skills/tutorial/references/track-1-guided.md +16 -18
  97. package/skills/tutorial/references/track-2-auto.md +12 -13
  98. package/skills/tutorial/references/track-3-projects-and-roadmaps.md +2 -2
  99. package/templates/agents.md +4 -7
  100. package/templates/project.yml +7 -0
  101. package/templates/roadmap.md +1 -1
  102. package/templates/savepoint.md +2 -2
  103. package/templates/session-intent.md +29 -0
  104. package/agents/plastic-brainstorming.md +0 -48
  105. package/agents/plastic-future-intent-researcher.md +0 -35
  106. package/agents/plastic-intent-curator.md +0 -47
  107. package/agents/plastic-intent-discovery.md +0 -43
  108. package/agents/plastic-planner.md +0 -56
  109. package/agents/plastic-spec-specialist.md +0 -53
  110. package/hooks/auto-arm +0 -5
  111. package/hooks/bash-gate +0 -3
  112. package/hooks/continue +0 -31
  113. package/hooks/edit-gates +0 -3
  114. package/hooks/future-intent-check +0 -25
  115. package/hooks/gate-check +0 -12
  116. package/hooks/power-tools +0 -8
  117. package/scripts/hook-auto-arm +0 -51
  118. package/scripts/hook-bash-gate +0 -78
  119. package/scripts/hook-code-gate +0 -38
  120. package/scripts/hook-continue +0 -48
  121. package/scripts/hook-create-gate +0 -51
  122. package/scripts/hook-edit-gates +0 -58
  123. package/scripts/hook-future-intent-check +0 -90
  124. package/scripts/hook-gate-check +0 -169
  125. package/scripts/hook-links-gate +0 -54
  126. package/scripts/hook-lock-gate +0 -47
  127. package/scripts/hook-power-tools +0 -38
  128. package/scripts/hook-savepoint-pre +0 -32
  129. package/scripts/lib/codex_edit_gates.rb +0 -138
  130. package/scripts/lib/edit_gates.rb +0 -398
  131. package/scripts/lib/links_gate.rb +0 -140
  132. package/scripts/lib/qmd_hook.rb +0 -41
  133. package/scripts/lib/spec_header.rb +0 -83
  134. package/scripts/lib/start_intent.rb +0 -296
  135. package/scripts/start-intent +0 -89
  136. package/skills/_active-intent-gate.md +0 -26
  137. package/skills/auto/references/tiers.md +0 -100
  138. package/skills/continuing/SKILL.md +0 -34
  139. package/skills/continuing/evals/evals.json +0 -91
  140. package/skills/conventions/references/gates-and-enforcement.md +0 -53
  141. package/skills/conventions/references/tiers-and-dispatch.md +0 -135
  142. package/skills/doctor/references/gates-stuck-detection.md +0 -43
  143. package/skills/intent-brainstorming/SKILL.md +0 -118
  144. package/skills/intent-brainstorming/evals/evals.json +0 -67
  145. package/skills/intent-continuing/evals/evals.json +0 -145
  146. package/skills/intent-discovering/SKILL.md +0 -56
  147. package/skills/intent-grilling/SKILL.md +0 -108
  148. package/skills/intent-linking/SKILL.md +0 -128
  149. package/skills/intent-linking/evals/evals.json +0 -22
  150. package/skills/intent-linking/references/zettelkasten.md +0 -45
  151. package/skills/intent-locking/SKILL.md +0 -64
  152. package/skills/intent-planning/SKILL.md +0 -145
  153. package/skills/intent-planning/evals/evals.json +0 -81
  154. package/skills/intent-planning/references/plan-format.md +0 -130
  155. package/skills/intent-researching/SKILL.md +0 -123
  156. package/skills/intent-researching/evals/evals.json +0 -22
  157. package/skills/intent-savepoint/SKILL.md +0 -83
  158. package/skills/intent-savepoint/references/context-management.md +0 -32
  159. package/skills/intent-starting/SKILL.md +0 -151
  160. package/skills/intent-starting/evals/evals.json +0 -117
  161. package/skills/intent-starting/references/boarding-matrix.md +0 -35
  162. package/skills/project-continuing/SKILL.md +0 -119
  163. package/skills/project-continuing/evals/evals.json +0 -100
  164. package/skills/roadmap-continuing/SKILL.md +0 -89
  165. package/skills/roadmap-continuing/evals/evals.json +0 -82
  166. package/skills/skill-creating/SKILL.md +0 -75
  167. package/skills/skill-creating/evals/evals.json +0 -108
  168. package/skills/skill-creating/references/agents.md +0 -168
  169. package/skills/skill-creating/references/defaults-first.md +0 -23
  170. package/skills/skill-creating/references/evals.md +0 -41
  171. package/skills/skill-creating/references/hooks.md +0 -251
  172. package/skills/skill-creating/references/progressive-disclosure.md +0 -176
  173. package/skills/skill-creating/references/scripts.md +0 -166
  174. package/skills/skill-creating/references/skills.md +0 -169
  175. package/skills/skill-creating/scripts/scaffold.rb +0 -313
  176. package/skills/skill-evaluating/SKILL.md +0 -141
  177. package/skills/skill-evaluating/assets/eval-template.json +0 -12
  178. package/skills/skill-evaluating/evals/evals.json +0 -75
  179. package/skills/skill-evaluating/references/convention-checks.md +0 -76
  180. package/skills/skill-evaluating/references/eval-methodology.md +0 -154
  181. package/skills/store-curating/SKILL.md +0 -64
  182. package/skills/store-curating/evals/evals.json +0 -38
  183. package/skills/store-indexing/SKILL.md +0 -93
  184. package/skills/store-indexing/evals/evals.json +0 -22
  185. package/skills/store-indexing/references/zettelkasten-linking.md +0 -32
  186. package/skills/store-provisioning/SKILL.md +0 -55
  187. /package/skills/{project-continuing → intent-continuing}/references/board-fill.md +0 -0
  188. /package/skills/{roadmap-continuing → intent-continuing}/references/liveness-ranking.md +0 -0
  189. /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