Repository navigation
Fix missing sidebar git branch metadata for workspaces - #2563
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughRefactors TabManager/Workspace git-metadata refresh scheduling to track probed directories per workspace+panel and schedule initial probes reliably for newly-created terminal surfaces. Adds C-ABI wrapper functions and refactors clipboard/read-clipboard callback handling in GhosttyTerminalView; adjusts bell-audio path access. Changes
Sequence DiagramsequenceDiagram
participant User
participant Workspace
participant TabManager
participant GitProbe
participant Surface
User->>Workspace: create split / new surface
activate Workspace
Workspace->>Workspace: newTerminalSplit() / newTerminalSurface()
Workspace->>TabManager: scheduleInitialWorkspaceGitMetadataRefreshIfPossible(reason)
deactivate Workspace
activate TabManager
TabManager->>TabManager: check non-remote, resolve directory (gitProbeDirectory)
TabManager->>TabManager: update workspaceGitTrackedDirectoryByKey
TabManager->>GitProbe: schedule probe for directory (with reason)
deactivate TabManager
activate GitProbe
GitProbe->>GitProbe: probe directory for branch/PR
GitProbe->>Surface: publish branch/PR metadata
deactivate GitProbe
Surface->>TabManager: emit branch info update
TabManager->>Surface: persist tracked state / notify UI
User->>User: sidebar displays branch
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR fixes sidebar git branch metadata being blank for workspaces that have not yet received a live git branch update (e.g., after session restore). It adds a fallback so that the focused panel is included as a polling candidate when Confidence Score: 5/5Safe to merge — logic is correct, test coverage is solid, no regressions identified. All findings are P2 or lower. The backfill fallback in trackedWorkspaceGitMetadataPollCandidatePanelIds is correctly guarded by workspace.gitBranch != nil, preventing spurious probes on fresh workspaces. The new scheduleWorkspaceGitMetadataRefreshIfPossible call in clearSurfaceGitBranch is de-duped by activeProbeKeys and only fires when there was something to clear. Two behavioral regression tests verify the exact bug scenarios through real git repos and observable state. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant TM as TabManager
participant WS as Workspace
participant GP as GitProbe
Note over TM,WS: Session restore / new background workspace
TM->>WS: workspace.gitBranch set (from snapshot)
WS-->>TM: panelGitBranches still empty
TM->>TM: trackedWorkspaceGitMetadataPollCandidatePanelIds()
Note right of TM: candidatePanelIds empty →<br/>fallback: gitBranch != nil &&<br/>gitProbeDirectory != nil →<br/>insert focusedPanelId
TM->>GP: scheduleWorkspaceGitMetadataRefreshIfPossible()
GP-->>TM: git branch result
TM->>WS: updatePanelGitBranch(panelId, "main")
WS->>WS: panelGitBranches[panelId] = "main"
WS->>WS: gitBranch = "main" (focused panel)
Note over TM,WS: clearSurfaceGitBranch (stale/reset)
TM->>WS: clearPanelGitBranch(panelId)
WS->>WS: panelGitBranches[panelId] = nil
WS->>WS: gitBranch = nil
TM->>GP: scheduleWorkspaceGitMetadataRefreshIfPossible() NEW
Note right of TM: workspaceGitTrackedDirectoryByKey<br/>still set → panel is candidate
GP-->>TM: git branch result
TM->>WS: updatePanelGitBranch(panelId, "main") restored
Reviews (1): Last reviewed commit: "Tighten sidebar git metadata polling" | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
cmuxTests/TabManagerUnitTests.swift (1)
628-632: Consider adding a comment explaining the 12-second timeout.The 12-second timeout is notably longer than the default 3-second timeout used elsewhere in the test suite. If this extended duration is intentional (e.g., waiting for initial workspace probing to settle before testing the remote split behavior), a brief comment would clarify this for future maintainers.
📝 Suggested clarification
+ // Wait for any initial git probe triggered by workspace creation to complete + // before testing that remote splits don't schedule additional probes. XCTAssertTrue( waitForCondition(timeout: 12.0) { manager.activeWorkspaceGitProbePanelIdsForTesting(workspaceId: workspace.id).isEmpty } )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmuxTests/TabManagerUnitTests.swift` around lines 628 - 632, Add a brief inline comment above the waitForCondition call that uses a 12.0 second timeout explaining why this test needs a longer wait than the usual 3s (e.g., to allow initial workspace probing/remote split propagation to settle before asserting manager.activeWorkspaceGitProbePanelIdsForTesting(workspaceId:) is empty); keep the comment short and reference the intent (initial probing/remote split) so future maintainers understand why waitForCondition(timeout: 12.0) is larger than the suite default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 1155-1159: The closure assigned to runtimeConfig.read_clipboard_cb
captures callbackContext.runtimeSurface before the DispatchQueue.main.async hop,
risking a data race because TerminalSurface.surface is created/cleared on the
main thread; move the access to callbackContext.runtimeSurface (and any use of
TerminalSurface.surface/native pointer passed to
ghostty_surface_complete_clipboard_request) inside the DispatchQueue.main.async
block so the runtimeSurface is read and used only on the main thread (update the
closure in runtimeConfig.read_clipboard_cb to call
GhosttyApp.callbackContext(from: userdata) before dispatching but obtain and use
callbackContext.runtimeSurface only within the DispatchQueue.main.async
closure).
In `@Sources/TabManager.swift`:
- Around line 1065-1071: The gitProbeDirectory logic short-circuits when
panelDirectories[panelId] or terminalPanel(for:).requestedWorkingDirectory is an
empty/whitespace string; change gitProbeDirectory to evaluate each candidate for
non-empty (trim whitespace) before coalescing so the fallback chain can
continue. Specifically, inspect the candidates in order
(workspace.panelDirectories[panelId], workspace.terminalPanel(for:
panelId)?.requestedWorkingDirectory, then workspace.focusedPanelId == panelId ?
workspace.currentDirectory) and only pass the first non-empty trimmed String
into normalizedWorkingDirectory (or skip to the next candidate if nil/empty),
ensuring empty/whitespace values don’t block falling back.
In `@Sources/Workspace.swift`:
- Around line 7644-7648: The code schedules initial git metadata refresh only
when terminals are created via newTerminalSplit(...) or newTerminalSurface(...),
but splitTabBar(_:didSplitPane:) and other direct TerminalPanel constructions
(including placeholder repair paths referenced around the other occurrence)
still instantiate TerminalPanel directly and miss the scheduling call; update
those direct construction sites to call
owningTabManager?.scheduleInitialWorkspaceGitMetadataRefreshIfPossible(workspaceId:
id, panelId: newPanel.id, reason: "splitCreate") immediately after creating the
TerminalPanel (or better, refactor creation to a single helper/factory used by
newTerminalSplit/newTerminalSurface and splitTabBar(_:didSplitPane:) so all
paths consistently invoke the scheduling), and ensure the placeholder-repair
path uses the same helper so Bonsplit-created terminals receive the initial git
backfill.
---
Nitpick comments:
In `@cmuxTests/TabManagerUnitTests.swift`:
- Around line 628-632: Add a brief inline comment above the waitForCondition
call that uses a 12.0 second timeout explaining why this test needs a longer
wait than the usual 3s (e.g., to allow initial workspace probing/remote split
propagation to settle before asserting
manager.activeWorkspaceGitProbePanelIdsForTesting(workspaceId:) is empty); keep
the comment short and reference the intent (initial probing/remote split) so
future maintainers understand why waitForCondition(timeout: 12.0) is larger than
the suite default.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 222b3635-8cb5-4421-922d-cb610b8641aa
📒 Files selected for processing (4)
Sources/GhosttyTerminalView.swiftSources/TabManager.swiftSources/Workspace.swiftcmuxTests/TabManagerUnitTests.swift
| private func gitProbeDirectory(for workspace: Workspace, panelId: UUID) -> String? { | ||
| // Match the sidebar directory fallback chain so hidden/background panels can | ||
| // still probe git metadata before OSC 7 has reported a live cwd. | ||
| let rawDirectory = workspace.panelDirectories[panelId] | ||
| ?? workspace.terminalPanel(for: panelId)?.requestedWorkingDirectory | ||
| ?? (workspace.focusedPanelId == panelId ? workspace.currentDirectory : nil) | ||
| return rawDirectory.flatMap(normalizedWorkingDirectory) |
There was a problem hiding this comment.
Blank directory strings still short-circuit the probe fallback chain.
panelDirectories[panelId] and requestedWorkingDirectory are coalesced before they're normalized. If either value is "" or whitespace, the ?? chain stops there, normalizedWorkingDirectory returns nil, and the initial/backfill probe never falls through to the next source. That can still leave the sidebar branch blank in the startup race this PR is trying to fix. Sources/Workspace.swift:7558-7567 already uses the non-empty check before picking requestedWorkingDirectory.
💡 Suggested fix
private func gitProbeDirectory(for workspace: Workspace, panelId: UUID) -> String? {
// Match the sidebar directory fallback chain so hidden/background panels can
// still probe git metadata before OSC 7 has reported a live cwd.
- let rawDirectory = workspace.panelDirectories[panelId]
- ?? workspace.terminalPanel(for: panelId)?.requestedWorkingDirectory
- ?? (workspace.focusedPanelId == panelId ? workspace.currentDirectory : nil)
- return rawDirectory.flatMap(normalizedWorkingDirectory)
+ let panelDirectory = normalizedWorkingDirectory(workspace.panelDirectories[panelId])
+ let requestedDirectory = normalizedWorkingDirectory(
+ workspace.terminalPanel(for: panelId)?.requestedWorkingDirectory
+ )
+ let focusedDirectory = workspace.focusedPanelId == panelId
+ ? normalizedWorkingDirectory(workspace.currentDirectory)
+ : nil
+ return panelDirectory ?? requestedDirectory ?? focusedDirectory
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/TabManager.swift` around lines 1065 - 1071, The gitProbeDirectory
logic short-circuits when panelDirectories[panelId] or
terminalPanel(for:).requestedWorkingDirectory is an empty/whitespace string;
change gitProbeDirectory to evaluate each candidate for non-empty (trim
whitespace) before coalescing so the fallback chain can continue. Specifically,
inspect the candidates in order (workspace.panelDirectories[panelId],
workspace.terminalPanel(for: panelId)?.requestedWorkingDirectory, then
workspace.focusedPanelId == panelId ? workspace.currentDirectory) and only pass
the first non-empty trimmed String into normalizedWorkingDirectory (or skip to
the next candidate if nil/empty), ensuring empty/whitespace values don’t block
falling back.
| owningTabManager?.scheduleInitialWorkspaceGitMetadataRefreshIfPossible( | ||
| workspaceId: id, | ||
| panelId: newPanel.id, | ||
| reason: "splitCreate" | ||
| ) |
There was a problem hiding this comment.
This still misses Bonsplit-created terminals.
These hooks only run when terminal creation goes through newTerminalSplit(...) or newTerminalSurface(...). splitTabBar(_:didSplitPane:) still constructs TerminalPanel directly for UI splits and placeholder repair, so those tabs can still come up with no initial git backfill and keep a blank branch label. Please route those direct creation sites through the same scheduling path as well.
Also applies to: 7725-7729
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/Workspace.swift` around lines 7644 - 7648, The code schedules initial
git metadata refresh only when terminals are created via newTerminalSplit(...)
or newTerminalSurface(...), but splitTabBar(_:didSplitPane:) and other direct
TerminalPanel constructions (including placeholder repair paths referenced
around the other occurrence) still instantiate TerminalPanel directly and miss
the scheduling call; update those direct construction sites to call
owningTabManager?.scheduleInitialWorkspaceGitMetadataRefreshIfPossible(workspaceId:
id, panelId: newPanel.id, reason: "splitCreate") immediately after creating the
TerminalPanel (or better, refactor creation to a single helper/factory used by
newTerminalSplit/newTerminalSurface and splitTabBar(_:didSplitPane:) so all
paths consistently invoke the scheduling), and ensure the placeholder-repair
path uses the same helper so Bonsplit-created terminals receive the initial git
backfill.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Sources/GhosttyTerminalView.swift (1)
991-998:⚠️ Potential issue | 🔴 CriticalMove the
runtimeSurfacelookup back onto the main queue.Line 992 still captures
callbackContext.runtimeSurfacebefore theDispatchQueue.main.asynchop.TerminalSurface.surfaceis created and cleared on the main thread in this file, so this reintroduces the stale-pointer race; the later equality check is too late becauserequestSurfaceis already captured.Suggested fix
fileprivate static func runtimeReadClipboardCallback( _ userdata: UnsafeMutableRawPointer?, _ location: ghostty_clipboard_e, _ state: UnsafeMutableRawPointer? ) -> Bool { - guard let callbackContext = Self.callbackContext(from: userdata), - let requestSurface = callbackContext.runtimeSurface else { return false } + guard let callbackContext = Self.callbackContext(from: userdata) else { return false } DispatchQueue.main.async { + guard let requestSurface = callbackContext.runtimeSurface else { return } func completeClipboardRequest(with text: String) { let finish = { guard callbackContext.runtimeSurface == requestSurface else { return } text.withCString { ptr in ghostty_surface_complete_clipboard_request(requestSurface, ptr, state, false)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/GhosttyTerminalView.swift` around lines 991 - 998, The code captures callbackContext.runtimeSurface into requestSurface before hopping to DispatchQueue.main.async, which can race because TerminalSurface.surface is only created/cleared on the main thread; instead, only capture callbackContext (via Self.callbackContext(from: userdata)) before the async and move the runtimeSurface lookup/guard (i.e., obtain requestSurface = callbackContext.runtimeSurface and the equality check that currently compares callbackContext.runtimeSurface == requestSurface) inside the DispatchQueue.main.async block (for example at the start of completeClipboardRequest or immediately upon entering the main block) so the surface is read on the main thread and the stale-pointer race is eliminated while preserving the later equality guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 14-18: The code binds to undocumented internal symbols
ghostty_surface_clear_selection and ghostty_surface_select_cursor_cell via
`@_silgen_name` wrappers (ghostty_surface_clear_selection_compat,
ghostty_surface_select_cursor_cell_compat); remove these private-symbol bindings
and any calls to those compatibility functions, and replace the selection logic
with the documented public embedding API (use the Ghostty input/mouse event
handling and rendering callbacks or the documented configuration options) or
else request/implement a formal public C shim in Ghostty and call that
instead—search for usages of ghostty_surface_clear_selection_compat and
ghostty_surface_select_cursor_cell_compat to remove/replace them.
---
Duplicate comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 991-998: The code captures callbackContext.runtimeSurface into
requestSurface before hopping to DispatchQueue.main.async, which can race
because TerminalSurface.surface is only created/cleared on the main thread;
instead, only capture callbackContext (via Self.callbackContext(from: userdata))
before the async and move the runtimeSurface lookup/guard (i.e., obtain
requestSurface = callbackContext.runtimeSurface and the equality check that
currently compares callbackContext.runtimeSurface == requestSurface) inside the
DispatchQueue.main.async block (for example at the start of
completeClipboardRequest or immediately upon entering the main block) so the
surface is read on the main thread and the stale-pointer race is eliminated
while preserving the later equality guard.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7973b5cc-56dd-4001-ba10-59e7cb8080be
📒 Files selected for processing (1)
Sources/GhosttyTerminalView.swift
| @_silgen_name("ghostty_surface_clear_selection") | ||
| private func ghostty_surface_clear_selection_compat(_ surface: ghostty_surface_t) -> Bool | ||
|
|
||
| @_silgen_name("ghostty_surface_select_cursor_cell") | ||
| private func ghostty_surface_select_cursor_cell_compat(_ surface: ghostty_surface_t) -> Bool |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== public headers / interfaces mentioning Ghostty selection APIs =="
fd -HI 'ghostty\.h$|\.swiftinterface$|module\.modulemap$' \
| xargs -r rg -n 'ghostty_surface_(clear_selection|select_cursor_cell|has_selection|read_selection|read_text|free_text)'
echo
echo "== vendored GhosttyKit / libghostty artifacts containing the private symbols =="
fd -HI 'GhosttyKit|libghostty|ghostty' \
| xargs -r rg -a -n 'ghostty_surface_(clear_selection|select_cursor_cell)'Repository: manaflow-ai/cmux
Length of output: 1798
🌐 Web query:
ghostty_surface_clear_selection ghostty_surface_select_cursor_cell public API
💡 Result:
The functions ghostty_surface_clear_selection and ghostty_surface_select_cursor_cell appear to be internal methods in Ghostty's Surface.zig implementation for managing terminal text selections, but they are not part of the documented public libghostty C API. The public embedding API (documented at ghostty-org-ghostty.mintlify.app) provides input handling (e.g., ghostty_surface_mouse_button, ghostty_surface_mouse_pos), rendering (ghostty_surface_draw), and core types like ghostty_surface_t, but no explicit public functions for direct selection manipulation like clearing or selecting the cursor cell. Ghostty's libghostty C API is explicitly noted as unstable and primarily used internally by the macOS app, with full documentation in Zig source files rather than comprehensive C headers. Selection management is handled internally via mouse events and config options like selection-clear-on-typing (from ghostty.org/docs/config/reference), with structures like ghostty_selection_s defined in include/ghostty.h for coordinates (top_left, bottom_right, rectangle flag). For embedding, selections are typically managed through mouse input callbacks rather than direct API calls. libghostty-vt (separate VT library) focuses on parsing/rendering but lacks surface-level selection APIs. No official public API documentation confirms these exact functions as exposed.
Citations:
- 1: https://ghostty-org-ghostty.mintlify.app/api/input
- 2: Surface: use rect selection state when setting selection on release ghostty-org/ghostty#7972
- 3: https://github.com/ghostty-org/ghostty/blob/2ac3c1f1/src/Surface.zig
- 4: https://ghostty-org-ghostty.mintlify.app/api/overview
- 5: https://github.com/ghostty-org/ghostty/blob/2ac3c1f1/include/ghostty.h
🌐 Web query:
Ghostty C API header ghostty.h public interface selection functions
💡 Result:
The ghostty.h header file defines the public C API for embedding Ghostty's terminal emulator (libghostty), primarily used by the macOS app but designed for general embedding. It includes opaque types (ghostty_app_t, ghostty_surface_t, etc.), enums for platforms, clipboards, input states, modifiers (GHOSTTY_MODS_), key codes (GHOSTTY_KEY_ based on W3C UI Events), mouse buttons, actions, and structs for input events (ghostty_input_key_s), clipboard content, surface configs, etc. No explicit "selection functions" (e.g., for text selection like get_selection, select_area) are visible in the partial header snippets. The API focuses on initialization (ghostty_init, ghostty_app_new, ghostty_surface_new), input handling (ghostty_surface_key, ghostty_surface_mouse_button, etc.), configuration, and rendering callbacks. Clipboard support includes GHOSTTY_CLIPBOARD_STANDARD and GHOSTTY_CLIPBOARD_SELECTION enums, suggesting selection clipboard integration, but specific functions like paste_from_clipboard or copy_to_selection are not shown in available sources. Full function declarations (e.g., ghostty_surface_get_selection) are not in the extracted snippets; documentation notes the API is unstable and primarily documented in Zig source. For complete details, view the full header at https://github.com/ghostty-org/ghostty/blob/main/include/ghostty.h. A separate stable libghostty-vt API (ghostty/vt.h) exists for VT parsing/terminal state without rendering/GUI.
Citations:
- 1: https://github.com/ghostty-org/ghostty/blob/2ac3c1f1/include/ghostty.h
- 2: https://ghostty-org-ghostty.mintlify.app/api/overview
- 3: https://ghostty-org-ghostty.mintlify.app/api/input
- 4: https://github.com/ghostty-org/ghostty/blob/main/include/ghostty/vt.h
- 5: https://libghostty.tip.ghostty.org/index.html
Remove dependency on undocumented Ghostty internal ABI symbols.
ghostty_surface_clear_selection and ghostty_surface_select_cursor_cell are not part of the documented public libghostty C API. Web search of official Ghostty documentation confirms these are internal Surface.zig implementation methods, not exposed public functions. The public embedding API (documented at ghostty-org-ghostty.mintlify.app) focuses on input handling and rendering callbacks; selection management is handled internally via mouse events and configuration options, not direct API calls. Binding to these private symbols via _silgen_name creates a fragile dependency that may break when Ghostty updates.
Either request Ghostty to stabilize and document these symbols as part of the public ABI, or redesign the selection logic to use the documented public input/event APIs instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/GhosttyTerminalView.swift` around lines 14 - 18, The code binds to
undocumented internal symbols ghostty_surface_clear_selection and
ghostty_surface_select_cursor_cell via `@_silgen_name` wrappers
(ghostty_surface_clear_selection_compat,
ghostty_surface_select_cursor_cell_compat); remove these private-symbol bindings
and any calls to those compatibility functions, and replace the selection logic
with the documented public embedding API (use the Ghostty input/mouse event
handling and rendering callbacks or the documented configuration options) or
else request/implement a formal public C shim in Ghostty and call that
instead—search for usages of ghostty_surface_clear_selection_compat and
ghostty_surface_select_cursor_cell_compat to remove/replace them.
* fix: fall back to focused surface when new-split target is stale When `new-split` receives a surface UUID that no longer exists (e.g. a closed teammate pane), fall back to the focused surface instead of returning a "Surface not found" error. This matches `new-pane` behavior and fixes agent spawning after team shutdown in Claude Code. * Add editable workspace descriptions (manaflow-ai#2475) * Add editable workspace descriptions * Add workspace description focus UI tests * Include workspace description UI tests in project * Fix workspace description palette focus * Stabilize workspace description UI tests * Force socket mode in description UI tests * Use tagged socket path in description UI tests * Fix workspace description UI test socket setup * Start control socket for UI test launches * Use socket env overrides in description UI tests * Rewrite description UI tests without socket access * Verify saved description by reopening editor * Add workspace description focus debug logs * Prevent terminal focus restore during command palette * Trace workspace description shift-enter handling * Add failing Shift-Enter description UI test * Fix Shift-Enter in workspace description editor * Use live multiline editor state for description submit * Log submitted and normalized workspace descriptions * Trace lower-level Shift-Enter editor routing * Add failing sidebar markdown line-break regression test * Preserve workspace description line breaks in sidebar * Add sidebar multiline description UI smoke test * Expose multiline sidebar descriptions to accessibility * Fix sidebar description refresh lag * Add failing workspace description whitespace test * Preserve workspace description whitespace * fix: keep multiline palette navigation in editor * fix: cap workspace description editor growth --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com> * Detect listening ports for remote SSH workspaces (manaflow-ai#2398) * Add failing SSH remote port detection regression * Detect listening ports for remote SSH workspaces * Retry remote SSH TTY reporting until the target surface exists * Address relay RPC review comments * Avoid host-wide remote port leaks for cmux ssh * Make remote port scans prompt-aware * Fall back when remote ss omits pid data * Tighten remote port scan handoff * Keep raw RPC output intact * Clean up relay TTY handoff review follow-up * Fix remote ssh port surfacing * Fix cmux ssh bootstrap and orphan cleanup * Address SSH remote review feedback * Stabilize SSH remote metadata regression * Fix shell timing and suppressed focus recovery * Harden suppressed focus recovery in tests * Stabilize focus recovery regression timing * Fix SSH relay auth and bootstrap handoff * Fix SSH metadata cleanup assertion --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com> * Add SSH foreground-auth regression tests * Defer remote bootstrap until SSH auth succeeds * Fix session restore terminal cursor focus race (manaflow-ai#2471) * Fix session restore terminal cursor focus race * Fix terminal ready focus observer key * wip * Revert accidental submodule pointer changes from wip commit The wip commit bumped ghostty and bonsplit pointers alongside the actual code fix. Revert them to match main so the PR only contains the session cursor race fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * Support chorded keyboard shortcuts (manaflow-ai#2528) * Support chorded keyboard shortcuts * Fix escape handling in shortcut recorder * Add settings.json shortcut overrides * Add regression test for chord reset on deactivate * Fix shortcut chord cleanup edge cases * Add regression tests for chord prefix edge cases * Fix configurable chord prefix routing * Add CLI reload-config command * Simplify reload-config CLI command * Add regression tests for shortcut reload edges * Fix shortcut reload and chord edge cases * Add managed settings.json defaults and schema docs * fix: harden shortcut routing edge cases * fix: preserve palette and browser shortcut routing * chore: retrigger missing PR workflows * feat: add settings.json entry points * feat: make workspace color palette a named dictionary * feat: add textedit button for settings file * refactor: reorder ghostty menu items * refactor: restore settings menu ordering * refactor: restyle settings file entry * refactor: move settings file actions into header * refactor: simplify settings file header action * docs: explain shortcut chords from settings * refactor: reorder shortcut chord actions * fix: notify after swapping settings file store --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com> * Find Homebrew go for dev remote bootstrap * Fix missing sidebar git branch metadata for workspaces (manaflow-ai#2563) * Fix sidebar workspace branch backfill * Add stale branch sidebar regression * Patch GhosttyKit header compatibility * Match Ghostty clipboard callback signature * Tighten sidebar git metadata polling * Fix Ghostty clipboard callback bridge --------- Co-authored-by: austinpower1258 <austinwang115@gmail.com> * Reuse SSH control master for remote relay * fix: harden deferred ssh reconnect handling * Fix missing sidebar ports for agent-run dev servers (manaflow-ai#2562) * test: cover agent-owned sidebar ports * fix: track agent dev-server ports in sidebar * fix: harden ssh localcommand escaping * Fix sidebar layout loop and CLI socket deadlocks (manaflow-ai#2601) * Fix sidebar layout loop and CLI socket deadlocks * Fix sidebar socket target validation * Move sidebar tab resolution onto main queue * Fix sidebar mutation closure parameters * fix: add missing CryptoKit import from upstream merge --------- Co-authored-by: Anusheel Bhushan <anusheel@gmail.com> Co-authored-by: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com> Co-authored-by: Austin Wang <austinwang115@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Closes #951.
Testing
./scripts/reload.sh --tag codex-dev-appNotes
Summary by cubic
Fixes missing or blank git branch labels in the sidebar by backfilling from the workspace cwd, tracking known-good directories, and probing on workspace/panel create so stale labels recover quickly. Closes #951.
read_clipboard_cbto bridgeBool/VoidGhosttyKitimporter variants and adding selection API compat wrappers to prevent runtime issues.Written for commit fbe1a69. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Improvements
Tests