Skip to content

fix(bash): keep $? for PROMPT_COMMAND hooks after cmux's - #15255

Merged
teamleaderleo merged 3 commits into
manaflow-ai:mainfrom
tk1475:fix-bash-prompt-status
Sep 29, 2026
Merged

teamleaderleo merged 3 commits into
manaflow-ai:mainfrom
tk1475:fix-bash-prompt-status

Conversation

@tk1475

@tk1475 tk1475 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • _cmux_prompt_command saved local last_status=$? but returned 0 from its three early exits (no socket/port-scan transport, no CMUX_TAB_ID, no CMUX_PANEL_ID) and otherwise fell off the end with the status of its last check.
  • cmux prepends itself to PROMPT_COMMAND, so every hook that runs after it saw $? as 0 after a failing command. Starship's status module (starship_precmd) never showed a failure.
  • Every exit path of _cmux_prompt_command now returns the saved last_status. No other behavior changes.

This builds on the approach in #9533 by @austinywang, thank you.

Closes #5389

Testing

  • Added test_hooks_after_cmux_prompt_command_see_last_exit_status to tests/test_issue_5164_starship_prompt_composition.py. It drives the real bootstrap and integration through /bin/bash with the existing Starship stub (now echoing the --status it receives) and checks that starship_precmd, which runs after _cmux_prompt_command, sees 0 after true, 1 after false, and 7 after (exit 7).
  • Two commits: the test-only commit fails (status=0 after false), the fix commit passes.
  • uv run --with pytest pytest -q tests/test_issue_5164_starship_prompt_composition.py: 3 passed. python3 tests/test_issue_5164_starship_prompt_composition.py also passes.
  • Manually sourced the integration against a listening unix socket (with and without CMUX_PANEL_ID) and confirmed (exit N); _cmux_prompt_command; echo $? prints N for 0, 1 and 7, which covers the full path and the panel-less early exit.
  • bash -n on the integration script.
  • No user-facing strings added, so no localization changes.

Checklist

  • I tested the change locally
  • I added or updated tests for behavior changes
  • I updated docs/changelog if needed
  • I requested bot reviews after my latest commit (copy/paste block above or equivalent)
  • All code review bot comments are resolved
  • All human review comments are resolved

🤖 Generated with Claude Code


Summary by cubic

Fixes _cmux_prompt_command so PROMPT_COMMAND hooks running after it see the real $? from the previous command.

  • Previously it returned 0 from its early exits and fell off the end with the status of its last check, so Starship's status module never showed a failure.
  • It now returns the saved last_status on every exit path; no other behavior changes.

Written for commit d1d531b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Shell prompt integrations now preserve the exit status of the command that ran previously, including when prompt processing cannot complete. Later prompt hooks receive the correct status after successful commands and commands that fail with different exit codes.

tk1475 and others added 2 commits September 28, 2026 13:52
…i#5389)

Add a regression test to the Starship prompt composition suite: after
`false` (or `exit 7`), the starship_precmd hook that runs after
_cmux_prompt_command must see the failing status. It fails today because
_cmux_prompt_command returns 0 on its early exits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…#5389)

_cmux_prompt_command saved the previous exit status but returned 0 from
its early exits and fell off the end with the status of its last check.
cmux prepends itself to PROMPT_COMMAND, so every later hook (Starship's
status module included) saw $? as 0 after a failing command. Return the
saved status on every exit path.

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

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9f730212-93e5-4c98-9753-4b513a0171ec

📥 Commits

Reviewing files that changed from the base of the PR and between 2425031 and d1d531b.

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cdb5505d-0355-485c-a3b0-8ee8966507cf

📥 Commits

Reviewing files that changed from the base of the PR and between c307ab0 and 2425031.

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

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

_cmux_prompt_command now returns the previous command’s exit status on early exits and after prompt processing. A regression test checks that a subsequent Starship hook receives statuses 0, 1, and 7.

Changes

Prompt status propagation

Layer / File(s) Summary
Return and verify command status
Resources/shell-integration/cmux-bash-integration.bash, tests/test_issue_5164_starship_prompt_composition.py
_cmux_prompt_command returns the saved status on early exits and after prompt processing. The regression test checks hook order and verifies that Starship receives statuses 0, 1, and 7.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 24250

The regression test checks that later prompt hooks receive the command’s exit status. No merge-blocking issue was identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 24250

The change is limited to preserving a command’s exit status for later prompt hooks. No new credential, service, or transport access was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The newly observable value is the prior command’s exit status in later hooks of the same shell. The production diff adds no transport call, credential access, or new caller.

Trust Boundaries and Controls

  • inferred — The test’s selectable Bash driver remains test-local: visible callers select in-file drivers, and the subprocess disables cmux socket transport. It does not establish a new production entrypoint.
🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (24 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preserving the previous exit status for later Bash PROMPT_COMMAND hooks.
Description check ✅ Passed The description is mostly complete. It explains the problem, implementation, regression coverage, test commands, manual verification, and localization impact. It omits separate Changelog and Demo Vide…
Linked Issues check ✅ Passed Issue #5389 requires _cmux_prompt_command to preserve the prior command status for every exit path. The PR changes the socket, tab-ID, and panel-ID early returns to return "$last_status", and adds…
Out of Scope Changes check ✅ Passed The changes stay within issue #5389. The shell changes implement status transparency. The test changes provide regression coverage for downstream PROMPT_COMMAND hooks and extend the existing Starshi…
Cmux Cloud Persistent Session And Early Input ✅ Passed PASS. The reviewed diff changes only _cmux_prompt_command exit-status preservation in the Bash integration and adds Starship regression tests. It does not change Cloud terminal creation, cmux-tui cl…
Cmux Swift Actor Isolation ✅ Passed PASS: The authoritative PR diff changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. It contains no Swift files or producti…
Cmux Swift Blocking Runtime ✅ Passed The scoped diff changes only one Bash integration file and one Python test file. It contains no production Swift changes and introduces no Swift blocking or timing-based synchronization. The check is …
Cmux Browser Automation Off-Main ✅ Passed PASS: The pull request changes only Bash shell integration and its Python regression test. The diff contains no browser socket commands, WebKit/AppKit access, socketWorkerMethods, processV2Command…
Cmux Expensive Synchronous Load ✅ Passed PASS: The pull request changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. The authoritative diff contains no production S…
Cmux Cache Substitution Correctness ✅ Passed PASS: The review-scoped diff changes only Resources/shell-integration/cmux-bash-integration.bash and a Python test. It contains no production Swift, TypeScript, or JavaScript changes, so the cache-s…
Cmux No Hacky Sleeps ✅ Passed The production diff only changes _cmux_prompt_command to return last_status on all exit paths. It introduces no sleep, timer, polling, fixed delay, or wall-clock synchronization. The test driver i…
Cmux Algorithmic Complexity ✅ Passed The production diff only changes _cmux_prompt_command early returns and its final return to preserve the saved status. It adds no collection traversal, sorting, filtering, joins, rescans, or other a…
Cmux Swift Concurrency ✅ Passed The pull request changes only Resources/shell-integration/cmux-bash-integration.bash and a Python regression test. The scoped diff contains no Swift files or Swift concurrency patterns. Therefore, t…
Cmux Swift @Concurrent ✅ Passed PASS: The pull request changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. The diff contains no Swift files, Swift declara…
Cmux Swift Package Boundaries ✅ Passed PASS: The authoritative PR diff changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. It introduces no production Swift chan…
Cmux Swiftpm Lockfiles ✅ Passed The pull request changes only the Bash integration and its Python regression test. It does not change SwiftPM manifests, Package.resolved files, Xcode project references, .gitignore files, workflows, …
Cmux Swift Logging ✅ Passed PASS. The reviewed diff changes only one Bash integration file and one Python test file. It adds no production Swift code or Swift logging, so the Swift logging failure conditions do not apply.
Cmux User-Facing Error Privacy ✅ Passed The production diff only returns the previously captured shell status from _cmux_prompt_command on all exit paths. It adds no user-facing error, alert, command-output text, API error body, or recove…
Cmux Full Internationalization ✅ Passed PASS: The PR changes only Bash status propagation and regression-test code. The production diff adds return "$last_status" paths and a developer comment; it adds no user-facing Swift, catalog, web, …
Cmux Swiftui State Layout ✅ Passed PASS: The reviewed diff changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. It contains no Swift or SwiftUI changes, so th…
Cmux Architecture Rethink ✅ Passed PASS: The PR changes only Bash shell integration and a Python regression test. It introduces no Swift architecture changes, timing repair, polling, locks, observers, duplicate Swift entrypoints, or sp…
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: The review-scoped diff changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. It contains no Swift, NSWindow, NSPanel, …
Cmux Source Artifacts ✅ Passed The PR changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. These are intentional source and regression-test files. The tes…
Cmux No Test Or Debug Seam In Production Source ✅ Passed The pull request changes only Resources/shell-integration/cmux-bash-integration.bash and tests/test_issue_5164_starship_prompt_composition.py. It changes no Swift file under a production `Sources/…
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Review (merge-train). This is a good catch and a good fix. Nothing to change.

Review

  1. The bug is exactly as described. _cmux_prompt_command runs first in PROMPT_COMMAND, and the last thing it used to do was an if that either ran _cmux_ports_kick refresh or fell off the end. Either way the function returned that command's status rather than the user's, so every hook after it saw $? as 0. Starship's status module can then never show a failure.

  2. Every exit path is covered. The function has four returns and the fix reaches all of them (:1622, :1623, :1648, and the new :1763), with :1763 immediately before the closing brace, so there is no path left that returns something else. local last_status=$? on the first line is the right place to capture it, before _cmux_tmux_sync_cmux_environment can clobber it.

  3. The test is not vacuous. I checked, because a regression test that passes against the unfixed code is worse than none. Restoring Resources/shell-integration/cmux-bash-integration.bash from origin/main and running test_hooks_after_cmux_prompt_command_see_last_exit_status against it fails the way the issue describes:

    starship_precmd saw the wrong exit status (PS1_AFTER_FALSE); expected status=1, PS1=<SS[cwd=... n=3 status=0]$ >
    PS1_AFTER_TRUE<SS[... status=0]$ >
    PS1_AFTER_FALSE<SS[... status=0]$ >
    PS1_AFTER_EXIT_7<SS[... status=0]$ >
    

    Every status pinned to 0, which is the reported symptom. With the fix in place all three tests pass. Testing 0, 1 and a non-trivial 7 is the right set; 7 is what catches a fix that only propagates "failed or not".

  4. The driver models both PROMPT_COMMAND shapes, the array form bash 5.1 added and the string form, and threads $? between array elements the way bash does. That is the part that is easy to get wrong in a stub, and it is right here.

Fixed: nothing to fix.

Left: nothing blocking. One observation for later, not for this PR: the same "hand $? through" property is worth checking in the zsh and fish integrations, since the fix here is specific to the bash one. Separate issue if it turns out to apply.

What I did: the branch was behind main, and Web complexity was failing on it. That check is content-hash keyed against a baseline, so a branch that is behind reports findings in services/apns, services/coderouter and services/vms that this PR never touched and that are already on main. I merged current main into the branch (clean, no conflicts) so the checks run against the state this actually lands in.

Merging on green. Thanks for the clear issue reference and for writing the test that proves it.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Correcting what I said earlier about merging on green: there is no green to merge on yet, and that is on our side rather than yours.

This is a fork PR, so the substantive workflows are all sitting at action_required waiting for a maintainer to approve them — CI, CI fast guards, Web validation and Testbox broker guard. The ten checks showing green on the PR are only the CLA, Socket, complexity and AI-reviewer bots. So nothing has actually compiled or run your new test.

I read both files end to end looking for anything that would make approving the run risky, and there is nothing: no workflow or build-script changes, and the new test spawns bash with a stubbed PATH inside a tempdir. I do not have permission to approve fork workflow runs from this session, though, so I am handing that to Leo rather than merging on bot-only checks.

Nothing needed from you. The review I posted still stands and the change still looks right to me. :)

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the focused diff and regression test. The hook now preserves the prior command status on every early return and at the end, and the test covers 0/1/7 through a following PROMPT_COMMAND hook. Approving.

@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 29, 2026 15:45
@teamleaderleo
teamleaderleo merged commit 4e0f7d2 into manaflow-ai:main Sep 29, 2026
60 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Merge receipt for d1d531b867: every check was green at merge (14 verified; 18 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 29, 2026
4e0f7d2 fix(bash): keep $? for PROMPT_COMMAND hooks after cmux's (manaflow-ai#15255)
ae49bf5 fix(examples): show custom description in Project Worktrees sidebar (manaflow-ai#15256)
a9a229d Add cross-provider token usage accounting for agent transcripts (manaflow-ai#15332)
860619f Add a .worktreeinclude reader for seeding new worktrees (manaflow-ai#15413)
3edbd83 Clear the stale Needs input badge when Claude's permission is decided in the terminal (manaflow-ai#15170)
9ed9294 CodeRouter: hold capacity errors on the same model instead of failing fast (manaflow-ai#15310)
56d4547 docs: add a front door for outside contributors (manaflow-ai#15263)
799f906 fix(ci): recognize GUI token acquisition failures (manaflow-ai#15449)
f118d43 ci: age parked builds by measured reuse distance (manaflow-ai#15616)
1f6744d ci: harden overflow switch recovery (manaflow-ai#15617)
9987778 Predicted echo: remote terminals only, withdraw on pasted and sent input (manaflow-ai#15211)
d9e199b Subtle selection follow-ups: group header hairline, no focus re-render for legacy rows, cmux.json test (manaflow-ai#15195)
c13afe1 test: cover UTF-8 workspace create commands (manaflow-ai#15622)
e76a660 fix: preserve Claude remote-control names on restore (manaflow-ai#15619)
900f248 feat: expose cmux-owned scratch metadata in session listing (manaflow-ai#15615)
b5604fa ci: say why compiled-product reuse refused an artifact (manaflow-ai#15553)

# Conflicts:
#	.github/workflows/ci-cloud-overflow-probe.yml
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.

_cmux_prompt_command clobbers $? for downstream PROMPT_COMMAND hooks (breaks Starship status module)

2 participants