Skip to content

Reduce typing latency in large busy workspaces - #7863

Merged
azooz2003-bit merged 135 commits into
mainfrom
perf-typing-lag-bottlenecks
Jul 14, 2026
Merged

azooz2003-bit merged 135 commits into
mainfrom
perf-typing-lag-bottlenecks

Conversation

@azooz2003-bit

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

Copy link
Copy Markdown
Collaborator

Summary

  • suspend hidden terminal renderer work and bound hidden workspace mounting
  • share process snapshots across port, memory, task, and agent scans; remove obsolete pane-memory polling
  • batch mobile, focus-menu, and Git result invalidations
  • single-flight Git snapshots and deduplicate pull-request refresh work
  • add opt-in Release counters for process, Git, pull-request, and mobile owners, plus renderer signposts

Browser focus ownership is retained as correctness support for hidden browser view unmount/remount. It is not presented as a performance improvement.

Profile evidence

The one-minute Nightly typing capture contained 28 workspaces, 38 panes, and 55 surfaces:

  • hidden or offscreen renderers contributed at least 178,711 samples, 86.36% of renderer activity
  • the port scanner existed for 96.59% of the capture; scanAgentPorts occupied 29,368 samples and repeatedly invoked process enumeration and lsof
  • background Git work accumulated 34,694 samples; main-thread Git apply occupied 1,397 samples and PR scheduling was nested in 1,195 samples
  • mobile workspace observation occupied 1,400 samples, 18.04% of active main-thread time

These captures identify owners and justify candidate fixes. They do not yet prove end-to-end improvement.

Scope audit

Removed after independent review because the captures did not justify them:

  • scoped report_pwd scheduling
  • remote orphan cleanup
  • workspace-save snapshotting
  • portal and Bonsplit geometry deduplication, whose isolated A/B was inconclusive overall

An independent audit classified all 167 paths in the then-current net diff and found no remaining unrelated implementation. The six paths added afterward are isolated Git/PR owner diagnostics and regression tests required by independent review, bringing the current net diff to 173 paths. The branch history still shows the reverted experiments above because several commits mix retained compile fixes with later-trimmed hunks; the final verified net diff should be squash-merged rather than rewritten across those red/green test pairs.

Verification status

  • exact source-attested Release baseline exists for main at e634825742; candidate source is frozen at pushed head a9033b4014 pending an exact Xcode 26.3 / macOS SDK 26.2 Release build
  • 27 focused app tests across process, listener-scan, mobile, and focus batching owners pass; the process proof passes 23 focused tests and independent review
  • Git passes 90/90 and sidebar Git passes 63/63 in both Debug and Release; control-socket policy passes 20/20
  • Git/PR cancellation regressions are recorded as a failing-test commit followed by the fix; independent review repeated the six focused tests 50 times and approved the result
  • the full macOS app test build passes with zero occurrences of the two branch-introduced sendability warnings
  • the offline harness passes syntax checking, strict type checking, and poisoned self-tests that reject subprocess launches, invalid measurement epochs, empty process captures, all-zero Git/PR evidence, counter drift, and invalid Git/PR causal relationships
  • invalid trials have been rejected for stale agent prompts, lost scrollback seed state, and excessive ambient CPU rather than counted as results
  • no valid matched baseline/candidate latency result exists yet
  • this Mac's Xcode 26.6 / SDK 26.5 environment is intentionally rejected for the candidate because the baseline uses Xcode 26.3 / SDK 26.2; the candidate must come from a verified matching cloud host
  • the earlier profile's Settings-open hypothesis remains unresolved and is scheduled for a controlled Settings-closed versus Settings-open trace

Draft blockers

  • pass six comb and six balanced matched Claude Code/Codex typing trials
  • prove process sharing, Git/PR coalescing, mobile batching, and hidden-renderer suppression with counters and Instruments
  • complete the Settings-open A/B and address it only if reproduced
  • pass required checks, autoreview, independent artifact verification, tagged preflight, and human dogfood

This PR remains a draft until those gates pass.

