Skip to content

Fix important issue regressions - #5240

Closed
azooz2003-bit wants to merge 1 commit into
mainfrom
issue-5237-5224-5221-5209-important-bugs
Closed

azooz2003-bit wants to merge 1 commit into
mainfrom
issue-5237-5224-5221-5209-important-bugs

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Make remote SSH file browser roots follow the workspace cwd and open remote files through an SSH-backed preview copy instead of treating remote paths as local files.
  • Resolve Claude Workflow directory sessions to the sibling resumable JSONL transcript so auto-resume does not use a non-resumable container id.
  • Move surface.read_text scrollback merge/tail/base64 work out of the main-actor capture path and reduce tail/scoring allocations.

Issues

Verification

  • ./scripts/setup.sh
  • ./scripts/reload.sh --tag bugfix

Notes

  • I did not run local xcodebuild tests because local test actions are prohibited for cmux; the PR includes focused unit coverage for the changed behavior.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes regressions in remote SSH browsing, Claude Workflow auto-resume, and terminal text capture, addressing #5237 #5224 #5221 #5209. Remote files open reliably, sessions resume correctly, and terminal reads are faster.

  • Bug Fixes

    • SSH file browser roots now follow the workspace cwd, and remote files open via an SSH-backed preview copy.
    • Claude Workflow directory sessions resolve to the sibling resumable .jsonl transcript for auto-resume.
  • Refactors

    • Moved scrollback merge/tail/base64 work off the main actor and added a lighter tail algorithm to reduce allocations.
    • Added streaming SSH download and pipe copying to lower memory usage during remote previews.

Written for commit 48f7d9c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added support for previewing files from remote SSH workspaces by materializing them locally
    • Added ability to configure a custom working directory for remote SSH workspace roots
    • Improved terminal text output handling and selection logic
  • Tests

    • Added test coverage for remote workspace root path configuration
    • Added test coverage for remote file preview materialization
    • Added test coverage for Claude workflow session resumption

@vercel

vercel Bot commented Jun 2, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 2, 2026 8:24pm
cmux-staging Building Building Preview, Comment Jun 2, 2026 8:24pm

@coderabbitai

coderabbitai Bot commented Jun 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR enhances remote workspace file handling and Claude session restoration. It adds SSH-based remote file downloads for preview caching, introduces explicit root-path support for remote workspaces, improves Claude workflow transcript selection during session restoration, refactors terminal text retrieval into a snapshot/payload pipeline, and provides comprehensive test coverage for the new features.

Changes

Remote File Preview & Session Management

Layer / File(s) Summary
Remote SSH file download transport
Sources/FileExplorerStore.swift
SSHFileExplorerTransport protocol adds async downloadFile, SSHFileExplorerProvider and ProcessSSHFileExplorerTransport implement the method via SSH command execution with SSHDownloadCommandProcess piping stdout to local file. Adds shellSingleQuote helper for safe path quoting and updates FileExplorerWorkspaceRoot.remoteSSH to include optional rootPath, with applyWorkspaceRoot and applyRemoteSSHWorkspaceRoot wiring the root-path parameter.
Remote file preview materialization and UI wiring
Sources/FileExplorerStore.swift, Sources/ContentView.swift, Sources/RightSidebarToolPanel.swift
FileExplorerStore.materializeRemoteFileForPreview downloads remote files into deterministic temp-cache URLs. ContentView.openFilePreviewFromSidebar and RightSidebarToolPanel.openFilePreview detect remote workspaces, asynchronously materialize the file, and open the local preview path. Both UI files wire workspace.currentDirectory as rootPath when applying remote SSH workspace roots.
Process pipe data copying utility
Sources/ProcessPipeReader.swift
Adds copyDataToEndOfFileOrDiscard(from:to:) for chunked file-handle-to-file-handle copying with error logging and byte tracking.
Claude workflow session transcript resolution
Sources/RestorableAgentSession.swift
During RestorableAgentSessionIndex.load, Claude hook sessions resolve an effectiveRecord via new helpers that scan project directories for sibling *.jsonl transcripts, exclude the current session ID, and select the most recently modified file, updating the record before snapshot and PID scoping.
Terminal text snapshot and payload pipeline
Sources/TerminalController.swift
Introduces TerminalTextRawSnapshot, TerminalTextPayload, and TerminalTextPayloadError types. Refactors terminal-text sync to first build a snapshot (viewport/screen/history/active based on includeScrollback), then convert to payload with text and base64 encoding. readTerminalTextBase64 uses the pipeline. Candidate selection for scrollback maximizes newline count then byte count. Reimplements tailTerminalLines as a static function scanning for newline boundaries instead of splitting/joining.
Test coverage for remote file features
cmuxTests/FileExplorerStoreTests.swift
MockSSHFileExplorerTransport gains downloads tracking and implements downloadFile. Tests verify remote workspace root with explicit rootPath loads correct directory and formats display path; remote preview materialization downloads file to cache and records path. Updates existing tests to explicitly set rootPath: nil.
Test coverage for Claude workflow and terminal processing
cmuxTests/RestorableAgentSessionIndexTests.swift, cmuxTests/TerminalControllerSocketWriteTests.swift
Adds test for Claude workflow directory layouts with sibling transcripts, asserting resume command targets the sibling session ID. Adds tests for tailTerminalLines tail semantics and terminalTextPayload scrollback truncation with base64 encoding validation.

