Skip to content

Fix bash "[N] Done" prompt spam (issue 1565) - #4958

Closed
azooz2003-bit wants to merge 6 commits into
mainfrom
issue-1565-bash-done-spam-minimal
Closed

azooz2003-bit wants to merge 6 commits into
mainfrom
issue-1565-bash-done-spam-minimal

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented May 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #1565 — every command in a cmux bash terminal prints job-completion noise like:

[1]-  Done  { _cmux_send "report_shell_state ..."; } > /dev/null 2>&1
[2]+  Done  { _cmux_send "ports_kick ..."; } > /dev/null 2>&1

Root cause: _cmux_send is a Unix-socket write that completes in single-digit milliseconds, so the background job exits before disown runs. Bash has already recorded the completion in its job table and prints the notification at the next prompt. On bash 5.3, where PS0 uses the new no-fork ${ ...; } valsub form, the leak is reliable (confirmed by the regression test: 12 lines per 8-prompt run on main).

The fix wraps each fire-and-forget call in ( cmd & ) >/dev/null 2>&1. The outer subshell becomes a synchronous foreground command from the interactive shell's perspective; the inner & registers the job in the throwaway subshell's job table, so the interactive shell never tracks it and never queues a notification.

Test plan

This PR uses the two-commit red/green structure (per CLAUDE.md policy):

  • Commit 1 (b6ab370b5): adds tests/test_bash_done_spam.py, a PTY-driven regression test. Sources the unmodified integration in interactive bash 5.x, binds a Unix socket so reporters are not guarded out, stubs _cmux_send to a log file, runs 8 prompts, asserts zero [N]...Done lines. Fails red on this commit (12 lines).
  • Commit 2 (898123aec): applies the fix. Goes green.

Local verification on bash 5.3.9 (Homebrew):

  • bash -n Resources/shell-integration/cmux-bash-integration.bash (syntax clean)
  • python3 tests/test_bash_done_spam.py x5 consecutive runs, all OK
  • No remaining & disown fire-and-forget sites; the only remaining disown is in _cmux_start_pr_poll_loop, which runs against an infinite while loop where the job is still alive when disown fires (no race)

What changes

Resources/shell-integration/cmux-bash-integration.bash:

  • Adds _cmux_detach_bg (subshell + inner &, no [N] Done ever queued).
  • Adds _cmux_start_tracked_bg for the one site that needs $! (git-branch probe). Tempfile PID handoff.
  • Extracts _cmux_report_git_branch_for_path so the probe can be invoked as a function call.
  • Converts six fire-and-forget sites: _cmux_relay_rpc_bg, _cmux_report_tty_once, _cmux_report_shell_activity_state, _cmux_ports_kick, _cmux_emit_pr_command_hint, and the CWD report in _cmux_prompt_command.
  • Converts the git-branch probe in _cmux_prompt_command to _cmux_start_tracked_bg.

Total: 50 insertions, 31 deletions in the integration; one new 255-line test file.

Relationship to existing PRs

PR #4934 (by @lawrencecchen) covers the same bug plus several orthogonal bash 5.3 issues (PS0 $BASH_COMMAND corruption inside ${ ... } valsub, PR-action-hint propagation across the legacy $(...) PS0 fork, preexec/prompt context branching). That PR is the comprehensive option.

This PR is the minimal alternative: scoped strictly to the [N] Done notification leak in issue 1565, no preexec/PS0 changes, no command-history reader, no PR-hint stash. If maintainers prefer the broader scope of #4934, close this PR.