Summary by CodeRabbit

  • New Features
    • Added opt-in terminal renderer profiling and performance metrics diagnostics.
    • Added shared process and port snapshot handling to improve diagnostic and agent detection workflows.
    • Added reliable background workspace priming and filesystem event processing.
    • Preserved text-editor selection across panel remounts.
  • Bug Fixes
    • Improved browser address-bar focus ownership and WebView handoff behavior.
    • Improved Git pull-request refresh consistency during branch and source changes.
    • Reduced stale terminal surface updates and retained occlusion state across recreation.
  • Settings
    • Removed the Runaway Memory Guardrail settings from Terminal preferences.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Handoff: pushed the 12 completed local performance commits through 449ec2e. The worktree has an unfinished merge of 27810b9 with nine unresolved paths; preserve it. Ghostty dependency manaflow-ai/ghostty#114 combines the 8 MiB per-surface scrollback cap with current absolute-scroll restoration. Confirmed remaining gap: Codex fire-and-forget hooks can fan out detached CLI plus 30-second watchdog trees under a slow socket; that bounded-delivery fix is not implemented in this PR.

@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

Caution

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

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

141-150: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Batch unbuffered system calls into a single write.

FileHandle.write(contentsOf:) executes a direct, unbuffered write system call. Emitting hundreds of tiny individual frames in a tight loop imposes unnecessary I/O overhead. Consider coalescing the data chunks to execute a single bulk write, flushing only when crossing the file-size rotation boundary.

