@zalom/plastic 2.0.0-alpha.2 → 2.0.0-alpha.20

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 (117) hide show
  1. package/agents/plastic-enforcer.md +3 -1
  2. package/agents/plastic-executor.md +9 -3
  3. package/hooks/hooks.json +12 -0
  4. package/hooks/message-display +134 -0
  5. package/hooks/statusline +4 -27
  6. package/package.json +2 -2
  7. package/scripts/agent-report +8 -2
  8. package/scripts/dashboard.rb +297 -18
  9. package/scripts/doctor.rb +652 -5
  10. package/scripts/end-intent +219 -0
  11. package/scripts/hook-capture +25 -113
  12. package/scripts/hook-message-display +81 -0
  13. package/scripts/hook-record +12 -4
  14. package/scripts/hook-session-start +23 -1
  15. package/scripts/index-projection +74 -0
  16. package/scripts/intent-screen +77 -0
  17. package/scripts/lib/action_graph_shim.rb +277 -0
  18. package/scripts/lib/arm.rb +26 -1
  19. package/scripts/lib/atomic_write.rb +31 -0
  20. package/scripts/lib/core_integrity.rb +71 -0
  21. package/scripts/lib/dashboard_screen.rb +40 -0
  22. package/scripts/lib/day_summary.rb +7 -2
  23. package/scripts/lib/doctor_core.rb +101 -5
  24. package/scripts/lib/doctor_session_ledger.rb +52 -0
  25. package/scripts/lib/graph_edges.rb +121 -0
  26. package/scripts/lib/graph_file.rb +246 -0
  27. package/scripts/lib/graph_tree.rb +98 -0
  28. package/scripts/lib/guarded_append.rb +155 -0
  29. package/scripts/lib/hook_registry.rb +14 -0
  30. package/scripts/lib/hook_replay.rb +211 -0
  31. package/scripts/lib/index_projection.rb +201 -0
  32. package/scripts/lib/installer_core.rb +131 -4
  33. package/scripts/lib/intent_screen.rb +309 -0
  34. package/scripts/lib/intent_screen_ansi.rb +262 -0
  35. package/scripts/lib/message_display.rb +586 -0
  36. package/scripts/lib/node_file.rb +214 -0
  37. package/scripts/lib/node_ids.rb +99 -0
  38. package/scripts/lib/node_ledger.rb +377 -0
  39. package/scripts/lib/node_packet.rb +886 -0
  40. package/scripts/lib/node_return.rb +199 -0
  41. package/scripts/lib/node_worktree.rb +337 -0
  42. package/scripts/lib/outcome_report.rb +440 -0
  43. package/scripts/lib/packet_wrapper.rb +132 -0
  44. package/scripts/lib/ready_set.rb +462 -0
  45. package/scripts/lib/release_guard.rb +16 -0
  46. package/scripts/lib/report_screen.rb +1967 -0
  47. package/scripts/lib/roadmap_graph.rb +210 -0
  48. package/scripts/lib/roadmap_migration.rb +95 -0
  49. package/scripts/lib/roadmap_queue.rb +155 -5
  50. package/scripts/lib/roadmap_render.rb +150 -0
  51. package/scripts/lib/roadmap_savepoint.rb +62 -12
  52. package/scripts/lib/runner_absorb.rb +620 -0
  53. package/scripts/lib/runner_answer.rb +206 -0
  54. package/scripts/lib/runner_core.rb +194 -0
  55. package/scripts/lib/runner_dispatch.rb +482 -0
  56. package/scripts/lib/runner_policy.rb +142 -0
  57. package/scripts/lib/runner_proposals.rb +254 -0
  58. package/scripts/lib/runner_rewind.rb +201 -0
  59. package/scripts/lib/runner_sweep.rb +231 -0
  60. package/scripts/lib/savepoint.rb +149 -12
  61. package/scripts/lib/screen_paint.rb +555 -0
  62. package/scripts/lib/screens/dashboard.rb +20 -0
  63. package/scripts/lib/screens/plan.rb +18 -0
  64. package/scripts/lib/screens/roadmap.rb +15 -0
  65. package/scripts/lib/session_git.rb +49 -18
  66. package/scripts/lib/session_ledger.rb +128 -0
  67. package/scripts/lib/verify_intent.rb +33 -0
  68. package/scripts/lib/work_graph_validator.rb +201 -0
  69. package/scripts/node-packet +92 -0
  70. package/scripts/node-transition +291 -0
  71. package/scripts/outcome-report +74 -0
  72. package/scripts/plastic-lock +8 -1
  73. package/scripts/ready-set +126 -0
  74. package/scripts/release-check +118 -0
  75. package/scripts/report-screen +281 -0
  76. package/scripts/roadmap-graph +119 -0
  77. package/scripts/roadmap-savepoint +7 -0
  78. package/scripts/runner +392 -0
  79. package/scripts/savepoint-note +69 -0
  80. package/scripts/spawn-preamble +9 -2
  81. package/scripts/validate-work-graph +39 -0
  82. package/scripts/verify-intent +2 -1
  83. package/skills/auto/SKILL.md +24 -17
  84. package/skills/auto/references/human-report-contract.md +136 -54
  85. package/skills/conventions/references/locks-and-worktrees.md +12 -0
  86. package/skills/dashboard/SKILL.md +13 -2
  87. package/skills/dashboard/templates/dashboard-global.md +1 -1
  88. package/skills/dashboard/templates/dashboard-project.md +2 -2
  89. package/skills/doctor/SKILL.md +10 -4
  90. package/skills/intent-continuing/SKILL.md +51 -41
  91. package/skills/intent-continuing/references/board-fill.md +9 -0
  92. package/skills/intent-continuing/references/boarding-matrix.md +6 -5
  93. package/skills/intent-continuing/references/context-management.md +1 -1
  94. package/skills/intent-ending/SKILL.md +36 -16
  95. package/skills/intent-executing/SKILL.md +17 -5
  96. package/skills/intent-executing/implementer-prompt.md +6 -1
  97. package/skills/intent-speccing/SKILL.md +7 -4
  98. package/skills/releasing/SKILL.md +39 -0
  99. package/skills/releasing/references/promotion-and-tagging.md +10 -6
  100. package/skills/releasing/references/release-lines.md +1 -1
  101. package/skills/roadmap/SKILL.md +26 -0
  102. package/skills/roadmap/references/file-format.md +10 -0
  103. package/templates/dashboard-screen.md +22 -0
  104. package/templates/display-fixture.md +21 -0
  105. package/templates/graph.md +16 -0
  106. package/templates/intent-screen.md +17 -0
  107. package/templates/node-decision.md +11 -0
  108. package/templates/node-research.md +11 -0
  109. package/templates/node-verify.md +13 -0
  110. package/templates/node-work.md +22 -0
  111. package/templates/outcome.md +19 -1
  112. package/templates/report-plan.md +15 -0
  113. package/templates/report-roadmap-delivered.md +10 -0
  114. package/templates/report-roadmap-plan.md +9 -0
  115. package/templates/report-roadmap-state.md +9 -0
  116. package/templates/report-state.md +11 -0
  117. package/templates/roadmap.md +13 -0
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "time"
5
5
  require "fileutils"
