@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.
- package/PLASTIC.md +101 -294
- package/README.md +5 -6
- package/agents/plastic-advisor.md +18 -17
- package/agents/plastic-enforcer.md +57 -69
- package/agents/plastic-executor.md +34 -20
- package/agents/plastic-faux-advisor.md +18 -18
- package/config_asks.yml +38 -0
- package/hooks/capture +4 -0
- package/hooks/check-update +12 -3
- package/hooks/close +5 -0
- package/hooks/hooks.json +9 -49
- package/hooks/record +4 -0
- package/package.json +3 -2
- package/scripts/agent-report +8 -9
- package/scripts/append-ledger +195 -0
- package/scripts/codex-hook +101 -95
- package/scripts/dashboard.rb +4 -4
- package/scripts/doctor.rb +106 -43
- package/scripts/end-intent +77 -76
- package/scripts/exec-worktree +11 -20
- package/scripts/file-session-intent +61 -0
- package/scripts/hook-capture +242 -0
- package/scripts/hook-close +34 -0
- package/scripts/hook-record +149 -0
- package/scripts/hook-session-start +95 -12
- package/scripts/install.rb +8 -6
- package/scripts/lib/agent_models.rb +1 -7
- package/scripts/lib/arm.rb +283 -0
- package/scripts/lib/backfill_intent.rb +316 -0
- package/scripts/lib/bridge.rb +24 -1597
- package/scripts/lib/doctor_core.rb +17 -88
- package/scripts/lib/doctor_session_ledger.rb +106 -0
- package/scripts/lib/exec_worktree.rb +14 -60
- package/scripts/lib/harness_text.rb +5 -4
- package/scripts/lib/hook_registry.rb +44 -107
- package/scripts/lib/insights.rb +3 -4
- package/scripts/lib/installer_core.rb +19 -30
- package/scripts/lib/lock.rb +6 -26
- package/scripts/lib/outcome_guard.rb +7 -5
- package/scripts/lib/project_validator.rb +30 -1
- package/scripts/lib/roadmap_queue.rb +3 -3
- package/scripts/lib/roadmap_savepoint.rb +1 -1
- package/scripts/lib/rule_catalog.rb +6 -3
- package/scripts/lib/savepoint.rb +320 -0
- package/scripts/lib/scaffold_intent.rb +13 -179
- package/scripts/lib/session_backfill.rb +184 -0
- package/scripts/lib/session_close.rb +84 -0
- package/scripts/lib/session_git.rb +469 -0
- package/scripts/lib/session_ledger.rb +497 -0
- package/scripts/lib/store_discovery.rb +1 -1
- package/scripts/lib/store_provisioning.rb +1 -1
- package/scripts/maintenance-run +7 -7
- package/scripts/new-intent +63 -4
- package/scripts/plastic-lock +94 -41
- package/scripts/promote-session-item +150 -0
- package/scripts/rebuild-graph +1 -1
- package/scripts/roadmap-next +2 -2
- package/scripts/rollback.rb +42 -3
- package/scripts/scaffold-intent +37 -63
- package/scripts/session-commit +116 -0
- package/scripts/spawn-preamble +5 -5
- package/scripts/update.rb +7 -3
- package/skills/agent-advisor/SKILL.md +3 -3
- package/skills/agent-advisor/references/advisor-protocol.md +20 -20
- package/skills/auto/SKILL.md +218 -317
- package/skills/auto/evals/evals.json +10 -10
- package/skills/auto/references/agent-architecture.md +102 -90
- package/skills/auto/references/agent-report-contract.md +4 -21
- package/skills/auto/references/end-tail.md +47 -53
- package/skills/auto/references/human-report-contract.md +8 -8
- package/skills/conventions/SKILL.md +1 -3
- package/skills/conventions/references/lifecycle-and-savepoints.md +35 -1
- package/skills/conventions/references/locks-and-worktrees.md +47 -55
- package/skills/conventions/references/maintenance-and-revisions.md +12 -12
- package/skills/dashboard/SKILL.md +1 -1
- package/skills/direct/SKILL.md +66 -0
- package/skills/direct/references/request-signals.md +59 -0
- package/skills/doctor/SKILL.md +58 -11
- package/skills/doctor/report.md +3 -1
- package/skills/install/SKILL.md +11 -9
- package/skills/intent-continuing/SKILL.md +131 -89
- package/skills/intent-continuing/references/boarding-matrix.md +34 -0
- package/skills/intent-continuing/references/context-management.md +2 -2
- package/skills/intent-creating/SKILL.md +2 -2
- package/skills/intent-creating/references/lifecycle.md +1 -1
- package/skills/intent-ending/SKILL.md +28 -37
- package/skills/intent-ending/evals/evals.json +4 -4
- package/skills/intent-executing/SKILL.md +12 -49
- package/skills/intent-executing/plan-reviewer-prompt.md +39 -0
- package/skills/intent-speccing/SKILL.md +108 -51
- package/skills/intent-speccing/evals/evals.json +5 -5
- package/skills/intent-speccing/references/per-section-fill-rules.md +4 -4
- package/skills/intent-speccing/references/self-verify-checklist.md +1 -2
- package/skills/project-creating/SKILL.md +1 -1
- package/skills/releasing/SKILL.md +7 -8
- package/skills/tutorial/SKILL.md +2 -2
- package/skills/tutorial/references/track-1-guided.md +16 -18
- package/skills/tutorial/references/track-2-auto.md +12 -13
- package/skills/tutorial/references/track-3-projects-and-roadmaps.md +2 -2
- package/templates/agents.md +4 -7
- package/templates/project.yml +7 -0
- package/templates/roadmap.md +1 -1
- package/templates/savepoint.md +2 -2
- package/templates/session-intent.md +29 -0
- package/agents/plastic-brainstorming.md +0 -48
- package/agents/plastic-future-intent-researcher.md +0 -35
- package/agents/plastic-intent-curator.md +0 -47
- package/agents/plastic-intent-discovery.md +0 -43
- package/agents/plastic-planner.md +0 -56
- package/agents/plastic-spec-specialist.md +0 -53
- package/hooks/auto-arm +0 -5
- package/hooks/bash-gate +0 -3
- package/hooks/continue +0 -31
- package/hooks/edit-gates +0 -3
- package/hooks/future-intent-check +0 -25
- package/hooks/gate-check +0 -12
- package/hooks/power-tools +0 -8
- package/scripts/hook-auto-arm +0 -51
- package/scripts/hook-bash-gate +0 -78
- package/scripts/hook-code-gate +0 -38
- package/scripts/hook-continue +0 -48
- package/scripts/hook-create-gate +0 -51
- package/scripts/hook-edit-gates +0 -58
- package/scripts/hook-future-intent-check +0 -90
- package/scripts/hook-gate-check +0 -169
- package/scripts/hook-links-gate +0 -54
- package/scripts/hook-lock-gate +0 -47
- package/scripts/hook-power-tools +0 -38
- package/scripts/hook-savepoint-pre +0 -32
- package/scripts/lib/codex_edit_gates.rb +0 -138
- package/scripts/lib/edit_gates.rb +0 -398
- package/scripts/lib/links_gate.rb +0 -140
- package/scripts/lib/qmd_hook.rb +0 -41
- package/scripts/lib/spec_header.rb +0 -83
- package/scripts/lib/start_intent.rb +0 -296
- package/scripts/start-intent +0 -89
- package/skills/_active-intent-gate.md +0 -26
- package/skills/auto/references/tiers.md +0 -100
- package/skills/continuing/SKILL.md +0 -34
- package/skills/continuing/evals/evals.json +0 -91
- package/skills/conventions/references/gates-and-enforcement.md +0 -53
- package/skills/conventions/references/tiers-and-dispatch.md +0 -135
- package/skills/doctor/references/gates-stuck-detection.md +0 -43
- package/skills/intent-brainstorming/SKILL.md +0 -118
- package/skills/intent-brainstorming/evals/evals.json +0 -67
- package/skills/intent-continuing/evals/evals.json +0 -145
- package/skills/intent-discovering/SKILL.md +0 -56
- package/skills/intent-grilling/SKILL.md +0 -108
- package/skills/intent-linking/SKILL.md +0 -128
- package/skills/intent-linking/evals/evals.json +0 -22
- package/skills/intent-linking/references/zettelkasten.md +0 -45
- package/skills/intent-locking/SKILL.md +0 -64
- package/skills/intent-planning/SKILL.md +0 -145
- package/skills/intent-planning/evals/evals.json +0 -81
- package/skills/intent-planning/references/plan-format.md +0 -130
- package/skills/intent-researching/SKILL.md +0 -123
- package/skills/intent-researching/evals/evals.json +0 -22
- package/skills/intent-savepoint/SKILL.md +0 -83
- package/skills/intent-savepoint/references/context-management.md +0 -32
- package/skills/intent-starting/SKILL.md +0 -151
- package/skills/intent-starting/evals/evals.json +0 -117
- package/skills/intent-starting/references/boarding-matrix.md +0 -35
- package/skills/project-continuing/SKILL.md +0 -119
- package/skills/project-continuing/evals/evals.json +0 -100
- package/skills/roadmap-continuing/SKILL.md +0 -89
- package/skills/roadmap-continuing/evals/evals.json +0 -82
- package/skills/skill-creating/SKILL.md +0 -75
- package/skills/skill-creating/evals/evals.json +0 -108
- package/skills/skill-creating/references/agents.md +0 -168
- package/skills/skill-creating/references/defaults-first.md +0 -23
- package/skills/skill-creating/references/evals.md +0 -41
- package/skills/skill-creating/references/hooks.md +0 -251
- package/skills/skill-creating/references/progressive-disclosure.md +0 -176
- package/skills/skill-creating/references/scripts.md +0 -166
- package/skills/skill-creating/references/skills.md +0 -169
- package/skills/skill-creating/scripts/scaffold.rb +0 -313
- package/skills/skill-evaluating/SKILL.md +0 -141
- package/skills/skill-evaluating/assets/eval-template.json +0 -12
- package/skills/skill-evaluating/evals/evals.json +0 -75
- package/skills/skill-evaluating/references/convention-checks.md +0 -76
- package/skills/skill-evaluating/references/eval-methodology.md +0 -154
- package/skills/store-curating/SKILL.md +0 -64
- package/skills/store-curating/evals/evals.json +0 -38
- package/skills/store-indexing/SKILL.md +0 -93
- package/skills/store-indexing/evals/evals.json +0 -22
- package/skills/store-indexing/references/zettelkasten-linking.md +0 -32
- package/skills/store-provisioning/SKILL.md +0 -55
- /package/skills/{project-continuing → intent-continuing}/references/board-fill.md +0 -0
- /package/skills/{roadmap-continuing → intent-continuing}/references/liveness-ranking.md +0 -0
- /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
|