WORKING: Fix split CWD inheritance and bash job spam - #1
Conversation
- Pass inherited working directory when creating split panes (panelDirectories fallback to currentDirectory) - Suppress bash job-done "[N] Done ..." notifications in shell integration by toggling job control (set +m / set -m) around background probes - Add integration test for split/tab CWD inheritance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR detaches several asynchronous bash background tasks by adding Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
|
@codex @coderabbitai @cursor @copilot [AI automation] Codex will implement the code updates while coderabbitai, cursor, and copilot focus on review support. Please make the following changes to this PR. Summary (Execution Flow):
PR Details:
Instructions: Tasks:
Automation Markers:
|
Rate Limit Exceeded
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
tests/test_split_cwd_inheritance.py (1)
41-55: Don’t swallow all predicate errors in_wait_for.Line 49 catches every exception, which can mask real test bugs and only surface as a timeout. Catch expected transport/polling failures and let unexpected exceptions fail fast.
🔧 Proposed fix
def _wait_for(predicate, timeout: float, interval: float, label: str): - start = time.time() + start = time.monotonic() last_error: Exception | None = None - while time.time() - start < timeout: + while time.monotonic() - start < timeout: try: value = predicate() if value: return value - except Exception as e: + except cmuxError as e: last_error = e time.sleep(interval)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_split_cwd_inheritance.py` around lines 41 - 55, The helper _wait_for currently swallows all exceptions (except timing out) by catching Exception; change it to only catch expected transient errors and re-raise any unexpected ones. Update the except block in _wait_for (where last_error is set) to either accept an explicit allowed_exceptions parameter (e.g., allowed_exceptions: tuple[type[Exception], ...] defaulting to common transient types like (ConnectionError, OSError, TimeoutError)) and use "except allowed_exceptions as e: last_error = e", or keep a hardcoded tuple of transient exceptions and add a bare "except Exception: raise" to re-raise unexpected exceptions; keep the rest of the retry logic and the final AssertionError behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/test_split_cwd_inheritance.py`:
- Line 131: Remove the unused exception binding "as e" from the AssertionError
handlers in the test file to satisfy Ruff F841; locate the two except blocks
that read "except AssertionError as e:" and change them to "except
AssertionError:" so the exception is still caught but the unused variable is not
declared (this applies to both occurrences noted in the diff).
- Around line 83-93: Replace the hardcoded /tmp dirs with isolated
TemporaryDirectory instances (use tempfile.TemporaryDirectory() to create
test_dir_a and test_dir_b and get resolved paths) and wrap the test logic that
runs after client.connect() in a try/finally so client.close() is always called
and directories are always cleaned up (or rely on the TemporaryDirectory context
to tear down). Ensure calls that may raise (e.g., _send_cd_and_wait,
_wait_for_focused_cwd, client.new_split()) are inside the try block and that the
finally calls client.close() (and any explicit cleanup if not using
TemporaryDirectory). Also remove unused exception variables from the test’s
except clauses so no unused names remain.
---
Nitpick comments:
In `@tests/test_split_cwd_inheritance.py`:
- Around line 41-55: The helper _wait_for currently swallows all exceptions
(except timing out) by catching Exception; change it to only catch expected
transient errors and re-raise any unexpected ones. Update the except block in
_wait_for (where last_error is set) to either accept an explicit
allowed_exceptions parameter (e.g., allowed_exceptions: tuple[type[Exception],
...] defaulting to common transient types like (ConnectionError, OSError,
TimeoutError)) and use "except allowed_exceptions as e: last_error = e", or keep
a hardcoded tuple of transient exceptions and add a bare "except Exception:
raise" to re-raise unexpected exceptions; keep the rest of the retry logic and
the final AssertionError behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 026d5f0c-02ac-46ea-9b65-6023a9932ace
📒 Files selected for processing (3)
Resources/shell-integration/cmux-bash-integration.bashSources/Workspace.swifttests/test_split_cwd_inheritance.py
| socket_path = None | ||
| if tag: | ||
| socket_path = f"/tmp/cmux-debug-{tag}.sock" | ||
| client = cmux(socket_path=socket_path) | ||
| client.connect() | ||
|
|
||
| # Use resolved paths to avoid /tmp -> /private/tmp symlink mismatch on macOS | ||
| test_dir_a = str(Path("/tmp/cmux_split_cwd_test_a").resolve()) | ||
| test_dir_b = str(Path("/tmp/cmux_split_cwd_test_b").resolve()) | ||
| os.makedirs(test_dir_a, exist_ok=True) | ||
| os.makedirs(test_dir_b, exist_ok=True) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "test_split_cwd_inheritance.py" -type fRepository: jleechanorg/cmux
Length of output: 97
🏁 Script executed:
wc -l tests/test_split_cwd_inheritance.pyRepository: jleechanorg/cmux
Length of output: 99
🏁 Script executed:
cat -n tests/test_split_cwd_inheritance.py | sed -n '80,170p'Repository: jleechanorg/cmux
Length of output: 3967
Use isolated temp dirs and guaranteed teardown for test reliability.
Lines 90–91 use hardcoded /tmp paths that collide across concurrent test runs. More critically, if any operation before line 157 fails (e.g., in _send_cd_and_wait, _wait_for_focused_cwd, or client.new_split()), both client.close() and directory cleanup are skipped, leaking resources. Wrap the entire test logic in try/finally to guarantee teardown, and use tempfile.TemporaryDirectory for isolation. Also remove the unused exception variables at lines 131 and 150.
Proposed fix
import os
import sys
import time
+import tempfile
from pathlib import Path
@@
- # Use resolved paths to avoid /tmp -> /private/tmp symlink mismatch on macOS
- test_dir_a = str(Path("/tmp/cmux_split_cwd_test_a").resolve())
- test_dir_b = str(Path("/tmp/cmux_split_cwd_test_b").resolve())
- os.makedirs(test_dir_a, exist_ok=True)
- os.makedirs(test_dir_b, exist_ok=True)
+ temp_root = tempfile.TemporaryDirectory(prefix="cmux_split_cwd_")
+ try:
+ root = Path(temp_root.name).resolve()
+ test_dir_a = str((root / "a").resolve())
+ test_dir_b = str((root / "b").resolve())
+ os.makedirs(test_dir_a, exist_ok=True)
+ os.makedirs(test_dir_b, exist_ok=True)
@@
- try:
- state = _wait_for_focused_cwd(client, test_dir_a, timeout=15.0)
- check("test1: split inherited test_dir_a", True)
- except AssertionError as e:
+ # ... test logic ...
+ try:
+ state = _wait_for_focused_cwd(client, test_dir_a, timeout=15.0)
+ check("test1: split inherited test_dir_a", True)
+ except AssertionError:
@@
- try:
- state = _wait_for_focused_cwd(client, test_dir_b, timeout=15.0)
- check("test2: new workspace inherited test_dir_b", True)
- except AssertionError as e:
+ try:
+ state = _wait_for_focused_cwd(client, test_dir_b, timeout=15.0)
+ check("test2: new workspace inherited test_dir_b", True)
+ except AssertionError:
@@
- print(f"\n{passed} passed, {failed} failed")
-
- client.close()
-
- # Cleanup
- for d in [test_dir_a, test_dir_b]:
- try:
- os.rmdir(d)
- except OSError:
- pass
+ print(f"\n{passed} passed, {failed} failed")
+ finally:
+ client.close()
+ temp_root.cleanup()🧰 Tools
🪛 Ruff (0.15.2)
[error] 85-85: Probable insecure usage of temporary file or directory: "/tmp/cmux-debug-"
(S108)
[error] 90-90: Probable insecure usage of temporary file or directory: "/tmp/cmux_split_cwd_test_a"
(S108)
[error] 91-91: Probable insecure usage of temporary file or directory: "/tmp/cmux_split_cwd_test_b"
(S108)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_split_cwd_inheritance.py` around lines 83 - 93, Replace the
hardcoded /tmp dirs with isolated TemporaryDirectory instances (use
tempfile.TemporaryDirectory() to create test_dir_a and test_dir_b and get
resolved paths) and wrap the test logic that runs after client.connect() in a
try/finally so client.close() is always called and directories are always
cleaned up (or rely on the TemporaryDirectory context to tear down). Ensure
calls that may raise (e.g., _send_cd_and_wait, _wait_for_focused_cwd,
client.new_split()) are inside the try block and that the finally calls
client.close() (and any explicit cleanup if not using TemporaryDirectory). Also
remove unused exception variables from the test’s except clauses so no unused
names remain.
set +m only suppresses notifications for jobs started after it runs. Jobs that complete between prompts still trigger Done output. Using disown removes jobs from bash job table entirely so bash never prints completion notifications for them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses terminal UX correctness by ensuring new splits inherit the expected working directory, and reduces interactive noise from bash shell integration background probes.
Changes:
- Pass an inherited
workingDirectorywhen creating new terminal splits (prefer per-panel reported cwd, fallback to workspace current directory). - Suppress bash
[N] Done ...job notifications by temporarily disabling job control during prompt-driven background probes and restoring it afterward. - Add an end-to-end socket-driven integration test to validate split and new-tab/workspace CWD inheritance behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
Sources/Workspace.swift |
Resolves and passes an inherited working directory into TerminalPanel creation for splits. |
Resources/shell-integration/cmux-bash-integration.bash |
Disables job control while spawning background probe jobs to avoid “Done” spam, then restores prior state. |
tests/test_split_cwd_inheritance.py |
New integration test intended to validate CWD inheritance for splits and new tabs via socket APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| print(" [test1] creating right split from test_dir_a...") | ||
| split_result = client.new_split("right") | ||
| check("split created", bool(split_result)) | ||
|
|
||
| # Wait for the new pane's shell to start and report cwd. | ||
| # The split should inherit test_dir_a from the source pane. | ||
| time.sleep(4) # wait for new bash to start + run PROMPT_COMMAND |
There was a problem hiding this comment.
This test doesn’t currently guarantee it’s asserting the new split pane’s working directory. It waits for focused_cwd == test_dir_a, but that will also be true if focus never moved off the original pane (which is already in test_dir_a), so the test can pass even when the split starts in the wrong directory. Consider capturing focused_panel before the split and asserting it changes, or use new_split()’s returned panel id / list_pane_surfaces + focus_surface to explicitly focus the new surface before checking focused_cwd.
| print(" [test2] cd to test_dir_b, then creating new workspace tab...") | ||
| _send_cd_and_wait(client, test_dir_b) | ||
|
|
||
| tab_result = client.new_tab() | ||
| check("new tab created", bool(tab_result)) | ||
|
|
||
| # New workspace should inherit test_dir_b from the previous workspace | ||
| time.sleep(4) | ||
| try: | ||
| state = _wait_for_focused_cwd(client, test_dir_b, timeout=15.0) | ||
| check("test2: new workspace inherited test_dir_b", True) |
There was a problem hiding this comment.
Similar to test1, this check can pass without validating the new workspace tab’s inherited CWD: focused_cwd == test_dir_b is already true in the current workspace right after _send_cd_and_wait, even if new_tab() doesn’t select the new tab (or if selection is delayed). To make the test sound, assert that the current tab/workspace id changes (e.g., via current_tab()/current_workspace()), or explicitly select_tab(tab_result) before waiting on focused_cwd.
|
@CodeRabbit-ai @greptileai @BugBot @copilot Summary (Review Flow):
PR Details:
Instructions: Tasks:
🎯 REQUIRED OUTPUT FORMAT (MANDATORY): Your response MUST follow this exact structure for clarity: ## ✅ AUTOMATION COMPLETE — [STATUS: READY FOR MERGE / NEEDS WORK / BLOCKED]
**Verified at commit `4402a5b0` (4402a5b0ed46d44c4de81430f6b3dca16b268fe1)**
---
## 📊 Current Status: **[PERFECT/ISSUES FOUND]** [✅/⚠️/❌]
| Category | Status |
|----------|--------|
| **CI Checks** | [✅ X/Y PASSING / ❌ FAILING] |
| **Merge Status** | [✅ CLEAN / ❌ CONFLICTS] |
| **PR State** | [✅ MERGEABLE / ❌ BLOCKED] |
| **Comments Addressed** | [✅ ALL DONE / ⚠️ X UNRESOLVED] |
| **Code Quality** | [✅ GOOD / ⚠️ ISSUES] |
---
## 🎯 Comment Resolution Summary
[List each outstanding comment and whether it was addressed - check for code fixes OR consolidated response summary]
---
## 🚀 **FINAL VERDICT: [APPROVE / REQUEST CHANGES / BLOCKED]**
[Brief explanation of decision]
---
🐰 [Sign-off emoji and message]IMPORTANT: Do NOT include raw script execution output or analysis chain details in your response. Only provide the clean formatted summary above. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 648f4c00db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| fi | ||
|
|
||
| # Restore job control if it was previously enabled. | ||
| [[ -n "$_cmux_old_monitor" ]] && set -m |
There was a problem hiding this comment.
Avoid re-enabling monitor before probe jobs finish
Re-enabling job control at the end of _cmux_prompt_command does not actually prevent [N] Done ... noise for the background probe jobs launched earlier in the same function: in interactive bash with monitor mode enabled, those jobs are still tracked and will be reported on the next prompt once set -m is restored. This means the intended spam suppression is ineffective in the normal case where git/gh probes complete after the prompt hook returns.
Useful? React with 👍 / 👎.
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 4338
✅ AUTOMATION COMPLETE — STATUS: NEEDS WORKVerified at commit 📊 Current Status: ISSUES FOUND
|
| Category | Status |
|---|---|
| CI Checks | |
| Merge Status | ✅ CLEAN (no conflicts) |
| PR State | |
| Comments Addressed | ❌ 5 UNRESOLVED (no code fixes or consolidated response found) |
| Code Quality |
🎯 Comment Resolution Summary
| # | Reviewer | Severity | Comment | Status |
|---|---|---|---|---|
| 1 | coderabbitai[bot] |
🟠 Major | Issue in tests/test_split_cwd_inheritance.py (analysis chain flagged) |
❌ Unresolved |
| 2 | coderabbitai[bot] |
🟡 Minor | Remove unused as e exception bindings to satisfy Ruff F841 |
❌ Unresolved |
| 3 | Copilot |
test1 doesn't guarantee the new split pane's CWD is verified — focused_cwd == test_dir_a can pass trivially if focus never moved off the original pane |
❌ Unresolved | |
| 4 | Copilot |
test2 same flaw — tab CWD check can pass without validating the new tab's inherited CWD |
❌ Unresolved | |
| 5 | chatgpt-codex-connector[bot] |
🟡 P2 | Avoid re-enabling job-control (set -m) in _cmux_prompt_command before probe jobs finish — race condition risk |
❌ Unresolved |
No consolidated "[AI responder] Consolidated Comment Response Summary" was found in the PR thread. None of the above comments appear to have been addressed via code changes or acknowledged replies as of commit 4402a5b0.
Key concerns to prioritize:
- Test correctness (Copilot
#3): Both split and tab inheritance tests may produce false positives. The tests need to explicitly verify CWD after focus switches to the new pane/tab, not just check the current focused CWD before the split/tab opens. - Shell integration race (chatgpt-codex-connector
#5): Re-enablingset -mat the end of_cmux_prompt_commandbefore background probe jobs finish may still trigger "Done" notifications — the fix may be incomplete. - Ruff F841 (
#2): Minor cleanup needed for unusedas eexception bindings.
🚀 FINAL VERDICT: REQUEST CHANGES
The PR's core logic (CWD inheritance fallback chain and set +m/set -m bracketing) is directionally sound, but the integration tests have correctness gaps that could mask regressions, the job-control fix has a potential race condition, and 5 review comments remain unaddressed. CI checks are still pending. Please address the open comments — particularly the test validity issues and the shell integration race — before merging.
🐰 Reviewed by CodeRabbit — helping keep the warren tidy, one pane at a time.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Resources/shell-integration/cmux-bash-integration.bash (1)
65-65: Disown with explicit PIDs instead of implicit "current job" to avoid ambiguity.Lines 65, 77, 126, 157, and 201 use bare
disown. For lines 65, 77, and 126, capture$!and pass it explicitly. For lines 157 and 201, use the stored PID variables_CMUX_GIT_JOB_PIDand_CMUX_PR_JOB_PIDrespectively to ensure the correct job is detached and suppress non-critical stderr noise.Proposed patch
- } >/dev/null 2>&1 & disown + } >/dev/null 2>&1 & + disown $! 2>/dev/null || true ... - } >/dev/null 2>&1 & disown + } >/dev/null 2>&1 & + disown $! 2>/dev/null || true ... - } >/dev/null 2>&1 & disown + } >/dev/null 2>&1 & + disown $! 2>/dev/null || true ... _CMUX_GIT_JOB_PID=$! - disown + disown "$_CMUX_GIT_JOB_PID" 2>/dev/null || true ... _CMUX_PR_JOB_PID=$! - disown + disown "$_CMUX_PR_JOB_PID" 2>/dev/null || trueAlso applies to: 77-77, 126-126, 157-157, 201-201
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Resources/shell-integration/cmux-bash-integration.bash` at line 65, Replace bare disown calls with explicit PID-based disowning: when launching background jobs at the sites that currently use "}&>/dev/null 2>&1 & disown" capture the spawned PID into a variable (e.g., pid=$!) immediately after starting the background job and call disown "$pid" (or disown -h "$pid" if you only want to remove job control without killing) instead of a bare disown; for the two locations already using stored PIDs, call disown "$_CMUX_GIT_JOB_PID" and disown "$_CMUX_PR_JOB_PID" respectively so the correct jobs are detached and stderr noise is suppressed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@Resources/shell-integration/cmux-bash-integration.bash`:
- Line 65: Replace bare disown calls with explicit PID-based disowning: when
launching background jobs at the sites that currently use "}&>/dev/null 2>&1 &
disown" capture the spawned PID into a variable (e.g., pid=$!) immediately after
starting the background job and call disown "$pid" (or disown -h "$pid" if you
only want to remove job control without killing) instead of a bare disown; for
the two locations already using stored PIDs, call disown "$_CMUX_GIT_JOB_PID"
and disown "$_CMUX_PR_JOB_PID" respectively so the correct jobs are detached and
stderr noise is suppressed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e614d98e-9112-4401-8e1a-79fce61b4242
📒 Files selected for processing (1)
Resources/shell-integration/cmux-bash-integration.bash
…g CWD Address PR review comments: - Test1: record original panel ID, wait for a *different* panel to report the expected CWD — prevents false pass when focus stays on source pane - Test2: record original tab ID, wait for a *different* tab with the expected CWD — prevents false pass when checking the old workspace - Remove unused `as e` exception bindings (Ruff F841) - Shell race (set -m) was already fixed in 4402a5b via disown Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidated Comment Response SummaryAddressing all 5 review items from the consolidated review at commit 1. Test correctness — split test false positive (Copilot #3) ✅ FixedTest now records 2. Test correctness — tab test false positive (Copilot #4) ✅ FixedTest now records 3. Unused
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_split_cwd_inheritance.py (1)
25-25: Unused import:cmuxError.
cmuxErroris imported but never used in this file.Proposed fix
-from cmux import cmux, cmuxError # noqa: E402 +from cmux import cmux # noqa: E402🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_split_cwd_inheritance.py` at line 25, Remove the unused symbol import by deleting `cmuxError` from the import statement that currently imports `cmux` and `cmuxError` (i.e., update the `from cmux import cmux, cmuxError` import so it only imports `cmux`), ensuring only used symbols remain imported and no other changes to the test logic in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/test_split_cwd_inheritance.py`:
- Line 25: Remove the unused symbol import by deleting `cmuxError` from the
import statement that currently imports `cmux` and `cmuxError` (i.e., update the
`from cmux import cmux, cmuxError` import so it only imports `cmux`), ensuring
only used symbols remain imported and no other changes to the test logic in this
file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 938e9de4-9d1a-463a-b70a-94ed2f7e55d2
📒 Files selected for processing (1)
tests/test_split_cwd_inheritance.py
|
@CodeRabbit-ai @greptileai @BugBot @copilot Summary (Review Flow):
PR Details:
Instructions: Tasks:
🎯 REQUIRED OUTPUT FORMAT (MANDATORY): Your response MUST follow this exact structure for clarity: ## ✅ AUTOMATION COMPLETE — [STATUS: READY FOR MERGE / NEEDS WORK / BLOCKED]
**Verified at commit `00451417` (00451417c2fecb5a075efff78b031a68380e7849)**
---
## 📊 Current Status: **[PERFECT/ISSUES FOUND]** [✅/⚠️/❌]
| Category | Status |
|----------|--------|
| **CI Checks** | [✅ X/Y PASSING / ❌ FAILING] |
| **Merge Status** | [✅ CLEAN / ❌ CONFLICTS] |
| **PR State** | [✅ MERGEABLE / ❌ BLOCKED] |
| **Comments Addressed** | [✅ ALL DONE / ⚠️ X UNRESOLVED] |
| **Code Quality** | [✅ GOOD / ⚠️ ISSUES] |
---
## 🎯 Comment Resolution Summary
[List each outstanding comment and whether it was addressed - check for code fixes OR consolidated response summary]
---
## 🚀 **FINAL VERDICT: [APPROVE / REQUEST CHANGES / BLOCKED]**
[Brief explanation of decision]
---
🐰 [Sign-off emoji and message]IMPORTANT: Do NOT include raw script execution output or analysis chain details in your response. Only provide the clean formatted summary above. |
Rate Limit Exceeded
|
Visual proof of fix: split panes inherit CWD from the source panel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Ghostty upgrade made paste_from_clipboard a performable binding. In performKeyEquivalent, the !isPerformable guard prevented the menu from handling Cmd+V, so keyDown was called directly. Inside keyDown, interpretKeyEvents triggered paste: (clipboard request #1) and then ghostty_surface_key fired the same binding (clipboard request #2), causing a double-paste race that corrupted the output. Remove the !isPerformable exclusion so performable bindings like paste also try the Edit menu first, restoring the single-request flow.
- Apply redactClaudeSensitiveSpans to message before classifyNotification so paths inside stringified error objects are redacted (CodeRabbit #1) - Add regression test testSummarizeJSONPathRedactsStringifiedError - Strengthen testSummarizeNonStringErrorPayload: use AND instead of OR to verify both fields appear in stringified error payload (CodeRabbit #2) - Update cmux.swift call site for rawInput parameter Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… polish - #4 (toggle for agents launched mid-session): the Mac now adopts a detected agent the instant its terminal title becomes the agent's (AgentChatTranscriptService observes .ghosttyDidSetTitle and calls TerminalController.adoptDetectedAgentSessions), so the session registers and pushes to the phone live, not only on next open. - #1 (scroll-to-bottom no longer dismisses keyboard): the dismiss tap excludes the button's frame (ChatScrollButtonFramePreferenceKey + excludedRegion). - #3 (smooth scroll): the button does a single animated proxy.scrollTo to the bottom anchor instead of stacked non-animated jumps. - #5 (toggle eases in): the session-list update is wrapped in withAnimation. - #2 (cramped grouping): intraGroupSpacing 2 -> 5 so a code block isn't flush against the next message bubble. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Demo
cd /tmp/cmux-cwd-demo+ right split createdpwdconfirming inherited CWDSummary
panelDirectories[panelId]→currentDirectoryfallback chain)[N] Done ...notifications from background shell integration probes via& disowntests/test_split_cwd_inheritance.pyto verify split and tab CWD inheritance via real socketsTest plan
split.cwd resolved=<correct path>[N] Done ...spam in bash terminals🤖 Generated with Claude Code