talking-stick 0.1.3 → 0.1.4
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.
|
@@ -31,7 +31,11 @@ export function handleJoinCommand(runtime, parsed) {
|
|
|
31
31
|
});
|
|
32
32
|
upsertSessionFromJoin(identity, joined);
|
|
33
33
|
printResult(parsed, joined, () => {
|
|
34
|
-
|
|
34
|
+
const lines = [`Joined ${joined.canonical_path} as ${joined.agent_id}`];
|
|
35
|
+
if (joined.warning) {
|
|
36
|
+
lines.push(`Warning: ${joined.warning}`);
|
|
37
|
+
}
|
|
38
|
+
return lines.join("\n");
|
|
35
39
|
});
|
|
36
40
|
}
|
|
37
41
|
export function handleLeaveCommand(runtime, parsed) {
|
package/dist/service.js
CHANGED
|
@@ -682,7 +682,11 @@ export class TalkingStickService {
|
|
|
682
682
|
if (forceNew) {
|
|
683
683
|
const exactRoom = this.findRoomByCanonicalPath(resolved.canonical_context_path);
|
|
684
684
|
if (exactRoom) {
|
|
685
|
-
return {
|
|
685
|
+
return {
|
|
686
|
+
room: exactRoom,
|
|
687
|
+
joinedExistingRoom: true,
|
|
688
|
+
warning: `force_new had no effect: a room already exists at ${exactRoom.canonical_path}. force_new only creates a nested room when an ancestor room exists; same-path duplicates are not supported. To get a fresh room for a separate topic, join a distinct subpath.`
|
|
689
|
+
};
|
|
686
690
|
}
|
|
687
691
|
return {
|
|
688
692
|
room: this.createRoom(resolved.canonical_context_path, timestamp),
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Talking Stick 0.1.4
|
|
2
|
+
|
|
3
|
+
Date: 2026-04-30
|
|
4
|
+
|
|
5
|
+
Patch release that clarifies the existing `force_new` semantics on `join_path`,
|
|
6
|
+
makes the resulting warnings visible to human CLI users, and stabilizes the
|
|
7
|
+
test suite.
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
### `force_new` no-op on exact-path joins now surfaces a warning
|
|
12
|
+
|
|
13
|
+
`join_path` has always had two shapes for `force_new=true`: it creates a nested
|
|
14
|
+
room when a different ancestor room exists, and it joins the existing room
|
|
15
|
+
when one already exists at the exact `canonical_path` (because
|
|
16
|
+
`path_rooms.canonical_path` is `UNIQUE`). The second shape is a deliberate
|
|
17
|
+
no-op — `force_new` cannot duplicate a room at the same canonical path — but
|
|
18
|
+
prior versions returned the existing room silently with `joined_existing_room:
|
|
19
|
+
true` and no further signal. Operators and harnesses who flipped `--force-new`
|
|
20
|
+
expecting a fresh room had no way to tell their request had no effect.
|
|
21
|
+
|
|
22
|
+
`join_path` now returns a `warning` field on this no-op path explaining what
|
|
23
|
+
happened and pointing the caller at the only remedy: join a distinct subpath
|
|
24
|
+
(for example a topic-scoped subdirectory) to get a fresh room. The nested-room
|
|
25
|
+
warning that has always existed is unchanged. Both warnings are now also
|
|
26
|
+
rendered in the default `tt join` text output, not just `--json`.
|
|
27
|
+
|
|
28
|
+
In practice this means a Claude Code or Codex harness asking for a new "topic
|
|
29
|
+
room" at the same path now sees an explicit signal that the API only knows
|
|
30
|
+
about path-scoped rooms, instead of silently working off the existing room.
|
|
31
|
+
|
|
32
|
+
The bundled skill's *While waiting* section also gains a more proactive
|
|
33
|
+
framing: the wait window is the right place to re-read the holder's last
|
|
34
|
+
handoff, follow its `artifacts[]`, investigate, and surface findings via
|
|
35
|
+
`add_note` — not idle sleep.
|
|
36
|
+
|
|
37
|
+
### Contention test no longer races the room-purge clock
|
|
38
|
+
|
|
39
|
+
`tests/talking-stick.test.ts > only one process can claim an idle room under
|
|
40
|
+
contention` reproduced as a `room_not_found` failure when the wall-clock date
|
|
41
|
+
crossed the `idleRoomTtlMs` window relative to the parent test's fake clock
|
|
42
|
+
fixed at 2026-04-22. The contention worker spawned in
|
|
43
|
+
`tests/fixtures/claim-worker.ts` constructed its `TalkingStickService` with
|
|
44
|
+
the real `Date.now()`, so the worker's `purgeExpiredIdleRooms` call evicted
|
|
45
|
+
the room that the parent had just created under the fake clock. The fix
|
|
46
|
+
threads the parent's fake-clock ISO timestamp into the worker so the service
|
|
47
|
+
shares the same `now()` source, and the test passes deterministically
|
|
48
|
+
regardless of wall-clock date.
|
|
49
|
+
|
|
50
|
+
### Identity-resolver memoization test no longer asserts internal call counts
|
|
51
|
+
|
|
52
|
+
`tests/mcp-server.test.ts > createConnectionIdentityResolver > memoizes
|
|
53
|
+
derived identity per session and re-derives on override` was written when
|
|
54
|
+
`deriveMcpHarnessIdentity` made one `inspector.inspect` call per derive.
|
|
55
|
+
Since 0.1.3's ancestry-walk fix (commit `ab4e843`), a single derive may walk
|
|
56
|
+
multiple parent processes. The resolver-level memoization is still correct;
|
|
57
|
+
the test was asserting the wrong invariant. Updated to assert call-count
|
|
58
|
+
*deltas* across resolver invocations (no growth on memoized hits, growth on
|
|
59
|
+
fresh sessions and overrides) instead of absolute counts, so the test
|
|
60
|
+
remains robust to changes in ancestry-walk depth.
|
|
61
|
+
|
|
62
|
+
## Verification
|
|
63
|
+
|
|
64
|
+
- `npm run typecheck`
|
|
65
|
+
- `npm test` — 225 tests across 14 files, all passing under Node 24.11.0
|
|
66
|
+
- `npm run build`
|
|
67
|
+
- `git diff --check`
|
|
68
|
+
- `npm pack --dry-run --ignore-scripts`
|
|
@@ -316,7 +316,7 @@ Resolution:
|
|
|
316
316
|
2. Resolve the preferred workspace root.
|
|
317
317
|
3. Walk up from the canonical `context_path` to the preferred workspace root looking for an existing room.
|
|
318
318
|
4. If found and `force_new = false`: join the deepest existing ancestor room.
|
|
319
|
-
5. If found and `force_new = true`: create a nested room at the canonical `context_path`, returning a warning that an ancestor room exists. If a room already exists at
|
|
319
|
+
5. If found and `force_new = true`: create a nested room at the canonical `context_path`, returning a warning that an ancestor room exists. If a room already exists at the exact `canonical_context_path`, join it and return a warning that `force_new` was a no-op. `force_new` only creates *nested* rooms; it never duplicates a room at the same canonical path because `path_rooms.canonical_path` is unique. Callers that want a fresh room for a separate topic must join a distinct subpath.
|
|
320
320
|
6. If not found: create a new room at the preferred workspace root.
|
|
321
321
|
|
|
322
322
|
The response includes the resolved `room_id`, the `canonical_path` the agent actually joined (which may differ from the request path when workspace root resolution or ancestor lookup redirected the call), the effective room policy (including `heartbeat_interval_ms`), and a `handoff_template` hint describing the expected handoff shape. For the MVP this template is static server-wide; room-specific prompting can be added later if real workflows need it.
|
package/package.json
CHANGED
|
@@ -91,7 +91,7 @@ If you do not have the stick:
|
|
|
91
91
|
- it is fine to read, plan, review, or help the user think — or any other work that does not mutate shared state
|
|
92
92
|
- tell the user who currently holds or is reserved the turn when that is useful
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
The wait is for *active* non-mutating work, not idle sleep. Re-read the holder's last handoff, follow up on its `artifacts[]`, investigate the area they are touching, and rethink the plan from your own angle. If you find something the holder should know — a missed invariant, a related bug, a sharper plan — leave a note with `add_note` rather than sitting on it until your next turn. Notes do not grant permission to edit shared files; they are observations and pointers, not coordination bypasses. The point: while you wait you can still move the work forward by feeding the holder, not by stalling.
|
|
95
95
|
|
|
96
96
|
When you do take the stick, first read the attached handoff and load any useful `artifacts[]`, then run `list_notes` once so you see what other members left for you. The owner's turn is the right place to act on a note, not to debate it with its author mid-turn.
|
|
97
97
|
|