Skip to content

Fix shell integration idle HEAD-change polling - #1702

Closed
lawrencecchen wants to merge 3 commits into
mainfrom
task-shell-integration-hot-path-sleeps
Closed

lawrencecchen wants to merge 3 commits into
mainfrom
task-shell-integration-hot-path-sleeps

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add regression coverage for idle HEAD changes while shell integration is polling PR state
  • clear stale PR metadata and refresh git branch state when HEAD changes under an idle shell
  • replace the timeout poll loop with a single watchdog so hung gh probes do not spin in the shell timeout path

Testing

  • python3 tests/test_issue_1138_sidebar_pr_polling.py (pass)
  • ./scripts/setup.sh (pass, reused cached GhosttyKit.xcframework)
  • ./scripts/reload.sh --tag task-shell-integration-hot-path-sleeps (pass)

Issues

  • Task: ensure cmux shell integration or anywhere on hot paths do not have hangs/sleeps

Summary by cubic

Fixes idle HEAD-change handling so the shell clears stale PR badges and refreshes branch/dirty status immediately, even while idle. Improves reliability of PR polling in bash and zsh and adds a watchdog to stop hung gh probes.

  • Bug Fixes

    • Detect .git/HEAD changes during idle polling; clear PR state, refresh branch/dirty badge, and force a new probe (bash and zsh).
    • Add regression test for idle branch switch; report_pr now includes --branch.
  • Refactors

    • Replace per-second timeout loop with a watchdog that kills hung gh probes.
    • Extract helpers for HEAD tracking and branch reporting; unify behavior across bash/zsh.

Written for commit 2a28c98. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Improved git branch detection and reporting in shell integrations for more responsive updates when switching branches.
    • Enhanced timeout handling for PR polling operations with watchdog-based constraints.
  • Tests

    • Expanded test coverage for branch switching scenarios, including idle state transitions.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented Mar 18, 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 Mar 25, 2026 4:55am

@greptile-apps greptile-apps Bot left a comment

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.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces centralized git HEAD tracking utilities and watchdog-based timeout mechanisms for PR probes in bash and zsh shell integration scripts, along with test coverage for idle branch-switching scenarios. HEAD changes trigger branch reporting and PR state refresh during polling and prompt flows.

Changes

Cohort / File(s) Summary
Shell Integration - Git HEAD Tracking
Resources/shell-integration/cmux-bash-integration.bash, Resources/shell-integration/cmux-zsh-integration.zsh
Added _cmux_update_git_head_tracking() to cache HEAD signatures and detect repository changes; added _cmux_report_git_branch_for_path() to report branch state to UI; updated _cmux_git_resolve_head_path() to accept optional directory argument; integrated HEAD tracking into prompt and polling loops to trigger PR refresh on changes.
Shell Integration - Probe Timeout Watchdog
Resources/shell-integration/cmux-bash-integration.bash, Resources/shell-integration/cmux-zsh-integration.zsh
Added _cmux_start_probe_timeout_watchdog() and _cmux_stop_probe_timeout_watchdog() to manage background watchdog processes that enforce PR probe timeouts; refactored _cmux_run_pr_probe_with_timeout() to use watchdog-based timeout instead of polling mechanism.
Test Updates
tests/test_issue_1138_sidebar_pr_polling.py
Added idle_branch_switch_clear test scenario covering HEAD changes during idle polling; expanded _report_line() helper signature to support branch parameter selection; adjusted test setup and validation logic to cover idle branch-switching flow.

Sequence Diagram

sequenceDiagram
    participant Shell as Shell (Prompt/Poll)
    participant Git as Git Repository
    participant Watchdog as Watchdog Process
    participant Probe as PR Probe
    participant UI as UI Panel

    loop Polling/Prompt Flow
        Shell->>Git: _cmux_update_git_head_tracking()
        Git-->>Shell: HEAD signature (detect change?)
        alt HEAD Changed
            Shell->>Probe: _cmux_start_pr_probe_with_timeout()
            Watchdog->>Watchdog: Start timeout timer
            Probe->>Probe: Query PR state
            activate Watchdog
            par Probe Execution
                Probe-->>Shell: Return PR status
            and Watchdog Timeout
                Watchdog->>Probe: Kill if timeout exceeded
            end
            deactivate Watchdog
            Shell->>UI: _cmux_report_git_branch_for_path()
            UI-->>Shell: Branch/status displayed
            Shell->>Shell: Set PR refresh flags
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A watchdog was born, so faithful and keen,
To guard every probe from timeout's dark scene;
HEAD signatures tracked through each branch that we roam,
The rabbit reports changes—no shell left alone. 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing shell integration idle HEAD-change polling, which directly addresses the primary objective of detecting HEAD changes while idle and updating PR state accordingly.
Description check ✅ Passed The pull request description covers the main summary, testing approach, and references relevant issues, but lacks some sections from the template.

✏️ 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 task-shell-integration-hot-path-sleeps

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.

@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

🧹 Nitpick comments (1)
tests/test_issue_1138_sidebar_pr_polling.py (1)

394-407: Consider extracting shared validation logic.

The validation for idle_branch_switch_clear (lines 394-407) is nearly identical to branch_switch_clear (lines 379-392). Additionally, the try/except ValueError block is unreachable—if old_report not in send_lines on line 396 passes, send_lines.index(old_report) on line 399 will never raise ValueError.

A small helper could reduce duplication and remove the dead code:

♻️ Suggested refactor
def _validate_branch_switch(scenario: str, send_lines: list[str], shell: str, idle: bool = False) -> tuple[int, str]:
    old_report = _report_line(111, branch="feature/old")
    if old_report not in send_lines:
        return (1, f"{shell}/{scenario}: missing old-branch report\n" + "\n".join(send_lines))
    old_index = send_lines.index(old_report)
    clear_indices = [idx for idx, line in enumerate(send_lines) if line.startswith("clear_pr ")]
    if not clear_indices:
        change_type = "idle HEAD change" if idle else "branch change"
        return (1, f"{shell}/{scenario}: expected clear_pr after {change_type}\n" + "\n".join(send_lines))
    if clear_indices[0] <= old_index:
        return (1, f"{shell}/{scenario}: clear_pr happened before old report\n" + "\n".join(send_lines))
    return (0, f"{shell}/{scenario}: ok")

Then in _run_case:

     if scenario == "branch_switch_clear":
-        old_report = _report_line(111, branch="feature/old")
-        ...
+        return _validate_branch_switch(scenario, send_lines, shell, idle=False)

     if scenario == "idle_branch_switch_clear":
-        old_report = _report_line(111, branch="feature/old")
-        ...
+        return _validate_branch_switch(scenario, send_lines, shell, idle=True)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_issue_1138_sidebar_pr_polling.py` around lines 394 - 407, Extract
the duplicated validation into a helper (e.g. _validate_branch_switch) that
takes scenario, send_lines, shell and an optional idle flag; move the logic that
builds old_report via _report_line(111, branch="feature/old"), checks
membership, finds old_index with send_lines.index(old_report) (removing the
unreachable try/except), computes clear_indices, and returns the same (1,msg) or
(0,msg) tuples; then replace the bodies of branch_switch_clear and
idle_branch_switch_clear in _run_case to call _validate_branch_switch(...,
idle=True/False) so both scenarios reuse the same logic and the dead ValueError
handling is removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Resources/shell-integration/cmux-bash-integration.bash`:
- Around line 395-397: The assignment _CMUX_PR_FORCE=1 inside the backgrounded
subshell (where _cmux_update_git_head_tracking is called) is ineffective because
variables set in that subshell do not propagate to the parent; remove the
redundant `_CMUX_PR_FORCE=1` assignment from the subshell block and rely on the
existing parent-scope assignment used by the prompt restart logic, keeping the
calls to _cmux_update_git_head_tracking and _cmux_clear_pr_for_panel intact and
verifying no other code expects _CMUX_PR_FORCE to be set inside the backgrounded
loop.

---

Nitpick comments:
In `@tests/test_issue_1138_sidebar_pr_polling.py`:
- Around line 394-407: Extract the duplicated validation into a helper (e.g.
_validate_branch_switch) that takes scenario, send_lines, shell and an optional
idle flag; move the logic that builds old_report via _report_line(111,
branch="feature/old"), checks membership, finds old_index with
send_lines.index(old_report) (removing the unreachable try/except), computes
clear_indices, and returns the same (1,msg) or (0,msg) tuples; then replace the
bodies of branch_switch_clear and idle_branch_switch_clear in _run_case to call
_validate_branch_switch(..., idle=True/False) so both scenarios reuse the same
logic and the dead ValueError handling is removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1d66dcb-5325-4ffb-a169-a2b43fda54ee

📥 Commits

Reviewing files that changed from the base of the PR and between 55cb5c6 and bb0c2d9.

📒 Files selected for processing (3)
  • Resources/shell-integration/cmux-bash-integration.bash
  • Resources/shell-integration/cmux-zsh-integration.zsh
  • tests/test_issue_1138_sidebar_pr_polling.py

Comment thread Resources/shell-integration/cmux-bash-integration.bash

@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

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Resources/shell-integration/cmux-zsh-integration.zsh">

<violation number="1" location="Resources/shell-integration/cmux-zsh-integration.zsh:459">
P1: Starting the timeout watchdog via command substitution can detach it from the poll-loop lifecycle, leaving a stale watchdog that may later SIGKILL a reused PID.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

return 1
fi
done
watchdog_pid="$(_cmux_start_probe_timeout_watchdog "$probe_pid" "$_CMUX_ASYNC_JOB_TIMEOUT" 2>/dev/null || true)"

@cubic-dev-ai cubic-dev-ai Bot Mar 18, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Starting the timeout watchdog via command substitution can detach it from the poll-loop lifecycle, leaving a stale watchdog that may later SIGKILL a reused PID.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Resources/shell-integration/cmux-zsh-integration.zsh, line 459:

<comment>Starting the timeout watchdog via command substitution can detach it from the poll-loop lifecycle, leaving a stale watchdog that may later SIGKILL a reused PID.</comment>

<file context>
@@ -394,35 +424,44 @@ _cmux_kill_process_tree() {
-            return 1
-        fi
-    done
+    watchdog_pid="$(_cmux_start_probe_timeout_watchdog "$probe_pid" "$_CMUX_ASYNC_JOB_TIMEOUT" 2>/dev/null || true)"
 
     wait "$probe_pid"
</file context>
Fix with Cubic

# Conflicts:
#	Resources/shell-integration/cmux-bash-integration.bash
#	Resources/shell-integration/cmux-zsh-integration.zsh
@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 — 2a28c983 Deployed Mar 25, 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.

2 participants