Sequence Diagram(s)

sequenceDiagram
  participant FileExplorerStore
  participant ProcessSSHFileExplorerTransport
  participant SSHDownloadCommandProcess
  participant LocalFileSystem
  FileExplorerStore->>ProcessSSHFileExplorerTransport: downloadFile(path)
  ProcessSSHFileExplorerTransport->>SSHDownloadCommandProcess: execute SSH command
  SSHDownloadCommandProcess->>LocalFileSystem: create destination
  SSHDownloadCommandProcess->>LocalFileSystem: write file content
  SSHDownloadCommandProcess-->>ProcessSSHFileExplorerTransport: result
  ProcessSSHFileExplorerTransport-->>FileExplorerStore: local URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#5154: Both PRs modify Sources/RestorableAgentSession.swift's Claude restoration logic, improving how the correct Claude transcript/project directory is chosen and used when building restorable session snapshots.

Poem

🐰 Remote files now hop directly to your preview—
SSH streams the bytes through the network's mew,
Claude workflows find their sibling scripts with care,
Terminal snapshots build with snapshot/payload fair,
The fuzzy coder cheers: "Much faster, much clearer!" 🚀


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (6 errors, 1 warning, 3 inconclusive)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error PR introduces NSLock in FileExplorerStore SSH classes without documented justification per swift-blocking-runtime.md requirements for lock usage. Add comments documenting why NSLock is necessary instead of actors, or refactor to use actor-based synchronization for cancelled flag and state protection.
Cmux Swift @Concurrent ❌ Error SSHFileExplorerTransport nonisolated async methods (downloadFile, listDirectory, resolveHomePath) perform network I/O but lack @concurrent annotation required by Swift 6. Add @concurrent annotation to nonisolated async methods in SSHFileExplorerTransport protocol and ProcessSSHFileExplorerTransport class implementations.
Cmux Swift File And Package Boundaries ❌ Error ProcessPipeReader masks write errors as EIO and logs wrong fd; RestorableAgentSessionIndexTests has incomplete regression test with only one sibling transcript instead of multiple. Fix ProcessPipeReader to preserve actual error codes and log output.fileDescriptor; add older sibling JSONL to test to verify newest transcript selection.
Cmux User-Facing Error Privacy ❌ Error FileExplorerError.sshCommandFailed includes raw SSH stderr in user-visible errorDescription, violating upstream error message exposure rules. Sanitize stderr before passing to sshCommandFailed - log raw details internally, expose only generic message to users.
Cmux Full Internationalization ❌ Error PR introduces "fileExplorer.error.sshFailed" localization key in FileExplorerStore.swift but omits it from Resources/Localizable.xcstrings, missing translations for all 20 supported locales. Add "fileExplorer.error.sshFailed" to Resources/Localizable.xcstrings with translations for all 20 locales (ar, bs, da, de, en, es, fr, it, ja, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant).
Cmux Architecture Rethink ❌ Error ProcessPipeReader logs synthetic EIO and wrong fd on write failures, masking real errors like ENOSPC/EPIPE/EBADF as noted in review comments. Log actual NSError's errno code and output fd to preserve destination-side write failure details for proper error diagnosis.
Docstring Coverage ⚠️ Warning Docstring coverage is 3.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive terms that don't convey meaningful information about the specific changes. Replace with a more descriptive title that specifies the main fixes, such as 'Fix remote SSH file browsing and Claude Workflow session resume'.
Cmux Swift Actor Isolation ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
Cmux Algorithmic Complexity ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
✅ Passed checks (8 passed)
Check name Status Explanation
Description check ✅ Passed The PR description includes all required sections with sufficient detail: summary explaining what changed and why, testing/verification steps, linked issues, and a checklist of completion items.
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 No Hacky Sleeps ✅ Passed PR contains only Swift (.swift) file changes. The rule explicitly covers TypeScript, JavaScript, shell, and non-Swift build/runtime scripts, with Swift deferred to swift-blocking-runtime.md.
Cmux Swift Concurrency ✅ Passed PR adds modern async/await patterns and uses Task only for required callback boundaries, with no legacy patterns like DispatchQueue.global, Combine, or completion handlers.
Cmux Swift Logging ✅ Passed All new code follows Swift logging rules: no print/debugPrint/dump/NSLog, proper Logger usage with nonisolated private declaration, no secrets exposed, no ad hoc file logging.
Cmux Swiftui State Layout ✅ Passed No new ObservableObject/@published state added; state mutations in event handlers only, not render-time; no GeometryReader additions; openFilePreview changes follow proper async/await patterns.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR creates no new NSWindow, NSPanel, NSWindowController, SwiftUI Window/WindowGroup. All window references are to existing windows passed as parameters; no new cmuxAuxiliaryWindowIdentifiers needed.
✨ 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 issue-5237-5224-5221-5209-important-bugs

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 and usage tips.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Closing in favor of split issue-specific PRs: #5241, #5242, and #5243.

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes three regressions: SSH file-browser roots now follow the workspace currentDirectory instead of always resolving to ~; Claude Workflow directory sessions resolve to their sibling resumable JSONL transcript so --resume gets a valid session ID; and surface.read_text scrollback processing is lifted out of the main-actor capture path into a pure static helper to reduce tail/scoring allocations.

  • SSH remote file preview (FileExplorerStore, ContentView, RightSidebarToolPanel): a new SSHDownloadCommandProcess downloads remote files via ssh … cat -- to a deterministic temp-directory cache path; rootPath is threaded through FileExplorerWorkspaceRoot.remoteSSH so the browser opens at the workspace cwd.
  • Claude Workflow resume (RestorableAgentSession): resolvedClaudeWorkflowRecord walks the Claude projects directory to find the sibling .jsonl transcript whose session ID is accepted by claude --resume, replacing the non-resumable workflow-container directory ID.
  • Terminal text payload refactor (TerminalController): terminalTextPayload is now a pure static method operating on a TerminalTextRawSnapshot, and tailTerminalLines is rewritten to scan from the end without allocating a split array.

