Skip to content

Fix Claude no-flicker sidebar status routing#6058

Open
austinywang wants to merge 61 commits into
mainfrom
issue-6048-no-flicker-sidebar-status
Open

Fix Claude no-flicker sidebar status routing#6058
austinywang wants to merge 61 commits into
mainfrom
issue-6048-no-flicker-sidebar-status

Conversation

@austinywang

@austinywang austinywang commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #6048

Summary

  • add a regression for Claude Code hooks inheriting a stale ambient CMUX_SURFACE_ID while CLAUDE_CODE_NO_FLICKER=1 is set
  • make Claude hook target resolution correct ambient surfaces with the caller TTY / agent PID binding, while keeping explicit --surface and mapped session surfaces authoritative
  • refresh the claude_code PID ownership gate on prompt-submit before writing Running status so the sidebar badge has both required signals on the same panel

Mechanism

Claude hooks still fire in no-flicker mode; the failure path is that inherited ambient cmux routing can point visible status at one panel while the sidebar badge gate requires the recorded agent PID owner and status entry to match the same live panel. The fix treats live terminal binding as ground truth only for ambient fallback surfaces.

Verification

  • git diff --check
  • python3 tests/test_claude_wrapper_hooks.py

Local app builds intentionally not run per issue instructions; CI should compile and run the Swift regression test.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes sidebar badge/status mismatches in no‑flicker mode by routing Claude hook status through the pane that owns the live process, tightening PID/target validation, and aligning feed telemetry to the same target. Also no‑ops ambient hooks when a mapped session’s target is gone and recovery isn’t possible; adds serialized Swift tests for these cases.

  • Bug Fixes

    • Route status and feed telemetry via the live, authenticated agent PID/workspace; include pid in set_status; honor explicit --surface/--workspace (including empty/equals); prefer the live PID over stored; never republish or persist stored PIDs (especially on fallback routes); avoid process snapshots on hot paths with an isolated, authenticated per‑hook socket.
    • On prompt-submit, refresh PID so status, feed, and the badge land on the same pane; correct feed routing/telemetry to that pane; skip workspace probes during binding correction; keep the last valid target on transient probe failures; gracefully no‑op for prompt-submit/stop/notification/pre-tool-use when the socket is missing, PID recovery can’t be validated, or a mapped session’s workspace or surface is closed. Gate mapped‑session bypass on both stored workspace and surface being reachable; leave session-end unchanged; bind only when the PID maps to a valid pane and ignore stale raw TTY/workspace/surface. Docs: update CLAUDE.md separators to avoid conflict marker scan false positives.
  • Dependencies

    • Regenerated .github/swift-file-length-budget.tsv to match current code size and tests.

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

Review in cubic

Summary by CodeRabbit

  • Refactor
    • Improved determinism in Claude hook routing by capturing environment/provenance once, recomputing workspace/surface/agent context per request, and tightening fork/session-end cleanup and resume/feed/prompt routing safeguards.
  • Bug Fixes
    • Strengthened “no-flicker” behavior to avoid stale ambient targeting, honor explicit --surface/--workspace flags (including empty/equals forms), and correct telemetry when --surface is explicitly provided; refined PID attribution and snapshot binding behavior.
    • Normalized terminal workspace context output by removing a trailing blank line.
  • Tests
    • Added a new Swift test harness and serialized routing suites covering binding, fork/session lifecycle, and transient recovery/fallback scenarios.

@vercel

vercel Bot commented Jun 13, 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 14, 2026 11:21pm
cmux-staging Building Building Preview, Comment Jun 14, 2026 11:21pm

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The CLI now snapshots environment state once, threads provenance-aware terminal-binding data through Claude hook resolution, and tightens PID and surface handling across hook paths. New serialized tests cover no-flicker routing, stored-session fallback, and transient recovery cases.

Changes

Claude hook routing and regression coverage

