Skip to content

Fix bash integration job completion noise - #4408

Closed
austinywang wants to merge 16 commits into
mainfrom
issue-4403-bash-done-noise
Closed

austinywang wants to merge 16 commits into
mainfrom
issue-4403-bash-done-noise

Conversation

@austinywang

@austinywang austinywang commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a PTY-backed regression test for Bash 5.3+ cmux shell integration job notifications
  • route cmux-owned fire-and-forget bash helpers through a shared detach helper
  • avoid Bash 5.3 inline PS0 exposing cmux helper jobs in the user's visible job table

Fixes #4403.

Verification

  • python3 tests/test_shell_bash_background_helpers_disowned.py

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

  • Let Codesmith autofix CI failures and bot reviews

Note

Medium Risk
Changes interactive bash preexec and background helper spawning for all Bash 5.3+ cmux terminals; mitigated by a targeted PTY regression and narrow version-gated PS0 path.

Overview
Fixes Bash 5.3+ showing stray [n]+ Done lines when cmux’s shell integration fires background socket/PR helpers during inline ${…} PS0 preexec.

cmux-bash-integration.bash adds _cmux_run_bg (command-substitution subshell + disown when inline PS0 is active) and routes _cmux_send_bg / detach helpers through it. Bash ≥5.3 uses _cmux_bash_preexec_inline_ps0 instead of calling the preexec hook directly from PS0; preexec prefers interactive history over BASH_COMMAND on that path, and older Bash passes $BASH_COMMAND into the subshell hook.

CI drops workflow actions: write, ensures Bash ≥ 5.3 on macOS test jobs (CMUX_TEST_BASH), and runs the new test_shell_bash_background_helpers_disowned.py PTY regression (no Done noise + gh pr still reported).

Reviewed by Cursor Bugbot for commit 10b87de. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Prevents Bash 5.3+ from printing stray "Done" job notifications by spawning cmux helpers from a detached subshell during inline PS0 preexec. Preserves $! and keeps gh pr reporting working.

  • Bug Fixes
    • Added _CMUX_BASH_PS0_INLINE_ACTIVE and _cmux_run_bg; _cmux_send_bg/_cmux_detach_bg now route through it. When PS0 is inline, helpers run via command-substitution subshell and are disowned so the interactive shell never owns them.
    • Introduced _cmux_bash_preexec_inline_ps0 for Bash ≥ 5.3 that reads the user command from interactive history. On older Bash, the subshell preexec now receives $BASH_COMMAND explicitly to keep command capture accurate and avoid clobbering $!.
    • CI ensures Bash ≥ 5.3 (CMUX_TEST_BASH) and runs tests/test_shell_bash_background_helpers_disowned.py; verifies no "Done" noise, $! is preserved, and gh pr actions still report. Hardened the PTY harness to avoid matching echoed marker text. Dropped unused workflow actions: write.

Written for commit 3fc363d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved shell integration background job handling to avoid internal completion (“Done”) notices showing up in interactive terminal output, including bash PS0 inline preexec scenarios.
  • Tests
    • Added a regression test covering bash background helper behavior to ensure no user-visible completion leakage.
  • Chores
    • Updated CI to guarantee Bash version ≥ 5.3 for shell regressions.
    • Tightened CI workflow permissions for improved security.
  • Documentation
    • Refreshed a developer-doc example’s formatting for generated app links.

@austinywang austinywang self-assigned this May 20, 2026
@vercel

vercel Bot commented May 20, 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 Jun 15, 2026 12:04am
cmux-staging Building Building Preview, Comment Jun 15, 2026 12:04am

@coderabbitai

coderabbitai Bot commented May 20, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds conditional background helpers and PS0 inline-state toggling in the bash integration, updates bash preexec command selection, introduces a comprehensive PTY-based regression test to validate '[N]+ Done' suppression, and wires the test into CI with Bash ≥5.3 detection.

Changes

Bash Background Job Leak Prevention

