opencode-thrum-pro 0.1.5 → 0.1.6
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/assets/commands/thrum-_compact-runtime-commands.md +61 -0
- package/assets/commands/thrum-_snapshot-protocol.md +17 -6
- package/assets/commands/thrum-_stamp-protocol.md +9 -2
- package/assets/commands/thrum-compact-extended.md +352 -0
- package/assets/commands/thrum-compact.md +300 -0
- package/assets/commands/thrum-inbox.md +1 -1
- package/assets/commands/thrum-restart-extended.md +84 -11
- package/assets/commands/thrum-restart.md +105 -13
- package/assets/commands/thrum-send.md +4 -1
- package/assets/commands/thrum-sleep-extended.md +67 -46
- package/assets/commands/thrum-sleep.md +67 -58
- package/assets/skills/adversarial-critique/SKILL.md +5 -8
- package/assets/skills/brainstorm-steward/SKILL.md +65 -11
- package/assets/skills/brainstormer-staying-current/SKILL.md +1 -4
- package/assets/skills/choosing-subagent-models/SKILL.md +14 -0
- package/assets/skills/conversation-only-context/SKILL.md +11 -11
- package/assets/skills/conversation-only-context/resources/driver-subagent-pattern.md +1 -1
- package/assets/skills/conversation-only-context/resources/remote-environment-scenarios.md +1 -1
- package/assets/skills/coordinator-assessing-agent-completion/SKILL.md +98 -6
- package/assets/skills/coordinator-context-monitoring/SKILL.md +29 -19
- package/assets/skills/coordinator-deploying-a-node/SKILL.md +124 -0
- package/assets/skills/coordinator-dispatching-work/SKILL.md +24 -11
- package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +62 -20
- package/assets/skills/coordinator-managing-state-and-lifecycle/SKILL.md +6 -6
- package/assets/skills/coordinator-merging-code/SKILL.md +25 -11
- package/assets/skills/coordinator-philosophy-merge-gate/SKILL.md +28 -7
- package/assets/skills/coordinator-plan-reconcile-gate/SKILL.md +3 -3
- package/assets/skills/coordinator-rolling-out-to-nodes/SKILL.md +92 -0
- package/assets/skills/coordinator-running-autonomous-feature-development/SKILL.md +3 -3
- package/assets/skills/coordinator-running-brainstorm-cycles/SKILL.md +21 -20
- package/assets/skills/coordinator-running-review-cycles/SKILL.md +15 -4
- package/assets/skills/diagnosing-fleet-and-daemon/SKILL.md +0 -2
- package/assets/skills/efficient-multi-agent-research/SKILL.md +3 -3
- package/assets/skills/implementer-orchestrating-execution/SKILL.md +11 -14
- package/assets/skills/orchestrate/SKILL.md +47 -35
- package/assets/skills/persistent-watcher-archetype/SKILL.md +172 -25
- package/assets/skills/persistent-watcher-archetype/resources/capture_archive.py +618 -0
- package/assets/skills/persistent-watcher-archetype/resources/jev_watcher_filter.py +837 -0
- package/assets/skills/persistent-watcher-archetype/resources/roster-watch.sh +1140 -0
- package/assets/skills/persistent-watcher-archetype/resources/roster-watch_test.sh +291 -0
- package/assets/skills/persistent-watcher-archetype/resources/thrum-capture-fallback.sh +2 -2
- package/assets/skills/persistent-watcher-archetype/resources/thrum-watch-pane-capture.sh +20 -8
- package/assets/skills/prime-agent/SKILL.md +10 -0
- package/assets/skills/project-setup/SKILL.md +22 -16
- package/assets/skills/project-setup/resources/implementation-agent.md +56 -35
- package/assets/skills/researcher-investigating/SKILL.md +2 -5
- package/assets/skills/thrum/SKILL.md +14 -7
- package/assets/skills/thrum/resources/ANTI_PATTERNS.md +16 -18
- package/assets/skills/thrum/resources/CLI_REFERENCE.md +17 -11
- package/assets/skills/thrum/resources/LISTENER_PATTERN.md +9 -3
- package/assets/skills/thrum/resources/MESSAGING.md +18 -11
- package/assets/skills/thrum/resources/WORKTREES.md +1 -1
- package/assets/skills/thrum-memory/SKILL.md +1 -1
- package/assets/skills/trace-spec-to-plan/resources/sweep-agent-prompt.md +1 -1
- package/assets/skills/update-queue/SKILL.md +6 -8
- package/assets/skills/using-the-dashboard/SKILL.md +30 -0
- package/assets/skills/using-the-queue/SKILL.md +76 -5
- package/assets/skills/using-thrum-state/SKILL.md +83 -0
- package/assets/skills/verify-against-plan/SKILL.md +120 -5
- package/assets/skills/watcher-archetype/SKILL.md +18 -0
- package/assets/skills/writing-durable-markdown/SKILL.md +5 -0
- package/dist/installer.js +4 -4
- package/dist/permission-fixture.integration.test.js +4 -4
- package/package.json +1 -1
- package/assets/skills/coordinator-deploying-the-fleet/SKILL.md +0 -396
- package/dist/allowlist-sync.test.d.ts +0 -1
- package/dist/allowlist-sync.test.js +0 -44
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
Per-runtime compact-equivalent command table consumed by compact.md,
|
|
4
|
+
compact-extended.md, and scripts/sync-skills.sh. Not user-invocable
|
|
5
|
+
directly.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Per-Runtime Compact Command Table (shared partial)
|
|
9
|
+
|
|
10
|
+
Consumers: `claude-plugin/commands/compact.md`, `compact-extended.md`
|
|
11
|
+
(reference this table for the command each runtime's synced copy sends), and
|
|
12
|
+
`scripts/sync-skills.sh`'s `compact_command_for_runtime()` (the executable
|
|
13
|
+
mirror of this table — keep the two in lockstep; a row added/changed here
|
|
14
|
+
without a matching case in that function is a defect at review, not a style
|
|
15
|
+
nit).
|
|
16
|
+
|
|
17
|
+
This is the canonical, cited source of truth for what "the compact command"
|
|
18
|
+
means on each runtime thrum drives. Do NOT invent a command for a runtime not
|
|
19
|
+
listed here or not sourced — an unverified guess sent to a runtime's pane is
|
|
20
|
+
worse than no compaction at all (it either no-ops or does something
|
|
21
|
+
unintended). A runtime found to have no equivalent is recorded as such, not
|
|
22
|
+
silently omitted.
|
|
23
|
+
|
|
24
|
+
| Runtime | Compact command | Source | Confidence |
|
|
25
|
+
| ------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------ |
|
|
26
|
+
| `claude` | `/compact` | Claude Code built-in slash command (already established in this project). | Verified — first-party. |
|
|
27
|
+
| `codex` | `/compact` | OpenAI Codex CLI context-compaction architecture (server-side `POST /v1/responses/compact`, manual trigger `/compact`). | Verified — corroborated by two independent technical write-ups; not fetched from `codex --help` directly. |
|
|
28
|
+
| `opencode` | `/compact` (alias `/summarize`) | opencode.ai official docs, keybinds reference — `session_compact` action, default slash command `/compact`. | Verified from official docs. **Caveat:** a differently-named community fork lacks this command — confirm against the specific `opencode` binary before relying on it blindly. |
|
|
29
|
+
| `cursor` | `/summarize` (alias `/compress`) | cursor.com CLI reference, slash-commands page. | Verified from official docs. **Differs from Claude's `/compact`** — this is the one runtime where the sent text must actually change. `/clear` (aliases `/new`, `/new-chat`) is a separate full-reset command, not compaction — do not confuse the two. |
|
|
30
|
+
| `copilot` | `/compact` | docs.github.com, "Managing context in GitHub Copilot CLI". | Verified from official docs. **Not currently shippable**: `copilot-plugin/` has no skills/commands-loading mechanism at all (pre-existing, documented in `scripts/sync-skills.sh`'s header) — this row exists for completeness/future readiness, not because the skill ships there today. Coverage tracked as a build-vs-accept scope call, owner-owned. |
|
|
31
|
+
| `muse` | **NONE FOUND** | musecodes.io official docs — documented commands are `/plan`, `/grill`, `/goal`, `/workflows`, `/model`, `/effort`, `Esc Esc` (rewind), `muse replay`. No manual compact/summarize trigger is documented; marketing copy describing context "compaction" refers to an automatic/internal mechanism only, not a user-invocable command. | Verified absence, not an inferred gap. Per E9 (owner-ruled), muse falls to the `restart-extended` exception — it does not receive compact.md/compact-extended.md. |
|
|
32
|
+
| `gemini` | **UNVERIFIED** | Not researched — no plugin tree exists for this runtime, and it was never included in this table's original research pass. | **Not a confirmed negative.** Go-side `internal/runtime.RuntimePreset` exists for this runtime with `CompactCommand` deliberately left empty (falls back to `/thrum:restart-extended`, never invented). This research is not yet done — do not research here; treat future work on this row as owned elsewhere. |
|
|
33
|
+
| `kiro-cli` | **UNVERIFIED** | Same as `gemini` — not researched, no plugin tree. | Same as `gemini` — empty `CompactCommand`, safe fallback, research pending. |
|
|
34
|
+
| `amp` | **UNVERIFIED** | Same as `gemini` — not researched, no plugin tree. | Same as `gemini` — empty `CompactCommand`, safe fallback, research pending. |
|
|
35
|
+
| `shell` | **UNVERIFIED** | Same as `gemini` — not researched, no plugin tree. | Same as `gemini` — empty `CompactCommand`, safe fallback, research pending. |
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
- `codex` and `opencode` resolve to the same literal string Claude uses
|
|
40
|
+
(`/compact`), so their synced copies are a functional no-op substitution —
|
|
41
|
+
still routed through the same mechanism for consistency and so a future
|
|
42
|
+
table change (e.g. if `opencode`'s fork situation resolves differently)
|
|
43
|
+
propagates without a second code path.
|
|
44
|
+
- `cursor` is the one runtime whose synced copy's sent command differs from
|
|
45
|
+
Claude's and must be verified after every sync run (`grep` step in
|
|
46
|
+
`scripts/sync-skills.sh`'s `compact_command_for_runtime` block).
|
|
47
|
+
- `muse` and `copilot` are both absent from compact/compact-extended's sync
|
|
48
|
+
targets today, for two *different* reasons — `muse` because no native
|
|
49
|
+
command exists to send, `copilot` because the plugin has no delivery
|
|
50
|
+
mechanism at all. Neither is a silent drop: both are recorded here and
|
|
51
|
+
reported in the completion report for this table's original research pass.
|
|
52
|
+
- `gemini`/`kiro-cli`/`amp`/`shell` are recorded as **UNVERIFIED**, not
|
|
53
|
+
merged into the `muse`/`copilot` "no equivalent" rows above — those two are
|
|
54
|
+
confirmed negatives (verified by reading official docs); the four
|
|
55
|
+
UNVERIFIED rows are an absence of research, not an absence of a command.
|
|
56
|
+
`scripts/sync-skills.sh`'s `compact_command_for_runtime()` already returns
|
|
57
|
+
`""` for any of these four via its `*` default case, so the executable
|
|
58
|
+
mirror and this table already agree — no code change is needed to keep
|
|
59
|
+
them in lockstep, only this documentation. See also
|
|
60
|
+
`dev-docs/reference/compaction-lifecycle-attach-points.md` for the same
|
|
61
|
+
four runtimes' hook-attach-point status.
|
|
@@ -21,12 +21,17 @@ terminal action.
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
# $REPO must be YOUR worktree — the directory `thrum prime` reads the restart
|
|
24
|
-
# snapshot back from. Resolve it
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
# snapshot back from. Resolve it ONLY via the daemon's authoritative,
|
|
25
|
+
# peercred-resolved identity (`thrum agent worktree --authoritative`) — NEVER
|
|
26
|
+
# via `thrum whoami --field worktree` (cwd-resolved) or `git rev-parse
|
|
27
|
+
# --show-toplevel` (also cwd-resolved). A cwd-derived pattern
|
|
28
|
+
# could silently resolve to a DIFFERENT agent's worktree, or the main repo,
|
|
29
|
+
# saving this snapshot where nothing ever reads it back. There is NO git
|
|
30
|
+
# fallback — if the daemon can't answer, refuse and stop rather than guess.
|
|
31
|
+
REPO=$(
|
|
32
|
+
thrum agent worktree --authoritative 2>/dev/null
|
|
33
|
+
) || { 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; }
|
|
34
|
+
[ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
|
|
30
35
|
AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
|
|
31
36
|
[ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
|
|
32
37
|
mkdir -p "${REPO}/.thrum/restart"
|
|
@@ -261,5 +266,11 @@ ${REPO}/.thrum/restart/${AGENT}.md
|
|
|
261
266
|
comes from `thrum tmux restart` or `thrum tmux create`). No bash heredoc or
|
|
262
267
|
`cat <<EOF` redirection is needed — write the file directly.
|
|
263
268
|
|
|
269
|
+
Variants that have a verify step (`/thrum:compact`, `/thrum:compact-extended`,
|
|
270
|
+
`/thrum:restart`, `/thrum:restart-extended`) mint a `SNAPSHOT_PATH` and
|
|
271
|
+
`SNAPSHOT_NONCE` in their own Step 3 PREP block: write to exactly that path, end
|
|
272
|
+
the file with `<!-- snapshot-nonce: <nonce> -->`, and record both for the verify
|
|
273
|
+
block. The sleep variants have no verify block and need no nonce.
|
|
274
|
+
|
|
264
275
|
After writing, return control to your variant skill (`/thrum:restart-extended`,
|
|
265
276
|
`/thrum:sleep`, or `/thrum:sleep-extended`) for the terminal action.
|
|
@@ -46,8 +46,15 @@ diffs if the remote-tracking ref might be stale.
|
|
|
46
46
|
# SHA of the current HEAD
|
|
47
47
|
git rev-parse HEAD
|
|
48
48
|
|
|
49
|
-
# Configured merge target
|
|
50
|
-
|
|
49
|
+
# Configured merge target -- resolve through .thrum/redirect when present.
|
|
50
|
+
# A worktree's .thrum/ holds only a `redirect` file (a single line: the
|
|
51
|
+
# absolute path to the MAIN REPO'S .thrum directory itself), not its own
|
|
52
|
+
# config.json -- reading .thrum/config.json directly from a worktree
|
|
53
|
+
# silently returns empty. Root repos have no redirect file, so CONFIG falls
|
|
54
|
+
# through to the direct path.
|
|
55
|
+
CONFIG=".thrum/config.json"
|
|
56
|
+
[ -f .thrum/redirect ] && CONFIG="$(cat .thrum/redirect)/config.json"
|
|
57
|
+
jq -r '.orchestration.merge_target' "$CONFIG"
|
|
51
58
|
```
|
|
52
59
|
|
|
53
60
|
The author must never type or remember either value by hand — both come from the
|
|
@@ -0,0 +1,352 @@
|
|
|
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
|
+
Read the partial at the absolute path (resolve `$REPO` yourself first via
|
|
55
|
+
`thrum agent worktree --authoritative` — NEVER `thrum whoami --field worktree`
|
|
56
|
+
or `git rev-parse --show-toplevel`, both cwd-resolved and the root cause of
|
|
57
|
+
a documented snapshot-save loss class; there is no git fallback):
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
${REPO}/claude-plugin/commands/_snapshot-protocol.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Apply its Step 2 (compose your continuation) per the structure guidance.
|
|
64
|
+
|
|
65
|
+
**Use the EXTENDED 16-section structure.** The structure block is `§1.` through
|
|
66
|
+
`§16.` with per-section guidance documented in the partial.
|
|
67
|
+
|
|
68
|
+
**Note on §3 framing:** For a compact snapshot, §3 frames as "where work stands
|
|
69
|
+
at compaction" — you are continuing in place, not completing and not parking.
|
|
70
|
+
|
|
71
|
+
**Your snapshot's FIRST line MUST be the compact resume header**, above §1. It
|
|
72
|
+
survives even if post-compact orientation fails, so it is read-first and
|
|
73
|
+
self-contained:
|
|
74
|
+
|
|
75
|
+
> 🔴 ON RESUME — you were COMPACTED, not restarted. Daemon binding is intact
|
|
76
|
+
> (subagents/loops/crons survived). Resume LEAN: this file is delivered inline in the injected briefing (read it FIRST). Expect a
|
|
77
|
+
> `thrum prime --light` briefing already auto-injected by the SessionStart
|
|
78
|
+
> hook; only run `thrum:prime-agent` if it did NOT appear. Do NOT manually run
|
|
79
|
+
> full `thrum prime`. Then continue from §16.
|
|
80
|
+
|
|
81
|
+
Then §1…§16 as normal, with §16 (immediate next actions) actionable from a
|
|
82
|
+
compacted (not cold) start.
|
|
83
|
+
|
|
84
|
+
### 3. Write the continuation
|
|
85
|
+
|
|
86
|
+
First run this PREP block (it mints the path and a per-run nonce), then use the
|
|
87
|
+
Write tool per Step 3 of the partial:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# The Bash tool does NOT persist shell state across calls, so this block
|
|
91
|
+
# re-resolves identity itself. Fail closed: no cwd/git fallback.
|
|
92
|
+
REPO=$(
|
|
93
|
+
thrum agent worktree --authoritative 2>/dev/null
|
|
94
|
+
) || { 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; }
|
|
95
|
+
[ -n "$REPO" ] || { echo "ERROR: thrum agent worktree --authoritative returned empty"; exit 1; }
|
|
96
|
+
AGENT=$(thrum whoami --field agent_id) || { echo "ERROR: agent not registered"; exit 1; }
|
|
97
|
+
[ -n "$AGENT" ] || { echo "ERROR: empty agent_id"; exit 1; }
|
|
98
|
+
mkdir -p "${REPO}/.thrum/restart"
|
|
99
|
+
SNAPSHOT="${REPO}/.thrum/restart/${AGENT}.md"
|
|
100
|
+
NONCE="zn6fx-$(date +%s)-$$-${RANDOM}${RANDOM}"
|
|
101
|
+
echo "SNAPSHOT_PATH=${SNAPSHOT}"
|
|
102
|
+
echo "SNAPSHOT_NONCE=${NONCE}"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Write to EXACTLY the printed `SNAPSHOT_PATH` (not a path you re-derive), and make
|
|
106
|
+
sure the file contains the marker below (as its final line), with the printed `SNAPSHOT_NONCE` — verify searches the whole file for it:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
<!-- snapshot-nonce: <SNAPSHOT_NONCE> -->
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Record both values** — the verify step pastes them into its verify block, which
|
|
113
|
+
fails closed unless the file at that exact path carries that nonce.
|
|
114
|
+
|
|
115
|
+
This file lives on disk and is UNAFFECTED by compaction — compaction rewrites
|
|
116
|
+
your conversation context, never your worktree. So the snapshot is readable after
|
|
117
|
+
`/compact` fires. This matters more for an extended snapshot: you are staging
|
|
118
|
+
wire contracts, a capability matrix, and design rationale that are expensive to
|
|
119
|
+
reconstruct and exist nowhere else. There is no durable-backup step (the worktree
|
|
120
|
+
is not torn down, so `/thrum:sleep-extended`'s teardown-loss risk does not apply).
|
|
121
|
+
|
|
122
|
+
### 4. Verify your snapshot, then fire /compact (ONE block)
|
|
123
|
+
|
|
124
|
+
> ✅ **Use the SINGLE-call self-send form below — it is required, not stylistic.**
|
|
125
|
+
> `thrum tmux send` (OD-5 = B; supersedes `thrum tmux key`,
|
|
126
|
+
> which is being retired) takes exactly ONE client call: the daemon
|
|
127
|
+
> types the text, waits a settle gap, checks for a pending dialog, and only then
|
|
128
|
+
> sends the confirming Enter — all server-side, in that order, within the SAME
|
|
129
|
+
> call. There is no separate `key --type` + `key Enter` pair to race, because
|
|
130
|
+
> there is no second client call: a client-side race is structurally impossible
|
|
131
|
+
> here, not merely avoided by convention.
|
|
132
|
+
> 🔴 **Never issue two separate calls (one for the text, one for Enter)** — that
|
|
133
|
+
> shape is what the retired `tmux key` two-step form did, and it is what
|
|
134
|
+
> produced a truncated `/compac` → invalid command → silent no-op (measured).
|
|
135
|
+
> A single `thrum tmux send "$SESSION" "<command>"` call has no such split to
|
|
136
|
+
> make.
|
|
137
|
+
|
|
138
|
+
On a non-Claude runtime this block sends that runtime's own compact-equivalent
|
|
139
|
+
command in place of `/compact` — see
|
|
140
|
+
`${REPO}/claude-plugin/commands/_compact-runtime-commands.md` for the cited
|
|
141
|
+
per-runtime table; `scripts/sync-skills.sh` applies the substitution when
|
|
142
|
+
syncing this file to each runtime's plugin tree.
|
|
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
|
+
if [ "$SEND_RC" -ne 0 ]; then
|
|
240
|
+
echo "ERROR: thrum tmux send exited ${SEND_RC} — the daemon-routed self-send failed; it will NOT queue or deliver /compact."
|
|
241
|
+
# Fail-loud fallback, NOT silent idle: attempt the compact command
|
|
242
|
+
# directly via a raw tmux send-keys call, but only after asserting an
|
|
243
|
+
# EXPLICIT socket. Never a bare/default-socket tmux operation here —
|
|
244
|
+
# TMUX_TMPDIR does not reliably isolate a fleet box's tmux server from
|
|
245
|
+
# the DEFAULT one, and a bare op under that condition has previously
|
|
246
|
+
# taken down a shared fleet server. $TMUX is the one source that is not
|
|
247
|
+
# a guess: tmux itself sets it, in THIS exact pane's own shell
|
|
248
|
+
# environment, to "<socket_path>,<server_pid>,<window_index>" — so
|
|
249
|
+
# splitting it gives the socket this pane is ACTUALLY connected
|
|
250
|
+
# through, not an assumed default.
|
|
251
|
+
RAW_TMUX_SOCK="${TMUX%%,*}"
|
|
252
|
+
if [ -z "$RAW_TMUX_SOCK" ] || [ ! -S "$RAW_TMUX_SOCK" ]; then
|
|
253
|
+
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)."
|
|
254
|
+
echo "Report the ERROR line above to your coordinator (or the operator if you are top-level) and stop."
|
|
255
|
+
exit 1
|
|
256
|
+
fi
|
|
257
|
+
if ! tmux -S "$RAW_TMUX_SOCK" has-session -t "$SESSION" 2>/dev/null; then
|
|
258
|
+
echo "HOLDING: session '$SESSION' does not resolve on socket $RAW_TMUX_SOCK — refusing the raw fallback."
|
|
259
|
+
echo "Report the ERROR line above to your coordinator (or the operator if you are top-level) and stop."
|
|
260
|
+
exit 1
|
|
261
|
+
fi
|
|
262
|
+
echo "Retrying via raw tmux send-keys on explicit socket $RAW_TMUX_SOCK..."
|
|
263
|
+
# ONE call: text + the Enter keystroke (C-m) together, same
|
|
264
|
+
# single-call discipline as the daemon-routed path above — a two-call
|
|
265
|
+
# split (send text, THEN a separate Enter call) is the exact shape
|
|
266
|
+
# that produced a truncated "/compac" -> invalid command -> silent
|
|
267
|
+
# no-op in the prior incident this file already documents. Do not
|
|
268
|
+
# split this into two `tmux send-keys` invocations.
|
|
269
|
+
tmux -S "$RAW_TMUX_SOCK" send-keys -t "${SESSION}:0.0" "/compact" C-m
|
|
270
|
+
FALLBACK_RC=$?
|
|
271
|
+
if [ "$FALLBACK_RC" -ne 0 ]; then
|
|
272
|
+
echo "HOLDING: raw tmux send-keys fallback ALSO failed (exit ${FALLBACK_RC})."
|
|
273
|
+
echo "Report the ERROR line(s) above to your coordinator (or the operator if you are top-level) and stop."
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
echo "Raw fallback send-keys succeeded on socket $RAW_TMUX_SOCK."
|
|
277
|
+
fi
|
|
278
|
+
# thrum-xyz: queue the RESUME PROMPT right behind /compact, on the same
|
|
279
|
+
# daemon queue (only when the daemon-routed /compact send succeeded; the raw
|
|
280
|
+
# fallback path leaves any hook-side nudge as the kickoff). Hook output is
|
|
281
|
+
# context only — it does not start a turn — so without this an idle pane with
|
|
282
|
+
# an empty inbox can sit at 0% context, empty composer, until a human types.
|
|
283
|
+
# The daemon dispatches it once the pane is idle again AFTER the compaction
|
|
284
|
+
# (FIFO behind /compact), so it is not a timer and it fires exactly once.
|
|
285
|
+
if [ "$SEND_RC" -eq 0 ]; then
|
|
286
|
+
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."
|
|
287
|
+
thrum tmux send "$AGENT" "$RESUME_PROMPT"
|
|
288
|
+
RESUME_RC=$?
|
|
289
|
+
if [ "$RESUME_RC" -eq 0 ]; then
|
|
290
|
+
:
|
|
291
|
+
else
|
|
292
|
+
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."
|
|
293
|
+
fi
|
|
294
|
+
fi
|
|
295
|
+
else
|
|
296
|
+
echo "HOLDING: not firing /compact. Report the VERIFY FAILED line(s) above to"
|
|
297
|
+
echo "your coordinator (or the operator if you are top-level) and stop."
|
|
298
|
+
exit 1
|
|
299
|
+
fi
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Fail-closed binding rule:** the block only passes for the exact path and nonce
|
|
303
|
+
the Step 3 PREP block printed. It refuses (VERIFY FAILED, no `/compact`) if the
|
|
304
|
+
values are empty or still placeholders, if a fresh re-resolution of your identity
|
|
305
|
+
yields a different path (drift), or if the file at that path lacks the nonce — a
|
|
306
|
+
fresh file merely sitting at the re-derived path is NOT proof you wrote it.
|
|
307
|
+
|
|
308
|
+
If EITHER check fails, the block HOLDS and does not fire `/compact` — holding is
|
|
309
|
+
safe; compacting against an unverified snapshot loses the context this command
|
|
310
|
+
exists to preserve (for an extended snapshot, wire contracts and design
|
|
311
|
+
rationale). If `thrum tmux send` exits 3 (Enter WITHHELD) or 4 (verdict
|
|
312
|
+
unknown), `/compact` is already typed in the pane: the block holds and does NOT
|
|
313
|
+
retype it. If BOTH checks pass but `thrum tmux send` itself exits nonzero (any
|
|
314
|
+
other code), the block does NOT go idle un-compacted silently — it prints the failure and falls
|
|
315
|
+
back to a raw, explicit-socket `tmux send-keys` retry (single call, text +
|
|
316
|
+
Enter together) before holding for real. After a successful send (daemon-routed
|
|
317
|
+
or raw fallback), the turn ends; emit no further tool calls or prose this turn.
|
|
318
|
+
The daemon-routed send is queued, not synchronous — it fires once your pane goes
|
|
319
|
+
idle, which only happens if you stop acting now; the raw fallback, when it runs,
|
|
320
|
+
is synchronous.
|
|
321
|
+
|
|
322
|
+
## How resume works
|
|
323
|
+
|
|
324
|
+
After `/compact` the SAME process and session survive — the daemon binding is
|
|
325
|
+
intact (that is why your sub-agents, loops, and crons kept running). Full
|
|
326
|
+
`thrum prime` rebuilds identity + binding + full briefing for a NEW session; none
|
|
327
|
+
of that is needed here. Resume LEAN, in this order:
|
|
328
|
+
|
|
329
|
+
1. **Your Resume Plan arrives inline — do not go looking for the file.** The
|
|
330
|
+
`SessionStart` hook auto-injects a `thrum prime --light` briefing zero-turn,
|
|
331
|
+
and its `# Previous Session Context` section carries your full snapshot,
|
|
332
|
+
including the `ON RESUME` header; execute §16 from it. That prime ARCHIVES the
|
|
333
|
+
snapshot (moves `${REPO}/.thrum/restart/${AGENT}.md` into the main repo's
|
|
334
|
+
`.thrum/agents/${AGENT}/sessions/`), so the original path is normally GONE
|
|
335
|
+
by the time you look — that is by design, not loss. Only if no briefing
|
|
336
|
+
appeared (daemon unreachable), read the newest `*-restart.md` in that
|
|
337
|
+
`sessions/` directory (or `${REPO}/.thrum/restart/${AGENT}.md` if it still
|
|
338
|
+
exists) and run `thrum:prime-agent` manually as the fallback. Never manually
|
|
339
|
+
run full `thrum prime` when the briefing is present.
|
|
340
|
+
2. **A resume prompt starts your first turn.** Step 4 queued a "Compaction
|
|
341
|
+
complete - please continue" prompt on the daemon right behind `/compact`; it
|
|
342
|
+
is delivered once the pane is idle after compaction, so a turn starts even
|
|
343
|
+
with an empty inbox.
|
|
344
|
+
3. **Continue** from §16. Reconnect to your still-live sub-agents, loops, and
|
|
345
|
+
crons rather than re-dispatching.
|
|
346
|
+
|
|
347
|
+
Runtime-specific compaction-recovery hooks (if this runtime has any) are
|
|
348
|
+
documented in that runtime's own plugin tree (its hooks manifest and
|
|
349
|
+
the scripts it points to), not here.
|
|
350
|
+
|
|
351
|
+
**Follow the Resume Plan in your injected `# Previous Session Context` (snapshot
|
|
352
|
+
saved at `${REPO}/.thrum/restart/${AGENT}.md`, archived by the prime) post-compact.**
|