Layer / File(s) Summary
Hook entry baseline and telemetry
CLI/cmux.swift
Recovery gating now uses explicit workspace and surface flag handling with environment fallbacks, computes hook PID from captured environment, and updates surface telemetry.
Resolver provenance contracts and binding sources
CLI/cmux.swift
Resolver helpers now carry provenance controls and binding cache inputs, surface listing can allow unknown targets, and process-terminal binding distinguishes authenticated process-snapshot bindings from TTY-derived bindings.
Session, prompt, and resume routing updates
CLI/cmux.swift
Session-start, mapped session-end, prompt-submit, notification, and resume/feed-like branches recompute Claude PID, seed terminal-binding caches, apply recovery guards, resolve surfaces through provenance-aware helpers, and gate lifecycle PID propagation on authoritative surfaces.
Fork cleanup and stop fallback handling
CLI/cmux.swift
Fork-parent-skipped session-end cleanup and stop handling use captured environment, fallback PID resolution, and non-snapshot fallback surface resolution, with nested suppression PID selection adjusted for consumed sessions.
Agent PID indirection in downstream routing
CLI/cmux.swift
Cached agentPID() inference replaces repeated inferred PID usage in telemetry suppression, resume routing, and process-binding fallback paths.
Formatting normalization
Sources/TerminalController+ControlWorkspaceContext.swift
Trailing blank lines are normalized at the end of controlWindowOrNull(_:).

Test infrastructure and regression coverage

Layer / File(s) Summary
Project file test target integration
cmux.xcodeproj/project.pbxproj, cmuxTests/ClaudeHookRoutingTestSupport.swift
Registers four new Swift test sources in the cmuxTests target through build-file, file-reference, group, and sources-phase entries. Adds isolated socket contexts, request recording, mock server looping, process execution with timeout handling, environment builders, hook-store seeding, JSON helpers, and socket/encoding/error utilities.
No-flicker routing regressions
cmuxTests/ClaudeNoFlickerHookRoutingTests.swift
Adds serialized prompt-submit and session-start tests for stale ambient recovery, PID routing, fallback selection, stored-vs-live PID replacement, and fork-session PID suppression under CLAUDE_CODE_NO_FLICKER.
Binding and stored-session regressions
cmuxTests/ClaudeNoFlickerHookBindingTests.swift
Adds prompt-submit tests for missing PID-bound surfaces, inherited TTY override, stored-session workspace/surface fallback, raw shell TTY ignore behavior, and stored-session fixture seeding.
Transient recovery regressions
cmuxTests/ClaudeNoFlickerHookTransientTests.swift
Adds prompt-submit and session-end tests for PID-only recovery misses, stored-target cleanup, snapshot skipping when the stored PID matches, env-missing stored target usage, fallback non-persistence, stored PID non-republishing without live hook PID, and transient workspace probe failure behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • manaflow-ai/cmux#5910: Related changes to session/fork lifecycle and authoritative-surface handling in CLI/cmux.swift.
  • manaflow-ai/cmux#5351: Related terminal-binding and surface-resolution adjustments for stale ambient CMUX_SURFACE_ID.

Poem

