@zalom/plastic 1.14.1 → 2.0.0-alpha.2

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 (200) 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/bin/lib/context_budget.rb +453 -0
  8. package/bin/plastic-bench +78 -0
  9. package/config_asks.yml +38 -0
  10. package/hooks/capture +4 -0
  11. package/hooks/close +5 -0
  12. package/hooks/hooks.json +9 -49
  13. package/hooks/record +4 -0
  14. package/hooks/savepoint +5 -5
  15. package/package.json +3 -2
  16. package/scripts/agent-report +8 -9
  17. package/scripts/append-ledger +208 -0
  18. package/scripts/codex-hook +78 -90
  19. package/scripts/dashboard.rb +4 -4
  20. package/scripts/day-summary +53 -0
  21. package/scripts/doctor.rb +106 -43
  22. package/scripts/end-intent +77 -76
  23. package/scripts/exec-worktree +11 -20
  24. package/scripts/file-session-intent +61 -0
  25. package/scripts/hook-capture +242 -0
  26. package/scripts/hook-close +36 -0
  27. package/scripts/hook-record +149 -0
  28. package/scripts/hook-savepoint +45 -0
  29. package/scripts/hook-session-start +106 -12
  30. package/scripts/install.rb +8 -6
  31. package/scripts/lib/agent_models.rb +1 -7
  32. package/scripts/lib/arm.rb +283 -0
  33. package/scripts/lib/backfill_intent.rb +316 -0
  34. package/scripts/lib/bridge.rb +24 -1597
  35. package/scripts/lib/compact_instructions.rb +56 -0
  36. package/scripts/lib/day_summary.rb +206 -0
  37. package/scripts/lib/doctor_core.rb +65 -88
  38. package/scripts/lib/doctor_session_ledger.rb +106 -0
  39. package/scripts/lib/exec_worktree.rb +14 -60
  40. package/scripts/lib/handoff.rb +184 -0
  41. package/scripts/lib/harness_text.rb +5 -4
  42. package/scripts/lib/hook_registry.rb +44 -107
  43. package/scripts/lib/insights.rb +3 -4
  44. package/scripts/lib/installer_core.rb +104 -40
  45. package/scripts/lib/lock.rb +6 -26
  46. package/scripts/lib/outcome_guard.rb +7 -5
  47. package/scripts/lib/project_validator.rb +30 -1
  48. package/scripts/lib/roadmap_queue.rb +3 -3
  49. package/scripts/lib/roadmap_savepoint.rb +1 -1
  50. package/scripts/lib/rule_catalog.rb +6 -3
  51. package/scripts/lib/savepoint.rb +320 -0
  52. package/scripts/lib/scaffold_intent.rb +13 -179
  53. package/scripts/lib/session_backfill.rb +184 -0
  54. package/scripts/lib/session_close.rb +104 -0
  55. package/scripts/lib/session_git.rb +469 -0
  56. package/scripts/lib/session_ledger.rb +497 -0
  57. package/scripts/lib/store_discovery.rb +1 -1
  58. package/scripts/lib/store_provisioning.rb +1 -1
  59. package/scripts/maintenance-run +7 -7
  60. package/scripts/new-intent +63 -4
  61. package/scripts/plastic-lock +94 -41
  62. package/scripts/promote-session-item +150 -0
  63. package/scripts/read-config +3 -0
  64. package/scripts/rebuild-graph +1 -1
  65. package/scripts/roadmap-next +2 -2
  66. package/scripts/rollback.rb +48 -3
  67. package/scripts/scaffold-intent +37 -63
  68. package/scripts/session-commit +116 -0
  69. package/scripts/spawn-preamble +5 -5
  70. package/scripts/update.rb +7 -3
  71. package/scripts/write-handoff +60 -0
  72. package/skills/agent-advisor/SKILL.md +3 -3
  73. package/skills/agent-advisor/references/advisor-protocol.md +20 -20
  74. package/skills/auto/SKILL.md +218 -317
  75. package/skills/auto/evals/evals.json +10 -10
  76. package/skills/auto/references/agent-architecture.md +102 -90
  77. package/skills/auto/references/agent-report-contract.md +4 -21
  78. package/skills/auto/references/end-tail.md +47 -53
  79. package/skills/auto/references/human-report-contract.md +8 -8
  80. package/skills/conventions/SKILL.md +1 -3
  81. package/skills/conventions/references/lifecycle-and-savepoints.md +35 -1
  82. package/skills/conventions/references/locks-and-worktrees.md +47 -55
  83. package/skills/conventions/references/maintenance-and-revisions.md +12 -12
  84. package/skills/dashboard/SKILL.md +1 -1
  85. package/skills/direct/SKILL.md +66 -0
  86. package/skills/direct/references/request-signals.md +59 -0
  87. package/skills/doctor/SKILL.md +58 -11
  88. package/skills/doctor/report.md +3 -1
  89. package/skills/install/SKILL.md +11 -9
  90. package/skills/intent-continuing/SKILL.md +132 -89
  91. package/skills/intent-continuing/references/boarding-matrix.md +34 -0
  92. package/skills/intent-continuing/references/context-management.md +2 -2
  93. package/skills/intent-creating/SKILL.md +2 -2
  94. package/skills/intent-creating/references/lifecycle.md +1 -1
  95. package/skills/intent-ending/SKILL.md +28 -37
  96. package/skills/intent-ending/evals/evals.json +4 -4
  97. package/skills/intent-executing/SKILL.md +12 -49
  98. package/skills/intent-executing/plan-reviewer-prompt.md +39 -0
  99. package/skills/intent-speccing/SKILL.md +108 -51
  100. package/skills/intent-speccing/evals/evals.json +5 -5
  101. package/skills/intent-speccing/references/per-section-fill-rules.md +4 -4
  102. package/skills/intent-speccing/references/self-verify-checklist.md +1 -2
  103. package/skills/project-creating/SKILL.md +1 -1
  104. package/skills/releasing/SKILL.md +7 -8
  105. package/skills/tutorial/SKILL.md +2 -2
  106. package/skills/tutorial/references/track-1-guided.md +16 -18
  107. package/skills/tutorial/references/track-2-auto.md +12 -13
  108. package/skills/tutorial/references/track-3-projects-and-roadmaps.md +2 -2
  109. package/templates/agents.md +4 -7
  110. package/templates/config.yml +5 -0
  111. package/templates/project.yml +7 -0
  112. package/templates/roadmap.md +1 -1
  113. package/templates/savepoint.md +2 -2
  114. package/templates/session-intent.md +29 -0
  115. package/agents/plastic-brainstorming.md +0 -48
  116. package/agents/plastic-future-intent-researcher.md +0 -35
  117. package/agents/plastic-intent-curator.md +0 -47
  118. package/agents/plastic-intent-discovery.md +0 -43
  119. package/agents/plastic-planner.md +0 -56
  120. package/agents/plastic-spec-specialist.md +0 -53
  121. package/hooks/auto-arm +0 -5
  122. package/hooks/bash-gate +0 -3
  123. package/hooks/continue +0 -31
  124. package/hooks/edit-gates +0 -3
  125. package/hooks/future-intent-check +0 -25
  126. package/hooks/gate-check +0 -12
  127. package/hooks/power-tools +0 -8
  128. package/scripts/hook-auto-arm +0 -51
  129. package/scripts/hook-bash-gate +0 -78
  130. package/scripts/hook-code-gate +0 -38
  131. package/scripts/hook-continue +0 -48
  132. package/scripts/hook-create-gate +0 -51
  133. package/scripts/hook-edit-gates +0 -58
  134. package/scripts/hook-future-intent-check +0 -90
  135. package/scripts/hook-gate-check +0 -169
  136. package/scripts/hook-links-gate +0 -54
  137. package/scripts/hook-lock-gate +0 -47
  138. package/scripts/hook-power-tools +0 -38
  139. package/scripts/hook-savepoint-pre +0 -32
  140. package/scripts/lib/codex_edit_gates.rb +0 -138
  141. package/scripts/lib/edit_gates.rb +0 -398
  142. package/scripts/lib/links_gate.rb +0 -140
  143. package/scripts/lib/qmd_hook.rb +0 -41
  144. package/scripts/lib/spec_header.rb +0 -83
  145. package/scripts/lib/start_intent.rb +0 -296
  146. package/scripts/start-intent +0 -89
  147. package/skills/_active-intent-gate.md +0 -26
  148. package/skills/auto/references/tiers.md +0 -100
  149. package/skills/continuing/SKILL.md +0 -34
  150. package/skills/continuing/evals/evals.json +0 -91
  151. package/skills/conventions/references/gates-and-enforcement.md +0 -53
  152. package/skills/conventions/references/tiers-and-dispatch.md +0 -135
  153. package/skills/doctor/references/gates-stuck-detection.md +0 -43
  154. package/skills/intent-brainstorming/SKILL.md +0 -118
  155. package/skills/intent-brainstorming/evals/evals.json +0 -67
  156. package/skills/intent-continuing/evals/evals.json +0 -145
  157. package/skills/intent-discovering/SKILL.md +0 -56
  158. package/skills/intent-grilling/SKILL.md +0 -108
  159. package/skills/intent-linking/SKILL.md +0 -128
  160. package/skills/intent-linking/evals/evals.json +0 -22
  161. package/skills/intent-linking/references/zettelkasten.md +0 -45
  162. package/skills/intent-locking/SKILL.md +0 -64
  163. package/skills/intent-planning/SKILL.md +0 -145
  164. package/skills/intent-planning/evals/evals.json +0 -81
  165. package/skills/intent-planning/references/plan-format.md +0 -130
  166. package/skills/intent-researching/SKILL.md +0 -123
  167. package/skills/intent-researching/evals/evals.json +0 -22
  168. package/skills/intent-savepoint/SKILL.md +0 -83
  169. package/skills/intent-savepoint/references/context-management.md +0 -32
  170. package/skills/intent-starting/SKILL.md +0 -151
  171. package/skills/intent-starting/evals/evals.json +0 -117
  172. package/skills/intent-starting/references/boarding-matrix.md +0 -35
  173. package/skills/project-continuing/SKILL.md +0 -119
  174. package/skills/project-continuing/evals/evals.json +0 -100
  175. package/skills/roadmap-continuing/SKILL.md +0 -89
  176. package/skills/roadmap-continuing/evals/evals.json +0 -82
  177. package/skills/skill-creating/SKILL.md +0 -75
  178. package/skills/skill-creating/evals/evals.json +0 -108
  179. package/skills/skill-creating/references/agents.md +0 -168
  180. package/skills/skill-creating/references/defaults-first.md +0 -23
  181. package/skills/skill-creating/references/evals.md +0 -41
  182. package/skills/skill-creating/references/hooks.md +0 -251
  183. package/skills/skill-creating/references/progressive-disclosure.md +0 -176
  184. package/skills/skill-creating/references/scripts.md +0 -166
  185. package/skills/skill-creating/references/skills.md +0 -169
  186. package/skills/skill-creating/scripts/scaffold.rb +0 -313
  187. package/skills/skill-evaluating/SKILL.md +0 -141
  188. package/skills/skill-evaluating/assets/eval-template.json +0 -12
  189. package/skills/skill-evaluating/evals/evals.json +0 -75
  190. package/skills/skill-evaluating/references/convention-checks.md +0 -76
  191. package/skills/skill-evaluating/references/eval-methodology.md +0 -154
  192. package/skills/store-curating/SKILL.md +0 -64
  193. package/skills/store-curating/evals/evals.json +0 -38
  194. package/skills/store-indexing/SKILL.md +0 -93
  195. package/skills/store-indexing/evals/evals.json +0 -22
  196. package/skills/store-indexing/references/zettelkasten-linking.md +0 -32
  197. package/skills/store-provisioning/SKILL.md +0 -55
  198. /package/skills/{project-continuing → intent-continuing}/references/board-fill.md +0 -0
  199. /package/skills/{roadmap-continuing → intent-continuing}/references/liveness-ranking.md +0 -0
  200. /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,104 @@
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
+ require_relative "handoff"
13
+
14
+ module SessionClose
15
+ module_function
16
+
17
+ NOOP_REASONS = %w[clear resume].freeze
18
+
19
+ # The default hand-off writer (intent 311, spec D6): renders this session's
20
+ # share of the pointer day into handoff--<session>.md before the tmp dir
21
+ # goes. The hook script builds it with the shipped templates dir.
22
+ def default_handoff(templates)
23
+ lambda do |store, day, session|
24
+ Handoff.write(store: store, day: day, session: session, trigger: "close", templates: templates)
25
+ end
26
+ end
27
+
28
+ # The default spawner starts the day filer detached so a slow filing never
29
+ # blocks the harness shutdown (Codex kills a SessionEnd hook after 3 s).
30
+ def default_spawner(script)
31
+ lambda do |args|
32
+ pid = Process.spawn({ "RUBYOPT" => nil }, RbConfig.ruby, script, *args,
33
+ pgroup: true, in: File::NULL, out: File::NULL, err: File::NULL)
34
+ Process.detach(pid)
35
+ end
36
+ end
37
+
38
+ # Returns a small report hash; never raises.
39
+ def run(payload:, store:, today:, spawner:, handoff: nil, now: Time.now)
40
+ report = { reason: nil, dropped: 0, removed_tmp: false, spawned: nil, handoff: false }
41
+ reason = payload.is_a?(Hash) ? payload["reason"].to_s : ""
42
+ report[:reason] = reason
43
+ return report if NOOP_REASONS.include?(reason)
44
+
45
+ session = SessionLedger.short_session_id(payload.is_a?(Hash) ? payload["session_id"] : nil, nil)
46
+ return report if session.empty?
47
+
48
+ pointer_day = safely { read_pointer_day(store, session) } || today
49
+ project = "global"
50
+
51
+ report[:dropped] = safely do
52
+ checklist = SessionLedger.checklist_path(store, pointer_day)
53
+ count = SessionLedger.flip_all(checklist, from: :pending, to: :dropped, session: session)
54
+ if count.positive?
55
+ SessionLedger.append_line(SessionLedger.savepoint_path(store, pointer_day),
56
+ SessionLedger.savepoint_line("Note", session, project,
57
+ "dropped #{count} pending lines at close", now: now))
58
+ end
59
+ count
60
+ end || 0
61
+
62
+ # The hand-off (intent 311, spec D6) is written after the drop, so it
63
+ # reflects it, and before the tmp dir goes, since the pointer lives there.
64
+ # An intent pointer falls back to today, the same as the drop above.
65
+ if handoff
66
+ report[:handoff] = safely do
67
+ handoff.call(store, pointer_day, session)
68
+ true
69
+ end || false
70
+ end
71
+
72
+ report[:removed_tmp] = safely do
73
+ dir = SessionLedger.session_tmp_dir(store, session)
74
+ FileUtils.rm_rf(dir) if Dir.exist?(dir)
75
+ true
76
+ end || false
77
+
78
+ if pointer_day < today
79
+ report[:spawned] = safely do
80
+ args = ["--day", pointer_day, "--carry-to", today, "--store", store]
81
+ spawner.call(args)
82
+ args
83
+ end
84
+ end
85
+ report
86
+ end
87
+
88
+ # The pointer names the intent this session records into; a day id means
89
+ # the day ledger. Anything else (an intent id) means an auto team owns the
90
+ # record and the close touches no day ledger.
91
+ def read_pointer_day(store, session)
92
+ path = SessionLedger.pointer_path(store, session)
93
+ return nil unless File.exist?(path)
94
+
95
+ value = File.read(path).strip
96
+ SessionLedger.valid_day_id?(value) ? value : nil
97
+ end
98
+
99
+ def safely
100
+ yield
101
+ rescue StandardError
102
+ nil
103
+ end
104
+ end