Skip to content

Fix #3428: suppress spurious 'Done' job notifications from bash integration - #3700

Closed
psh4607 wants to merge 3 commits into
manaflow-ai:mainfrom
psh4607:fix/3428-bash-done-notifications
Closed

psh4607 wants to merge 3 commits into
manaflow-ai:mainfrom
psh4607:fix/3428-bash-done-notifications

Conversation

@psh4607

@psh4607 psh4607 commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Status: superseded by current main

This PR originally fixed spurious Bash [N] Done notifications by moving cmux's fire-and-forget shell reporters away from parent-shell job tracking.

After refreshing the branch against current main, the PR has no remaining diff. main already contains a more general shared implementation through _cmux_detach_bg / _cmux_send_bg, and the PTY-based tests/test_bash_integration_no_done_notifications.py regression coverage is already present there.

The conflict was resolved by keeping the current shared implementation rather than restoring the older duplicated call-site changes.

Verification

  • Final PR diff: 0 files.
  • Tagged Debug app build succeeded with Zig 0.15.2.
  • GitHub reports the refreshed head as mergeable.
  • Current bot reviews report no actionable findings.

Recommended disposition: close as superseded because there is nothing left to merge.

Refs #3428 and #1565

Copilot AI review requested due to automatic review settings May 7, 2026 11:55
@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

@psh4607 is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Replaces { cmd } & disown with ( cmd & ) in five async reporter call sites in the bash integration to prevent interactive-shell "Done" notifications; adds a PTY-based Python regression test exercising those sites and runs it as a CI guard step.

Changes

Bash Async Backgrounding Fix & Regression Test

Layer / File(s) Summary
Async Backgrounding Fix
Resources/shell-integration/cmux-bash-integration.bash
Five _cmux_send call sites changed from { ... } & disown to ( _cmux_send ... & ) to confine background jobs to a subshell and prevent "Done" job notifications (TTY report, shell-state, ports-kick, PR hint, prompt CWD).
Test Header, Regexes & Driver Template
tests/test_bash_integration_no_done_notifications.py (lines 1–97)
Adds executable header and docstring; defines ANSI-stripping and "Done" detection regexes and the embedded bash driver template that sources the integration and calls reporter functions.
PTY Capture Implementation
tests/test_bash_integration_no_done_notifications.py (lines 99–203)
_capture() forks a PTY, runs interactive bash with LANG=C and a controlled environment, streams output with timeout/EIO handling, strips ANSI escapes, performs cleanup, and returns the cleaned transcript.
Assertions & CLI Entrypoint
tests/test_bash_integration_no_done_notifications.py (lines 205–244)
main() locates bash/integration, runs _capture, verifies __PROBE_END__, fails with diagnostics if any "Done" job-control lines are found, otherwise prints PASS; adds if __name__ == "__main__": raise SystemExit(main()).
CI Guard Step
.github/workflows/ci.yml (lines 57–59)
Adds a workflow-guard-tests step that runs python3 tests/test_bash_integration_no_done_notifications.py to validate no spurious "Done" notifications.

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

codex

🐰 I nudged the shell to hush its cheer,
Subshells now hum where jobs disappear,
The PTY watched the markers, calm and bright,
No "Done" at prompt — quiet through the night,
A jitter-free hop, a soft moonlight.

