Skip to content

Make tab drag lifecycle cleanup synchronous - #204

Merged
austinywang merged 2 commits into
mainfrom
issue-9521-sticky-drag-latches
Aug 7, 2026
Merged

austinywang merged 2 commits into
mainfrom
issue-9521-sticky-drag-latches

Conversation

@austinywang

@austinywang austinywang commented Aug 7, 2026 •

Copy link
Copy Markdown

Follow-up for manaflow-ai/cmux#9521.

  • invoke drag lifecycle cleanup synchronously from AppKit main-thread callbacks
  • make monitor registration cleanup survive controller teardown via isolated deinit
  • assert app-resign teardown completes before notification posting returns

Validation: arch -arm64 swift test (217 XCTest + 7 Swift Testing passed).


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

Make tab drag cleanup synchronous to prevent sticky drag latches and stale session callbacks. Event monitors and app-resign handling now end the drag immediately on main-thread callbacks and during controller teardown, addressing issue 9521.

  • Bug Fixes
    • End the active drag generation synchronously from AppKit main-thread monitors (ESC, mouseUp, app resign) via MainActor.assumeIsolated (no deferred Task).
    • Make teardown synchronous during stop() and deinit: store monitor tokens as nonisolated(unsafe) and remove them via a static helper, avoiding an isolated-deinit runtime dependency.
    • Update test to assert immediate cleanup on app resign (no Task.yield).

Written for commit 6a9b467. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@austinywang, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

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

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3918d3e9-183e-423a-be27-b8be3a555b5b

📥 Commits

Reviewing files that changed from the base of the PR and between aa16fb9 and 6a9b467.

📒 Files selected for processing (1)
  • Sources/Bonsplit/Internal/Controllers/TabDragLifecycleMonitor.swift
📝 Walkthrough

Walkthrough

The tab drag lifecycle monitor centralizes AppKit observer cleanup, performs synchronous main-actor end handling, and removes deferred cleanup tasks. The app-resign test now verifies drag-state cleanup synchronously.

Changes

Tab drag lifecycle

Layer / File(s) Summary
Observer registration ownership
Sources/Bonsplit/Internal/Controllers/TabDragLifecycleMonitor.swift
The monitor stores AppKit observers in Registrations and removes them through removeAll().
Synchronous drag-end handling
Sources/Bonsplit/Internal/Controllers/TabDragLifecycleMonitor.swift, Tests/BonsplitTests/BonsplitTests.swift
Event callbacks use MainActor.assumeIsolated. requestEnd() retains generation and stopped-state guards. Deinitialization and stop() remove registrations. The app-resign test checks cleanup without yielding.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: lawrencecchen

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: making tab drag lifecycle cleanup synchronous.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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-9521-sticky-drag-latches

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.

@austinywang
austinywang merged commit af2e3a8 into main Aug 7, 2026
6 checks passed
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.

1 participant