opencode-thrum-pro 0.1.5 → 0.1.7

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 (90) hide show
  1. package/assets/commands/thrum-compact-extended.md +367 -0
  2. package/assets/commands/thrum-compact.md +301 -0
  3. package/assets/commands/thrum-inbox.md +1 -1
  4. package/assets/commands/thrum-restart-extended.md +85 -16
  5. package/assets/commands/thrum-restart.md +106 -14
  6. package/assets/commands/thrum-send.md +4 -1
  7. package/assets/commands/thrum-sleep-extended.md +68 -51
  8. package/assets/commands/thrum-sleep.md +68 -63
  9. package/assets/commands/thrum-update-project.md +1 -1
  10. package/assets/skills/adversarial-critique/SKILL.md +5 -8
  11. package/assets/skills/brainstorm-steward/SKILL.md +65 -11
  12. package/assets/skills/brainstormer-staying-current/SKILL.md +1 -4
  13. package/assets/skills/choosing-subagent-models/SKILL.md +200 -70
  14. package/assets/skills/closing-findings-while-warm/SKILL.md +1 -2
  15. package/assets/skills/configure-roles/SKILL.md +2 -2
  16. package/assets/skills/conversation-only-context/SKILL.md +11 -11
  17. package/assets/skills/conversation-only-context/resources/driver-subagent-pattern.md +1 -1
  18. package/assets/skills/conversation-only-context/resources/remote-environment-scenarios.md +1 -1
  19. package/assets/skills/coordinator-assessing-agent-completion/SKILL.md +98 -6
  20. package/assets/skills/coordinator-branch-split-on-block/SKILL.md +1 -2
  21. package/assets/skills/coordinator-context-monitoring/SKILL.md +29 -19
  22. package/assets/skills/coordinator-deploying-a-node/SKILL.md +124 -0
  23. package/assets/skills/coordinator-dispatching-work/SKILL.md +26 -14
  24. package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +65 -21
  25. package/assets/skills/coordinator-interacting-with-the-owner/SKILL.md +1 -2
  26. package/assets/skills/coordinator-maintaining-memory/SKILL.md +1 -1
  27. package/assets/skills/coordinator-managing-state-and-lifecycle/SKILL.md +7 -8
  28. package/assets/skills/coordinator-merging-code/SKILL.md +43 -22
  29. package/assets/skills/coordinator-philosophy-merge-gate/SKILL.md +29 -9
  30. package/assets/skills/coordinator-plan-reconcile-gate/SKILL.md +4 -5
  31. package/assets/skills/coordinator-rolling-out-to-nodes/SKILL.md +92 -0
  32. package/assets/skills/coordinator-running-autonomous-feature-development/SKILL.md +3 -3
  33. package/assets/skills/coordinator-running-brainstorm-cycles/SKILL.md +25 -25
  34. package/assets/skills/coordinator-running-review-cycles/SKILL.md +16 -6
  35. package/assets/{commands/thrum-_deviations-protocol.md → skills/deviations-protocol/SKILL.md} +6 -12
  36. package/assets/skills/diagnosing-fleet-and-daemon/SKILL.md +0 -2
  37. package/assets/skills/efficient-multi-agent-research/SKILL.md +3 -3
  38. package/assets/skills/generator-idempotency-protocol/SKILL.md +9 -0
  39. package/assets/skills/implementer-maintaining-memory/SKILL.md +3 -4
  40. package/assets/skills/implementer-orchestrating-execution/SKILL.md +12 -16
  41. package/assets/skills/implementer-receiving-dispatch/SKILL.md +15 -17
  42. package/assets/skills/implementer-receiving-review-feedback/SKILL.md +3 -4
  43. package/assets/skills/implementer-status-and-handoff/SKILL.md +1 -14
  44. package/assets/skills/implementer-tdd-and-quality/SKILL.md +2 -2
  45. package/assets/skills/memory-curate/SKILL.md +1 -1
  46. package/assets/skills/memory-write-discipline/SKILL.md +1 -1
  47. package/assets/skills/orchestrate/SKILL.md +77 -36
  48. package/assets/skills/persistent-watcher-archetype/SKILL.md +172 -25
  49. package/assets/skills/persistent-watcher-archetype/resources/capture_archive.py +618 -0
  50. package/assets/skills/persistent-watcher-archetype/resources/jev_watcher_filter.py +837 -0
  51. package/assets/skills/persistent-watcher-archetype/resources/roster-watch.sh +1140 -0
  52. package/assets/skills/persistent-watcher-archetype/resources/roster-watch_test.sh +291 -0
  53. package/assets/skills/persistent-watcher-archetype/resources/thrum-capture-fallback.sh +2 -2
  54. package/assets/skills/persistent-watcher-archetype/resources/thrum-watch-pane-capture.sh +20 -8
  55. package/assets/skills/prime-agent/SKILL.md +10 -0
  56. package/assets/skills/project-hotpath-gate/SKILL.md +1 -2
  57. package/assets/skills/project-philosophy/SKILL.md +1 -2
  58. package/assets/skills/project-rules-protocol/SKILL.md +12 -0
  59. package/assets/skills/project-setup/SKILL.md +27 -22
  60. package/assets/skills/project-setup/resources/implementation-agent.md +55 -35
  61. package/assets/skills/researcher-answering-queries/SKILL.md +1 -2
  62. package/assets/skills/researcher-investigating/SKILL.md +8 -13
  63. package/assets/skills/researcher-maintaining-memory/SKILL.md +1 -1
  64. package/assets/{commands/thrum-_snapshot-protocol.md → skills/snapshot-protocol/SKILL.md} +27 -21
  65. package/assets/{commands/thrum-_stamp-protocol.md → skills/stamp-protocol/SKILL.md} +14 -8
  66. package/assets/skills/thrum/SKILL.md +14 -7
  67. package/assets/skills/thrum/resources/ANTI_PATTERNS.md +16 -18
  68. package/assets/skills/thrum/resources/CLI_REFERENCE.md +17 -11
  69. package/assets/skills/thrum/resources/LISTENER_PATTERN.md +9 -3
  70. package/assets/skills/thrum/resources/MESSAGING.md +18 -11
  71. package/assets/skills/thrum/resources/WORKTREES.md +1 -1
  72. package/assets/skills/thrum-memory/SKILL.md +1 -1
  73. package/assets/skills/trace-spec-to-plan/resources/sweep-agent-prompt.md +1 -1
  74. package/assets/skills/update-queue/SKILL.md +6 -8
  75. package/assets/skills/using-the-dashboard/SKILL.md +30 -0
  76. package/assets/skills/using-the-queue/SKILL.md +76 -5
  77. package/assets/skills/using-thrum-state/SKILL.md +85 -2
  78. package/assets/skills/verify-against-plan/SKILL.md +122 -7
  79. package/assets/skills/watcher-archetype/SKILL.md +18 -0
  80. package/assets/skills/writing-durable-markdown/SKILL.md +5 -0
  81. package/dist/installer-assets.test.js +41 -0
  82. package/dist/installer.js +4 -4
  83. package/dist/permission-fixture.integration.test.js +4 -4
  84. package/package.json +1 -1
  85. package/assets/commands/thrum-_generator-idempotency-protocol.md +0 -15
  86. package/assets/commands/thrum-_project-rules-protocol.md +0 -18
  87. package/assets/commands/thrum-_verify-before-write-protocol.md +0 -27
  88. package/assets/skills/coordinator-deploying-the-fleet/SKILL.md +0 -396
  89. package/dist/allowlist-sync.test.js +0 -44
  90. /package/dist/{allowlist-sync.test.d.ts → installer-assets.test.d.ts} +0 -0