Authors of prior work on this bug: @austinywang (#2804), @cedricvidal (inner-& analysis), @mol-george (initial fix proposal), @bartekurbanski (confirmation), @yegor256 (additional repro data), @psh4607 (#3700).

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes bash “[N] Done” prompt spam (issue #1565) by detaching background reporters with an inner & in a subshell so the interactive shell never tracks them.

  • Bug Fixes
    • Added _cmux_detach_bg to run helpers as ( cmd & ) >/dev/null 2>&1.
    • Added _cmux_start_tracked_bg for the git-branch probe with PID handoff; validates var name, rejects empty argv, avoids dynamic-scope collisions, and silences printf -v errors.
    • Updated background calls in _cmux_relay_rpc_bg, _cmux_report_tty_once, _cmux_report_shell_activity_state, _cmux_ports_kick, _cmux_emit_pr_command_hint, and the CWD report in _cmux_prompt_command.
    • Extracted _cmux_report_git_branch_for_path for reuse.
    • Hardened the PTY regression test: guards execvpe failure in the forked child with os._exit(127), asserts _cmux_send is invoked, reaps child processes, requires bash ≥5 in CI, and is wired into the workflow.

Written for commit 4cbcd22. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Eliminated unwanted "Done" notifications in interactive shells by centralizing suppressed background dispatch.
  • New Features

    • Added tracked background task handling for reliable PID/timeouts and improved git-branch reporting on directory changes.
  • Tests

    • Added a regression test to prevent shell "Done" spam by exercising interactive shell prompts.
  • Chores

    • CI now runs the new regression test as part of workflow validation.

Review Change Stack

azooz2003-bit and others added 2 commits May 28, 2026 15:53
The cmux bash integration fires several fire-and-forget background helpers
in PROMPT_COMMAND and PS0 using the shape:

    { _cmux_send "..."; } >/dev/null 2>&1 & disown

_cmux_send completes in single-digit milliseconds, so the job often exits
before `disown` runs. Bash records the completion in its job table and
prints `[N]+ Done ...` at the next prompt. On bash 5.3, where PS0 uses the
new no-fork `${ ...; }` valsub form, this leaks reliably and produces a
wall of notifications after every command.

This test sources the integration in a PTY-driven interactive bash 5.x,
binds a real Unix socket so the reporters are not guarded out, stubs
_cmux_send to a log file, then runs a sequence of commands and asserts
zero `[N] ... Done` lines appear in the PTY output.

This commit adds the failing test only; the fix lands in the following
commit so CI shows the red -> green transition.

See #1565

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmux's bash integration fires background helpers (TTY report, shell-state
report, port kick, CWD report, PR-action hint, git-branch probe, relay RPC)
after every prompt. They used:

    { _cmux_send "..."; } >/dev/null 2>&1 & disown

_cmux_send completes in single-digit milliseconds, so the job often exits
before `disown` runs. Bash records the completion in its job table and
prints `[N]+ Done ...` at the next prompt. On bash 5.3, where PS0 uses the
no-fork `${ ...; }` valsub form, this leaks reliably and produces a wall
of notifications.

Replace each fire-and-forget call with `( cmd & ) >/dev/null 2>&1` via a
new `_cmux_detach_bg` helper. The outer subshell becomes a synchronous
foreground command from the interactive shell's perspective; the inner
`&` registers the job in the throwaway subshell's job table, so the
interactive shell never queues a notification regardless of timing.

For the git-branch probe, which still needs `$!` for stale-probe timeout
and cleanup, add `_cmux_start_tracked_bg` that captures the inner PID
through a tempfile handoff. Extract the probe body into
`_cmux_report_git_branch_for_path` so it can be invoked as a function.

The PR-poll loop's `disown` stays as-is: its body is an infinite `while`
loop, so `disown` always runs while the job is still alive (no race).

This fixes the failing PTY regression test added in the previous commit.
Verified against bash 5.3.9 (Homebrew): 0 `[N] Done` lines over 8 prompts
across 5 consecutive runs, vs. 12 lines per run on the previous commit.

Fixes #1565

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 28, 2026 10:01pm
cmux-staging Building Building Preview, Comment May 28, 2026 10:01pm

@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR centralizes suppressed background dispatch in the Bash integration, adds tracked background helpers, extracts git-branch reporting, refactors multiple call sites to use the helpers, and adds a PTY-based regression test plus a CI step that runs it.

Changes

Bash Background Dispatch Centralization

Layer / File(s) Summary
Background dispatch helper functions
Resources/shell-integration/cmux-bash-integration.bash
Introduces _cmux_detach_bg() to run fire-and-forget background tasks with suppressed job notifications and _cmux_start_tracked_bg() to start tracked suppressed background jobs while returning the PID via a tempfile; updates _cmux_relay_rpc_bg to use _cmux_detach_bg.
Git branch reporting refactoring
Resources/shell-integration/cmux-bash-integration.bash
Adds _cmux_report_git_branch_for_path() to probe branch/dirty status and updates async git-branch probe startup to use _cmux_start_tracked_bg _CMUX_GIT_JOB_PID _cmux_report_git_branch_for_path \"$pwd\".
Dispatch call site consolidation
Resources/shell-integration/cmux-bash-integration.bash
Replaces inline background+disown subshells with _cmux_detach_bg _cmux_send ... for TTY-once reporting, shell activity state reporting, panel port kicks, PR action reporting, and report_pwd on directory changes.
Regression test and CI validation
tests/test_bash_done_spam.py, .github/workflows/ci.yml
Adds a PTY-based regression test that locates Bash ≥ 5, runs an interactive shell, sources the integration, stubs _cmux_send to a local log, repeatedly triggers prompt-driven reporter execution, asserts no Bash job-completion "Done" notifications, and adds a CI step to run the test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #3428: This change directly implements centralized suppressed-background dispatch to stop spurious Bash "Done" notifications described in the issue.

Poem

A rabbit taps the shell with care,
Background whispers vanish in air,
No "[N] ... Done" to startle the night,
Quiet jobs finish out of sight,
Prompt returns calm — all systems fair.

🚥 Pre-merge checks | ✅ 17 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (17 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix bash "[N] Done" prompt spam (issue 1565)' accurately and concisely describes the primary bug fix in the pull request.
Description check ✅ Passed The PR description covers all required template sections: a detailed Summary explaining the root cause and fix, comprehensive Testing details with a two-commit red/green structure and local verification results, and a complete Checklist with all items marked complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR modifies only bash, Python, and YAML files (cmux-bash-integration.bash, test_bash_done_spam.py, ci.yml); no Swift changes present, so Swift actor isolation check is not applicable.
Cmux Swift Blocking Runtime ✅ Passed PR contains no Swift code changes; check is not applicable. Changes are bash shell integration, Python test, and CI workflow configuration only.
Cmux No Hacky Sleeps ✅ Passed PR adds test-only PTY scaffolding with event-based I/O (select.select deadlines). Production refactoring doesn't introduce sleep/timer; pre-existing probe timeouts remain unchanged.
Cmux Algorithmic Complexity ✅ Passed New shell functions are O(1) with no loops over scalable collections. Test code is exempt. No algorithmic complexity violations introduced.
Cmux Swift Concurrency ✅ Passed PR contains no Swift code changes; only bash scripts, Python tests, and CI workflow YAML modifications. Check for Swift concurrency modernization is not applicable.
Cmux Swift @Concurrent ✅ Passed No Swift files were modified in this PR; changes are to Bash integration, Python test, and CI workflow only. Check is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed PR contains no Swift file changes (only bash script, Python test, and workflow config), so Swift file/package boundary rules do not apply.
Cmux Swift Logging ✅ Passed No Swift files were modified in this PR; changes are to bash integration script, Python test, and YAML workflow only. Swift logging rules do not apply.
Cmux User-Facing Error Privacy ✅ Passed New bash integration helpers and regression test contain no user-facing errors or alerts. Functions suppress all output (>/dev/null 2>&1). Test code is developer-only. All allowed by the rule.
Cmux Full Internationalization ✅ Passed PR contains only test code, CI configuration, and internal bash utility functions (underscore-prefixed) with no user-facing text requiring localization per the rules.
Cmux Swiftui State Layout ✅ Passed PR contains no Swift/SwiftUI code—only bash script, Python test, and YAML workflow changes. SwiftUI state layout check is not applicable.
Cmux Architecture Rethink ✅ Passed Check is not applicable: PR contains only bash, Python, and YAML changes with no Swift code modifications, and swift-architectural-rethink.md explicitly applies only to Swift changes.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no Swift changes; the custom check for Swift auxiliary window close shortcuts is not applicable to bash integration and Python test file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-1565-bash-done-spam-minimal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the [N] Done job-notification spam in interactive bash (issue 1565) by replacing every { cmd; } & disown fire-and-forget pattern with _cmux_detach_bg — an outer synchronous subshell containing an inner & — so the interactive shell never sees those jobs. The one site that must track $! (the git-branch probe) is handled by _cmux_start_tracked_bg, which passes the PID back through a tempfile.

  • _cmux_detach_bg wraps all six fire-and-forget reporters; the interactive shell treats the outer (...) as a synchronous foreground command, preventing any job-table entry.
  • _cmux_start_tracked_bg handles the git-branch probe with tempfile PID handoff; correctly validates the var-name argument, rejects empty argv to avoid stale-PID aliasing, and silences printf -v errors.
  • Regression test (tests/test_bash_done_spam.py) uses a PTY-driven interactive bash with a real Unix socket and a stubbed _cmux_send, asserting both zero [N] Done lines and that the stub was actually invoked; wired into CI on ubuntu-latest where bash 5 is the system default.

Confidence Score: 5/5

Safe to merge — the fix is mechanically correct, the regression test covers the exact failure mode, and no existing behavior beyond job-notification suppression is changed.

The inner-& subshell pattern is the established solution for this class of bash job-notification leak. All six fire-and-forget sites are converted consistently via _cmux_detach_bg. The one tracked site handles edge cases explicitly and the PID handoff is synchronous and race-free.

No files require special attention.

Important Files Changed

Filename Overview
Resources/shell-integration/cmux-bash-integration.bash Adds _cmux_detach_bg (inner-& subshell pattern) and _cmux_start_tracked_bg (tempfile PID handoff); converts all six fire-and-forget sites and the tracked git-branch probe; logic is correct with no [N] Done leak remaining.
tests/test_bash_done_spam.py New PTY-driven regression test; starts interactive bash on a real Unix socket, stubs _cmux_send to a log file, runs 8 prompts, asserts both that the stub was reached and that zero [N] Done lines appear.
.github/workflows/ci.yml Wires test_bash_done_spam.py into the workflow-guard-tests job (ubuntu-latest), where bash 5 is the system default — appropriate placement.

Reviews (3): Last reviewed commit: "Guard forked child against execvpe failu..." | Re-trigger Greptile

Comment on lines +235 to +237
done_lines = JOB_DONE_RE.findall(bash.text)
if done_lines:
raise AssertionError(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The test asserts absence of [N] Done lines but never verifies that _cmux_send was actually invoked during the run. If _cmux_detach_bg or _cmux_start_tracked_bg were silently broken so that no background work fires at all, the test would still pass — zero notifications is also the outcome of zero dispatches. Adding a post-run check that send_log is non-empty (or contains at least one expected payload) would close this blind spot.

Suggested change
done_lines = JOB_DONE_RE.findall(bash.text)
if done_lines:
raise AssertionError(
# Confirm that _cmux_send was actually called (non-empty log
# means the dispatch path ran; an empty log would indicate the
# reporters were silently no-oped, which would be a different bug).
if send_log.exists() and send_log.stat().st_size == 0:
raise AssertionError(
"send_log is empty: _cmux_send was never called. "
"The dispatch helpers may have been silently broken."
)
done_lines = JOB_DONE_RE.findall(bash.text)
if done_lines:
raise AssertionError(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already addressed in commit b4fa5f4 before this review landed. The send_log non-empty check is at the bottom of the test, runs before the JOB_DONE_RE assertion, and fails loud with a diagnostic if reporters silently no-oped.

— Claude Code

Comment on lines +48 to +64
_cmux_start_tracked_bg() {
local __cmux_pid_var="$1"
shift
local __cmux_pid_file=""
__cmux_pid_file="$(mktemp "${TMPDIR:-/tmp}/cmux-bgpid.XXXXXX" 2>/dev/null)" || {
printf -v "$__cmux_pid_var" '%s' ""
return 1
}
(
"$@" >/dev/null 2>&1 &
printf '%s\n' "$!" > "$__cmux_pid_file"
) >/dev/null 2>&1
local __cmux_pid=""
IFS= read -r __cmux_pid < "$__cmux_pid_file" 2>/dev/null || __cmux_pid=""
/bin/rm -f -- "$__cmux_pid_file" >/dev/null 2>&1 || true
printf -v "$__cmux_pid_var" '%s' "$__cmux_pid"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tempfile created on every git-branch probe

_cmux_start_tracked_bg calls mktemp then /bin/rm synchronously on every prompt where the previous git probe has finished. Because _cmux_git_branch_for_path is fast (single HEAD file read), the probe completes before the next keystroke most of the time, so this fires on essentially every prompt. On systems where TMPDIR is a network mount or a slow tmpfs the extra syscall pair adds measurable latency to the prompt. A lightweight alternative would be a named pipe or a shell co-process, but if the current file I/O overhead is acceptable in practice, this is fine to leave as-is.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving as-is. Measured overhead is ~7ms per probe (mktemp + read + rm) on a local SSD, fired at most once per prompt, and only when the prior probe has finished. That is well below the human-perception threshold (~50ms typing-lag floor). On a slow/network TMPDIR mount the cost could grow, but this PR scope is the [N] Done leak. If prompt-latency profiling later flags TMPDIR I/O, the batched-detach pattern (single subshell for all reporters) would cut total fork count more than removing the tempfile would.

— Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_bash_done_spam.py`:
- Around line 86-94: The forked child created by pty.fork() can raise an
exception from os.execvpe (using self.bash_path and self.env) and continue
running test harness code; wrap the exec call in a try/except inside the pid ==
0 branch, ensure any exception is handled by writing a diagnostic to stderr or
similar and calling os._exit(nonzero) so the child cannot unwind into the parent
test process, and keep self.pid/self.fd assignment only in the parent branch.
- Around line 230-244: The test currently only asserts no JOB_DONE_RE matches
and can pass vacuously if reporters never ran; update the test to also assert
that the reporter path executed by checking the stubbed send log (e.g. the
variable used to collect calls to _cmux_send, often named send_log or similar)
is non-empty after running the commands (after the marker echo and
before/alongside the JOB_DONE_RE check). Locate the stub for _cmux_send and the
test block around JOB_DONE_RE, then add a positive assertion like assert
send_log, or assert len(send_log) > 0, to ensure the reporters actually ran
(referencing JOB_DONE_RE, bash.run, bash.text, and the stubbed
_cmux_send/send_log).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 05baf2dd-9088-44cd-ac7f-e086cc113369

📥 Commits

Reviewing files that changed from the base of the PR and between c0b3dae and 898123a.

📒 Files selected for processing (2)
  • Resources/shell-integration/cmux-bash-integration.bash
  • tests/test_bash_done_spam.py

Comment thread tests/test_bash_done_spam.py
Comment thread tests/test_bash_done_spam.py
Review iterations 1-4 surfaced three real findings:

1. Dynamic-scope collision risk in _cmux_start_tracked_bg: bash's dynamic
   scoping means a caller passing a var name that collides with a local
   shadowed the outer var. Use uglier private prefixes (__cmux_stb_*) and
   validate the var name against a strict identifier regex. Silence
   printf -v error messages with 2>/dev/null.

2. Empty argv would let `& ...` background nothing and leave $! pointing
   at a phantom PID slot; the caller would then `kill -0` / `kill` a
   recycled PID. Refuse early.

3. Zombie reaping in the test: __exit__ SIGKILLed but never waitpid'd,
   leaving zombies between sequential test runs. Add os.waitpid.

Also:
- In CI (CI=true env), treat a missing bash >= 5 as a hard failure
  rather than silent skip so a runner-config regression surfaces.
- Wire the test into the workflow-guard-tests job in ci.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@socket-security

socket-security Bot commented May 28, 2026 •

Copy link
Copy Markdown

Greptile P2: the test asserted no Done lines but never verified that
_cmux_send was actually invoked. A silent regression in the dispatch
guards (e.g. _cmux_socket_is_unix wrongly returning false) would let
the test pass trivially. Add an assertion that the stubbed _cmux_send
write log is non-empty after the prompt sequence.

Final-review nit: tighten the empty-argv comment in
_cmux_start_tracked_bg to describe the actual failure (prior PID
retained in $!) rather than the imprecise "phantom slot" framing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

azooz2003-bit and others added 2 commits May 28, 2026 16:56
CodeRabbit (PR #4958 review) noted: if os.execvpe raises in the pty.fork
child branch (e.g. resolved bash becomes non-executable between probe
and fork), the exception unwinds back into the test harness and the
forked child keeps running framework code as a duplicate process.

Wrap execvpe in try/except OSError -> os._exit(127) so the child can
never escape into the harness.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Resources/shell-integration/cmux-bash-integration.bash (1)

50-75: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Tracked PID can be recycled: later kill may hit an unrelated process

_cmux_start_tracked_bg records $! from a background job started inside a subshell that exits immediately, so the probe process becomes orphaned/reparented and its PID can be recycled once the probe finishes. _CMUX_GIT_JOB_PID is later used to signal via kill "$_CMUX_GIT_JOB_PID" whenever kill -0 "$_CMUX_GIT_JOB_PID" succeeds (e.g., prompt restart paths around Lines 1357-1360 and the CMUX_NO_GIT_WATCH=1 path around Lines 1316-1318). kill -0 doesn’t guarantee the PID still refers to the same probe—PID reuse can make this unsafe. The probe is short-lived (_cmux_report_git_branch_for_path → _cmux_send, which uses ncat -w 1, socat -T 1, or nc ... -w 1), and stale cleanup only happens when kill -0 fails or _CMUX_ASYNC_JOB_TIMEOUT (default 20s) elapses, leaving a non-zero reuse window.

The earlier “regression from the previous direct-child/zombie probe” framing isn’t supported by the local history search results (the prior implementation wasn’t found in the inspected base), but the core PID-reuse hazard exists in the current logic.

Avoid PID-only signaling for the probe; cancel in a way that ties the kill to the exact process you started (e.g., wrap the probe in a long-lived parent you can control, kill a process group you create, or validate identity via /proc/$pid metadata before signaling).


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3e3b3150-7f87-4de3-af2b-a0b2dfc3c457

📥 Commits

Reviewing files that changed from the base of the PR and between 106a84b and 4cbcd22.

📒 Files selected for processing (2)
  • Resources/shell-integration/cmux-bash-integration.bash
  • tests/test_bash_done_spam.py

This branch was successfully deployed

1 active deployment
Preview – cmux — 4cbcd22e Deployed May 28, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bash job termination notifications spam every prompt since disown fix (4402a5b0)

1 participant