I sniffed the env with rabbit care,
And chased the PID through socket air,
No flicker blinked; the sidebar shone,
Stored paws and live paws found their own,
Hop-hop—background badges now dance along. 🐇


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error PR adds 496 user-facing CLI error messages in new CLI/cmux.swift file without String(localized:defaultValue:) API, violating full-internationalization rule. Only ~3.8% (19) of error messages use pr... Wrap all CLIError(message:) strings in String(localized:defaultValue:) with matching keys in Resources/Localizable.xcstrings with complete translations for all supported locales (currently en, ja, ko, zh-Hans, zh-Hant per the catalog add...
Docstring Coverage ⚠️ Warning Docstring coverage is 1.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: fixing Claude no-flicker sidebar status routing, which directly addresses the linked issue #6048.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #6048 by fixing sidebar status routing for Claude no-flicker mode through terminal binding resolution, PID refresh, and surface resolution logic.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the no-flicker sidebar routing issue: hook target resolution, process binding tracking, surface resolution helpers, and comprehensive regression tests.
Cmux Swift Actor Isolation ✅ Passed PR introduces no new actor isolation violations. New Claude hook classes (ClaudeHookParsedInput, ClaudeHookResolvedSurface, ClaudeHookSessionStore) are properly isolated; existing CodexTeamsAsyncBo...
Cmux Swift Blocking Runtime ✅ Passed All blocking primitives found (NSLock, DispatchSemaphore) are in test-only files (cmuxTests/). Production code changes in CLI/cmux.swift contain pre-existing blocking operations not introduced by t...
Cmux Expensive Synchronous Load ✅ Passed The PR modifies CLI/cmux.swift hook handlers to add system.top calls via resolveAgentProcessTerminalBinding, but only in CLI context (not main thread), and is properly guarded: allowProcessSnapshot...
Cmux Cache Substitution Correctness ✅ Passed PR correctly handles cache substitution: fresh hookClaudePid from env gates persistence/visibility mutations; stale mappedSession?.pid from disk never republished via set_agent_pid (only if f...
Cmux No Hacky Sleeps ✅ Passed All code changes are in Swift (covered by different check) or test code (exempt). No TypeScript, JavaScript, shell, or build/runtime script changes with hacky sleeps found.
Cmux Algorithmic Complexity ✅ Passed The PR modifies CLI hook target resolution (claudeHookSurfaceIsListed, resolvePreferredSurfaceForClaudeHookDetailed) but does not violate algorithmic complexity rules: no loops over scalable collec...
Cmux Swift Concurrency ✅ Passed PR introduces no new legacy async patterns in cmux-owned code. Hook code refactoring uses no DispatchQueue/Combine; test files use Swift Testing (@Test) framework with DispatchQueue only in test ha...
Cmux Swift @Concurrent ✅ Passed PR adds test support and test cases for Claude hook routing. All functions are either synchronous or properly use DispatchQueue.async for background work. No nonisolated async functions, no invalid...
Cmux Swift File And Package Boundaries ✅ Passed PR adds 236 net lines to CLI/cmux.swift (under 250-line threshold for violations) with all changes marked private; four new test files in cmuxTests/ directory are properly classified test code (all...
Cmux Swift Logging ✅ Passed No Swift logging violations found. All print() statements in production code are CLI hook responses or debug output (allowed); no NSLog, debugPrint, or unsafe file I/O for logging.
Cmux User-Facing Error Privacy ✅ Passed PR changes comply with user-facing error privacy rules. New hook code outputs only safe data: PIDs, workspace/surface IDs (user-visible), and status values. No env var names, credentials, tokens, o...
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI code changes—all modifications are to Swift CLI tool, tests, and project configuration. No violations of SwiftUI state/layout rules detected.
Cmux Architecture Rethink ✅ Passed PR does not violate Swift architectural rethink rules. Main fix adds isAuthoritative gate to conditionally gate PID/status persistence, with clear owner, invariant naming, and no timing/dispatch...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds no standalone windows: changes are test-only fixtures (cmuxTests/*.swift), CLI hook logic (CLI/cmux.swift), and formatting. No NSWindow/NSPanel/NSWindowController/SwiftUI Window instantiati...
Cmux Source Artifacts ✅ Passed All changed files are legitimate source code, tests, configs, or fixtures with no source control artifacts, local tool output, generated logs, caches, build output, or temp directories.
Description check ✅ Passed The PR description covers the key aspects of the fix with clear sections for Summary, Mechanism, and Verification, addressing what changed and why.
✨ 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-6048-no-flicker-sidebar-status

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.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the no-flicker (CLAUDE_CODE_NO_FLICKER=1) sidebar badge/status routing regression where hooks inheriting a stale ambient CMUX_SURFACE_ID routed status to the wrong panel while the badge PID gate checked the live-process panel.

  • Ambient routing now uses live terminal binding as ground truth: resolvePreferredWorkspaceIdForClaudeHook and resolvePreferredSurfaceForClaudeHookDetailed both accept a terminalBindingCache and, when an ambient fallback is in play, prefer the live PID or TTY binding over the inherited env var.
  • set_agent_pid is refreshed on prompt-submit: Before writing the Running status badge, the hook re-registers the live PID on the authoritative surface so both signals land on the same panel.
  • Ambient hooks gracefully no-op when target is unrecoverable: New missingRecoveredHookTarget / mappedHookTargetIsReachable guards skip all mutations when no valid target can be found.

Confidence Score: 5/5

Safe to merge; the fix is well-scoped to ambient no-flicker hook routing and does not regress normal sessions.

All routing changes are correctly gated: the expensive system.top process-snapshot lookup is only triggered when allowProcessSnapshotBinding is true, which is only set when the hook live PID differs from the stored session PID. Stored-session workspace and surface are preserved on transient surface.list failures via allowUnknown:true. The set_agent_pid refresh on prompt-submit only fires when the resolved surface is authoritative. Four new serialized Swift test suites cover binding, routing, and transient-failure paths.

No files require special attention; CLI/cmux.swift changes are well-bounded and the test files provide direct coverage of the new paths.

Important Files Changed

Filename Overview
CLI/cmux.swift Core routing fix: adds terminal-binding cache, ambient-vs-authoritative fallback discrimination, set_agent_pid refresh on prompt-submit, and new guard functions for unrecoverable targets.
cmuxTests/ClaudeHookRoutingTestSupport.swift New test scaffolding: Unix-socket mock server, process runner, and common fixtures. DispatchSemaphore usage is test-only synchronization.
cmuxTests/ClaudeNoFlickerHookBindingTests.swift Regression tests for PID-binding surface resolution covering stale bound-surface, cross-workspace snapshot, and TTY-only fallback scenarios.
cmuxTests/ClaudeNoFlickerHookRoutingTests.swift Serialized routing test suite covering fork/session lifecycle, prompt-submit PID refresh, and feed telemetry target alignment.
cmuxTests/ClaudeNoFlickerHookTransientTests.swift Tests for transient failure cases: missing socket graceful no-op, PID-only recovery with gone surface, and mapped-session workspace probe failure paths.
CLAUDE.md Separator characters changed from ======= to ------- to prevent false positives in conflict-marker scanners.
.github/swift-file-length-budget.tsv Regenerated file-length budget to account for four new test files and the cmux.swift additions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Claude hook fires] --> B{needsRecoveredHookTarget}
    B -->|No - normal session| C[mappedHookTargetIsReachable returns true]
    B -->|Yes - ambient no-flicker| D{Mapped session workspace and surface reachable?}
    D -->|Yes| E[Use mapped session target]
    D -->|No| F{PID or TTY binding found?}
    F -->|Binding found| G[resolvePreferredWorkspaceId]
    F -->|No binding| Z[Graceful no-op]
    E --> G
    C --> G
    G --> H{Preferred UUID set and workspace accessible?}
    H -->|Yes - PID snapshot, different workspace| J[Return live binding workspace]
    H -->|Yes - same workspace or TTY| K[Return preferred workspace]
    H -->|No - ambient fallback| L{Terminal binding found?}
    L -->|Yes| M[Return binding workspace]
    L -->|No| N[Default workspace resolution]
    J --> O[resolvePreferredSurfaceDetailed]
    K --> O
    M --> O
    N --> O
    O --> P{Preferred surface accessible?}
    P -->|Yes - PID snapshot overrides| Q[Live binding surface - isAuthoritative=true]
    P -->|Yes - matches stored| R[Preferred surface - isAuthoritative=true]
    P -->|No - ambient fallback with binding| Q
    P -->|No binding| T[Focused panel fallback - isAuthoritative=false]
    Q --> U[prompt-submit: set_agent_pid refresh then set status with pid]
    R --> U
    T --> V[setAgentLifecycle and status without pid]
Loading

Reviews (30): Last reviewed commit: "fix: tighten Claude hook routing and fee..." | Re-trigger Greptile

Comment thread CLI/cmux.swift

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

🤖 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 `@CLI/cmux.swift`:
- Around line 23185-23210: The cache currently only tracks didResolve and
binding (ClaudeHookTerminalBindingCache) so
resolveClaudeHookTerminalBinding(...) may return a binding regardless of
differing agentPID or allowProcessSnapshotBinding; change the cache to include
the lookup key (store agentPID and allowProcessSnapshotBinding in
ClaudeHookTerminalBindingCache or add an assertion that the incoming key matches
the cached key), then update resolveClaudeHookTerminalBinding(...) to check the
cached key before returning cached.binding and to set both the key and binding
when populating the cache (or assert on mismatch), referencing the
ClaudeHookTerminalBindingCache and resolveClaudeHookTerminalBinding symbols and
the CallerTerminalBinding type so mixed-lookups cannot return
stale/over-permissive bindings.

In `@cmuxTests/ClaudeHookRoutingTestSupport.swift`:
- Around line 145-152: The timed-out subprocess may not exit after
process.terminate(), causing subsequent readDataToEndOfFile() calls to block;
update the timeout-handling block so that after calling process.terminate() you
check if the process is still running and if so escalate to SIGKILL (use
kill(process.processIdentifier, SIGKILL) or equivalent), wait again for the
exitSignal (e.g., another wait with a short timeout) until the process has
exited, and only then call stdoutPipe.fileHandleForReading.readDataToEndOfFile()
and stderrPipe...; modify the block around timedOut, terminate(),
exitSignal.wait(...), and the return that constructs ProcessRunResult so the
kill-and-wait escalation is performed before draining stdout/stderr and reading
process.terminationStatus.
- Around line 104-106: The write call inside response.withCString ignoring
Darwin.write's return can produce partial writes and truncated JSON; update the
code around response.withCString { ptr in Darwin.write(clientFD, ptr,
strlen(ptr)) } to loop until the full byte count is written: compute the total
length (e.g., with strlen(ptr)), call Darwin.write in a loop advancing the
pointer by the number of bytes written, handle short writes and retry on EINTR,
and treat negative return values as errors (break/log/close as appropriate);
reference the existing response.withCString closure, Darwin.write, and clientFD
when making the change.
🪄 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: 2022bd44-7d07-4909-b9db-af3960322b13

📥 Commits

Reviewing files that changed from the base of the PR and between 854277a and bd79085.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (4)
  • CLI/cmux.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/ClaudeHookRoutingTestSupport.swift
  • cmuxTests/ClaudeNoFlickerHookRoutingTests.swift

Comment thread CLI/cmux.swift
Comment thread cmuxTests/ClaudeHookRoutingTestSupport.swift Outdated
Comment thread cmuxTests/ClaudeHookRoutingTestSupport.swift
@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 `@CLI/cmux.swift`:
- Around line 23053-23054: The fallback path is currently accepting PID-derived
bindings if claudeHookWorkspaceIsAccessible(...) is true even when
binding.isProcessSnapshotBound is true; require the bound workspace to also be
the currently selected workspace before returning a PID-derived binding. In
CLI/cmux.swift (lines 23053-23054) modify the resolver that returns
boundWorkspaceId to add a condition alongside
claudeHookWorkspaceIsAccessible(boundWorkspaceId, client: client) that the
workspace is the selected/current workspace (e.g.
workspaceIsSelected(boundWorkspaceId, client: client) or equivalent check) when
binding.isProcessSnapshotBound is true; and in CLI/cmux.swift (lines
23068-23069) apply the same selected-workspace gate in the fallback-only
resolver so that stop/cleanup paths cannot accept a background PID match (no
change needed to other logic—just add the selected-workspace predicate where
claudeHookWorkspaceIsAccessible is currently used for process-snapshot
bindings).
🪄 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: d3dd822c-5421-4399-9596-4ada1558e202

📥 Commits

Reviewing files that changed from the base of the PR and between 854277a and ec79ecd.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (4)
  • CLI/cmux.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/ClaudeHookRoutingTestSupport.swift
  • cmuxTests/ClaudeNoFlickerHookRoutingTests.swift

Comment thread CLI/cmux.swift 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: 2

Caution

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

⚠️ Outside diff range comments (1)
cmuxTests/ClaudeNoFlickerHookRoutingTests.swift (1)

296-410: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add missing stale workspace negative assertion.

The test seeds a stored session with staleWorkspaceId (lines 302, 313, 321) and the surface.list mock returns different surfaces based on workspace (lines 335, 339), but the assertions only verify that no commands target staleSurfaceId (lines 394-400). Tests 1–3 verify both --panel and --tab negatives when checking stale bindings (lines 118-130, 196-201, 287-293). Since staleWorkspaceId does not appear in the system.top response (lines 341-356), the CLI should never send commands to it.

✅ Add workspace negative assertion
         "Stored stale PID surface must not receive Claude's visible status or PID gate, saw \(commands)"
     )
+    `#expect`(
+        !commands.contains {
+            ($0.hasPrefix("set_status claude_code Running ") || $0.hasPrefix("set_agent_pid claude_code "))
+                && $0.contains("--tab=\(staleWorkspaceId)")
+        },
+        "Stored session's stale workspace must not receive Claude's visible status or PID gate, saw \(commands)"
+    )
     let systemTopCalls = commands.filter {
🤖 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 `@cmuxTests/ClaudeNoFlickerHookRoutingTests.swift` around lines 296 - 410, The
test claudePromptSubmitPrefersLiveHookPIDOverStoredPID() validates that the CLI
ignores a stale stored workspace and surface when a live PID hook is available.
Currently, it asserts that commands don't target staleSurfaceId via the --panel
parameter (line 394-400), but it's missing a parallel assertion to verify
commands don't target staleWorkspaceId via the --tab parameter. Add a negative
assertion after the existing staleSurfaceId check that verifies no commands
contain --tab=staleWorkspaceId, following the pattern used in earlier tests like
claudePromptSubmitFiltersSessionsByWorkspace() and
claudePromptSubmitPrefersSessionFromActiveList() which validate both --tab and
--panel negatives for stale bindings.
🤖 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 `@cmuxTests/ClaudeNoFlickerHookBindingTests.swift`:
- Line 446: The combined statement in ClaudeNoFlickerHookBindingTests should be
split into separate lines for readability; update the environment initialization
and the subsequent TTY assignment in the test setup so they are written as
distinct statements, using the existing support.baseHookEnvironment(context:)
and environment["TTY"] reference points.
- Line 423: The compound let declaration that declares staleWorkspaceId,
staleSurfaceId, and staleTTY on a single line exceeds 145 characters and reduces
readability. Split this into separate let declarations with each variable on its
own line to comply with Swift style guidelines.

---

Outside diff comments:
In `@cmuxTests/ClaudeNoFlickerHookRoutingTests.swift`:
- Around line 296-410: The test
claudePromptSubmitPrefersLiveHookPIDOverStoredPID() validates that the CLI
ignores a stale stored workspace and surface when a live PID hook is available.
Currently, it asserts that commands don't target staleSurfaceId via the --panel
parameter (line 394-400), but it's missing a parallel assertion to verify
commands don't target staleWorkspaceId via the --tab parameter. Add a negative
assertion after the existing staleSurfaceId check that verifies no commands
contain --tab=staleWorkspaceId, following the pattern used in earlier tests like
claudePromptSubmitFiltersSessionsByWorkspace() and
claudePromptSubmitPrefersSessionFromActiveList() which validate both --tab and
--panel negatives for stale bindings.
🪄 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: 14e71662-bc24-4e40-99ac-393f81485af1

📥 Commits

Reviewing files that changed from the base of the PR and between 0087600 and ada9f71.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (3)
  • CLI/cmux.swift
  • cmuxTests/ClaudeNoFlickerHookBindingTests.swift
  • cmuxTests/ClaudeNoFlickerHookRoutingTests.swift

Comment thread cmuxTests/ClaudeNoFlickerHookBindingTests.swift Outdated
Comment thread cmuxTests/ClaudeNoFlickerHookBindingTests.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 `@cmuxTests/ClaudeNoFlickerHookTransientTests.swift`:
- Around line 300-363: The test
claudePromptSubmitDoesNotRepublishStoredPIDWithoutLiveHookPID is missing a
defensive assertion to verify that system.top is not called as part of the
hot-path optimization when using a stored PID without a live hook PID. Add an
assertion after the existing storedPIDWasRepublished check to verify that the
commands captured from context.state.snapshot() do not contain any invocation of
system.top, consistent with the pattern demonstrated in the sibling tests that
validate this optimization.
🪄 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: 16b40e7d-1b48-41a6-af5c-02265932db7b

📥 Commits

Reviewing files that changed from the base of the PR and between 988f7c4 and bb851aa.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • cmuxTests/ClaudeNoFlickerHookTransientTests.swift

Comment thread cmuxTests/ClaudeNoFlickerHookTransientTests.swift
…-sidebar-status

# Conflicts:
#	.github/swift-file-length-budget.tsv
austinywang and others added 2 commits June 14, 2026 01:45
Failing regression test (no fix) per the two-commit regression policy. An
ambient no-flicker prompt-submit carrying a mapped session whose stored
workspace was closed (and whose PID/TTY cannot be recovered) must no-op
instead of borrowing the focused pane and publishing Running status / the
PID gate onto it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A mapped session record bypassed the recovered-target no-op
unconditionally, so an ambient no-flicker prompt-submit / stop /
notification / pre-tool-use whose stored target had been closed (and
whose PID/TTY could not be recovered) fell through to the app-selected
focused pane and published Running/Idle/Needs-input status and the PID
gate onto it — the cross-pane routing class this PR fixes.

Gate the bypass on the stored target still being reachable via a shared
mappedHookTargetIsReachable() helper that requires BOTH the stored
workspace AND the stored surface to still be present. When either is gone
and PID recovery misses, the hook no-ops exactly as it does without a
record. session-end is intentionally unchanged: it mutates the consumed
record's own stored target, not the resolved focused fallback.

Adds regression tests for the closed-workspace and closed-surface cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

Sidebar integration with Claude Code doesn't seem to detect status of sessions running with CLAUDE_CODE_NO_FLICKER=1?

1 participant