Skip to content

Unbreak main CI: canvas reorder overflow + duplicate shortcut notification - #8146

Closed
azooz2003-bit wants to merge 3 commits into
mainfrom
fix-canvas-reorder-overflow
Closed

azooz2003-bit wants to merge 3 commits into
mainfrom
fix-canvas-reorder-overflow

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator

Two regressions landed on main while its CI runs were cancelled during the CI/CD pause; both fail required checks on every PR via the pull_request merge ref. This PR fixes both.

1. swift-package-tests: SIGTRAP in CmuxCanvasUI — #8080 computes currentIndex + offset in CanvasModel.reorderPanel before clamping, which traps on the Int.max/Int.min offsets its own regression test passes. Fixed by saturating the addition (addingReportingOverflow) before clamping. Verified: swift test --package-path Packages/macOS/CmuxCanvasUI goes from SIGTRAP to 45/45 exit 0 locally, and swift-package-tests passed on this PR's earlier run at e860e84.

2. app-host unit tests (4/4): duplicate shortcut notification — HostSettingsActions.notifyShortcutSettingsDidChange posts didChangeNotification unconditionally after reload() already posted for an observed change, so a changed cmux.json posts twice. HostSettingsShortcutNotificationTests (added with #8091) expects exactly one post for changed and unchanged files. Fixed by having reload() report whether it notified; the action posts only when it did not.

No new tests: both defects are pinned by regression tests already on main (reorderPanelClampsExtremeOffsetsWithoutOverflow, HostSettingsShortcutNotificationTests) that go red→green with these fixes. Example victim of both: #7670.

🤖 Generated with Claude Code

reorderPanel computed currentIndex + offset before clamping, which traps
on Int.max/.min offsets. The existing regression test
reorderPanelClampsExtremeOffsetsWithoutOverflow (landed with #8080 while
main CI was paused) crashes the whole CmuxCanvasUI test process with
SIGTRAP, failing swift-package-tests for every PR. Saturate the addition
before clamping to the valid index range.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled Jul 15, 2026 3:19pm
cmux-staging Building Building Preview, Comment Jul 15, 2026 3:19pm

@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

reorderPanel now safely handles extreme offsets, while shortcut settings reload reports notification emission so callers avoid duplicate settings-change notifications.

Changes

Behavior safety updates

Layer / File(s) Summary
Safe destination index calculation
Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasModel.swift
reorderPanel detects overflow when adding the current index and offset, saturates to integer bounds, and clamps to the valid tab-index range.
Coordinated settings notifications
Sources/KeyboardShortcutSettingsFileStore.swift, Sources/HostSettingsActions.swift
reload() returns whether shortcut-related state changed and emitted its notification, allowing notifyShortcutSettingsDidChange() to suppress duplicate notifications.

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

Possibly related PRs

  • manaflow-ai/cmux#8141: Both changes update CanvasModel.reorderPanel(_:by:) to handle integer overflow.
  • manaflow-ai/cmux#8157: Both changes coordinate shortcut settings reload and notification emission.
  • manaflow-ai/cmux#8091: Both changes involve notifyShortcutSettingsDidChange() and shortcut settings notification routing.

Suggested reviewers: austinywang

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
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.
Cmux Swift Actor Isolation ✅ Passed Touched production code stays on @MainActor UI paths or the existing lock-based store; no new actor-isolation mismatch or background UI access was introduced.
Cmux Swift Blocking Runtime ✅ Passed Diff only adds overflow-safe arithmetic and notification gating; no new waits/sleeps/syncs/locks were introduced, and the existing NSLock is pre-existing.
Cmux Browser Automation Off-Main ✅ Passed PR only changes settings-reload files; it doesn’t touch TerminalController or the ControlSocket policy, and no browser automation routing was added or moved off-main.
Cmux Expensive Synchronous Load ✅ Passed No agent-history/session/transcript load was added or moved; changes are only panel-index clamping and shortcut notification deduping. Existing settings reload path wasn’t worsened.
Cmux Cache Substitution Correctness ✅ Passed Changed code only dedupes shortcut-change notifications; reload() still resolves settings from disk, and no persistence/snapshot path swaps in cached state.
Cmux No Hacky Sleeps ✅ Passed Only Swift files changed; the no-hacky-sleeps rule explicitly excludes Swift and covers non-Swift runtime scripts.
Cmux Algorithmic Complexity ✅ Passed Diff only adds overflow-safe arithmetic and conditional notification; it introduces no new nested scans, rescans, or repeated sorting/filtering in scalable paths.
Cmux Swift Concurrency ✅ Passed Diff only adds synchronous overflow clamping and Bool reload signaling; no new DispatchQueue, Combine, completion-handler, or unowned fire-and-forget Task patterns were introduced.
Cmux Swift @Concurrent ✅ Passed Changed methods are synchronous UI-bound code; no new @concurrent, nonisolated async, or actor-hop issues appear in the diff.
Cmux Swift Package Boundaries ✅ Passed Changes are package-local CanvasModel math plus app-lifecycle glue around shortcut settings reload; no new reusable domain logic is introduced in the app target.
Cmux Swiftpm Lockfiles ✅ Passed Diff only changes two .swift sources; no Package.swift, .gitignore, workflow, or Package.resolved files were touched, so the lockfile policy isn’t implicated.
Cmux Swift Logging ✅ Passed Diff only changes notification flow and overflow handling; it adds no print/debugPrint/dump/NSLog calls or new/mutated Logger setup.
Cmux User-Facing Error Privacy ✅ Passed The patch only changes internal control flow and comments; it adds no user-visible errors, alerts, command output, or recovery copy exposing restricted details.
Cmux Full Internationalization ✅ Passed Diff only changes overflow/notification logic and comments; no user-facing text, catalogs, routing, or locale files were added or modified.
Cmux Swiftui State Layout ✅ Passed PASS — the diff only changes model/reload logic and an AppKit bridge; it adds no new SwiftUI state/layout patterns or render-time writes.
Cmux Architecture Rethink ✅ Passed Small local correctness fixes; owners stay clear (CanvasModel and settings store), with no timing hack, duplicate wiring, or new state owner introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes canvas overflow handling and shortcut settings reload; it adds no new/changed standalone window code or cmux.* identifier wiring.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source files; the diff adds only code/comments and no logs, caches, build output, temp dirs, or other source-control artifacts.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS: The touched production files only change overflow handling and notification flow; no #if DEBUG, test-only accessor, or seam-like member was added.
Cmux No Ambient Global State ✅ Passed The diff only changes instance methods to return a Bool and conditionally notify; it adds no new file-scope funcs, globals, namespaces, or singletons.
Title check ✅ Passed The title is concise and accurately summarizes the two main fixes in the changeset.
Description check ✅ Passed The description covers the summary and testing sections well, but it omits the demo video and checklist details from the template.
✨ 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 fix-canvas-reorder-overflow

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.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two independent bugs: a SIGTRAP crash in CanvasModel.reorderPanel caused by unchecked integer overflow on extreme offsets, and a double-notification in notifyShortcutSettingsDidChange where reload() and the caller both unconditionally posted didChangeNotification on every detected settings change.

  • CanvasModel.swift: Replaces currentIndex + offset with addingReportingOverflow, then saturates to Int.max/Int.min on overflow before clamping to the valid index range. The saturation direction (offset > 0 ? Int.max : Int.min) is correct, and the existing !panelIds.isEmpty guard ensures panelIds.index(before: panelIds.endIndex) is safe.
  • KeyboardShortcutSettingsFileStore.swift / HostSettingsActions.swift: reload() now returns Bool indicating whether it already posted didChangeNotification; the caller uses this to post exactly once per notifyShortcutSettingsDidChange() invocation in all cases.

Confidence Score: 5/5

Both fixes are narrow, targeted, and well-scoped; the crashing regression test already on main validates the overflow fix, and the double-notification change preserves exactly-once semantics in all cases.

The overflow saturation logic is correct for all input combinations (positive/negative overflow, empty-pane guard in place). The Bool-returning reload() change eliminates a double-post without dropping any notification that should fire. No rule violations found across all three changed files.

No files require special attention.

Important Files Changed

Filename Overview
Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasModel.swift Fixes SIGTRAP crash in reorderPanel by saturating currentIndex + offset with addingReportingOverflow before clamping; logic is correct for all overflow directions.
Sources/KeyboardShortcutSettingsFileStore.swift Adds @discardableResult and returns Bool from reload() / private overload to let callers know whether the notification was already posted; change detection condition is unchanged from pre-existing code.
Sources/HostSettingsActions.swift Consumes the new Bool return from reload() to avoid double-posting didChangeNotification when settings do change; still posts once when reload() detects no change, preserving forced-refresh semantics.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["reorderPanel(_:by:)"] --> B{guard: panel in pane,\npanelIds non-empty}
    B -- false --> C[return false]
    B -- true --> D["addingReportingOverflow(offset)"]
    D --> E{overflowed?}
    E -- yes, offset > 0 --> F[target = Int.max]
    E -- yes, offset ≤ 0 --> G[target = Int.min]
    E -- no --> H[target = sum]
    F & G & H --> I["destinationIndex = clamp(target, startIndex…lastIndex)"]
    I --> J{destinationIndex\n== currentIndex?}
    J -- yes --> K[return true, no-op]
    J -- no --> L[removePanel → addPanel at destinationIndex]
    L --> M[revision &+= 1\nreturn true]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["reorderPanel(_:by:)"] --> B{guard: panel in pane,\npanelIds non-empty}
    B -- false --> C[return false]
    B -- true --> D["addingReportingOverflow(offset)"]
    D --> E{overflowed?}
    E -- yes, offset > 0 --> F[target = Int.max]
    E -- yes, offset ≤ 0 --> G[target = Int.min]
    E -- no --> H[target = sum]
    F & G & H --> I["destinationIndex = clamp(target, startIndex…lastIndex)"]
    I --> J{destinationIndex\n== currentIndex?}
    J -- yes --> K[return true, no-op]
    J -- no --> L[removePanel → addPanel at destinationIndex]
    L --> M[revision &+= 1\nreturn true]
Loading

Reviews (3): Last reviewed commit: "Fix duplicate shortcut change notificati..." | Re-trigger Greptile

cmux reload-cloud and others added 2 commits July 15, 2026 02:22
HostSettingsActions.notifyShortcutSettingsDidChange posted
didChangeNotification unconditionally after reload() had already posted
it for an observed change, so a changed cmux.json produced two posts.
The HostSettingsShortcutNotificationTests added with #8091 expect
exactly one post for both changed and unchanged files; the changed case
fails app-host unit tests (4/4) on every PR merge ref. reload() now
reports whether it notified and the action only posts when it did not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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.

Caution

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

⚠️ Outside diff range comments (1)
Sources/KeyboardShortcutSettingsFileStore.swift (1)

187-193: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add focused tests for the exactly-once notification contract.

Test that a changed reload returns true and emits one notification, while an unchanged reload returns false; also cover HostSettingsActions.notifyShortcutSettingsDidChange() forwarding exactly one notification in both cases.

🤖 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 `@Sources/KeyboardShortcutSettingsFileStore.swift` around lines 187 - 193, add
focused tests around the reload comparison logic in
KeyboardShortcutSettingsFileStore, verifying changed reloads return true and
emit exactly one notification while unchanged reloads return false. Also test
HostSettingsActions.notifyShortcutSettingsDidChange() to confirm it forwards
exactly one notification for both changed and unchanged cases, using
notification counts and return values to enforce the contract.
🤖 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.

Outside diff comments:
In `@Sources/KeyboardShortcutSettingsFileStore.swift`:
- Around line 187-193: add focused tests around the reload comparison logic in
KeyboardShortcutSettingsFileStore, verifying changed reloads return true and
emit exactly one notification while unchanged reloads return false. Also test
HostSettingsActions.notifyShortcutSettingsDidChange() to confirm it forwards
exactly one notification for both changed and unchanged cases, using
notification counts and return values to enforce the contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e910ed6c-3b12-4ced-abcd-d6033d426d8e

📥 Commits

Reviewing files that changed from the base of the PR and between e860e84 and b2725e1.

📒 Files selected for processing (2)
  • Sources/HostSettingsActions.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift

@azooz2003-bit azooz2003-bit changed the title Fix arithmetic overflow crash in CanvasModel.reorderPanel Unbreak main CI: canvas reorder overflow + duplicate shortcut notification Jul 15, 2026
@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Superseded: both fixes landed on main independently — the reorder overflow via #8153 and the duplicate shortcut notification via bcfc5df, with functionally identical implementations (saturating add before clamp; reload() reporting whether it notified). This PR's CI did go fully green at b2725e1, independently confirming both fixes. Closing; branch left in place.

This branch was successfully deployed

1 active deployment
Preview – cmux — b2725e17 Deployed Jul 15, 2026 by vercel[bot]
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