Layer / File(s) Summary
Background helper functions and state initialization
Resources/shell-integration/cmux-bash-integration.bash
Introduces _CMUX_BASH_PS0_INLINE_ACTIVE state variable, _cmux_run_bg() to conditionally execute via subshell or background-plus-disown, and _cmux_send_bg() wrapper routing async sends through the helper.
Preexec command selection and documentation
Resources/shell-integration/cmux-bash-integration.bash
Updates _cmux_preexec_command() to derive target command from explicit argument or BASH_COMMAND based on argument presence, and documents bash 5.3 inline PS0 behavior updating BASH_COMMAND before expansion.
PS0 inline-state wrapper and bash 5.3+ integration
Resources/shell-integration/cmux-bash-integration.bash
Introduces _cmux_bash_preexec_inline_ps0() wrapper that toggles _CMUX_BASH_PS0_INLINE_ACTIVE=1 during hook execution. Updates _CMUX_BASH_PS0 readonly definition so bash ≥5.3 uses the inline wrapper while earlier versions retain prior subshell behavior.
Regression test utilities and socket infrastructure
tests/test_shell_bash_background_helpers_disowned.py
Defines ANSI-stripping and completion-line detection regexes, implements bash version helpers, and introduces BoundUnixSocket context manager for accepting and cleaning pending connections.
Test helper functions and interactive bash runner
tests/test_shell_bash_background_helpers_disowned.py
Provides utilities to write stub tools, clean PTY output, drain streams, and terminate processes. Implements _run_interactive_bash() to fork a PTY-running bash ≥5.3, configure CMUX environment, source integration, send probes, monitor PTY and socket activity, and return exit code plus captured output.
Test validation and entry point
tests/test_shell_bash_background_helpers_disowned.py
Implements main() to skip/fail based on Bash availability, run the interactive test, assert probe markers and $! preservation, verify integration calls via send logs, and fail if any '[N]+ Done' lines are detected in output. Includes __main__ guard for direct execution.
CI: workflow permissions, bash detection, and test registration
.github/workflows/ci.yml
Removes actions: write permission, adds CI step to detect or install Bash ≥5.3 (exports CMUX_TEST_BASH), and appends the new regression test to "Run CLI no-socket regressions" script.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#4934: Addresses bash background-disown behavior and PS0 inline-state toggling to prevent '[N] Done' notification leakage in similar scenarios.

Poem

🐰
I hopped into the shell at night,
Quiet helpers tucked out of sight,
No blinking "Done" to spoil the view,
Tests confirm the hush is true,
A tidy burrow — clean and light.

