Skip to content

Set Claude idle after reentrant stop without work - #16635

Merged
teamleaderleo merged 3 commits into
mainfrom
fix/claude-reentrant-stop-idle-16495
Oct 3, 2026
Merged

teamleaderleo merged 3 commits into
mainfrom
fix/claude-reentrant-stop-idle-16495

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Claude Code can send a final re-entrant Stop with stop_hook_active: true after an async wake-up. When no background task or scheduled wake-up remains, cmux records the turn as idle but leaves the sidebar pill on Running until another user prompt arrives.

Fix

Treat stop_hook_active as hook recursion only. The status decision now depends on the authoritative background-work fields:

  • Stop failures remain visible as errors.
  • Live background tasks and scheduled wake-ups remain Waiting.
  • A re-entrant Stop with no background work settles the pane to Idle.

Validation

  • Swift frontend parsing for CLI/cmux.swift and the regression test
  • python3 scripts/verify-local.py --affected mf/main
  • Updated the real CLI hook harness regression for a re-entrant Stop without background work
  • git diff --check

Fixes #16495


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Fixes the sidebar pill staying stuck on Running after a re-entrant Stop with no background work. The status decision now treats stop_hook_active as hook recursion only, so a final re-entrant Stop with no pending work settles the pane to Idle instead of waiting for the next user prompt.

Stop failures remain visible as errors, and live background tasks and scheduled wake-ups still show Waiting.

Fixes #16495.

Written for commit 28690c9. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Claude sessions now settle to Idle after a Stop hook when no background work is reported, rather than remaining Running. Re-entrant Stop hooks no longer imply pending work.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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

🧰 Additional context used
📚 Code guidelines (3)
.github/review-bot-rules/test-determinism.md — configured
.github/review-bot-rules/swift-architectural-rethink.md — configured
.github/review-bot-rules/source-control-artifacts.md — configured

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: ee72f185-edc5-4a4b-901c-9f11d473bbc8
📥 Commits

Reviewing files that changed from the base of the PR and between 9c41d59 and 28690c9.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • cmuxTests/ClaudeBackgroundWorkNotifyTests.swift
  • tests/test_claude_hook_stop_last_assistant.py

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


📝 Walkthrough

Walkthrough

Claude Stop handling no longer treats stop_hook_active as pending work or uses it to set Running. Tests now expect a re-entrant Stop without background work to settle to Idle.

Changes

Claude Stop status

Layer / File(s) Summary
Re-entrant Stop status settlement
CLI/cmux.swift, cmuxTests/ClaudeBackgroundWorkNotifyTests.swift, tests/test_claude_hook_stop_last_assistant.py
The status-selection path no longer sets Running for a re-entrant Stop. Unit and integration tests expect Idle and verify that no Running status is emitted.

Priority: ⬇️ Low

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

Change: Bug fix · Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to 28690

Re-entrant Stops without background work now select Idle while pending work and Stop failures retain their respective statuses. No merge-blocking issue remains after normal checks.