⚡ Proposed fix to batch frames before writing
-            for data in lines {
-                if currentSize + UInt64(data.count) > maxEventLogBytes {
-                    try handle.close()
-                    try rotate(fileManager: fileManager)
-                    handle = try FileHandle(forWritingTo: eventLogURL)
-                    currentSize = 0
-                }
-                try handle.write(contentsOf: data)
-                currentSize += UInt64(data.count)
-            }
+            var batch = Data()
+            for data in lines {
+                if currentSize + UInt64(batch.count + data.count) > maxEventLogBytes {
+                    if !batch.isEmpty {
+                        try handle.write(contentsOf: batch)
+                        currentSize += UInt64(batch.count)
+                        batch.removeAll(keepingCapacity: true)
+                    }
+                    try handle.close()
+                    try rotate(fileManager: fileManager)
+                    handle = try FileHandle(forWritingTo: eventLogURL)
+                    currentSize = 0
+                }
+                batch.append(data)
+            }
+            if !batch.isEmpty {
+                try handle.write(contentsOf: batch)
+                currentSize += UInt64(batch.count)
+            }
🤖 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/CmuxEventLogWriter.swift` around lines 141 - 150, Update the write
loop in the event-log append flow to coalesce consecutive data chunks into a
single buffer and perform one FileHandle.write(contentsOf:) call per batch.
Flush the accumulated buffer before rotating when adding the next chunk would
exceed maxEventLogBytes, then reset the batch and continue writing while
preserving currentSize accounting and rotation behavior.
🤖 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 `@Sources/SharedLiveAgentIndex.swift`:
- Around line 27-40: Mark the pure static helper hookEventReloadInterval, along
with any static constants it reads if required by Swift isolation rules, as
nonisolated so non-MainActor callers can invoke it synchronously. Preserve its
current clamping and interval calculation behavior.

---

Outside diff comments:
In `@Sources/CmuxEventLogWriter.swift`:
- Around line 141-150: Update the write loop in the event-log append flow to
coalesce consecutive data chunks into a single buffer and perform one
FileHandle.write(contentsOf:) call per batch. Flush the accumulated buffer
before rotating when adding the next chunk would exceed maxEventLogBytes, then
reset the batch and continue writing while preserving currentSize accounting and
rotation behavior.
🪄 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: 49b98eca-f65e-4b10-af26-1598b1780b97

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9575f and 449ec2e.

📒 Files selected for processing (35)
  • Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/Concurrency/AtomicBooleanGate.swift
  • Packages/macOS/CmuxFoundation/Sources/CmuxFoundationAtomicsC/CmuxFoundationAtomicsC.c
  • Packages/macOS/CmuxFoundation/Sources/CmuxFoundationAtomicsC/include/CmuxFoundationAtomicsC.h
  • Packages/macOS/CmuxFoundation/Tests/CmuxFoundationTests/Concurrency/AtomicBooleanGateTests.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Lifecycle/TerminalSurfaceRuntimeTeardownCoordinator.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Lifecycle/TerminalSurfaceRuntimeTeardownRequest.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Runtime/TerminalScrollbackBudget.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalManualIOWrite.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Debug.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeSurfaceCreation.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalScrollbackBudgetTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceRuntimeTeardownCoordinatorTests.swift
  • Packages/macOS/CmuxTerminal/Tests/GhosttyRuntimeTestStubs/GhosttyRuntimeTestStubs.c
  • Packages/macOS/CmuxTerminal/Tests/GhosttyRuntimeTestStubs/include/GhosttyRuntimeTestStubs.h
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Interop/GhosttyRuntimeCInterop.swift
  • Sources/CmuxEventBus.swift
  • Sources/CmuxEventLogWriter.swift
  • Sources/CmuxEventStream.swift
  • Sources/CmuxTopProcessArguments.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/MobileTerminalByteTee.swift
  • Sources/SharedLiveAgentIndex.swift
  • Sources/SharedLiveAgentIndexLoader.swift
  • Sources/VaultAgentProcessCandidateSelector.swift
  • Sources/VaultAgentProcessScanner.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CmuxEventBusTests.swift
  • cmuxTests/MobileHostAuthorizationTests.swift
  • cmuxTests/SharedLiveAgentIndexAgentLivenessTests.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
  • cmuxTests/VaultAgentProcessCandidateSelectorTests.swift
  • ghostty

Comment on lines +27 to +40
private static let maxEventReloadInterval: TimeInterval = 30.0
private static let liveAgentsPerReloadIntervalStep = 8

static func hookEventReloadInterval(liveAgentCount: Int) -> TimeInterval {
let clampedAgentCount = max(0, liveAgentCount)
let intervalSteps = max(
1,
(clampedAgentCount + liveAgentsPerReloadIntervalStep - 1) / liveAgentsPerReloadIntervalStep
)
return min(
maxEventReloadInterval,
TimeInterval(intervalSteps) * minEventReloadInterval
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider marking the new pure interval helper nonisolated.

hookEventReloadInterval only reads its own static constants and performs pure arithmetic — it doesn't touch any instance/actor state. Since Swift 5.10, static let/static func declared inside an @MainActor class are MainActor-isolated by default, so callers outside this actor (e.g. future non-MainActor callers, or unit tests that aren't themselves @MainActor) would need await for what is otherwise a trivial, thread-safe computation.

♻️ Suggested nonisolated annotations
-    private static let minEventReloadInterval: TimeInterval = 5.0
-    private static let maxEventReloadInterval: TimeInterval = 30.0
-    private static let liveAgentsPerReloadIntervalStep = 8
+    private nonisolated static let minEventReloadInterval: TimeInterval = 5.0
+    private nonisolated static let maxEventReloadInterval: TimeInterval = 30.0
+    private nonisolated static let liveAgentsPerReloadIntervalStep = 8

-    static func hookEventReloadInterval(liveAgentCount: Int) -> TimeInterval {
+    nonisolated static func hookEventReloadInterval(liveAgentCount: Int) -> TimeInterval {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static let maxEventReloadInterval: TimeInterval = 30.0
private static let liveAgentsPerReloadIntervalStep = 8
static func hookEventReloadInterval(liveAgentCount: Int) -> TimeInterval {
let clampedAgentCount = max(0, liveAgentCount)
let intervalSteps = max(
1,
(clampedAgentCount + liveAgentsPerReloadIntervalStep - 1) / liveAgentsPerReloadIntervalStep
)
return min(
maxEventReloadInterval,
TimeInterval(intervalSteps) * minEventReloadInterval
)
}
private nonisolated static let minEventReloadInterval: TimeInterval = 5.0
private nonisolated static let maxEventReloadInterval: TimeInterval = 30.0
private nonisolated static let liveAgentsPerReloadIntervalStep = 8
nonisolated static func hookEventReloadInterval(liveAgentCount: Int) -> TimeInterval {
let clampedAgentCount = max(0, liveAgentCount)
let intervalSteps = max(
1,
(clampedAgentCount + liveAgentsPerReloadIntervalStep - 1) / liveAgentsPerReloadIntervalStep
)
return min(
maxEventReloadInterval,
TimeInterval(intervalSteps) * minEventReloadInterval
)
}
🤖 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/SharedLiveAgentIndex.swift` around lines 27 - 40, Mark the pure
static helper hookEventReloadInterval, along with any static constants it reads
if required by Swift isolation rules, as nonisolated so non-MainActor callers
can invoke it synchronously. Preserve its current clamping and interval
calculation behavior.