🚥 Pre-merge checks | ✅ 12 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description is missing the required template sections for Summary, Demo Video, Review Trigger, and Checklist. Rewrite the description to follow the repository template and add the missing sections, or explicitly note why each is not applicable.
✅ Passed checks (12 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main fix: suppressing spurious 'Done' job notifications from bash integration, directly addressing issue #3428.
Linked Issues check ✅ Passed All coding requirements from #3428 and #1565 are met: five reporter sites in cmux-bash-integration.bash converted from { ... } >/dev/null 2>&1 & disown to ( ... & ) >/dev/null 2>&1; behavioral regression test added; CI integration added.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the five reporter sites and the regression test. zsh integration, git-branch probe, and PR poll loop are correctly left out of scope as documented in the PR description.
Cmux Swift Actor Isolation ✅ Passed PR contains no Swift code changes—only YAML, Bash, and Python modifications. Swift actor isolation check only applies to production Swift changes, which are absent.
Cmux Swift Blocking Runtime ✅ Passed PR contains no Swift file modifications. Changes are limited to bash shell integration, Python test, and CI configuration. Check is not applicable.
Cmux No Hacky Sleeps ✅ Passed PR does not violate runtime-no-hacky-sleeps rule. Sleeps in test file are deterministic test-only scaffolding (allowed). Production bash sleeps are existing code not introduced by this PR.
Cmux Swift Concurrency ✅ Passed Custom check for Swift concurrency patterns is not applicable. PR modifies only non-Swift code: bash shell integration, Python test, and CI workflow YAML. No Swift code changes present.
Cmux Swift @Concurrent ✅ Passed PR contains no Swift file changes. Custom check for @concurrent annotations on Swift code is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files are modified in this PR. Changes are limited to YAML workflow config, Bash shell integration, and Python test code. The custom check for Swift file/package boundaries is not applicable.
Cmux Swift Logging ✅ Passed Custom check for Swift logging is not applicable. PR contains only YAML, Bash, and Python changes—no Swift code modifications.
Cmux Swiftui State Layout ✅ Passed Custom check for SwiftUI state layout is not applicable. PR contains no Swift/SwiftUI code changes—only bash shell script modifications, Python test additions, and CI workflow updates.
Cmux Architecture Rethink ✅ Passed PR contains no Swift code changes. Custom check is scoped to "Swift architecture changes" only. PR modifies bash shell integration script and adds Python test—outside check scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps

greptile-apps Bot commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes spurious [N] Done bash job-control notifications that appeared after nearly every command in the cmux shell integration. The root cause was a SIGCHLD race in { cmd; } >/dev/null 2>&1 & disown: because _cmux_send is a fast Unix-socket write, the background job often completed before disown executed, so bash registered the completion in its job table and printed the notification at the next prompt.

  • 5 reporter sites in cmux-bash-integration.bash switched from the racy { cmd; } >/dev/null 2>&1 & disown to ( cmd & ) >/dev/null 2>&1: the inner & orphans the work inside the synchronous subshell's own job table, so the parent interactive shell never tracks it and no notification is emitted.
  • New PTY-driven regression test (tests/test_bash_integration_no_done_notifications.py) spawns an interactive bash -i via pty.fork(), stubs _cmux_send, creates a real Unix socket so [[ -S ... ]] guards on sites 2 and 4 pass, exercises all five reporter sites, and asserts zero [N] Done lines in the transcript; wired into ci.yml.
  • The local qpwd variable in the CWD reporter is correctly moved out of the brace group into the enclosing function scope; bash has no block-scoping, so this is semantically identical.

Confidence Score: 5/5

Safe to merge: surgical bash backgrounding pattern swap with no compiled code or Swift logic affected.

All five changed sites use the correct inner-ampersand subshell pattern. The regression test creates a real Unix socket so both [[ -S ]]-guarded sites are genuinely exercised rather than silently skipped, ChildProcessError is caught in the cleanup loop, and LC_ALL=C makes the assertion robust on international CI runners. No production Swift or compiled code is touched.

No files require special attention.

Important Files Changed

Filename Overview
Resources/shell-integration/cmux-bash-integration.bash Five { cmd; } >/dev/null 2>&1 & disown sites replaced with ( cmd & ) >/dev/null 2>&1; the local qpwd extraction from the CWD block is semantically identical. Fix is correct and well-bounded.
tests/test_bash_integration_no_done_notifications.py PTY-driven behavioral regression test that exercises all five reporter sites; creates a real Unix socket so [[ -S ]] guards pass, properly handles ChildProcessError in the cleanup loop, and uses LC_ALL=C for locale-stable assertion.
.github/workflows/ci.yml One step added to workflow-guard-tests to invoke the new regression test; no other CI logic changed.

Sequence Diagram

sequenceDiagram
    participant Shell as Interactive bash
    participant BG as Background job
    participant JobTable as Bash job table

    note over Shell,JobTable: OLD pattern with race condition

    Shell->>JobTable: "register bg job [N]"
    Shell->>BG: "start fast socket write"
    BG-->>JobTable: "SIGCHLD: job done"
    note right of Shell: "job done BEFORE disown runs"
    Shell->>JobTable: "disown [N] - too late"
    Shell->>Shell: "next prompt prints [N]+ Done"

    note over Shell,JobTable: NEW pattern - inner ampersand

    Shell->>Shell: "fork synchronous subshell"
    Shell->>Shell: "subshell backgrounds cmd internally"
    Shell->>Shell: "subshell exits immediately"
    note right of Shell: "parent never tracks the job"
    Shell->>Shell: "next prompt - no Done notification"
Loading

Reviews (3): Last reviewed commit: "Fix #3428: suppress spurious 'Done' bash..." | Re-trigger Greptile

Comment thread tests/test_bash_integration_no_done_notifications.py Outdated
Comment thread tests/test_bash_integration_no_done_notifications.py Outdated

Copilot AI 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.

Pull request overview

This PR addresses cmux issue #3428 by changing how the bash shell integration backgrounds its fast _cmux_send reporter writes, preventing interactive bash from emitting spurious job-control completion lines like [N] Done ... at the next prompt.

Changes:

  • Update 5 bash-integration reporter call sites to use subshell-isolated backgrounding ( cmd & ) >/dev/null 2>&1 instead of { ... } ... & disown.
  • Add a PTY-based interactive bash regression test that asserts no [N] Done notifications are emitted.
  • Wire the new regression test into the CI workflow-guard-tests job.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Resources/shell-integration/cmux-bash-integration.bash Switch reporter backgrounding pattern to avoid registering jobs in the interactive shell’s job table.
tests/test_bash_integration_no_done_notifications.py Add regression test that drives interactive bash via PTY and scans output for job-control “Done” lines.
.github/workflows/ci.yml Run the new regression test in CI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_bash_integration_no_done_notifications.py Outdated

@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: 1

🤖 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_integration_no_done_notifications.py`:
- Around line 72-73: The suppression `_CMUX_GIT_JOB_PID=$$` is ineffective
because `_CMUX_GIT_LAST_PWD` is unset (defaults to ""), causing
`_cmux_prompt_command` to treat the directory as changed and re-run the git
probe which clears the PID and exposes the disown race; fix this by
initializing/pinning `_CMUX_GIT_LAST_PWD` to the current working directory at
the same place you set `_CMUX_GIT_JOB_PID` so that inside `_cmux_prompt_command`
the guard comparing pwd vs _CMUX_GIT_LAST_PWD will be false and the seeded `$$`
PID remains intact during the prompt command execution.
🪄 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: 6d2c3451-9b76-4290-bb09-7bc34bdc4261

📥 Commits

Reviewing files that changed from the base of the PR and between 226b7a5 and 16d497c.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • Resources/shell-integration/cmux-bash-integration.bash
  • tests/test_bash_integration_no_done_notifications.py

Comment thread tests/test_bash_integration_no_done_notifications.py Outdated
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@psh4607 Thank you for the detailed update — all three fixes look correct and well-thought-out.

On my specific concern: seeding _CMUX_GIT_LAST_PWD="$PWD" alongside _CMUX_GIT_JOB_PID=$$ is the right fix. Without the matching _CMUX_GIT_LAST_PWD, the stale-probe reset branch (if [[ "$_CMUX_GIT_LAST_PWD" != "$PWD" ]]) would clear the PID immediately on the first prompt, letting the git-branch probe escape into the interactive shell's job table and pollute the assertion with out-of-scope Done lines. With both variables seeded, the reset branch is never taken, and the mute holds for the duration of the test.

The socket harness improvement for sites 2 & 4 is particularly valuable — without a real AF_UNIX listener, those two sites would have been silently skipped by the [[ -S "$CMUX_SOCKET_PATH" ]] guard, giving a false-green even without the fix. The revised test now genuinely covers all five reporter call sites.

The 47 → 0 Done-line count is a clean signal. The PR looks good to me.

@greptile-apps

greptile-apps Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

All three issues are properly resolved in the current code. Let me verify the specifics:

P1 — waitpid double-reap: The child_reaped flag is correctly threaded through both the main read loop and the finally cleanup, and all os.waitpid calls (including the final SIGKILL path) are guarded with except (OSError, ChildProcessError). Clean.

P2/P4 — Socket guard bypass: The real AF_UNIX listener is bound before pty.fork(), its path replaces __SOCKET_PATH__ in the driver, and it's torn down in finally. Both _cmux_report_shell_activity_state and _cmux_emit_pr_command_hint will now pass their [[ -S "$CMUX_SOCKET_PATH" ]] guards and actually exercise the backgrounding code.

Git probe mute: _CMUX_GIT_LAST_PWD="$PWD" is set alongside _CMUX_GIT_JOB_PID=$$, which prevents the stale-probe eviction path from clearing the seeded PID. The git-branch probe stays dormant for the duration of the test.

The empirical table in the PR description (47 Done lines on commit 1, 0 on commit 2) confirms all five sites are now genuinely exercised and caught by the red commit. The implementation is correct and the test is sound. LGTM — this is ready to merge.

@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: 1

🤖 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_integration_no_done_notifications.py`:
- Around line 126-131: The substitution injects INTEGRATION verbatim into the
bash script so paths with spaces break; change the replacement to shell-quote
the value (use shlex.quote) before inserting and add an import for shlex.
Concretely, update the .replace("__INTEGRATION__", str(INTEGRATION)) call in the
construction of script to use shlex.quote(str(INTEGRATION)) and ensure shlex is
imported at the top of the test file.
🪄 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: 87f4acf1-8fee-4175-8502-a32a8429d124

📥 Commits

Reviewing files that changed from the base of the PR and between 16d497c and f700eae.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • Resources/shell-integration/cmux-bash-integration.bash
  • tests/test_bash_integration_no_done_notifications.py

Comment thread tests/test_bash_integration_no_done_notifications.py Outdated
psh4607 added 2 commits May 7, 2026 21:36
Spawns interactive bash via PTY, sources cmux-bash-integration.bash, and
exercises the five reporter sites that background _cmux_send via
`{ ...; } & disown`. Asserts that no `[N] Done` job-control line is
emitted (LC_ALL=C forces English).

This commit only adds the failing test, per AGENTS.md regression policy.
The fix that switches the five sites to `( ... & ) >/dev/null 2>&1`
follows in the next commit, at which point CI flips from red to green.
The bash shell integration's reporters (`_cmux_report_tty_once`,
`_cmux_report_shell_activity_state`, `_cmux_ports_kick`,
`_cmux_emit_pr_command_hint`, CWD reporter) backgrounded jobs with
`{ cmd; } >/dev/null 2>&1 & disown`. Because the inner `_cmux_send`
is a fast Unix-socket write, the bg job often completes before
`disown` runs - so the interactive bash registers the completion via
SIGCHLD and prints `[N] Done ...` (or the localized equivalent, e.g.
`[N] Fertig ...`) at the next prompt, after almost every command.