🚥 Pre-merge checks | ✅ 24 | ❓ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ❓ Inconclusive Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 … 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: Claude settles to Idle after a re-entrant Stop with no background work.
Description check ✅ Passed The description explains the problem, resulting behavior, validation, and linked issue. It omits the template’s Changelog, Demo Video, and Checklist sections, but the core information is complete.
Linked Issues check ✅ Passed [#16495] CLI/cmux.swift no longer treats stop_hook_active as a Running-status case. When no Stop failure or authoritative background-work signal exists, the status path settles to Idle. `ClaudeBac…
Out of Scope Changes check ✅ Passed The source change and both regression-test updates directly address [#16495]. The tests verify the no-work Stop behavior; no unrelated changes appear in the reviewed diff summary or focused source dif…
Cmux Cloud Persistent Session And Early Input ✅ Passed The check covers Cloud terminal creation, persistent transport, manual panes, and runtime admission. The diff changes only Claude Stop-hook status handling and related tests. It does not change Cloud …
Cmux Swift Actor Isolation ✅ Passed The production Swift diff changes Stop-hook status control flow only. It removes a local isReentrantStop check and its Running status branch, so a Stop without background work uses the existing `I…
Cmux Swift Blocking Runtime ✅ Passed The Swift production diff removes the isReentrantStop branch that set Running and lets the existing status path set Idle when no background work exists. It adds no blocking waits, sleeps, delayed di…
Cmux Browser Automation Off-Main ✅ Passed The pull request changes only CLI/cmux.swift and two Claude-hook tests. The diff contains no browser socket automation commands, worker routing, WebKit/AppKit access, or browser-state changes. The b…
Cmux Expensive Synchronous Load ✅ Passed The production Swift diff adds only comments and removes the re-entrant Running status branch. It adds or moves no synchronous agent-history load. The Stop handler’s existing completion-summary and se…
Cmux Cache Substitution Correctness ✅ Passed The diff removes the special stop_hook_active branch that sent a Running sidebar status. The remaining status path sends Idle or Waiting based on hasActiveClaudeBackgroundWork(parsedInput), …
Cmux No Hacky Sleeps ✅ Passed The check passes. The diff changes CLI/cmux.swift, a Swift file, and updates one Python regression test. It adds no production TypeScript, JavaScript, shell, or build/runtime-script delay. Swift tim…
Cmux Algorithmic Complexity ✅ Passed The production Swift diff removes the stop_hook_active status branch and changes comments; it adds no collection scans, sorting, filtering, joins, or other algorithm. The existing background-work ch…
Cmux Swift Concurrency ✅ Passed The Swift diff changes Claude Stop status selection and updates assertions in one regression test. It removes the re-entrant Running-status branch and adds no concurrency APIs or asynchronous work. Th…
Cmux Swift @Concurrent ✅ Passed The Swift diff changes Claude Stop status branching and test expectations only. The enclosing runClaudeHook handler is synchronous (throws), and the diff adds no async helpers, nonisolated async…
Cmux Swift Package Boundaries ✅ Passed The Swift diff changes only Claude Stop-hook status handling in CLI/cmux.swift: it removes the re-entrant-Stop Running branch and uses the existing background-work check to choose Waiting or `Id…
Cmux Swiftpm Lockfiles ✅ Passed The PR changes only CLI/cmux.swift, cmuxTests/ClaudeBackgroundWorkNotifyTests.swift, and tests/test_claude_hook_stop_last_assistant.py. The diff contains no SwiftPM package, Xcode project, `.git…
Cmux Swift Logging ✅ Passed The Swift diff changes Claude status selection and comments, and removes the re-entrant Running status call. It adds or materially changes no logging statements, output sinks, Logger constants, or dia…
Cmux User-Facing Error Privacy ✅ Passed The production change reaches cmux users through the Claude Stop hook, which sends the status value to the app’s sidebar status entry. The changed branch now emits the generic “Idle” status instead of…
Cmux Full Internationalization ✅ Passed The production change selects the existing localized Idle status via String(localized: "agent.generic.notification.status.idle", defaultValue: "Idle"). Its string-catalog entry has translated values…
Cmux Swiftui State Layout ✅ Passed The diff changes Claude Stop-hook status handling in CLI/cmux.swift and updates regression tests. It adds no SwiftUI state, layout measurement, lazy/list row store reference, or render-time state mu…
Cmux Architecture Rethink ✅ Passed The Swift diff makes a small local correctness change in the existing Claude Stop handler. That handler remains the owner of the status transition, and hasActiveClaudeBackgroundWork remains the sour…
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The Swift diff only changes Claude Stop-hook status handling in CLI/cmux.swift. It adds or materially changes no standalone cmux-owned window. The other changes update regression tests, which the chec…
Cmux Source Artifacts ✅ Passed The PR changes only CLI/cmux.swift, cmuxTests/ClaudeBackgroundWorkNotifyTests.swift, and tests/test_claude_hook_stop_last_assistant.py. The diff contains intentional source and regression-test u…
Cmux No Test Or Debug Seam In Production Source ✅ Passed The changed-file inventory contains CLI/cmux.swift, a test Swift file, and a Python test. No changed Swift file is under a production Sources/ path, so the custom check does not apply.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 too large.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 OpenGrep (1.30.0)
CLI/cmux.swift

OpenGrep scan timed out


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.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

CI failure attribution

CI failed on 28690c9423 (run 37155743646 attempt 1): 1 code, 1 unknown.

Job Verdict Why
Fast static checks code a static check failed
guards / workflow-guard-tests / ci unknown no known signature; failed step: Propagate failed independent fast guard
Matched log lines
Fast static checks: FAILED localization (1.17s)

Not re-run automatically: Fast static checks, guards / workflow-guard-tests / ci are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's.

teamleaderleo and others added 2 commits October 3, 2026 17:37
…test

The Python test still asserted the old Running behaviour this change removes.

Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

CI fast guards failed on 28690c9423 (https://github.com/manaflow-ai/cmux/actions/runs/37155743398). It does not block the merge; a red guard merged into main breaks it for every open PR.

Validate macOS jobs select a pinned Xcode (red on main too, not this PR)

Main has failed this step since #17206 by @teamleaderleo (self-merged) (#17169). Merge main again once the fix lands there.

Validate owned Mac build state (red on main too, not this PR)

Main has failed this step since #17168 by @teamleaderleo (self-merged) (#17169). Merge main again once the fix lands there.

Validate fork runner routing (red on main too, not this PR)

Main has failed this step since #17206 by @teamleaderleo (self-merged) (#17169). Merge main again once the fix lands there.

Run canonical CMUX CI guard profile (red on main too, not this PR)

Main has failed this step since #17206 by @teamleaderleo (self-merged) (#17169). Merge main again once the fix lands there.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

@teamleaderleo
teamleaderleo merged commit 00f182f into main Oct 3, 2026
61 of 68 checks passed
@teamleaderleo
teamleaderleo deleted the fix/claude-reentrant-stop-idle-16495 branch October 3, 2026 21:49
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Merge receipt for 28690c9423, merged 2026-10-03 21:48:59 UTC

  • Not verified at merge: ci-status (failure), CI fast guards (failure), Fast static checks (failure), guards (19) (failure), linux-preflight (failure), tests (failure)
  • Verified: backend migrations applied, CI timing, GhosttyKit release check, plan, Web complexity, web-validation
  • Skipped by policy: apply-production, apply-staging, browser, Claude wrapper regressions, Dogfood build #​${{ github.event.pull_request.number }}, full-suite-coverage, macos, macOS admission gate, remote-daemon, suite-coverage, ui-tests, web, and 3 more
  • Full suite: runs on main after merge.

Labeled merged-unverified: if main breaks near this merge, look here first.

@github-actions github-actions Bot added the merged-unverified A judging check was not green at merge; see the merge receipt comment label Oct 3, 2026
rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 3, 2026
00f182f Set Claude idle after reentrant stop without work (manaflow-ai#16635)
d8ef7e7 Reject unknown and valueless options in cmux hooks setup (manaflow-ai#17183)
46b5f9c remote-tmux: let a failed socket request say what failed (manaflow-ai#17134)
e88d636 remote-tmux: re-read a pane from tmux when its width changes, not only when it grows (manaflow-ai#17140)
984baea remote-tmux: close a window emptied by gathering its mirrors into a new one (manaflow-ai#17141)
9c41d59 fix: recover hidden terminal renderer after window attach (manaflow-ai#16548)
70854a5 ci: publish dogfood artifacts from red CI runs (manaflow-ai#17210)
db77e58 fix(cli): reject missing notify text values (manaflow-ai#16778)
76ccbfc ci: activate org-member dogfood artifact publisher (manaflow-ai#17206)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged-unverified A judging check was not green at merge; see the merge receipt comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude_code pill stays Running after a final re-entrant Stop (asyncRewake), follow-up to #15595

1 participant