cmux Debugging
Debug event log
When adding debug event instrumentation, put events (keys, mouse, focus, splits, tabs) in the unified DEBUG build log. This is not a blanket requirement to add logs to every new code path. Most temporary probes should be added only during the dogfood debug loop and removed before merge.
tail -f "$(cat /tmp/cmux-last-debug-log-path 2>/dev/null || echo /tmp/cmux-debug.log)"
- Untagged Debug app:
/tmp/cmux-debug.log - Tagged Debug app (
./scripts/reload.sh --tag <tag>):/tmp/cmux-debug-<tag>.log reload.shwrites the current path to/tmp/cmux-last-debug-log-pathreload.shwrites the selected dev CLI path to/tmp/cmux-last-cli-pathreload.shupdates/tmp/cmux-cliand$HOME/.local/bin/cmux-devto that CLI- Implementation:
Packages/macOS/CMUXDebugLog/Sources/CMUXDebugLog/DebugEventLog.swift - App shim:
Sources/App/DebugLogging.swift - Free function
cmuxDebugLog("message")logs with timestamp and appends to file in real time from cmux code - The package implementation and app shim are
#if DEBUG; all call sites must be wrapped in#if DEBUG/#endif - 500-entry ring buffer;
CMUXDebugLog.DebugEventLog.shared.dump()writes full buffer to file - Key events logged in
AppDelegate.swift(monitor, performKeyEquivalent) - Mouse/UI events logged inline in views (ContentView, BrowserPanelView, etc.)
- Focus events:
focus.panel,focus.bonsplit,focus.firstResponder,focus.moveFocus - Bonsplit events:
tab.select,tab.close,tab.dragStart,tab.drop,pane.focus,pane.drop,divider.dragStart
Debug menu
The app has a Debug menu in the macOS menu bar only in DEBUG builds. Use it for visual iteration.
- Debug > Debug Windows contains panels for tuning layout, colors, and behavior. Entries are alphabetical with no dividers.
- To add a debug toggle or visual option: create an
NSWindowControllersubclass with asharedsingleton, add it to the "Debug Windows" menu inSources/cmuxApp.swift, and add a SwiftUI view with@AppStoragebindings for live changes. - When the user says "debug menu" or "debug window", they mean this menu, not
defaults write.
Runtime pitfalls
- Custom UTTypes for drag-and-drop must be declared in
Resources/Info.plistunderUTExportedTypeDeclarations. - Do not add an app-level display link or manual
ghostty_surface_drawloop; rely on Ghostty wakeups/renderer to avoid typing lag. WindowTerminalHostView.hitTest()is typing-latency-sensitive. All divider/sidebar/drag routing is gated to pointer events only. Do not add work outside theisPointerEventguard.TabItemViewusesEquatableconformance plus.equatable()to skip body re-evaluation during typing. Do not add environment/store/binding reads without updating equality and the call site.TerminalSurface.forceRefresh()is called on every keystroke. Do not add allocations, file I/O, or formatting there.SurfaceSearchOverlaymust be mounted fromGhosttySurfaceScrollViewinSources/GhosttyTerminalView.swift, not from SwiftUI panel containers.- List subtrees with
LazyVStack,LazyHStack,List, orForEachmust pass immutable row snapshots plus closures below the boundary. Do not pass observable stores into row views. - Functions called from SwiftUI
bodymust not mutate state or schedule store writes. - Foundation, SwiftUI, AttributeGraph, and WebKit semantics can change between macOS major versions. Test on the reporter's macOS before declaring a user repro disproven.
Detailed references
- Read references/debug-event-log.md when adding or interpreting debug log probes.
- Read references/runtime-pitfalls.md before touching terminal rendering, hit testing, tab rows, list virtualization, search overlay layering, or OS-version-sensitive code.



