talking-stick 0.1.4 → 0.3.0

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.
@@ -0,0 +1,224 @@
1
+ # CLI-Only Coordination And MCP Removal
2
+
3
+ **Status:** draft by `codex:d4bc2492`, incorporating Claude OOB review. **Branch:** `oob-identity-fix-plan`.
4
+
5
+ ## Why This Change
6
+
7
+ Out-of-band messaging exposed a deeper problem: Talking Stick has two harness integration paths, CLI and MCP, and they do not share the same process context. A message addressed to the MCP-side agent id can disappear from `tt msg recv --target self` if the CLI-side resolver produces a different id. Fixing one resolver bug helps, but keeping MCP in the active path preserves the same class of failure.
8
+
9
+ The new direction is simpler: **the `tt` CLI is the only harness contract.** Agents coordinate by running `tt` commands. MCP is removed from install, docs, skills, tests, and eventually package dependencies. The SQLite-backed service/core can stay as internal implementation; the public automation surface is `tt`.
10
+
11
+ ## Goals
12
+
13
+ - Make every agent workflow expressible through CLI commands: `tt join`, `tt wait`, `tt release`, `tt assign`, `tt take`, `tt state`, `tt events`, `tt notes`, and `tt msg`.
14
+ - Make ambient receive CLI-first: `tt events --follow --json` for live stdout consumers, `tt events --wait --after <cursor> --json` for process-completion consumers, and `tt msg recv` only for messages-only consumers. `--wait` and `--follow` default to `--target self`.
15
+ - Remove MCP registration from `tt install` and remove `tt mcp` as a supported command.
16
+ - Automatically remove stale Talking Stick MCP registrations from every supported harness during update/first-run migration.
17
+ - Update the bundled skill so harnesses prefer CLI even when MCP tools exist in older installations.
18
+ - Replace MCP smoke coverage with real child-process CLI smoke coverage, including SIGTERM/cursor behavior.
19
+ - Keep long turns safe by making the CLI-owned lease guardian a first-class contract of `tt wait` / `tt take`.
20
+ - Preserve human-friendly CLI defaults: human shells get readable text unless `--json` is explicit; harness-detected shells get JSON where machine output is expected.
21
+
22
+ ## Non-Goals
23
+
24
+ - No backward compatibility promise for MCP users beyond automatic cleanup of stale Talking Stick-owned MCP registrations. This is an intentional breaking simplification.
25
+ - No plugin requirement. Plugins may later wrap the CLI, but v1 must work with subprocesses and stdout.
26
+ - No daemon. The existing SQLite service and `tt guard` process model remain enough; do not reintroduce a long-running automation server under another name.
27
+ - No change to the single-writer room semantics.
28
+
29
+ ## Target Architecture
30
+
31
+ ### Public Surface
32
+
33
+ `tt` is the public API. Harnesses call it through shell/exec facilities and parse JSON when they need structured output.
34
+
35
+ Core command mapping:
36
+
37
+ | Need | CLI |
38
+ |---|---|
39
+ | Join room | `tt join [path] --json` |
40
+ | Wait/claim | `tt wait [path] --json` |
41
+ | Probe | `tt try [path] --json` |
42
+ | Release | `tt release [path] --stdin` |
43
+ | Assign specific next holder | `tt assign <agent_id> [path] --stdin` |
44
+ | Operator takeover | `tt take [path] --operator-requested --reason ... --json` |
45
+ | State | `tt state [path] --json` |
46
+ | Events | `tt events [path] --after N --target any --json` |
47
+ | Notes | `tt notes add/list ... --json` |
48
+ | Send OOB | `tt msg send <recipient|room> <body...> --json` |
49
+ | Ambient receive | `tt events --follow --json` |
50
+ | Messages-only receive | `tt msg recv --follow --json` |
51
+
52
+ The skill should teach these exact commands. It should not mention `join_path`, `wait_for_turn`, `release_stick`, `send_message`, or any MCP tool as a preferred path.
53
+
54
+ Successful `tt wait` and `tt take` calls must start or repair the internal `tt guard` lease guardian and return its `guardian_pid` in JSON. That guardian is not a harness API; it is the CLI's mechanism for keeping long turns alive after the `tt wait` process exits.
55
+
56
+ ### Internal Surface
57
+
58
+ Keep `TalkingStickService` / `TalkingStickCommands` as internal TypeScript modules backing the CLI. They are not the harness API. The command layer owns input parsing, identity, JSON/text output policy, guardian spawning, and child-process behavior.
59
+
60
+ The CLI-only rule applies to external harness coordination. Same-package processes may still call internal modules when they are part of Talking Stick itself. The diff walker should use direct SQLite or internal service reads for its hot attribution/watch path, then use normal CLI commands for rare outward actions such as sending an annotation message or adding a note.
61
+
62
+ ### Holder Lease Guardian
63
+
64
+ Claude's main review concern is load-bearing: if `tt wait` exits and no process continues heartbeating, a holder doing a long edit can time out and look stale. The current `tt wait` path already has the right shape: it spawns internal `tt guard` and records the guardian in the CLI session. The CLI-only migration should make that behavior explicit and tested, not incidental.
65
+
66
+ Required behavior:
67
+
68
+ - `tt wait --json` returning `your_turn` includes `guardian_pid`.
69
+ - `tt take --json` returning success includes `guardian_pid`.
70
+ - If the caller already owns the turn and the old guardian is gone, `tt wait` repairs it by spawning a replacement.
71
+ - `tt release`, `tt pass`, and `tt assign` do not require the original `tt wait` process to still exist; they rely on the persisted CLI session and identity.
72
+ - Tests kill the guardian and prove the next owner command either repairs or reports the problem clearly.
73
+
74
+ This avoids a harness-level `tt heartbeat` timer and avoids wrapping all work in a daemon-like `tt hold` command.
75
+
76
+ ### Process Cost
77
+
78
+ CLI-only coordination pays a Node startup and SQLite-open cost for every command. That is acceptable for `tt wait` cycles, handoffs, notes, and OOB messages. Do not answer this by adding a long-running daemon unless the operator explicitly reverses the CLI-only decision; a daemon would recreate the same dual-context class of failure that led to removing MCP.
79
+
80
+ ### Identity
81
+
82
+ With MCP removed, issue #19 becomes a CLI-only identity reliability issue instead of a CLI-vs-MCP comparison. The resolver still needs to be stable across repeated shell-outs from the same harness:
83
+
84
+ 1. `TT_HARNESS_AGENT_ID` wins when explicitly exported.
85
+ 2. Harness-provided stable ids win next (`CODEX_THREAD_ID`, `OPENCODE_RUN_ID`, similar).
86
+ 3. Harness-root ancestry wins before terminal ids for no-session-id harnesses like Claude Code.
87
+ 4. Terminal ids (`ITERM_SESSION_ID`, `CMUX_TAB_ID`, etc.) are fallback only after no harness root can be found.
88
+ 5. Human fallback remains `human:<username>`.
89
+
90
+ Live receivers should either use the resolved harness id directly or run under `TT_HARNESS_AGENT_ID=<agent_id>` when a wrapper has already joined a room as that id.
91
+
92
+ ### Install
93
+
94
+ `tt install` should become a skill/bootstrap installer, not an MCP installer. Update paths should also clean up old MCP registrations automatically so existing harnesses stop seeing the removed integration surface.
95
+
96
+ Proposed behavior:
97
+
98
+ - `tt install <harness...> | --all [--print] [--copy] [--link]`: install/update the bundled skill only.
99
+ - Remove `tt install-skill` / `tt uninstall-skill` once `tt install` owns skill installation. There is no compatibility requirement to keep parallel install verbs.
100
+ - `tt uninstall`: remove the skill install and Talking Stick-owned stale MCP config entries.
101
+ - Remove MCP config writes for Claude, Codex, Gemini, and OpenCode.
102
+ - Remove `DEFAULT_SERVER_COMMAND = ["tt", "mcp"]`.
103
+ - Remove native `harness mcp add/remove` command planning.
104
+
105
+ ### Update-Time MCP Cleanup
106
+
107
+ Backward compatibility means removing the stale broken path, not keeping it alive. A user who updates from an MCP-capable version should not have to manually edit every harness config.
108
+
109
+ Required behavior:
110
+
111
+ - The package update lifecycle runs a cleanup pass when the package manager executes install/update scripts.
112
+ - `tt self-update` in versions that know about this migration runs the cleanup pass after the package manager completes.
113
+ - The first normal `tt` invocation after a package version change runs the same cleanup pass, covering users who update from an older `tt self-update`, skip lifecycle scripts, or use `npm update -g`, `mise`, `pnpm`, or another manager.
114
+ - Cleanup targets all supported harness config locations, not only the currently detected harness. Old entries in Claude, Codex, Gemini, and OpenCode should be removed together.
115
+ - The cleanup is idempotent and silent when nothing changes. If it modifies config during an interactive human CLI run, print a concise summary; harness JSON commands should not get noisy text.
116
+ - Always append an audit entry for removals with harness, config path, and removed server key/name so an operator can inspect why an MCP entry disappeared.
117
+ - Only remove Talking Stick-owned MCP entries by strict inverse-of-install matching: canonical server name/key `talking-stick` plus the exact command/args shape this installer previously wrote for that harness.
118
+ - Leave hand-edited or custom entries alone, even if they mention `talking-stick`, when the name/key or command/args no longer match the canonical installed shape.
119
+ - Do not remove unrelated MCP servers or unrelated harness settings.
120
+ - Config writes remain atomic and should preserve formatting where the existing installer machinery already can.
121
+
122
+ Implementation shape:
123
+
124
+ - Add `removeStaleMcpRegistrations({ harnesses: "all", reason: "update" })` in the install/maintenance layer.
125
+ - Track the last package version that completed migration in the Talking Stick data dir, separate from room state, so startup can cheaply decide whether to run it.
126
+ - Expose a non-interactive internal cleanup entrypoint that package lifecycle scripts can call without joining rooms or emitting harness JSON.
127
+ - Reuse the existing harness config discovery/write helpers and adapter-level install shape. Removal should be the inverse of install, not a second parser/matcher.
128
+ - Delete MCP add planning after cleanup support lands, but keep the adapter-owned stale-entry removal path.
129
+ - Add fixture tests for each supported harness proving a stale canonical Talking Stick MCP entry is removed, a neighboring unrelated MCP entry survives, and a hand-edited Talking Stick-looking entry survives.
130
+ - The audit log lives in a dedicated `${TALKING_STICK_DATA_DIR}/update-migrations.log`, not the general maintenance log. Update migrations are infrequent destructive events that operators may need to grep for after the fact ("why did my MCP entry disappear?"); mixing them with daily startup chatter makes that grep noisy and the destructive history harder to see at a glance. Format: one JSON line per migration run with `{ ts, package_version_from, package_version_to, harness, config_path, action, server_name }`.
131
+
132
+ ### OOB Messaging And Ambient Receive
133
+
134
+ OOB stays on the existing `room_events` / `message_sent` substrate, but all live receive UX is CLI-based.
135
+
136
+ The recommended ambient consumer is **`tt events --follow --json`**, not `tt msg recv --follow`. The event stream surfaces direct messages, broadcasts, **and** turn passes/reservations on a single ordered feed. A messages-only receiver silently misses the moment another agent passes you the stick — exactly the gap that motivated this section. The skill teaches the unified consumer in §2 (start it right after `tt join`) and references it from §4.5.
137
+
138
+ Required receive contract:
139
+
140
+ - `tt events --follow --json` emits one JSON event per line for messages, broadcasts, and turn handoffs targeting the caller.
141
+ - It exits cleanly on SIGTERM/SIGHUP and writes the cursor to stderr as today.
142
+ - `tt events --wait --after <cursor> --json` exits after the next matching batch for harnesses that cannot monitor long-running stdout.
143
+ - Event receive is observer-only. It does not claim the stick, does not start a guardian, and must not be treated as permission to edit. A receiver that wakes on `pass`, `release`, or `assignment` still has to run `tt wait --json` and get `your_turn` before touching shared files.
144
+ - `tt msg recv --follow` and `tt msg recv --wait` remain available as messages-only feeds for harnesses that explicitly want the narrower stream, but the skill marks them as fallback rather than primary.
145
+ - First launch starts at the room tail unless `--after` is explicit, avoiding historical replay.
146
+ - Direct messages and room broadcasts are included for `target=self`; the caller's own broadcasts are excluded.
147
+
148
+ ## Implementation Sequence
149
+
150
+ ### Stage 1 — Skill And Plan
151
+
152
+ This PR:
153
+
154
+ - Add this plan.
155
+ - Update `skills/talking-stick/SKILL.md` to be CLI-only.
156
+ - Remove MCP-first language from the OOB skill section.
157
+ - Keep source code unchanged except docs/skill, so Claude can review direction before the destructive code pass.
158
+
159
+ ### Stage 2 — CLI Identity Hardening
160
+
161
+ - Change resolver precedence so harness-root ancestry beats terminal-session fallback when a harness env marker exists but no stable session id is present.
162
+ - Add tests for Claude CLI shell-out matching repeated invocations from the same harness root.
163
+ - Add child-process tests proving `tt events --wait` / `tt events --follow` default to self and receive direct messages and turn handoffs addressed to the active CLI member without `TT_HARNESS_AGENT_ID`.
164
+ - Keep narrower coverage proving `tt msg recv` receives direct messages without widening into non-message events.
165
+ - Add guardian tests proving `tt wait` / `tt take` return a live `guardian_pid`, and that a subsequent `tt wait` repairs a dead guardian for an existing owner.
166
+ - Keep `TT_HARNESS_AGENT_ID` override as the explicit escape hatch.
167
+
168
+ ### Stage 3 — Installer De-MCP
169
+
170
+ - Change `tt install` / `tt uninstall` to manage skill installs plus cleanup of stale Talking Stick MCP entries.
171
+ - Add automatic stale MCP cleanup to package update lifecycle, `tt self-update`, and first-run-after-version-change startup maintenance.
172
+ - Delete MCP add planning from `src/install.ts`; keep only the adapter-owned inverse-of-install removal/migration code needed to clean stale Talking Stick-owned entries.
173
+ - Remove stale Talking Stick MCP entries during `tt uninstall` too; do not remove unrelated harness config.
174
+ - Update README install instructions.
175
+ - Add tests showing `tt install --all` skips missing harnesses silently and does not write MCP config.
176
+ - Add update-migration tests covering all supported harnesses, idempotency, and preservation of unrelated MCP servers.
177
+
178
+ ### Stage 4 — Remove MCP Server Surface
179
+
180
+ - Remove `tt mcp` from the command registry and help.
181
+ - Delete `src/mcp-server.ts` and MCP-specific tests.
182
+ - Delete `src/server.ts` unless another stdio entrypoint still needs it.
183
+ - Remove `@modelcontextprotocol/sdk` from `package.json`.
184
+ - Remove MCP exports from `src/index.ts`.
185
+ - Replace MCP smoke tests with CLI child-process smoke tests covering join/wait/release/msg/notes.
186
+
187
+ ### Stage 5 — Docs And Release Notes
188
+
189
+ - Rewrite README around CLI-first coordination.
190
+ - Update `docs/receive-consumer-contract.md` to describe CLI subprocess consumers, not MCP wrappers.
191
+ - Update older OOB plan docs with a superseded note pointing here.
192
+ - Add a release note marking the change as breaking.
193
+
194
+ ### Stage 6 — Live Dogfood
195
+
196
+ Before merging the code-removal PR:
197
+
198
+ - Start Claude receiver with `tt events --follow --json`.
199
+ - Start Codex receiver the same way.
200
+ - Send direct and broadcast messages both directions.
201
+ - Verify `tt wait --json`, `tt release --stdin`, and `tt assign <agent>` work from harness shell-outs.
202
+ - Verify no MCP subprocess is required for the room to operate.
203
+
204
+ ## Risks
205
+
206
+ - **Harnesses without reliable shell execution.** If a harness cannot run `tt`, it cannot use Talking Stick. That is acceptable under the new direction; the installer/skill should say so clearly.
207
+ - **Lease expiry during long turns.** If guardian spawning regresses, long owner turns can time out after `tt wait` exits. Treat guardian behavior as required CLI surface and cover it with child-process tests.
208
+ - **Per-command process cost.** Repeated `tt` shell-outs pay Node startup and SQLite open cost. Accept this as the cost of a single CLI context; do not reintroduce a daemon/MCP-like server without an explicit operator change.
209
+ - **Long-running receiver lifecycle.** Some harnesses cannot monitor stdout from a running child. Keep `--wait --after` as the supported fallback.
210
+ - **Combined wait/event command ambiguity.** A future `tt await`-style command needs a separate design pass. It must distinguish observer events from ownership results so an agent cannot mistake a message or handoff wake for permission to edit.
211
+ - **Skill drift.** Installed skills may still have old MCP-first text. Human CLI startup sync helps file-based installs, but Gemini registry installs still need `tt install gemini`.
212
+ - **Config cleanup safety.** Automatic update cleanup can be destructive if the matcher is too broad. Match only canonical inverse-of-install Talking Stick MCP entries and test with unrelated neighboring MCP servers plus hand-edited Talking Stick-looking entries in every harness fixture.
213
+ - **Large test deletion.** Removing MCP tests can accidentally reduce protocol coverage. Replace them with CLI tests before deleting assertions.
214
+ - **Diff walker hot path.** A live watcher should not spawn `tt` for every file event. Use direct SQLite/internal reads for hot attribution and CLI subprocesses only for outward coordination events.
215
+
216
+ ## Decisions From Claude Review
217
+
218
+ 1. `tt install` fully replaces `tt install-skill`; remove the parallel verb in the destructive code pass.
219
+ 2. `tt mcp` is removed immediately rather than kept as a migration shim.
220
+ 3. Update/first-run maintenance must automatically remove Talking Stick-owned stale MCP config entries across all supported harnesses, and `tt uninstall` should run the same cleanup. It must not touch unrelated harness config.
221
+ 4. Stale MCP cleanup must reuse each harness adapter's install resolver as the inverse operation. Do not reimplement config matching separately.
222
+ 5. The diff walker can read SQLite/internal state directly for its hot watch path because it ships in this package; user-facing annotations still go through `tt msg send` or `tt notes add`.
223
+ 6. The skill's primary ambient receiver is `tt events --follow --json`, not `tt msg recv --follow`. A messages-only consumer silently misses turn passes/reservations and forces harnesses back to polling for the most load-bearing event in the protocol. The unified event stream is the recommended primary path; `tt msg recv` becomes a narrower fallback.
224
+ 7. Cleanup audit lives in a dedicated `update-migrations.log`, not folded into general maintenance logging. Update migrations are infrequent destructive events; isolating them keeps "why did my MCP entry disappear" answerable by a single `cat`.