From 9dc161bf5f5db02a79a871e1533521b4efa2cc01 Mon Sep 17 00:00:00 2001 From: Mukul Sharma Date: Mon, 19 Oct 2026 19:09:04 -0700 Subject: [PATCH] comms warm session: launch claude through PATH so cmux wraps it The warm Slack session was launched as ~/.local/bin/claude, which skips cmux's per-surface claude shim, so cmux's wrapper never registered the session and only the cmux-auto-resume hook knew about it. The pane command is now `command claude` (PATH lookup, alias bypassed); with mukul-memory's ~/.zlogin shim fix the login shell resolves that to cmux's shim. CLAUDE_BIN still overrides it. Co-Authored-By: Claude Opus 5.5 (1M context) --- bin/comms_session.py | 6 ++++-- tests/test_comms_session.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/bin/comms_session.py b/bin/comms_session.py index 1051c44..ebb6730 100644 --- a/bin/comms_session.py +++ b/bin/comms_session.py @@ -88,7 +88,9 @@ def warm_workspace_title(paths: comms_lib.Paths) -> str: # instead of going through model_tiers like pulse.py/strategist.py/ # lesson-extractor.py/narrate-brief.py already do — it broke the moment the # operator's alias stopped defaulting to Bedrock). -CLAUDE_BIN = os.environ.get("CLAUDE_BIN", str(HOME / ".local/bin/claude")) +# Unset: the pane runs `command claude`, a PATH lookup that reaches cmux's claude +# shim (its wrapper registers the session with cmux) and skips the shell alias. +CLAUDE_BIN = os.environ.get("CLAUDE_BIN") def _resolve_warm_model_and_backend() -> tuple[str, str, bool]: @@ -838,7 +840,7 @@ def _warm_launch(agent: str, resolved: tuple[str, str, bool] | None = None) -> s prefix = "" if pinned else f"CLAUDE_CODE_USE_BEDROCK={'1' if backend == 'bedrock' else '0'} " return ( f"{prefix}" - f"{shlex.quote(CLAUDE_BIN)} --model {shlex.quote(model)} " + f"{shlex.quote(CLAUDE_BIN) if CLAUDE_BIN else 'command claude'} --model {shlex.quote(model)} " f"--dangerously-skip-permissions " f"--add-dir {shlex.quote(str(REPO_ROOT))} --add-dir {shlex.quote(str(HOME / '.assistant'))} " f"--add-dir {shlex.quote(str(HOME / '.architect'))} --add-dir /tmp" diff --git a/tests/test_comms_session.py b/tests/test_comms_session.py index f5946d8..013c105 100644 --- a/tests/test_comms_session.py +++ b/tests/test_comms_session.py @@ -116,6 +116,22 @@ def test_warm_launch_pinned_model_skips_the_backend_prefix(monkeypatch): assert "us.anthropic.claude-sonnet-4-6[1m]" in cmd +def test_warm_launch_finds_claude_on_path_so_cmux_wraps_it(monkeypatch): + # The pane's login shell must resolve `claude` through PATH (cmux's shim) — a + # hardcoded ~/.local/bin/claude skips cmux's wrapper and its session hooks. + monkeypatch.setattr(cs, "CLAUDE_BIN", None) + cmd = cs._warm_launch(ag.CLAUDE) + assert " command claude --model " in f" {cmd}" + assert ".local/bin/claude" not in cmd + + +def test_warm_launch_honors_an_explicit_claude_bin(monkeypatch): + monkeypatch.setattr(cs, "CLAUDE_BIN", "/opt/agents/claude code") + cmd = cs._warm_launch(ag.CLAUDE) + assert "'/opt/agents/claude code' --model " in cmd + assert "command claude" not in cmd + + def test_resolve_warm_model_and_backend_respects_env(monkeypatch): monkeypatch.setenv("CLAUDE_CODE_USE_BEDROCK", "1") model, backend, pinned = cs._resolve_warm_model_and_backend()