6
+ require_relative "guarded_append"
6
7
 
7
8
  # RoadmapSavepoint - the roadmap's machine counterpart to its human `## Log` (intent 134).
8
9
  #
@@ -66,6 +67,25 @@ module RoadmapSavepoint
66
67
  File.read(ledger_path).each_line.filter_map { |line| parse_pair(line) }
67
68
  end
68
69
 
70
+ # Public (intent 331c): a roadmap's own ledger, parsed into `[Time, event, detail]` triples in
71
+ # file order - so a screen reader never re-derives the "<iso> <event> <detail>" line shape.
72
+ # `roadmap_path` is the roadmap `.md` file, never the `.savepoint.md` sibling directly (mirrors
73
+ # `ledger_path_for`'s own convention). No paired ledger file -> `[]`, never an invented event.
74
+ def ledger_entries(roadmap_path)
75
+ ledger_path = ledger_path_for(roadmap_path)
76
+ return [] unless File.exist?(ledger_path)
77
+
78
+ File.readlines(ledger_path).filter_map do |line|
79
+ parts = line.strip.split(/\s{2,}/, 3)
80
+ next nil unless parts.length == 3
81
+ begin
82
+ [Time.iso8601(parts[0]), parts[1], parts[2]]
83
+ rescue ArgumentError
84
+ nil
85
+ end
86
+ end
87
+ end
88
+
69
89
  def parse_pair(line)
70
90
  parts = line.strip.split(/\s{2,}/)
71
91
  parts.length >= 3 ? [parts[1], parts[2]] : nil
@@ -76,17 +96,37 @@ module RoadmapSavepoint
76
96
  # recorded (no-op). Creates the paired ledger file (and its directory) lazily. Raises
77
97
  # ArgumentError when `event` is outside the controlled vocabulary. Returns true when a line
78
98
  # was written, false on a dedup no-op.