@@ -0,0 +1,367 @@
1
+ ---
2
+ description:
3
+ Stage a comprehensive 16-section snapshot, verify it landed, then send /compact
4
+ to this agent's OWN tmux pane so it stays alive and resumes at low context. Use
5
+ for designer/architect-grade work needing wire contracts, capability matrix,
6
+ and design rationale that the standard /thrum:compact format cannot carry.
7
+ ---
8
+
9
+ # Compact — Extended (16-section snapshot)
10
+
11
+ Compose a comprehensive 16-section prose continuation, write it directly to your
12
+ restart file, verify it landed, then send `/compact` to your own tmux pane. Same
13
+ in-place semantics as `/thrum:compact` — the agent stays ALIVE,
14
+ sub-agents/loops/crons survive (confirmed empirically), no session end, no tmux
15
+ kill. The only difference is snapshot grade.
16
+
17
+ ## When to use extended vs standard
18
+
19
+ - **Use `/thrum:compact` (standard)** for routine in-place context relief where
20
+ post-compact you can reconstruct from the compaction summary + a compact
21
+ 11-section snapshot.
22
+ - **Use `/thrum:compact-extended` (this variant)** for designer/architect-grade
23
+ work: a complex brainstorm with multiple owner-decided forks, a fanout
24
+ implementation (≥3 call sites or ≥2 epics), or any compaction where the
25
+ post-compact summary is likely to drop wire-contract precision you cannot
26
+ cheaply re-derive.
27
+
28
+ ## Steps
29
+
30
+ ### 1. Verify tmux session (Tier 1 pre-check; run BEFORE composing anything)
31
+
32
+ ```bash
33
+ # compact SENDS /compact to your own pane, so a non-tmux agent has no pane to
34
+ # target. Fail fast before you spend a snapshot composing.
35
+ SESSION_RAW=$(thrum whoami --field tmux_session)
36
+ if [ -z "$SESSION_RAW" ]; then
37
+ echo "ERROR: the compact-extended command requires a tmux-managed agent session (tmux_session field is empty)."
38
+ echo "For a non-tmux agent, run /compact yourself and Read your snapshot by hand afterward."
39
+ exit 1
40
+ fi
41
+ ```
42
+
43
+ If `tmux_session` is empty: ABORT before composing. Exit code 1.
44
+
45
+ ### 2. Read the shared snapshot-composition partial
46
+
47
+ **First, persist state + queue.** Update your personal state
48
+ (`thrum state set --kind personal_state ...` — see the `using-thrum-state`
49
+ skill) and your committed work (`thrum queue` — see the `using-the-queue`
50
+ skill) BEFORE composing your snapshot below — these survive compaction
51
+ independently of the prose continuation file and are what a post-compact
52
+ render reads back.
53
+
54
+ Invoke the /thrum:snapshot-protocol skill and follow it, starting with its
55
+ Step 1 (resolve your worktree ONLY via `thrum agent worktree --authoritative` —
56
+ NEVER `thrum whoami --field worktree` or `git rev-parse --show-toplevel`, both
57
+ cwd-resolved and the root cause of a documented snapshot-save loss class;
58
+ there is no git fallback).
59
+
60
+ Apply its Step 2 (compose your continuation) per the structure guidance.
61
+
62
+ **Use the EXTENDED 16-section structure.** The structure block is `§1.` through
63
+ `§16.` with per-section guidance documented in the partial.
64
+
65
+ **Note on §3 framing:** For a compact snapshot, §3 frames as "where work stands
66
+ at compaction" — you are continuing in place, not completing and not parking.
67
+
68
+ **Your snapshot's FIRST line MUST be the compact resume header**, above §1. It
69
+ survives even if post-compact orientation fails, so it is read-first and
70
+ self-contained:
71
+
72
+ > 🔴 ON RESUME — you were COMPACTED, not restarted. Daemon binding is intact
73
+ > (subagents/loops/crons survived). Resume LEAN: this file is delivered inline in the injected briefing (read it FIRST). Expect a
74
+ > `thrum prime --light` briefing already auto-injected by the SessionStart
75
+ > hook; only run `thrum:prime-agent` if it did NOT appear. Do NOT manually run
76
+ > full `thrum prime`. Then continue from §16.
77
+ > If you must re-read this file (no briefing appeared): read the NEWEST
78
+ > `.thrum/agents/<your-id>/sessions/*-restart.md` in the MAIN repo (the prime
79
+ > ARCHIVES the snapshot there); fall back to `.thrum/restart/<your-id>.md` only
80
+ > if it still exists. Find the newest with:
81
+ > `R=$(git rev-parse --show-toplevel); ls -1t "$(cat "$R/.thrum/redirect" 2>/dev/null || echo "$R/.thrum")"/agents/<your-id>/sessions/*-restart.md | head -1`
82
+ > (`$R/.thrum/redirect` in a worktree names the main repo's `.thrum`; using `$R`
83
+ > keeps this independent of your current directory). Open it with the Read tool
84
+ > (not Bash sed/cat). The `sessions/` file lives under the repo's `.thrum`, so
85
+ > that is allowed.
86
+
87
+ Then §1…§16 as normal, with §16 (immediate next actions) actionable from a
88
+ compacted (not cold) start.
89
+
90
+ ### 3. Write the continuation
91
+
92
+ First run this PREP block (it mints the path and a per-run nonce), then use the
93
+ Write tool per Step 3 of the partial:
94
+
95
+ ```bash
96
+ # The Bash tool does NOT persist shell state across calls, so this block
97
+ # re-resolves identity itself. Fail closed: no cwd/git fallback.
98
+ REPO=$(
99
+ thrum agent worktree --authoritative 2>/dev/null
100
+ ) || { echo "ERROR: cannot authoritatively resolve your worktree via the daemon. Refusing to fall back to cwd/git-toplevel — that would risk silently saving this snapshot to the wrong repo. Check daemon connectivity and retry."; exit 1; }
101
+ [ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
102
+ AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
103
+ [ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
104
+ mkdir -p "${REPO}/.thrum/restart"
105
+ SNAPSHOT="${REPO}/.thrum/restart/${AGENT}.md"
106
+ NONCE="zn6fx-$(date +%s)-$$-${RANDOM}${RANDOM}"
107
+ echo "SNAPSHOT_PATH=${SNAPSHOT}"
108
+ echo "SNAPSHOT_NONCE=${NONCE}"
109
+ ```
110
+
111
+ Write to EXACTLY the printed `SNAPSHOT_PATH` (not a path you re-derive), and make
112
+ sure the file contains the marker below (as its final line), with the printed `SNAPSHOT_NONCE` — verify searches the whole file for it:
113
+
114
+ ```text
115
+ <!-- snapshot-nonce: <SNAPSHOT_NONCE> -->
116
+ ```
117
+
118
+ **Record both values** — the verify step pastes them into its verify block, which
119
+ fails closed unless the file at that exact path carries that nonce.
120
+
121
+ This file lives on disk and is UNAFFECTED by compaction — compaction rewrites
122
+ your conversation context, never your worktree. So the snapshot is readable after
123
+ `/compact` fires. This matters more for an extended snapshot: you are staging
124
+ wire contracts, a capability matrix, and design rationale that are expensive to
125
+ reconstruct and exist nowhere else. There is no durable-backup step (the worktree
126
+ is not torn down, so `/thrum:sleep-extended`'s teardown-loss risk does not apply).
127
+
128
+ ### 4. Verify your snapshot, then fire /compact (ONE block)
129
+
130
+ > ✅ **Use the SINGLE-call self-send form below — it is required, not stylistic.**
131
+ > `thrum tmux send` (OD-5 = B; supersedes `thrum tmux key`,
132
+ > which is being retired) takes exactly ONE client call: the daemon
133
+ > types the text, waits a settle gap, checks for a pending dialog, and only then
134
+ > sends the confirming Enter — all server-side, in that order, within the SAME
135
+ > call. There is no separate `key --type` + `key Enter` pair to race, because
136
+ > there is no second client call: a client-side race is structurally impossible
137
+ > here, not merely avoided by convention.
138
+ > 🔴 **Never issue two separate calls (one for the text, one for Enter)** — that
139
+ > shape is what the retired `tmux key` two-step form did, and it is what
140
+ > produced a truncated `/compac` → invalid command → silent no-op (measured).
141
+ > A single `thrum tmux send "$SESSION" "<command>"` call has no such split to
142
+ > make.
143
+
144
+ Firing `/compact` before the Step 3 Write lands compacts a pre-write context and
145
+ resumes from a stale/blank file — the exact failure this command prevents. This
146
+ block (paste the two values printed by the Step 3 PREP block into its first two
147
+ lines) re-resolves identity (the Bash tool does NOT persist shell state across
148
+ calls, so earlier steps' variables are gone), verifies the snapshot on disk, and
149
+ only then fires `/compact` — all in ONE call so nothing depends on a prior block.
150
+
151
+ ```bash
152
+ SNAPSHOT="<PASTE SNAPSHOT_PATH FROM STEP 3>"
153
+ NONCE="<PASTE SNAPSHOT_NONCE FROM STEP 3>"
154
+ # Fresh authoritative re-resolution; must agree with the path you actually wrote.
155
+ REPO=$(
156
+ thrum agent worktree --authoritative 2>/dev/null
157
+ ) || { echo "ERROR: cannot authoritatively resolve your worktree via the daemon. Refusing to fall back to cwd/git-toplevel — that would risk silently resuming from the wrong repo's snapshot. Check daemon connectivity and retry."; exit 1; }
158
+ [ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
159
+ AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
160
+ [ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
161
+ SESSION_RAW=$(thrum whoami --field tmux_session)
162
+ SESSION=${SESSION_RAW%%:*}
163
+ EXPECTED="${REPO}/.thrum/restart/${AGENT}.md"
164
+ BIND_OK=1
165
+ if [ -z "$SNAPSHOT" ] || [ -z "$NONCE" ] || [[ "$SNAPSHOT$NONCE" == *"<PASTE"* ]]; then
166
+ echo "VERIFY FAILED: SNAPSHOT/NONCE not filled in — paste SNAPSHOT_PATH and SNAPSHOT_NONCE from Step 3. SNAPSHOT_OK=0"; BIND_OK=0
167
+ elif [ "$SNAPSHOT" != "$EXPECTED" ]; then
168
+ echo "VERIFY FAILED: identity/worktree DRIFT between write time and verify time. Written path: $SNAPSHOT — re-resolved path: $EXPECTED. SNAPSHOT_OK=0"; BIND_OK=0
169
+ fi
170
+
171
+ # mtime: choose the stat dialect explicitly. GNU `stat -f` means --file-system
172
+ # (wrong, multiline) — the `stat -f %m . || stat -c %Y` one-liner is fragile on
173
+ # Linux, so branch on which dialect this box speaks.
174
+ if stat -f %m . >/dev/null 2>&1; then
175
+ MTIME=$(
176
+ stat -f %m "$SNAPSHOT" 2>/dev/null
177
+ )
178
+ else
179
+ MTIME=$(
180
+ stat -c %Y "$SNAPSHOT" 2>/dev/null
181
+ )
182
+ fi
183
+ NOW=$(date +%s)
184
+ AGE=$(( NOW - ${MTIME:-0} ))
185
+
186
+ SNAPSHOT_OK=1
187
+ if [ "$BIND_OK" = 0 ]; then
188
+ SNAPSHOT_OK=0 # binding failure already reported above
189
+ elif [ ! -s "$SNAPSHOT" ] || [ ! -r "$SNAPSHOT" ]; then
190
+ echo "VERIFY FAILED: snapshot missing/empty/unreadable at $SNAPSHOT"; SNAPSHOT_OK=0
191
+ elif [ "$AGE" -gt 300 ]; then
192
+ echo "VERIFY FAILED: snapshot is ${AGE}s old — stale (prior write), not this session's"; SNAPSHOT_OK=0
193
+ elif ! grep -qF -- "$NONCE" "$SNAPSHOT"; then
194
+ echo "VERIFY FAILED: nonce not found in $SNAPSHOT — file at that path is not this session's write"; SNAPSHOT_OK=0
195
+ else
196
+ echo "VERIFY OK: snapshot non-empty, readable, written ${AGE}s ago"
197
+ fi
198
+
199
+ SESSION_OK=1
200
+ if [ -n "$SESSION" ] && tmux has-session -t "$SESSION" 2>/dev/null; then
201
+ echo "VERIFY OK: tmux session '$SESSION' resolves"
202
+ else
203
+ echo "VERIFY FAILED: tmux session '$SESSION' (from '$SESSION_RAW') does not resolve"; SESSION_OK=0
204
+ fi
205
+
206
+ if [ "$SNAPSHOT_OK" = 1 ] && [ "$SESSION_OK" = 1 ]; then
207
+ # ONE call: `thrum tmux send` queues this text+Enter delivery on the daemon
208
+ # (internal/daemon/rpc.HandleQueue -> sendQueuedCommand). The daemon waits
209
+ # for your own pane to go idle (this turn ending is what makes it idle —
210
+ # emit nothing further after this call), types the command, pauses a settle
211
+ # gap, checks the pane for a pending dialog, and ONLY THEN sends Enter. If a
212
+ # dialog is showing at that moment, the daemon withholds Enter itself
213
+ # (queuedPaneShowsDialogFn / ErrPaneShowsDialog) — a HOLD happens
214
+ # server-side, automatically, with no separate client-side check needed
215
+ # here. There is no second client call for Enter, so there is nothing to
216
+ # race: the old two-step `key --type` + `key Enter` truncation class
217
+ # (measured: a timed-out type left a separate Enter to submit "/compac")
218
+ # cannot occur when text+Enter are ONE call.
219
+ #
220
+ # This dispatch is ASYNC, not instant: it typically fires within a few
221
+ # seconds of your pane going idle, not in the same instant this call
222
+ # returns. That is expected — you are not emitting anything further this
223
+ # turn regardless, so the delay costs nothing. A NONZERO exit here means
224
+ # the daemon never even queued the command (unreachable daemon, rejected
225
+ # call, etc.) — the async delivery described above will never happen at
226
+ # all, so this is caught synchronously, not inferred later from silence.
227
+ thrum tmux send "$AGENT" "/compact"
228
+ SEND_RC=$?
229
+ # Exit 3 = HELD, exit 4 = VERDICT UNKNOWN (thrum tmux send --help). In both
230
+ # the "/compact" text is ALREADY TYPED into this pane and Enter was withheld
231
+ # or unconfirmed. NEVER fall through to the raw retry below: it would append a
232
+ # second "/compact" to the typed one ("/compact/compact") and press Enter into
233
+ # whatever dialog the daemon just judged to be open. Surface it and stop.
234
+ if [ "$SEND_RC" -eq 3 ] || [ "$SEND_RC" -eq 4 ]; then
235
+ echo "HOLDING: thrum tmux send exited ${SEND_RC} (3=Enter WITHHELD, 4=verdict unknown) — /compact is typed but not confirmed submitted. NOT retrying."
236
+ echo "Report this to your coordinator (or the operator if you are top-level) and stop; a human or coordinator must inspect the pane and submit or clear it."
237
+ exit 1
238
+ fi
239
+ RESUME_PROMPT="Compaction complete - please continue: your Resume Plan is in the auto-injected briefing (# Previous Session Context). If it is missing, read ${SNAPSHOT} or the newest *-restart.md in .thrum/agents/${AGENT}/sessions/ of the main repo, then execute its numbered resume plan."
240
+ if [ "$SEND_RC" -ne 0 ]; then
241
+ echo "ERROR: thrum tmux send exited ${SEND_RC} — the daemon-routed self-send failed; it will NOT queue or deliver /compact."
242
+ # Fail-loud fallback, NOT silent idle: attempt the compact command
243
+ # directly via a raw tmux send-keys call, but only after asserting an
244
+ # EXPLICIT socket. Never a bare/default-socket tmux operation here —
245
+ # TMUX_TMPDIR does not reliably isolate a fleet box's tmux server from
246
+ # the DEFAULT one, and a bare op under that condition has previously
247
+ # taken down a shared fleet server. $TMUX is the one source that is not
248
+ # a guess: tmux itself sets it, in THIS exact pane's own shell
249
+ # environment, to "<socket_path>,<server_pid>,<window_index>" — so
250
+ # splitting it gives the socket this pane is ACTUALLY connected
251
+ # through, not an assumed default.
252
+ RAW_TMUX_SOCK="${TMUX%%,*}"
253
+ if [ -z "$RAW_TMUX_SOCK" ] || [ ! -S "$RAW_TMUX_SOCK" ]; then
254
+ echo "HOLDING: cannot resolve/verify this pane's own tmux socket (\$TMUX unset, or the socket path is stale/missing) — refusing a bare-default-socket tmux fallback (fleet-safety)."
255
+ echo "Report the ERROR line above to your coordinator (or the operator if you are top-level) and stop."
256
+ exit 1
257
+ fi
258
+ if ! tmux -S "$RAW_TMUX_SOCK" has-session -t "$SESSION" 2>/dev/null; then
259
+ echo "HOLDING: session '$SESSION' does not resolve on socket $RAW_TMUX_SOCK — refusing the raw fallback."
260
+ echo "Report the ERROR line above to your coordinator (or the operator if you are top-level) and stop."
261
+ exit 1
262
+ fi
263
+ echo "Retrying via raw tmux send-keys on explicit socket $RAW_TMUX_SOCK..."
264
+ # ONE call: text + the Enter keystroke (C-m) together, same
265
+ # single-call discipline as the daemon-routed path above — a two-call
266
+ # split (send text, THEN a separate Enter call) is the exact shape
267
+ # that produced a truncated "/compac" -> invalid command -> silent
268
+ # no-op in the prior incident this file already documents. Do not
269
+ # split this into two `tmux send-keys` invocations.
270
+ tmux -S "$RAW_TMUX_SOCK" send-keys -t "${SESSION}:0.0" "/compact" C-m
271
+ FALLBACK_RC=$?
272
+ if [ "$FALLBACK_RC" -ne 0 ]; then
273
+ echo "HOLDING: raw tmux send-keys fallback ALSO failed (exit ${FALLBACK_RC})."
274
+ echo "Report the ERROR line(s) above to your coordinator (or the operator if you are top-level) and stop."
275
+ exit 1
276
+ fi
277
+ echo "Raw fallback send-keys succeeded on socket $RAW_TMUX_SOCK."
278
+ # The daemon route is known-dead here (that is why this is the
279
+ # fallback), and on several runtimes no hook fires at all after
280
+ # compaction — so queue the resume prompt on the same explicit socket,
281
+ # same single-call text+C-m discipline. Without this the pane idles
282
+ # after compaction with an empty composer until a human types.
283
+ tmux -S "$RAW_TMUX_SOCK" send-keys -t "${SESSION}:0.0" "$RESUME_PROMPT" C-m
284
+ RAW_RESUME_RC=$?
285
+ if [ "$RAW_RESUME_RC" -eq 0 ]; then
286
+ :
287
+ else
288
+ echo "WARNING: raw resume-prompt send-keys exited ${RAW_RESUME_RC} — the post-compact resume prompt was NOT queued. If the pane sits idle after compaction, send it a resume prompt by hand."
289
+ fi
290
+ fi
291
+ # resume-prompt: queue the RESUME PROMPT right behind /compact — on the daemon
292
+ # queue when the daemon-routed send succeeded (dispatched once the pane is
293
+ # idle again AFTER the compaction, FIFO behind /compact: not a timer, fires
294
+ # exactly once), or via the raw explicit-socket send above when the raw
295
+ # fallback carried /compact. Hook output is
296
+ # context only — it does not start a turn — so without this an idle pane with
297
+ # an empty inbox can sit at 0% context, empty composer, until a human types.
298
+ # The daemon dispatches it once the pane is idle again AFTER the compaction
299
+ # (FIFO behind /compact), so it is not a timer and it fires exactly once.
300
+ if [ "$SEND_RC" -eq 0 ]; then
301
+ thrum tmux send "$AGENT" "$RESUME_PROMPT"
302
+ RESUME_RC=$?
303
+ if [ "$RESUME_RC" -eq 0 ]; then
304
+ :
305
+ else
306
+ echo "WARNING: thrum tmux send exited ${RESUME_RC} — the post-compact resume prompt was NOT queued (exit 3/4 = already typed, do NOT retype). If the pane sits idle after compaction, send it a resume prompt by hand."
307
+ fi
308
+ fi
309
+ else
310
+ echo "HOLDING: not firing /compact. Report the VERIFY FAILED line(s) above to"
311
+ echo "your coordinator (or the operator if you are top-level) and stop."
312
+ exit 1
313
+ fi
314
+ ```
315
+
316
+ **Fail-closed binding rule:** the block only passes for the exact path and nonce
317
+ the Step 3 PREP block printed. It refuses (VERIFY FAILED, no `/compact`) if the
318
+ values are empty or still placeholders, if a fresh re-resolution of your identity
319
+ yields a different path (drift), or if the file at that path lacks the nonce — a
320
+ fresh file merely sitting at the re-derived path is NOT proof you wrote it.
321
+
322
+ If EITHER check fails, the block HOLDS and does not fire `/compact` — holding is
323
+ safe; compacting against an unverified snapshot loses the context this command
324
+ exists to preserve (for an extended snapshot, wire contracts and design
325
+ rationale). If `thrum tmux send` exits 3 (Enter WITHHELD) or 4 (verdict
326
+ unknown), `/compact` is already typed in the pane: the block holds and does NOT
327
+ retype it. If BOTH checks pass but `thrum tmux send` itself exits nonzero (any
328
+ other code), the block does NOT go idle un-compacted silently — it prints the failure and falls
329
+ back to a raw, explicit-socket `tmux send-keys` retry (single call, text +
330
+ Enter together) before holding for real. After a successful send (daemon-routed
331
+ or raw fallback), the turn ends; emit no further tool calls or prose this turn.
332
+ The daemon-routed send is queued, not synchronous — it fires once your pane goes
333
+ idle, which only happens if you stop acting now; the raw fallback, when it runs,
334
+ is synchronous.
335
+
336
+ ## How resume works
337
+
338
+ After `/compact` the SAME process and session survive — the daemon binding is
339
+ intact (that is why your sub-agents, loops, and crons kept running). Full
340
+ `thrum prime` rebuilds identity + binding + full briefing for a NEW session; none
341
+ of that is needed here. Resume LEAN, in this order:
342
+
343
+ 1. **Your Resume Plan arrives inline — do not go looking for the file.** The
344
+ `SessionStart` hook auto-injects a `thrum prime --light` briefing zero-turn,
345
+ and its `# Previous Session Context` section carries your full snapshot,
346
+ including the `ON RESUME` header; execute §16 from it. That prime ARCHIVES the
347
+ snapshot (moves `${REPO}/.thrum/restart/${AGENT}.md` into the main repo's
348
+ `.thrum/agents/${AGENT}/sessions/`), so the original path is normally GONE
349
+ by the time you look — that is by design, not loss. Only if no briefing
350
+ appeared (daemon unreachable), read the NEWEST `*-restart.md` in that
351
+ `sessions/` directory, e.g. `ls -1t "$(cat "${REPO}/.thrum/redirect" 2>/dev/null || echo "${REPO}/.thrum")"/agents/${AGENT}/sessions/*-restart.md | head -1`
352
+ (fall back to `${REPO}/.thrum/restart/${AGENT}.md` only if it still
353
+ exists; use the Read tool, not Bash sed/cat, on `~/.claude` paths) and run `thrum:prime-agent` manually as the fallback. Never manually
354
+ run full `thrum prime` when the briefing is present.
355
+ 2. **A resume prompt starts your first turn.** Step 4 queued a "Compaction
356
+ complete - please continue" prompt on the daemon right behind `/compact`; it
357
+ is delivered once the pane is idle after compaction, so a turn starts even
358
+ with an empty inbox.
359
+ 3. **Continue** from §16. Reconnect to your still-live sub-agents, loops, and
360
+ crons rather than re-dispatching.
361
+
362
+ Runtime-specific compaction-recovery hooks (if this runtime has any) are
363
+ documented in that runtime's own plugin tree (its hooks manifest and
364
+ the scripts it points to), not here.
365
+
366
+ **Follow the Resume Plan in your injected `# Previous Session Context` (snapshot
367
+ saved at `${REPO}/.thrum/restart/${AGENT}.md`, archived by the prime) post-compact.**
@@ -0,0 +1,301 @@
1
+ ---
2
+ description:
3
+ Stage a standard 11-section snapshot, verify it landed, then send /compact to
4
+ this agent's OWN tmux pane so it stays alive (subagents, loops, crons survive)
5
+ and resumes at low context. Use for in-place context relief when you do NOT
6
+ want to kill the session.
7
+ ---
8
+
9
+ # Compact — Snapshot Then Compact In Place
10
+
11
+ Compose a standard 11-section prose continuation, write it directly to your
12
+ restart file, verify it landed, then send `/compact` to your own tmux pane.
13
+ Unlike `/thrum:restart` and `/thrum:sleep`, compact does NOT end your session and
14
+ does NOT kill your tmux pane — the agent stays ALIVE. Any sub-agents, background
15
+ loops, and cron schedules you own keep running across the compaction (confirmed
16
+ empirically). After compaction you Read your snapshot by hand and re-prime lean.
17
+
18
+ ## When to Use
19
+
20
+ - Your context is high and you want relief WITHOUT losing the live session —
21
+ you own sub-agents, a background loop, or a cron that must survive.
22
+ - You are mid-task and a fresh session (which loses in-flight sub-agent handles
23
+ and scheduler state) would be more expensive than a compaction.
24
+
25
+ For context-exhaustion or stuck-state cases where a FRESH session is the right
26
+ answer — or where the coordinator should move you — use `/thrum:restart`. For
27
+ operator-shutdown parking that kills the session, use `/thrum:sleep`. Compact is
28
+ the only one of the three that keeps the runtime process alive.
29
+
30
+ ## Steps
31
+
32
+ ### 1. Verify tmux session (Tier 1 pre-check; run BEFORE composing anything)
33
+
34
+ ```bash
35
+ # compact SENDS /compact to your own pane, so a non-tmux agent has no pane to
36
+ # target. Fail fast before you spend a snapshot composing.
37
+ SESSION_RAW=$(thrum whoami --field tmux_session)
38
+ if [ -z "$SESSION_RAW" ]; then
39
+ echo "ERROR: the compact command requires a tmux-managed agent session (tmux_session field is empty)."
40
+ echo "For a non-tmux agent, run /compact yourself and Read your snapshot by hand afterward."
41
+ exit 1
42
+ fi
43
+ ```
44
+
45
+ If `tmux_session` is empty: ABORT before composing. Exit code 1.
46
+
47
+ ### 2. Read the shared snapshot-composition partial
48
+
49
+ **First, persist state + queue.** Update your personal state
50
+ (`thrum state set --kind personal_state ...` — see the `using-thrum-state`
51
+ skill) and your committed work (`thrum queue` — see the `using-the-queue`
52
+ skill) BEFORE composing your snapshot below — these survive compaction
53
+ independently of the prose continuation file and are what a post-compact
54
+ render reads back.
55
+
56
+ Invoke the /thrum:snapshot-protocol skill and follow it, starting with its
57
+ Step 1 (resolve your worktree ONLY via `thrum agent worktree --authoritative` —
58
+ NEVER `thrum whoami --field worktree` or `git rev-parse --show-toplevel`, both
59
+ cwd-resolved and the root cause of a documented snapshot-save loss class;
60
+ there is no git fallback).
61
+
62
+ Apply its Step 2 (compose your continuation) per the structure guidance.
63
+
64
+ **Use the STANDARD 11-section structure.** For comprehensive
65
+ designer/architect-grade snapshots, use `/thrum:compact-extended` instead.
66
+
67
+ **Note on §1 framing:** For a compact snapshot, §1 frames as "where work stands
68
+ at compaction" — you are continuing in place, not completing and not parking.
69
+
70
+ **Your snapshot's FIRST line MUST be the compact resume header**, above §1. It
71
+ survives even if post-compact orientation fails, so it is read-first and
72
+ self-contained:
73
+
74
+ > 🔴 ON RESUME — you were COMPACTED, not restarted. Daemon binding is intact
75
+ > (subagents/loops/crons survived). Resume LEAN: this file is delivered inline in the injected briefing (read it FIRST). Expect a
76
+ > `thrum prime --light` briefing already auto-injected by the SessionStart
77
+ > hook; only run `thrum:prime-agent` if it did NOT appear. Do NOT manually run
78
+ > full `thrum prime`. Then continue from §9.
79
+ > If you must re-read this file (no briefing appeared): read the NEWEST
80
+ > `.thrum/agents/<your-id>/sessions/*-restart.md` in the MAIN repo (the prime
81
+ > ARCHIVES the snapshot there); fall back to `.thrum/restart/<your-id>.md` only
82
+ > if it still exists. Find the newest with:
83
+ > `R=$(git rev-parse --show-toplevel); ls -1t "$(cat "$R/.thrum/redirect" 2>/dev/null || echo "$R/.thrum")"/agents/<your-id>/sessions/*-restart.md | head -1`
84
+ > (`$R/.thrum/redirect` in a worktree names the main repo's `.thrum`; using `$R`
85
+ > keeps this independent of your current directory). Open it with the Read tool
86
+ > (not Bash sed/cat). The `sessions/` file lives under the repo's `.thrum`, so
87
+ > that is allowed.
88
+
89
+ Then §1…§11 as normal, with §9 (Numbered resume plan) actionable from a
90
+ compacted (not cold) start.
91
+
92
+ ### 3. Write the continuation
93
+
94
+ First run this PREP block (it mints the path and a per-run nonce), then use the
95
+ Write tool per Step 3 of the partial:
96
+
97
+ ```bash
98
+ # The Bash tool does NOT persist shell state across calls, so this block
99
+ # re-resolves identity itself. Fail closed: no cwd/git fallback.
100
+ REPO=$(
101
+ thrum agent worktree --authoritative 2>/dev/null
102
+ ) || { echo "ERROR: cannot authoritatively resolve your worktree via the daemon. Refusing to fall back to cwd/git-toplevel — that would risk silently saving this snapshot to the wrong repo. Check daemon connectivity and retry."; exit 1; }
103
+ [ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
104
+ AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
105
+ [ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
106
+ mkdir -p "${REPO}/.thrum/restart"
107
+ SNAPSHOT="${REPO}/.thrum/restart/${AGENT}.md"
108
+ NONCE="zn6fx-$(date +%s)-$$-${RANDOM}${RANDOM}"
109
+ echo "SNAPSHOT_PATH=${SNAPSHOT}"
110
+ echo "SNAPSHOT_NONCE=${NONCE}"
111
+ ```
112
+
113
+ Write to EXACTLY the printed `SNAPSHOT_PATH` (not a path you re-derive), and make
114
+ sure the file contains the marker below (as its final line), with the printed `SNAPSHOT_NONCE` — verify searches the whole file for it:
115
+
116
+ ```text
117
+ <!-- snapshot-nonce: <SNAPSHOT_NONCE> -->
118
+ ```
119
+
120
+ **Record both values** — the verify step pastes them into its verify block, which
121
+ fails closed unless the file at that exact path carries that nonce.
122
+
123
+ This file lives on disk and is UNAFFECTED by compaction — compaction rewrites
124
+ your conversation context, never your worktree. So the snapshot you write now is
125
+ readable after `/compact` fires. There is no durable-backup step (the worktree is
126
+ not torn down, so `/thrum:sleep`'s teardown-loss risk does not apply here).
127
+
128
+ ### 4. Verify your snapshot, then fire /compact (ONE block)
129
+
130
+ > ✅ **Use the SINGLE-call self-send form below — it is required, not stylistic.**
131
+ > `thrum tmux send` (OD-5 = B; supersedes `thrum tmux key`,
132
+ > which is being retired) takes exactly ONE client call: the daemon
133
+ > types the text, waits a settle gap, checks for a pending dialog, and only then
134
+ > sends the confirming Enter — all server-side, in that order, within the SAME
135
+ > call. There is no separate `key --type` + `key Enter` pair to race, because
136
+ > there is no second client call: a client-side race is structurally impossible
137
+ > here, not merely avoided by convention.
138
+ > 🔴 **Never issue two separate calls (one for the text, one for Enter)** — that
139
+ > shape is what the retired `tmux key` two-step form did, and it is what
140
+ > produced a truncated `/compac` → invalid command → silent no-op (measured).
141
+ > A single `thrum tmux send "$SESSION" "<command>"` call has no such split to
142
+ > make.
143
+
144
+ Firing `/compact` before the Step 3 Write lands compacts a pre-write context and
145
+ resumes from a stale/blank file — the exact failure this command prevents. This
146
+ block (paste the two values printed by the Step 3 PREP block into its first two
147
+ lines) re-resolves identity (the Bash tool does NOT persist shell state across
148
+ calls, so earlier steps' variables are gone), verifies the snapshot on disk, and
149
+ only then fires `/compact` — all in ONE call so nothing depends on a prior block.
150
+
151
+ ```bash
152
+ SNAPSHOT="<PASTE SNAPSHOT_PATH FROM STEP 3>"
153
+ NONCE="<PASTE SNAPSHOT_NONCE FROM STEP 3>"
154
+ # Fresh authoritative re-resolution; must agree with the path you actually wrote.
155
+ REPO=$(
156
+ thrum agent worktree --authoritative 2>/dev/null
157
+ ) || { echo "ERROR: cannot authoritatively resolve your worktree via the daemon. Refusing to fall back to cwd/git-toplevel — that would risk silently resuming from the wrong repo's snapshot. Check daemon connectivity and retry."; exit 1; }
158
+ [ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
159
+ AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
160
+ [ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
161
+ SESSION_RAW=$(thrum whoami --field tmux_session)
162
+ SESSION=${SESSION_RAW%%:*}
163
+ EXPECTED="${REPO}/.thrum/restart/${AGENT}.md"
164
+ BIND_OK=1
165
+ if [ -z "$SNAPSHOT" ] || [ -z "$NONCE" ] || [[ "$SNAPSHOT$NONCE" == *"<PASTE"* ]]; then
166
+ echo "VERIFY FAILED: SNAPSHOT/NONCE not filled in — paste SNAPSHOT_PATH and SNAPSHOT_NONCE from Step 3. SNAPSHOT_OK=0"; BIND_OK=0
167
+ elif [ "$SNAPSHOT" != "$EXPECTED" ]; then
168
+ echo "VERIFY FAILED: identity/worktree DRIFT between write time and verify time. Written path: $SNAPSHOT — re-resolved path: $EXPECTED. SNAPSHOT_OK=0"; BIND_OK=0
169
+ fi
170
+
171
+ # mtime: choose the stat dialect explicitly. GNU `stat -f` means --file-system
172
+ # (wrong, multiline) — the `stat -f %m . || stat -c %Y` one-liner is fragile on
173
+ # Linux, so branch on which dialect this box speaks.
174
+ if stat -f %m . >/dev/null 2>&1; then
175
+ MTIME=$(
176
+ stat -f %m "$SNAPSHOT" 2>/dev/null
177
+ )
178
+ else
179
+ MTIME=$(
180
+ stat -c %Y "$SNAPSHOT" 2>/dev/null
181
+ )
182
+ fi
183
+ NOW=$(date +%s)
184
+ AGE=$(( NOW - ${MTIME:-0} ))
185
+
186
+ SNAPSHOT_OK=1
187
+ if [ "$BIND_OK" = 0 ]; then
188
+ SNAPSHOT_OK=0 # binding failure already reported above
189
+ elif [ ! -s "$SNAPSHOT" ] || [ ! -r "$SNAPSHOT" ]; then
190
+ echo "VERIFY FAILED: snapshot missing/empty/unreadable at $SNAPSHOT"; SNAPSHOT_OK=0
191
+ elif [ "$AGE" -gt 300 ]; then
192
+ echo "VERIFY FAILED: snapshot is ${AGE}s old — stale (prior write), not this session's"; SNAPSHOT_OK=0
193
+ elif ! grep -qF -- "$NONCE" "$SNAPSHOT"; then
194
+ echo "VERIFY FAILED: nonce not found in $SNAPSHOT — file at that path is not this session's write"; SNAPSHOT_OK=0
195
+ else
196
+ echo "VERIFY OK: snapshot non-empty, readable, written ${AGE}s ago"
197
+ fi
198
+
199
+ SESSION_OK=1
200
+ if [ -n "$SESSION" ] && tmux has-session -t "$SESSION" 2>/dev/null; then
201
+ echo "VERIFY OK: tmux session '$SESSION' resolves"
202
+ else
203
+ echo "VERIFY FAILED: tmux session '$SESSION' (from '$SESSION_RAW') does not resolve"; SESSION_OK=0
204
+ fi
205
+
206
+ if [ "$SNAPSHOT_OK" = 1 ] && [ "$SESSION_OK" = 1 ]; then
207
+ # ONE call: `thrum tmux send` queues this text+Enter delivery on the daemon
208
+ # (internal/daemon/rpc.HandleQueue -> sendQueuedCommand). The daemon waits
209
+ # for your own pane to go idle (this turn ending is what makes it idle —
210
+ # emit nothing further after this call), types the command, pauses a settle
211
+ # gap, checks the pane for a pending dialog, and ONLY THEN sends Enter. If a
212
+ # dialog is showing at that moment, the daemon withholds Enter itself
213
+ # (queuedPaneShowsDialogFn / ErrPaneShowsDialog) — a HOLD happens
214
+ # server-side, automatically, with no separate client-side check needed
215
+ # here. There is no second client call for Enter, so there is nothing to
216
+ # race: the old two-step `key --type` + `key Enter` truncation class
217
+ # (measured: a timed-out type left a separate Enter to submit "/compac")
218
+ # cannot occur when text+Enter are ONE call.
219
+ #
220
+ # This dispatch is ASYNC, not instant: it typically fires within a few
221
+ # seconds of your pane going idle, not in the same instant this call
222
+ # returns. That is expected — you are not emitting anything further this
223
+ # turn regardless, so the delay costs nothing.
224
+ thrum tmux send "$SESSION" "/compact"
225
+ SEND_RC=$?
226
+ # Exit 3 = HELD (Enter withheld), 4 = VERDICT UNKNOWN: "/compact" is already
227
+ # typed in the pane. Do NOT retype it (that yields "/compact/compact"); report
228
+ # and stop so a human/coordinator can inspect the pane.
229
+ if [ "$SEND_RC" -eq 3 ] || [ "$SEND_RC" -eq 4 ]; then
230
+ echo "HOLDING: thrum tmux send exited ${SEND_RC} (3=Enter WITHHELD, 4=verdict unknown) — /compact is typed but not confirmed submitted. NOT retrying."
231
+ echo "Report this to your coordinator (or the operator if you are top-level) and stop."
232
+ exit 1
233
+ elif [ "$SEND_RC" -ne 0 ]; then
234
+ echo "ERROR: thrum tmux send exited ${SEND_RC} — /compact was not queued. Report and stop."
235
+ exit 1
236
+ fi
237
+ # resume-prompt: queue the RESUME PROMPT right behind /compact, on the same
238
+ # daemon queue. Hook output is context only — it does not start a turn — so
239
+ # without this an idle pane with an empty inbox can sit at 0% context, empty
240
+ # composer, until a human types. The daemon dispatches it once the pane is
241
+ # idle again AFTER the compaction (FIFO behind /compact), so it is not a
242
+ # timer and it fires exactly once.
243
+ RESUME_PROMPT="Compaction complete - please continue: your Resume Plan is in the auto-injected briefing (# Previous Session Context). If it is missing, read ${SNAPSHOT} or the newest *-restart.md in .thrum/agents/${AGENT}/sessions/ of the main repo, then execute its numbered resume plan."
244
+ thrum tmux send "$SESSION" "$RESUME_PROMPT"
245
+ RESUME_RC=$?
246
+ if [ "$RESUME_RC" -eq 0 ]; then
247
+ :
248
+ else
249
+ echo "WARNING: thrum tmux send exited ${RESUME_RC} — the post-compact resume prompt was NOT queued (exit 3/4 = already typed, do NOT retype). If the pane sits idle after compaction, send it a resume prompt by hand."
250
+ fi
251
+ else
252
+ echo "HOLDING: not firing /compact. Report the VERIFY FAILED line(s) above to"
253
+ echo "your coordinator (or the operator if you are top-level) and stop."
254
+ exit 1
255
+ fi
256
+ ```
257
+
258
+ **Fail-closed binding rule:** the block only passes for the exact path and nonce
259
+ the Step 3 PREP block printed. It refuses (VERIFY FAILED, no `/compact`) if the
260
+ values are empty or still placeholders, if a fresh re-resolution of your identity
261
+ yields a different path (drift), or if the file at that path lacks the nonce — a
262
+ fresh file merely sitting at the re-derived path is NOT proof you wrote it.
263
+
264
+ If EITHER check fails, the block HOLDS and does not fire `/compact` — holding is
265
+ safe; compacting against an unverified snapshot loses the context this command
266
+ exists to preserve. After a successful send, the turn ends; emit no further tool
267
+ calls or prose this turn. The send is queued, not synchronous — it fires once
268
+ your pane goes idle, which only happens if you stop acting now.
269
+
270
+ ## How resume works
271
+
272
+ After `/compact` the SAME process and session survive — the daemon binding is
273
+ intact (that is why your sub-agents, loops, and crons kept running). Full
274
+ `thrum prime` rebuilds identity + binding + full briefing for a NEW session; none
275
+ of that is needed here. Resume LEAN, in this order:
276
+
277
+ 1. **Your Resume Plan arrives inline — do not go looking for the file.** The
278
+ `SessionStart` hook auto-injects a `thrum prime --light` briefing zero-turn,
279
+ and its `# Previous Session Context` section carries your full snapshot,
280
+ including the `ON RESUME` header; execute §9 from it. That prime ARCHIVES the
281
+ snapshot (moves `${REPO}/.thrum/restart/${AGENT}.md` into the main repo's
282
+ `.thrum/agents/${AGENT}/sessions/`), so the original path is normally GONE
283
+ by the time you look — that is by design, not loss. Only if no briefing
284
+ appeared (daemon unreachable), read the NEWEST `*-restart.md` in that
285
+ `sessions/` directory, e.g. `ls -1t "$(cat "${REPO}/.thrum/redirect" 2>/dev/null || echo "${REPO}/.thrum")"/agents/${AGENT}/sessions/*-restart.md | head -1`
286
+ (fall back to `${REPO}/.thrum/restart/${AGENT}.md` only if it still
287
+ exists; use the Read tool, not Bash sed/cat, on `~/.claude` paths) and run `thrum:prime-agent` manually as the fallback. Never manually
288
+ run full `thrum prime` when the briefing is present.
289
+ 2. **A resume prompt starts your first turn.** Step 4 queued a "Compaction
290
+ complete - please continue" prompt on the daemon right behind `/compact`; it
291
+ is delivered once the pane is idle after compaction, so a turn starts even
292
+ with an empty inbox.
293
+ 3. **Continue** from §9. Reconnect to your still-live sub-agents, loops, and
294
+ crons rather than re-dispatching.
295
+
296
+ Runtime-specific compaction-recovery hooks (if this runtime has any) are
297
+ documented in that runtime's own plugin tree (its hooks manifest and
298
+ the scripts it points to), not here.
299
+
300
+ **Follow the Resume Plan in your injected `# Previous Session Context` (snapshot
301
+ saved at `${REPO}/.thrum/restart/${AGENT}.md`, archived by the prime) post-compact.**