Switch to subshell-isolated backgrounding: `( cmd & ) >/dev/null 2>&1`.
The job belongs to the subshell's job table, never enters the
interactive shell's, and the noisy line is gone.

zsh integration is unchanged - it already uses `&!` (immediate disown),
which doesn't have this race.
@psh4607
psh4607 force-pushed the fix/3428-bash-done-notifications branch from f700eae to 8f68428 Compare May 7, 2026 12:36
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me quickly verify the current state of the fix in the test file.


🏁 Script executed:

#!/bin/bash
rg -n 'shlex|INTEGRATION|sock_path|_DRIVER' tests/test_bash_integration_no_done_notifications.py | head -60

Length of output: 537


@psh4607 The fix looks exactly right. Both substitution sites are covered:

  • Line 129 — shlex.quote(str(INTEGRATION)) guards the integration script path.
  • Line 130 — shlex.quote(sock_path) guards the socket path.

shlex.quote() is the idiomatic stdlib choice here — it produces a POSIX-safe single-quoted token that handles spaces, parentheses, and any other shell-special characters, so the bash driver heredoc will parse correctly regardless of what the host's temp directory looks like. The local space-in-path smoke test confirms the round-trip works end-to-end.

The PR is in good shape. ✅

@greptile-apps

greptile-apps Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@psh4607 psh4607 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

