Three follow-ups from the 1.34.x multi-session correctness train, all backwards-compatible. 1.34.14 — stale CLAUDEMESH_CONFIG_DIR falls back. The launch flow exposes CLAUDEMESH_CONFIG_DIR=<tmpdir> to its spawned claude; if a later claudemesh invocation inherited that env (Bash tool inside Claude Code, tmux update-environment, exported var), the inherited path pointed at a tmpdir that no longer existed and readConfig() silently returned empty. paths.ts now memoizes resolution: env unset → default; env points at a real dir → trust it; env set but dir gone → TTY-only stderr warning with shell-specific unset hint, fall back to ~/.claudemesh. 1.34.15 — peer list --mesh actually scopes. peers.ts and launch.ts were calling tryListPeersViaDaemon() with no argument; the daemon's ?mesh= filter (server-side, since 1.26.0) was already correct, the CLI just wasn't passing the slug. Forwarding fixed in both sites; send.ts cross-mesh hex-prefix resolution intentionally untouched. 1.34.15 — kick refuses no-op kicks on control-plane. Pre-1.34.15 kicking a daemon's member-WS just closed the socket and triggered auto-reconnect — a no-op with a misleading "session ended" message. Broker now skips peers where peerRole === "control-plane" and surfaces them in a new additive ack field skipped_control_plane; the CLI reads it and prints a clearer hint pointing at ban / daemon down. Soft disconnect verb keeps old behavior. PeerConn gains a peerRole slot populated at both connections.set sites. Tests: 4 new for paths-stale-env, 5 for kick-control-plane-skip. CLI 87/87 green; broker 55/55 unit green (integration tests pre-existing infra failure on this machine). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
/**
|
|
* Kick control-plane skip: 1.34.15 (gap #3a) refuses to close
|
|
* long-lived control-plane connections (claudemesh daemon, dashboard)
|
|
* via `kick`, because they auto-reconnect within seconds and the verb
|
|
* was effectively a no-op. The soft `disconnect` verb keeps the old
|
|
* behavior so users can still nudge a control-plane peer to
|
|
* re-authenticate.
|
|
*
|
|
* Pure-logic test — mirrors the branch inside handleSend's kick case
|
|
* without spinning up a broker. Same pattern as
|
|
* grants-enforcement.test.ts.
|
|
*/
|
|
|
|
import { describe, expect, test } from "vitest";
|
|
|
|
type PeerRole = "control-plane" | "session" | "service";
|
|
|
|
/** Mirrors the predicate inserted into the kick handler. */
|
|
function shouldSkipKick(args: {
|
|
verb: "kick" | "disconnect";
|
|
peerRole: PeerRole;
|
|
}): boolean {
|
|
const skipControlPlane = args.verb === "kick";
|
|
return skipControlPlane && args.peerRole === "control-plane";
|
|
}
|
|
|
|
describe("kick control-plane skip (gap #3a)", () => {
|
|
test("kick on control-plane → skipped (would auto-reconnect)", () => {
|
|
expect(shouldSkipKick({ verb: "kick", peerRole: "control-plane" })).toBe(true);
|
|
});
|
|
|
|
test("kick on session → not skipped (closes user session)", () => {
|
|
expect(shouldSkipKick({ verb: "kick", peerRole: "session" })).toBe(false);
|
|
});
|
|
|
|
test("kick on service → not skipped", () => {
|
|
expect(shouldSkipKick({ verb: "kick", peerRole: "service" })).toBe(false);
|
|
});
|
|
|
|
test("disconnect on control-plane → not skipped (intentional nudge)", () => {
|
|
expect(shouldSkipKick({ verb: "disconnect", peerRole: "control-plane" })).toBe(false);
|
|
});
|
|
|
|
test("disconnect on session → not skipped", () => {
|
|
expect(shouldSkipKick({ verb: "disconnect", peerRole: "session" })).toBe(false);
|
|
});
|
|
});
|