79
- def append(roadmap_path, event, detail, now: Time.now)
99
+ #
100
+ # Intent 335 (spec D14): the dedup check moves INSIDE one GuardedAppend hold, which makes
101
+ # "is this pair already recorded" and "append it" atomic against a second writer without
102
+ # changing what this method returns. `strict: false` keeps this ledger's existing
103
+ # flock-less-filesystem fallback exactly as it was before the guard existed (spec D12a): a
104
+ # single O_APPEND write still lands whole there, so this ledger never refuses on such a
105
+ # filesystem, unlike a strict transition append elsewhere in 335. `guard:` is the sole thing
106
+ # this module takes from 335 (spec D14: "RoadmapSavepoint takes GuardedAppend and nothing else");
107
+ # `flock:`/`sleeper:` pass straight through as GuardedAppend test seams, never read from an
108
+ # environment variable. The directory is created BEFORE the guard is called (spec D12b): the
109
+ # guard's own File.open would raise Errno::ENOENT on a missing parent, which must propagate as
110
+ # itself rather than be mistaken for lock contention.
111
+ def append(roadmap_path, event, detail, now: Time.now, guard: GuardedAppend, **guard_opts)
80
112
  unless EVENTS.include?(event)
81
113
  raise ArgumentError, "event must be one of #{EVENTS.join(', ')}, got #{event.inspect}"
82
114
  end
83
115
 
84
116
  ledger_path = ledger_path_for(roadmap_path)
85
- return false if recorded_pairs(ledger_path).include?([event, detail])
86
-
87
117
  FileUtils.mkdir_p(File.dirname(ledger_path))
88
- File.open(ledger_path, "a") { |io| io.write(format_line(now, event, detail)) }
89
- true
118
+
119
+ pair = [event, detail]
120
+ written = false
121
+ guard.call(ledger_path, strict: false, **guard_opts) do |content|
122
+ if content.each_line.filter_map { |line| parse_pair(line) }.include?(pair)
123
+ nil
124
+ else
125
+ written = true
126
+ format_line(now, event, detail)
127
+ end
128
+ end
129
+ written
90
130
  end
91
131
 
92
132
  def format_line(time, event, detail)
@@ -141,11 +181,13 @@ module RoadmapSavepoint
141
181
  end
142
182
  private_class_method :parse_log_time
143
183
 
184
+ # Public (intent 331c): the Log table on a roadmap's `delivered` screen classifies every
185
+ # `## Log` line through this same keyword vocabulary, so a screen reader never grows a second
186
+ # copy of KEYWORD_TABLE.
144
187
  def classify_event(text)
145
188
  hit = KEYWORD_TABLE.find { |regex, _event| text =~ regex }
146
189
  hit && hit[1]
147
190
  end
148
- private_class_method :classify_event
149
191
 
150
192
  WAVE_ENTRY = /\A-\s*\[([ xX])\]\s+(\S+)\s+.+—\s*(\S+)\s*\z/.freeze
151
193
 
@@ -217,13 +259,21 @@ module RoadmapSavepoint
217
259
  # already calling `ledger_path_for`), so this is the smaller diff than a new shared module.
218
260
  # Raises MissingGroupingHeading, naming the offending path, when neither heading is present.
219
261
  def grouping_section_body(text, path: nil)
220
- GROUPING_HEADINGS.each do |heading|
221
- m = text.match(/^##\s+#{Regexp.escape(heading)}\s*$(.*?)(?=^##\s|\z)/m)
222
- return m[1] if m
262
+ heading = grouping_heading(text)
263
+ unless heading
264
+ raise MissingGroupingHeading,
265
+ "#{path || '(unknown roadmap file)'}: found neither '## Batches' (canonical) nor " \
266
+ "'## Waves' (legacy) grouping heading"
223
267
  end
224
- raise MissingGroupingHeading,
225
- "#{path || '(unknown roadmap file)'}: found neither '## Batches' (canonical) nor " \
226
- "'## Waves' (legacy) grouping heading"
268
+ text.match(/^##\s+#{Regexp.escape(heading)}\s*$(.*?)(?=^##\s|\z)/m)[1]
269
+ end
270
+
271
+ # Public (intent 331c): "Batches" or "Waves", whichever grouping heading `text` carries - the
272
+ # one owner of that label so a screen's own field row (and its entries table's column header)
273
+ # never hand-picks between them a second way. nil when neither heading is present (mirrors
274
+ # grouping_section_body's own detection, one call site cheaper than two).
275
+ def grouping_heading(text)
276
+ GROUPING_HEADINGS.find { |heading| text.match?(/^##\s+#{Regexp.escape(heading)}\s*$/) }
227
277
  end
228
278
 
229
279
  # Stable dedup on the `(event, detail)` pair, keeping the first occurrence in the given