리뷰 이슈가 없습니다. LGTM! ✅


ℹ️ Read-only 리뷰 모드

이 repo owner는 UNWATCHED_REVIEW_TRUSTED_OWNERS에 포함되어 있지 않아 dependency install/build/test를 실행하지 않았습니다. diff 기반 코드 리뷰만 진행했습니다.


🔄 수동 재리뷰: PR 우측 사이드바 Reviewers 섹션의 Re-request review 버튼(아래 빨간 박스)을 누르면 이 봇이 다시 리뷰합니다.

Re-request review 버튼

@bitti

bitti commented May 27, 2026

Copy link
Copy Markdown

+1 on this fix. I hit the same symptom on cmux 0.64.10 with Homebrew bash 5.3.9. [N]+ Done [...] lines after every command, two per prompt (one each from _cmux_report_shell_activity_state and _cmux_ports_kick).

I Locally applied the same ( cmd & ) inner-subshell pattern this PR uses, to all five fire-and-forget sites in cmux-bash-integration.bash (the four originally listed in #3428 plus the _cmux_emit_pr_command_hint site). Symptom gone immediately, no functional regressions. Confirms the analysis here. Outer-& is not sufficient (matches what @cedricvidal noted on #1565).

Would be great to see this merged; #3428 has been open for a while and there are several near-duplicate PRs floating around (#2804, #4408, #4747, #4757), but this one has the cleanest write-up and a regression test. Thanks @psh4607!

…tifications

# Conflicts:
#	.github/workflows/ci.yml
#	Resources/shell-integration/cmux-bash-integration.bash
#	tests/test_bash_integration_no_done_notifications.py
@cursor

cursor Bot commented Jul 13, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@psh4607 psh4607 closed this Jul 13, 2026
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.

3 participants