From 279b8b91e7636c24021c9d8d02dc637520fd542c Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:18:29 -0800 Subject: [PATCH] Fix workspace-scoped tab action resolution --- CLI/cmux.swift | 10 +++++++++- tests_v2/test_tab_workspace_action_naming.py | 16 ++++++++++++++++ vendor/bonsplit | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CLI/cmux.swift b/CLI/cmux.swift index 168540a2..1873e352 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -1679,7 +1679,15 @@ struct CMUXCLI { : nil) let workspaceId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true) - let surfaceId = try normalizeTabHandle(tabArg, client: client, workspaceHandle: workspaceId, allowFocused: true) + // If a workspace is explicitly targeted and no tab/surface is provided, let server-side + // tab.action resolve that workspace's focused tab instead of using global focus. + let allowFocusedFallback = (workspaceId == nil) + let surfaceId = try normalizeTabHandle( + tabArg, + client: client, + workspaceHandle: workspaceId, + allowFocused: allowFocusedFallback + ) let inferredTitle = positional.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines) let title = (titleOpt ?? (inferredTitle.isEmpty ? nil : inferredTitle))?.trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/tests_v2/test_tab_workspace_action_naming.py b/tests_v2/test_tab_workspace_action_naming.py index 5a7a2c14..6b3f4805 100644 --- a/tests_v2/test_tab_workspace_action_naming.py +++ b/tests_v2/test_tab_workspace_action_naming.py @@ -125,6 +125,22 @@ def main() -> int: ws_other = str(other_created.get("workspace_id") or "") _must(bool(ws_other), f"workspace.create (second) returned no workspace_id: {other_created}") c._call("workspace.select", {"workspace_id": ws_other}) + ws_target_ref = "" + ws_list = c._call("workspace.list", {}) or {} + for row in ws_list.get("workspaces") or []: + if str(row.get("id") or "") == ws_id: + ws_target_ref = str(row.get("ref") or "") + break + + # Regression: workspace-scoped tab-action without --tab should target that workspace, + # not whichever tab is globally focused in another workspace. + cli_scoped = _run_cli_json(cli, ["tab-action", "--workspace", ws_id, "--action", "mark-unread"]) + _must(str(cli_scoped.get("tab_ref") or "").startswith("tab:"), f"Expected tab_ref in scoped tab-action result: {cli_scoped}") + got_scoped_workspace = str(cli_scoped.get("workspace_id") or cli_scoped.get("workspace_ref") or "") + _must( + got_scoped_workspace in {x for x in [ws_id, ws_target_ref] if x}, + f"workspace-scoped tab-action should resolve target workspace: {cli_scoped}", + ) # Regression: tab_id alone should resolve both tab manager + workspace, even when another workspace is selected. by_tab_only = c._call("tab.action", {"tab_id": tab_ref, "action": "mark_unread"}) or {} diff --git a/vendor/bonsplit b/vendor/bonsplit index af9461ef..dd20247b 160000 --- a/vendor/bonsplit +++ b/vendor/bonsplit @@ -1 +1 @@ -Subproject commit af9461efdb4c84f901f27fac9e4838b2d297d842 +Subproject commit dd20247b5536b4bd5b9b15cdf940e847daa1a18d