From d9e5ac215cb1e2b9f39556cf2ad82273634c686f Mon Sep 17 00:00:00 2001 From: Etan Joseph Heyman Date: Tue, 17 Mar 2026 12:53:33 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20address=20CodeRabbit=20review=20?= =?UTF-8?q?=E2=80=94=20reportPorts=20async=20+=20tab=20resolution=20fast-p?= =?UTF-8?q?ath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert reportPorts from DispatchQueue.main.sync to schedulePanelMetadataMutation helper, matching reportPullRequest and clearPullRequest (#1550) - Wire up existing but unused shouldReplacePorts() for dedup/coalescing - Add off-main fast-path in resolveTabIdForSidebarMutation: when explicit --tab UUID is provided, skip main.sync entirely (agent/shell flows always provide IDs) Co-Authored-By: Claude Opus 4.6 (1M context) --- Sources/TerminalController.swift | 67 +++++++++++++------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index f87ebafa8e1c..2dd598266bf2 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -13687,17 +13687,27 @@ class TerminalController { reportArgs: String, options: [String: String] ) -> (tabId: UUID?, error: String?) { - var tabId: UUID? + // Off-main fast-path: honor explicit --tab UUID without touching the main thread. + // High-frequency agent/shell flows typically provide explicit IDs. + if let rawTab = options["tab"]?.trimmingCharacters(in: .whitespacesAndNewlines), + !rawTab.isEmpty { + guard let explicitId = UUID(uuidString: rawTab) else { + return (nil, "ERROR: Invalid tab id '\(rawTab)'") + } + return (explicitId, nil) + } + + // Fallback: resolve the currently-selected tab on main as a short, bounded hop. + var selectedId: UUID? DispatchQueue.main.sync { if let tab = resolveTabForReport(reportArgs) { - tabId = tab.id + selectedId = tab.id } } - if let tabId { - return (tabId, nil) + if let selectedId { + return (selectedId, nil) } - let error = options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected" - return (nil, error) + return (nil, "ERROR: No tab selected") } private func tabForSidebarMutation(id: UUID) -> Tab? { @@ -14356,46 +14366,21 @@ class TerminalController { } ports.append(port) } + let normalizedPorts = Array(Set(ports)).sorted() - var result = "OK" - DispatchQueue.main.sync { - guard let tab = resolveTabForReport(args) else { - result = parsed.options["tab"] != nil ? "ERROR: Tab not found" : "ERROR: No tab selected" - return - } - - let validSurfaceIds = Set(tab.panels.keys) - tab.pruneSurfaceMetadata(validSurfaceIds: validSurfaceIds) - - let panelArg = parsed.options["panel"] ?? parsed.options["surface"] - let surfaceId: UUID - if let panelArg { - if panelArg.isEmpty { - result = "ERROR: Missing panel id — usage: report_ports [port2...] [--tab=X] [--panel=Y]" - return - } - guard let parsedId = UUID(uuidString: panelArg) else { - result = "ERROR: Invalid panel id '\(panelArg)'" - return - } - surfaceId = parsedId - } else { - guard let focused = tab.focusedPanelId else { - result = "ERROR: Missing panel id (no focused surface)" - return - } - surfaceId = focused - } - - guard validSurfaceIds.contains(surfaceId) else { - result = "ERROR: Panel not found '\(surfaceId.uuidString)'" + // Use the shared helper to parse/resolve off-main and schedule mutation async, + // avoiding main thread starvation from synchronous socket telemetry (see #1550). + return schedulePanelMetadataMutation( + args: args, + options: parsed.options, + missingPanelUsage: "report_ports [port2...] [--tab=X] [--panel=Y]" + ) { tab, surfaceId in + guard Self.shouldReplacePorts(current: tab.surfaceListeningPorts[surfaceId], next: normalizedPorts) else { return } - - tab.surfaceListeningPorts[surfaceId] = ports + tab.surfaceListeningPorts[surfaceId] = normalizedPorts tab.recomputeListeningPorts() } - return result } private func reportPwd(_ args: String) -> String {