🚥 Pre-merge checks | ✅ 20 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix bash integration job completion noise' is concise and clearly summarizes the main change—suppressing unwanted '[N]+ Done' notifications from bash job control.
Description check ✅ Passed The PR description covers the Summary section, provides a Verification method, and includes linked issue reference; it is largely complete although the Testing and Review Trigger sections from the template are not present.
Linked Issues check ✅ Passed The PR fully addresses issue #4403: introduces _cmux_run_bg and routes helpers through it, handles Bash 5.3+ inline PS0 via _cmux_bash_preexec_inline_ps0, adds a PTY regression test, and hardens CI to verify no '[N]+ Done' leaks.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to fixing job completion noise: bash integration refactoring, CI improvements for Bash 5.3+ testing, and a new targeted regression test. CLAUDE.md documentation update is incidental and minimal.
Cmux Swift Actor Isolation ✅ Passed No Swift code changes present in this PR; custom check applies only to production Swift changes. All modified files are YAML, Bash, Python, and Markdown.
Cmux Swift Blocking Runtime ✅ Passed This PR modifies only Bash, Python, YAML, and Markdown files (.github/workflows/ci.yml, cmux-bash-integration.bash, test_shell_bash_background_helpers_disowned.py, CLAUDE.md)—no Swift production co...
Cmux Expensive Synchronous Load ✅ Passed This PR contains no Swift source code changes—only shell scripts, Python tests, CI configuration, and documentation. The check only applies to production Swift changes.
Cmux Cache Substitution Correctness ✅ Passed Check is not applicable: PR contains no production Swift, TypeScript, or JavaScript changes. All changes are to Bash scripts, YAML workflow config, Python tests, and documentation.
Cmux No Hacky Sleeps ✅ Passed PR introduces no hacky sleeps in production code. New shell integration functions (_cmux_run_bg, _cmux_bash_preexec_inline_ps0) use disown and flag-based state management without timing synchroniza...
Cmux Algorithmic Complexity ✅ Passed All new production functions (_cmux_run_bg, _cmux_bash_preexec_inline_ps0, _cmux_send_bg, _cmux_detach_bg) are O(1) operations with no collection scans, loops over scalable data, or algorithmic com...
Cmux Swift Concurrency ✅ Passed PR contains no Swift code changes. Modified files are: YAML workflow config, Bash shell integration, Python test, and Markdown docs. Swift concurrency check not applicable.
Cmux Swift @Concurrent ✅ Passed No Swift file changes in PR; check only applies to Swift modifications and is not applicable here.
Cmux Swift File And Package Boundaries ✅ Passed PR contains no Swift files; changes are to workflow YAML, Bash scripts, Python tests, and markdown only. Custom check for Swift file/package boundaries is not applicable.
Cmux Swift Logging ✅ Passed No Swift source files (.swift) were modified in this PR. The changes are to CI workflow (YAML), Bash shell integration, Python tests, and documentation—none are subject to Swift logging rules.
Cmux User-Facing Error Privacy ✅ Passed PR contains no user-facing errors or alerts; changes are internal shell functions, test code, CI configuration, and developer documentation that comply with privacy rules.
Cmux Full Internationalization ✅ Passed All PR changes are exempt from i18n requirements: CI configuration, internal shell integration code, tests, and developer documentation. No user-facing strings were added that require localization.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI code changes; only Bash shell integration, Python tests, CI workflow (YAML), and documentation (Markdown) were modified.
Cmux Architecture Rethink ✅ Passed Custom check applies to Swift architecture changes only; this PR modifies only Bash, Python, YAML, and Markdown files—no Swift code present.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no Swift code changes. Modified files are YAML workflow (.github/workflows/ci.yml), Bash script (Resources/shell-integration/cmux-bash-integration.bash), Python test (tests/test_shell_b...
Cmux Source Artifacts ✅ Passed All changed files are hand-written source, config, tests, or documentation intentionally part of the product. No artifacts, generated logs, caches, build output, or temp directories were added.

✏️ 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-4403-bash-done-noise

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.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5c0cf65. Configure here.

Comment thread tests/test_shell_bash_background_helpers_disowned.py
Comment thread tests/test_shell_bash_background_helpers_disowned.py

@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_shell_bash_background_helpers_disowned.py`:
- Line 72: The __enter__ method in the BoundUnixSocket class has a quoted return
type ("BoundUnixSocket"); since the module uses from __future__ import
annotations you should remove the quotes so the annotation is unquoted (def
__enter__(self) -> BoundUnixSocket:) — update the __enter__ definition to use
the unquoted type name to make the annotation a forward reference handled by PEP
563.
- Around line 164-179: When waitpid indicates the child has exited (the block
where waited_pid == pid and you set exit_status and break), drain any remaining
data from the PTY before returning to avoid losing buffered output: after
detecting child exit (in the same scope where waited_pid, status are handled)
loop reading from the PTY master (using the same read logic used earlier—e.g.,
select/poll + os.read) until EOF or EIO, append bytes to output, then proceed to
decode and return exit_status and output.decode(...); ensure you reference the
existing variables pid, waited_pid, status, exit_status and output so no new
state is needed.
🪄 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: d8dfe8e7-c41a-43e3-93c5-e79cdd94a1af

📥 Commits

Reviewing files that changed from the base of the PR and between c6db818 and 5c0cf65.

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

Comment thread tests/test_shell_bash_background_helpers_disowned.py Outdated
Comment thread tests/test_shell_bash_background_helpers_disowned.py Outdated

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/test_shell_bash_background_helpers_disowned.py Outdated
@greptile-apps

greptile-apps Bot commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes Bash 5.3+'s inline ${ } PS0 expansion leaking cmux background helpers into the user-visible job table (printing stray [N]+ Done lines), while also preserving $! and ensuring gh pr commands are still captured and reported correctly.

  • _cmux_run_bg: new shared dispatcher that, when _CMUX_BASH_PS0_INLINE_ACTIVE=1, starts helpers via a command-substitution subshell ($(… &)) so the interactive shell never owns the job; otherwise uses the existing foreground-subshell path. _cmux_send_bg and _cmux_detach_bg both delegate to it.
  • _cmux_bash_preexec_inline_ps0: new Bash 5.3+ entry point that brackets the preexec hook with the _CMUX_BASH_PS0_INLINE_ACTIVE flag and resets it even on hook failure; command capture relies on interactive history rather than BASH_COMMAND (which Bash 5.3 clobbers before the function runs).
  • PTY regression test (test_shell_bash_background_helpers_disowned.py): drives a real interactive Bash session and asserts no Done noise, $! stability, and correct report_pr_action payload emission.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to Bash 5.3+ interactive PS0 hook dispatch and is guarded behind a version check, with a PTY regression test covering the exact failure mode.

All three correctness properties (no Done noise, $! preservation, gh pr reporting) are verified by the new PTY test. The _cmux_run_bg dispatch logic is straightforward, and _CMUX_BASH_PS0_INLINE_ACTIVE ownership is correctly guarded on both normal and error paths in _cmux_bash_preexec_inline_ps0. No production behavior is changed on Bash < 5.3.

No files require special attention.

Important Files Changed

Filename Overview
Resources/shell-integration/cmux-bash-integration.bash Adds _cmux_run_bg to route all fire-and-forget helpers through a shared path; introduces _cmux_bash_preexec_inline_ps0 for Bash 5.3+ inline PS0 to suppress "Done" noise. $! preservation and command-capture correctness are addressed.
tests/test_shell_bash_background_helpers_disowned.py New PTY regression test that drives an interactive Bash session and asserts: no [N]+ Done lines appear, $! is preserved after cmux helpers fire, and gh pr view still triggers the expected report_pr_action payload. Includes bounded _terminate_child with SIGTERM→SIGKILL escalation.
.github/workflows/ci.yml Drops the now-unused actions: write workflow permission and adds a macOS CI step that finds or installs Bash >= 5.3, exports CMUX_TEST_BASH, then runs the new PTY regression. Step ordering is correct.
.github/swift-file-length-budget.tsv Purely cosmetic re-sort of the budget table as file lengths change; no logic impact.
CLAUDE.md Replaces ======= separator lines in the developer-doc example with -------, no functional change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    PS0["PS0 expansion fires"] --> VersionCheck{Bash version?}
    VersionCheck -->|"≥ 5.3 inline ${ }"| InlinePS0["_cmux_bash_preexec_inline_ps0()"]
    VersionCheck -->|"4.4–5.2 command sub $()"| SubshellPS0["_cmux_bash_preexec_hook_subshell($BASH_COMMAND)"]
    InlinePS0 --> SetFlag["_CMUX_BASH_PS0_INLINE_ACTIVE=1"]
    SetFlag --> HookInline["_cmux_bash_preexec_hook() — history preferred"]
    HookInline --> PreExec["_cmux_preexec_command(cmd)"]
    PreExec --> ResetFlag["_CMUX_BASH_PS0_INLINE_ACTIVE=0"]
    SubshellPS0 --> HookSubshell["_cmux_bash_preexec_hook($BASH_COMMAND)"]
    HookSubshell --> PreExec2["_cmux_preexec_command(cmd)"]
    PreExec --> RunBg["_cmux_run_bg()"]
    PreExec2 --> RunBg
    RunBg --> InlineCheck{"_CMUX_BASH_PS0_INLINE_ACTIVE == 1?"}
    InlineCheck -->|yes| CmdSub[": $(cmd & disown) — job never owned by interactive shell"]
    InlineCheck -->|no| SubshellFg["( cmd & disown ) — subshell, outer shell doesn't own job"]
Loading

Reviews (10): Last reviewed commit: "test: avoid matching echoed bash marker ..." | Re-trigger Greptile

Comment thread Resources/shell-integration/cmux-bash-integration.bash Outdated
Comment thread tests/test_shell_bash_background_helpers_disowned.py Outdated
Comment thread Resources/shell-integration/cmux-bash-integration.bash

@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 @.github/workflows/ci.yml:
- Around line 10-13: The workflow currently grants broad permission "actions:
write" under the permissions block; since the job only relies on GitHub's native
concurrency.cancel-in-progress and does not call the Actions API, remove
"actions: write" to follow least-privilege, or if there's an intentional reason
to keep it add an inline comment explaining why the workflow requires "actions:
write" and reference concurrency.cancel-in-progress in that comment to justify
the exception.
🪄 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: cb6e9c14-6197-45d1-bdde-cce98f891385

📥 Commits

Reviewing files that changed from the base of the PR and between 5c0cf65 and 101a9f6.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

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 @.github/workflows/ci.yml:
- Around line 10-13: The workflow currently grants broad permission "actions:
write" under the permissions block; since the job only relies on GitHub's native
concurrency.cancel-in-progress and does not call the Actions API, remove
"actions: write" to follow least-privilege, or if there's an intentional reason
to keep it add an inline comment explaining why the workflow requires "actions:
write" and reference concurrency.cancel-in-progress in that comment to justify
the exception.
🪄 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: cb6e9c14-6197-45d1-bdde-cce98f891385

📥 Commits

Reviewing files that changed from the base of the PR and between 5c0cf65 and 101a9f6.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml
🛑 Comments failed to post (1)
.github/workflows/ci.yml (1)

10-13: ⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for any usage of actions that might require actions:write permission

rg -n -C3 'cancel.*workflow|actions/cache@.*save|create.*workflow|delete.*workflow' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 42


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --context=3 'concurrency:|cancel-in-progress' .github/workflows/ci.yml
rg -n --context=3 'permissions:' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --context=3 '(GITHUB_TOKEN|github\.token)' .github/workflows/ci.yml
rg -n --context=3 '(actions\/workflows|workflow-runs|workflow runs|cancel|rerun|re-run|create.*workflow|delete.*workflow|gh api|api\.github\.com)' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 42


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --context=3 '\bgh\s+(api|workflow)|api\.github\.com|curl\s+https?://|curl\s+-X|wget\s+https?://' .github/workflows/ci.yml
rg -n --context=3 '\bgh\s' .github/workflows/ci.yml
rg -n --context=2 'permissions:' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 42


🌐 Web query:

Does GitHub Actions concurrency.cancel-in-progressrequirepermissions: actions: write (GITHUB_TOKEN permissions)?

💡 Result:

No. The built-in concurrency.cancel-in-progress feature does not require you to grant permissions: actions: write to GITHUB_TOKEN. GitHub’s documentation for workflow/job concurrency describes cancel-in-progress: true as a built-in scheduler behavior (“GitHub Actions will cancel any… already running with that key”) and does not mention any GITHUB_TOKEN permission requirement for that functionality [1][2]. The actions: write permission is relevant when an action (or custom cancel action) cancels workflow runs by calling the GitHub Actions API using a token. For example, Marketplace actions that cancel runs explicitly state that the token needs actions:write to cancel workflow runs [3]. So: - If you are only using native YAML concurrency: ... cancel-in-progress: true, no special permissions: actions: write is required for GITHUB_TOKEN [1][2]. - If you use an external “cancel workflow runs” action that calls the API, then that action may require permissions: actions: write (as those actions’ docs state) [3].

Citations:


Clarify or drop permissions: actions: write in .github/workflows/ci.yml

.github/workflows/ci.yml (lines 10-13) grants permissions: actions: write, but this workflow only uses native concurrency.cancel-in-progress and doesn’t appear to call any GitHub API/CLI that would require that scope. GitHub’s docs indicate native cancel-in-progress doesn’t require actions: write; remove it for least privilege or document the rationale.

🧰 Tools
🪛 zizmor (1.25.2)

[error] 12-12: overly broad permissions (excessive-permissions): actions: write is overly broad at the workflow level

(excessive-permissions)


[warning] 12-12: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)

🤖 Prompt for 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.

In @.github/workflows/ci.yml around lines 10 - 13, The workflow currently grants
broad permission "actions: write" under the permissions block; since the job
only relies on GitHub's native concurrency.cancel-in-progress and does not call
the Actions API, remove "actions: write" to follow least-privilege, or if
there's an intentional reason to keep it add an inline comment explaining why
the workflow requires "actions: write" and reference
concurrency.cancel-in-progress in that comment to justify the exception.

Source: Linters/SAST tools

@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.

Caution

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

⚠️ Outside diff range comments (1)
tests/test_shell_bash_background_helpers_disowned.py (1)

260-285: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat PTY carriage returns as line boundaries before matching markers.

Right now output.splitlines() runs before _clean_line() drops \r, so a prompt redraw like CMUX_TEST_PROMPT> \r[1]+ Done ... becomes CMUX_TEST_PROMPT> [1]+ Done ... and both the DONE_LINE_RE check and the CMUX_TEST_BANG_CHANGED sentinel can false-pass. That leaves the regression test blind to the exact Bash noise this PR is trying to catch.

Suggested fix
-    cleaned_lines = [_clean_line(raw) for raw in output.splitlines()]
+    normalized_output = ANSI_ESCAPE_RE.sub("", output).replace("\r", "\n")
+    cleaned_lines = [line.strip() for line in normalized_output.splitlines()]
     user_bg_matches = re.findall(r"CMUX_TEST_USER_BG_PID=([0-9]+)", output)
     current_bang_matches = re.findall(r"CMUX_TEST_CURRENT_BANG=([0-9]+)", output)
     if not user_bg_matches or not current_bang_matches:
         print("FAIL: interactive bash did not report $! preservation markers")
         print(output)
         return 1
     if user_bg_matches[-1] != current_bang_matches[-1] or "CMUX_TEST_BANG_CHANGED" in cleaned_lines:
         print("FAIL: cmux bash prompt helpers changed the user's last background PID")
         print(output)
         return 1

Based on learnings, "When a user reports tests missed a bug, add or adjust behavior-level coverage around the exact repro path before claiming the fix is complete."

🤖 Prompt for 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.

In `@tests/test_shell_bash_background_helpers_disowned.py` around lines 260 - 285,
The issue is that output.splitlines() is called before _clean_line() removes
carriage returns, so PTY carriage returns are not treated as line boundaries
during the split operation. This causes lines like CMUX_TEST_PROMPT> \r[1]+ Done
... to be incorrectly combined. Fix this by cleaning the entire output string
first (removing \r characters), then splitting it into lines. This ensures
carriage returns are properly treated as line boundaries before the DONE_LINE_RE
regex matching and CMUX_TEST_BANG_CHANGED sentinel checks occur, preventing
false-passes in the regression test.

Source: Learnings

🤖 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.

Outside diff comments:
In `@tests/test_shell_bash_background_helpers_disowned.py`:
- Around line 260-285: The issue is that output.splitlines() is called before
_clean_line() removes carriage returns, so PTY carriage returns are not treated
as line boundaries during the split operation. This causes lines like
CMUX_TEST_PROMPT> \r[1]+ Done ... to be incorrectly combined. Fix this by
cleaning the entire output string first (removing \r characters), then splitting
it into lines. This ensures carriage returns are properly treated as line
boundaries before the DONE_LINE_RE regex matching and CMUX_TEST_BANG_CHANGED
sentinel checks occur, preventing false-passes in the regression test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7d9f1a6c-23d6-4487-8b5d-7316582c4d28

📥 Commits

Reviewing files that changed from the base of the PR and between 31bb0da and 3fc363d.

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

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

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

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bash shell integration: '[N]+ Done' notifications leak after every command

3 participants