Confidence Score: 3/5

The SSH preview download path has two production correctness issues that need addressing before merge.

The Claude Workflow resume fix and the TerminalController refactor are clean and well-tested. The SSH remote file preview feature introduces a concurrent-write race — two rapid clicks on the same remote file produce two SSHDownloadCommandProcess instances that both truncate and stream to the same cache path simultaneously, which can silently corrupt the preview. Separately, copyDataToEndOfFileOrDiscard returns early on a write failure without draining its input pipe, leaving the SSH child process blocked on a full pipe buffer while waitUntilExit() spins forever on the background thread.

Sources/FileExplorerStore.swift (materializeRemoteFileForPreview and SSHDownloadCommandProcess) and Sources/ProcessPipeReader.swift (copyDataToEndOfFileOrDiscard)

Important Files Changed

Filename Overview
Sources/FileExplorerStore.swift Adds SSH file download and remote root-path wiring; materializeRemoteFileForPreview has a concurrent-write race and downloadFile uses the legacy DispatchQueue.global bridging pattern
Sources/ProcessPipeReader.swift New copyDataToEndOfFileOrDiscard helper exits early on write failure without draining the input pipe, risking a deadlock in waitUntilExit()
Sources/RestorableAgentSession.swift Adds Claude Workflow directory session resolution via sibling JSONL transcript lookup; logic is well-contained and the new test covers the key regression scenario
Sources/TerminalController.swift Extracts scrollback merge/tail/base64 work from the main-actor capture path into a pure static helper; allocation reduction and refactor look correct, tests verify the tailing semantics
Sources/ContentView.swift Routes remote file opens through the SSH preview download path; errors are silently dropped with only NSSound.beep()
Sources/RightSidebarToolPanel.swift Mirrors ContentView's remote preview routing with the same silent-beep error handling
cmuxTests/FileExplorerStoreTests.swift Adds mock download support and two new tests covering remote root tracking and preview materialization; coverage is focused and appropriate
cmuxTests/RestorableAgentSessionIndexTests.swift New test for Claude workflow container-ID resolution to a sibling resumable JSONL transcript; correctly isolates the production helper from the test
cmuxTests/TerminalControllerSocketWriteTests.swift Adds unit tests for tailTerminalLines boundary cases and terminalTextPayload tailing; test expectations match the new static-method semantics