Source: Coding guidelines

cmux reload-cloud added 2 commits July 14, 2026 13:35
…enecks

# Conflicts:
#	Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Debug/ControlCommandCoordinator+Debug.swift
#	Sources/GhosttyTerminalView.swift
#	Sources/Mobile/MobileWorkspaceListObserver.swift
#	Sources/PortScanner.swift
#	Sources/TabManager.swift
#	Sources/TerminalController.swift
#	cmux.xcodeproj/project.pbxproj
#	cmuxTests/PortScannerTests.swift
#	ghostty
@azooz2003-bit
azooz2003-bit merged commit dcf320a into main Jul 14, 2026
7 of 8 checks passed
@azooz2003-bit
azooz2003-bit deleted the perf-typing-lag-bottlenecks branch July 14, 2026 21:04

@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 `@Sources/Mobile/MobileHostService.swift`:
- Around line 1605-1608: Remove the redundant
debugHasTerminalOutputSubscribersForTesting() production wrapper; tests should
access MobileHostEventSubscriptionTracker.hasTerminalOutputSubscribers()
directly through `@testable` import, preserving the existing internal API.
🪄 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: 720c23d4-125c-4529-8da8-b8e24b9e4c01

📥 Commits

Reviewing files that changed from the base of the PR and between 449ec2e and 78fa146.

📒 Files selected for processing (25)
  • Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/Resources/ArtifactParity/codex-adversarial.jsonl
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/ControlCommandContext.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/ControlCommandCoordinator.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Debug/ControlCommandCoordinator+Debug.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Debug/ControlCommandCoordinator+DebugInteraction.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Debug/ControlDebugContext.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs+Debug.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/CmuxTopProcessCPUTracker.swift
  • Sources/CmuxTopProcessEnumeration.swift
  • Sources/CmuxTopSnapshot.swift
  • Sources/CmuxTopSnapshotScopeCache.swift
  • Sources/ContentView.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/Mobile/AgentChat/AgentChatSessionRegistry.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/MobileWorkspaceListObserver.swift
  • Sources/PortScanSnapshotStore.swift
  • Sources/PortScanner+SnapshotProjection.swift
  • Sources/PortScanner.swift
💤 Files with no reviewable changes (1)
  • Resources/Localizable.xcstrings

Comment on lines +1605 to +1608
nonisolated static func debugHasTerminalOutputSubscribersForTesting() -> Bool {
MobileHostEventSubscriptionTracker.hasTerminalOutputSubscribers()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove redundant test-only seam.

As per path instructions, do not add test-only seams to production Swift source when the internal state can be reached directly via @testable import. hasTerminalOutputSubscribers() is already internal and is accessible to tests, so this debug wrapper is unnecessary.

♻️ Proposed fix
-    nonisolated static func debugHasTerminalOutputSubscribersForTesting() -> Bool {
-        MobileHostEventSubscriptionTracker.hasTerminalOutputSubscribers()
-    }
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
nonisolated static func debugHasTerminalOutputSubscribersForTesting() -> Bool {
MobileHostEventSubscriptionTracker.hasTerminalOutputSubscribers()
}
🤖 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/Mobile/MobileHostService.swift` around lines 1605 - 1608, Remove the
redundant debugHasTerminalOutputSubscribersForTesting() production wrapper;
tests should access
MobileHostEventSubscriptionTracker.hasTerminalOutputSubscribers() directly
through `@testable` import, preserving the existing internal API.

Source: Path instructions

austinywang added a commit that referenced this pull request Jul 14, 2026
The rebase conflict resolution in the socket-verbs commit accidentally
staged the stale local ghostty checkout (eb500e9), reverting main's
pointer bump from #7863 (a630590) and breaking the app build on the
new ghostty_renderer_event_e type. No cmux-side change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
austinywang added a commit that referenced this pull request Jul 15, 2026
This reverts commit dcf320a.

Co-authored-by: austinpower1258 <austinwang115@gmail.com>
@austinywang austinywang mentioned this pull request Sep 14, 2026
4 of 6 tasks

This branch was successfully deployed

1 active deployment
Preview – cmux — 4b376339 Deployed Jul 14, 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