Sequence Diagram

sequenceDiagram
    participant U as User (click file)
    participant CV as ContentView / RightSidebarToolPanel
    participant FES as FileExplorerStore
    participant SSH as SSHDownloadCommandProcess
    participant FS as FileSystem (tmp)
    participant WS as Workspace

    U->>CV: openFilePreview(filePath)
    CV->>CV: isRemoteWorkspace?
    alt remote
        CV->>FES: materializeRemoteFileForPreview(path)
        FES->>FES: remotePreviewCacheURL(displayTarget, remotePath)
        FES->>SSH: downloadFile(path, to: cacheURL)
        SSH->>FS: createFile(cacheURL) [truncate]
        SSH->>SSH: ssh … cat -- 'path'
        SSH->>FS: copyDataToEndOfFileOrDiscard(stdout → cacheURL)
        SSH-->>FES: cacheURL (local URL)
        FES-->>CV: localURL
        CV->>WS: openFileSurfaces([localURL.path])
    else local
        CV->>WS: openFileSurfaces([filePath])
    end
Loading

Reviews (1): Last reviewed commit: "fix: address important issue regressions" | Re-trigger Greptile

Comment on lines +882 to +892
func materializeRemoteFileForPreview(path: String) async throws -> URL {
guard let sshProvider = provider as? SSHFileExplorerProvider else {
throw FileExplorerError.providerUnavailable
}
let cacheURL = Self.remotePreviewCacheURL(
displayTarget: sshProvider.displayTarget,
remotePath: path
)
try await sshProvider.downloadFile(path: path, to: cacheURL)
return cacheURL
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Concurrent writes to the same cache URL can corrupt the preview file

remotePreviewCacheURL is deterministic — two rapid calls for the same remote path (e.g. a quick double-click) produce the same cacheURL. Each call then creates a separate SSHDownloadCommandProcess that truncates the file at line 579 (FileManager.default.createFile(atPath: outputURL.path, contents: nil)) and opens an independent FileHandle for writing. Both SSH processes stream bytes to the same path concurrently, producing an interleaved/corrupt file. A guard that serialises or coalesces in-flight downloads for a given path is needed before the downloadFile call.

Comment on lines +110 to +117
} catch {
logReadFailure(
ProcessPipeReadError(operation: "copyDataToEndOfFile.write", errnoCode: EIO),
fileDescriptor: input.fileDescriptor,
partialByteCount: copiedBytes
)
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Early return on write failure can deadlock waitUntilExit()

When output.write throws (e.g. disk full), the function returns at line 116 with input still live and unread. The remote cat process continues writing into the SSH pipe until the 64 KB kernel pipe buffer fills, then blocks. The caller in SSHDownloadCommandProcess.run() subsequently calls process.waitUntilExit() at line 607, which never returns because the blocked child process is waiting for the reader — a permanent deadlock on the DispatchQueue.global thread. When write fails, the loop should fall through to read-and-discard mode rather than returning immediately, or the process should be terminated before returning.

Comment on lines +452 to +460
let result = try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
continuation.resume(with: Result { try commandProcess.run() })
}
}
} onCancel: {
commandProcess.terminate()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 DispatchQueue.global for async work — cmux-swift-concurrency-modernization

withCheckedThrowingContinuation + DispatchQueue.global.async to bridge a blocking Process.run() into Swift concurrency is the legacy completion-handler pattern flagged by the concurrency-modernization rule. The existing SSHCommandProcess in this file uses the same pattern; both should use a dedicated blocking executor rather than borrowing from the cooperative pool-adjacent global queue.

Rule Used: Flag new legacy async patterns in cmux-owned Swift... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +106 to +108
} catch {
NSSound.beep()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Silent beep gives the user no actionable feedback on SSH download failure

Both RightSidebarToolPanel.openFilePreview (line 107) and ContentView (line 2594) swallow all errors from materializeRemoteFileForPreview with only NSSound.beep(). The user sees the file explorer tree, clicks a file, hears a beep, and has no way to tell whether the remote connection dropped, the file was deleted, or disk space ran out. At minimum the error should be surfaced through the workspace's existing error-presentation path with a message stating what happened in cmux terms.

This branch was successfully deployed

1 active deployment
Preview – cmux — 48f7d9c8 Deployed Jun 2, 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