Skip to content

Route blocking v2 socket methods off main - #3340

Merged
lawrencecchen merged 8 commits into
mainfrom
feat-repro-feed-dispatch-crash
Apr 30, 2026
Merged

lawrencecchen merged 8 commits into
mainfrom
feat-repro-feed-dispatch-crash

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Replaces ad hoc v2 socket worker bypasses with one execution policy for blocking socket methods.
  • Runs feed push/reply, auth waiters, feedback submit, and VM methods on the socket worker.
  • Keeps main-actor v2 dispatch guarded against worker-only methods.

Verification

  • xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination platform=macOS -derivedDataPath /tmp/cmux-fdcrsh-check build CODE_SIGNING_ALLOWED=NO
  • ./scripts/reload.sh --tag fdcrsh
  • Reproduced https://gist.github.com/mxschmitt/065a81bc2b04df0a6781d9b8af0ccd2d against /tmp/cmux-debug-fdcrsh.sock; RPC returned { "status": "timed_out" }, app process stayed alive, and no new cmux DEV crash report was written.

Summary by cubic

Unifies V2 socket routing behind a single execution policy and runs blocking methods on the socket worker (including system.top). Also trims inputs and fixes feed prompt workspace resolution to keep main-actor dispatch safe.

  • Refactors

    • Added execution policy that routes auth.*, feedback.submit, feed.push, feed.*.reply, all vm.*, and system.top to the socket worker; main dispatch rejects these with invalid_dispatch.
    • Centralized V2 parsing/dispatch via parseV2SocketRequest (trims input), socketWorkerV2ResponseIfNeeded, socketWorkerV2Response, and processCommandUsingSocketExecutionPolicy; handleSocketLine is nonisolated.
    • Pruned worker-only cases from main; VM handling now goes through socketWorkerCloudVMResponse(method:id:params:).
  • Bug Fixes

    • Prevents main-actor crashes by keeping feed/VM/system.top off the main thread; long-running RPCs no longer block the app and repro still returns {"status":"timed_out"}.
    • Ensures feed prompt side effects resolve the correct workspace by trimming IDs and using AppDelegate.tabManagerFor; feed push/reply handlers are nonisolated to run on the worker.

Written for commit 7c282c4. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Refactor

    • Centralized V2/socket command routing into a single dispatcher and unified previously separate response paths.
    • Relaxed isolation/visibility for several socket handlers so they can be invoked directly when messages are already parsed.
  • Bug Fixes

    • Enforced execution policy to prevent worker-designated socket work from running on the main thread.
    • Improved error responses for unknown or misrouted socket methods and removed a redundant raw-message wrapper.

@vercel

vercel Bot commented Apr 30, 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 Apr 30, 2026 9:54am
cmux-staging Building Building Preview, Comment Apr 30, 2026 9:54am

@coderabbitai

coderabbitai Bot commented Apr 30, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes the JSON-parsing gatekeeper for VM socket commands and exposes the VM response handler; centralizes V2 socket dispatch in TerminalController by parsing typed V2 requests, classifying execution policy, and routing calls to either a socket-worker path or the main actor, rejecting misrouted methods.

Changes

Cohort / File(s) Summary
Cloud VM Socket Command Handling
Sources/Cloud/VMClientSocketCommands.swift
Deleted socketWorkerCloudVMResponseIfNeeded(for:) (JSON parsing + policy check). Made socketWorkerCloudVMResponse(method:id:params:) non-private to accept already-parsed inputs.
V2 Socket Routing & Dispatch
Sources/TerminalController.swift
Added V2 request parser (parseV2SocketRequest / V2SocketRequest), execution policy classifier (executionPolicy(forV2Method:)), and unified socket-worker dispatcher (socketWorkerV2ResponseIfNeeded / socketWorkerV2Response). Introduced processCommandUsingSocketExecutionPolicy(_:), made handleSocketLine(_:) nonisolated, hardened processV2Command(_:) to reject worker-classified methods on the main actor, and moved worker-classified handling (auth.*, feedback.submit, feed.*, system.top, vm.*) to the socket-worker path. Feed handlers and prompt-submit side-effects updated to be nonisolated and resolve TabManager via AppDelegate.shared?.tabManagerFor(tabId:).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (Socket)
    participant Dispatch as Socket Dispatch
    participant Parser as V2 Request Parser
    participant Policy as Execution Policy
    participant SWHandler as Socket Worker
    participant MainActor as Main Actor
    participant CloudVM as Cloud VM Handler

    Client->>Dispatch: send raw V2 JSON line
    Dispatch->>Parser: parseV2SocketRequest(line)
    Parser-->>Dispatch: { method, id, params }
    Dispatch->>Policy: executionPolicy(forV2Method:)
    Policy-->>Dispatch: (.socketWorker or .mainActor)

    alt .socketWorker
        Dispatch->>SWHandler: socketWorkerV2Response(method,id,params)
        alt method == vm.*
            SWHandler->>CloudVM: socketWorkerCloudVMResponse(method,id,params)
            CloudVM-->>SWHandler: vm result
            SWHandler-->>Client: response
        else known worker method
            SWHandler-->>Client: response
        else unknown
            SWHandler-->>Client: method_not_found
        end
    else .mainActor
        Dispatch->>MainActor: processV2Command / v2MainSync
        MainActor-->>Client: response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰 A carrot-coded socket song,
Parser hops and routes along.
Worker door opens, VM sings true,
Main actor waits for its cue.
Rabbit dances — new dispatch, whoo!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: routing blocking V2 socket methods away from the main actor to the socket worker.
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.
Description check ✅ Passed PR description covers the summary and verification testing comprehensively, though lacks sections for manual testing details and demo video.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-repro-feed-dispatch-crash

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.

@greptile-apps

greptile-apps Bot commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR consolidates the three scattered socket-worker bypass functions (socketWorkerAuthResponseIfNeeded, socketWorkerFeedbackResponseIfNeeded, socketWorkerCloudVMResponseIfNeeded) into a single executionPolicy(forV2Method:) predicate and a unified socketWorkerV2Response dispatcher. A defensive guard added to processV2Command correctly rejects worker-only methods that reach the main-actor path.

  • The auth.status, auth.begin_sign_in, auth.sign_out, and all vm.* case arms inside the processV2Command switch (around lines 2278–2355) are now dead code — they can never be reached because the new guard returns invalid_dispatch first. These should be pruned to keep the main-actor switch honest.

Confidence Score: 4/5

Safe to merge; the refactoring is logically sound and the only finding is unreachable dead code left in the main-actor switch.

All findings are P2 (dead code, no runtime impact). The execution-policy routing is consistent across both the socket path and the in-process handleSocketLine path, and the defensive guard in processV2Command provides a correct safety net.

Sources/TerminalController.swift — the auth.* and vm.* cases inside processV2Command's switch should be removed.

Important Files Changed

Filename Overview
Sources/TerminalController.swift Replaces three ad-hoc socket-worker handlers with a unified executionPolicy + socketWorkerV2Response dispatch; adds a defensive guard in processV2Command that rejects worker-only methods, but this leaves the pre-existing auth.* and vm.* switch cases as dead code.
Sources/Cloud/VMClientSocketCommands.swift Removes the now-redundant socketWorkerCloudVMResponseIfNeeded wrapper and makes socketWorkerCloudVMResponse nonisolated and internal so it can be called directly from the new unified dispatcher; no logic changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Socket line received] --> B[authResponseIfNeeded]
    B -- "auth required response" --> Z[Return response]
    B -- "pass" --> C[processCommandUsingSocketExecutionPolicy]

    C --> D{socketWorkerV2ResponseIfNeeded}
    D -- "executionPolicy == .socketWorker" --> E[withSocketCommandPolicy socket worker thread]
    E --> F[socketWorkerV2Response switch]
    F --> F1["auth.status / auth.begin_sign_in / auth.sign_out semaphore to @MainActor"]
    F --> F2["feedback.submit / feed.push / feed.*.reply"]
    F --> F3["vm.* to socketWorkerCloudVMResponse"]
    F --> F4["default to method_not_found"]
    F1 & F2 & F3 & F4 --> Z

    D -- "executionPolicy == .mainActor or non-v2 command" --> G[v2MainSync to processCommand @MainActor dispatch]
    G --> H[processV2Command]
    H --> I{executionPolicy guard}
    I -- ".socketWorker blocked" --> J[invalid_dispatch error]
    I -- ".mainActor ok" --> K[main-actor switch system.* / feed.jump / etc.]
    K --> Z
Loading

Comments Outside Diff (1)

  1. Sources/TerminalController.swift, line 2278-2312 (link)

    P2 Dead code — auth. cases in processV2Command are unreachable*

    The new guard at line 2248 (guard Self.executionPolicy(forV2Method: method) == .mainActor) will return invalid_dispatch for "auth.status", "auth.begin_sign_in", and "auth.sign_out" because all three are listed in socketWorkerV2Methods and resolve to .socketWorker. The corresponding case arms in the switch below can never execute. The same applies to all vm.* cases further down (around lines 2316–2355). These stale cases should be removed to avoid misleading future maintainers into thinking the main-actor path still handles them.

Reviews (1): Last reviewed commit: "Route blocking v2 socket methods off mai..." | Re-trigger Greptile

@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 the current code and only fix it if needed.

Inline comments:
In `@Sources/TerminalController.swift`:
- Around line 1841-1857: Detect and short‑circuit socket-worker requests made
from the main thread: in handleSocketLine(_:) check Thread.isMainThread and call
socketWorkerV2ResponseIfNeeded(for: line) (or equivalent predicate) and if it
returns non‑nil, return a fast error response (e.g. "socket worker requests
cannot be made from main thread") instead of dispatching to
processCommandUsingSocketExecutionPolicy(_:); leave other paths unchanged and
add a TODO comment to route these into an async/v2MainSync path later so v2
handlers that need `@MainActor` work use v2MainSync rather than blocking the main
thread.
🪄 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: bc7f627b-d763-4e93-8220-59a04f4d58b3

📥 Commits

Reviewing files that changed from the base of the PR and between 9713725 and 2597d88.

📒 Files selected for processing (2)
  • Sources/Cloud/VMClientSocketCommands.swift
  • Sources/TerminalController.swift

Comment thread Sources/TerminalController.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2597d88b01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/TerminalController.swift Outdated
Comment on lines 1416 to 1418
guard command.hasPrefix("{"),
let data = command.data(using: .utf8),
let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Trim V2 input before worker-policy dispatch

handleSocketLine now routes through processCommandUsingSocketExecutionPolicy, but parseV2SocketRequest only accepts strings that literally start with {. If an in-process caller passes a valid JSON-RPC line with leading whitespace (which previously worked because processCommand trimmed first), worker-only methods like feed.push/auth.status skip worker dispatch and then hit processV2Command's new invalid_dispatch guard. This creates a behavior regression for formatted or indented JSON input on the public in-process entry point.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already fixed in the current branch: parseV2SocketRequest trims leading whitespace before JSON parsing, and the dogfood stress run included leading-whitespace worker RPCs successfully.

— Claude Code

@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 the current code and only fix it if needed.

Inline comments:
In `@Sources/TerminalController.swift`:
- Around line 1408-1412: Make executionPolicy(forV2Method:) the authoritative
classifier for "system.top" by treating methods equal to "system.top" (in
addition to vm.* and socketWorkerV2Methods.contains) as returning .socketWorker;
then remove or stop special-casing "system.top" in
socketWorkerSystemTopResponseIfNeeded so it no longer bypasses the
executionPolicy logic and the invalid_dispatch checks in the dispatch paths
become effective.
🪄 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: 76149363-5785-4520-b20d-4cd9e17b27b2

📥 Commits

Reviewing files that changed from the base of the PR and between 2597d88 and af2de20.

📒 Files selected for processing (1)
  • Sources/TerminalController.swift

Comment thread Sources/TerminalController.swift

@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.

♻️ Duplicate comments (1)
Sources/TerminalController.swift (1)

1408-1502: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fold system.top into the worker policy.

system.top still bypasses the new execution-policy gate: it is excluded from socketWorkerV2Methods, handled by socketWorkerSystemTopResponseIfNeeded, and still treated as .mainActor in processV2Command(_:). That leaves the blocking path outside the off-main routing this refactor is supposed to enforce.

As per coding guidelines, "If adding a new socket command, default to off-main handling; require an explicit reason in code comments when main-thread execution is necessary."

Suggested consolidation
     private nonisolated static let socketWorkerV2Methods: Set<String> = [
         "auth.status",
         "auth.begin_sign_in",
         "auth.sign_out",
         "feedback.submit",
         "feed.push",
         "feed.permission.reply",
         "feed.question.reply",
         "feed.exit_plan.reply",
+        "system.top",
     ]
 
     private nonisolated func socketWorkerV2Response(_ request: V2SocketRequest) -> String {
         switch request.method {
@@
         case "feed.exit_plan.reply":
             return v2Result(id: request.id, v2FeedExitPlanReply(params: request.params))
+        case "system.top":
+            return v2Result(id: request.id, v2SystemTop(params: request.params))
         case let method where method.hasPrefix("vm."):
             return socketWorkerCloudVMResponse(method: method, id: request.id, params: request.params)
         default:
             return v2Error(id: request.id, code: "method_not_found", message: "Unknown method")
         }
     }
-
-    private nonisolated func socketWorkerSystemTopResponseIfNeeded(for command: String) -> String? {
-        guard let request = parseV2SocketRequest(command),
-              request.method == "system.top" else {
-            return nil
-        }
-
-        return withSocketCommandPolicy(commandKey: request.method, isV2: true) {
-            v2Result(id: request.id, v2SystemTop(params: request.params))
-        }
-    }

Also applies to: 2273-2279

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 1408 - 1502, The review points
out that "system.top" is still handled on the main actor instead of being folded
into the off-main worker policy; update the routing so "system.top" is treated
like other socketWorker V2 methods. Add "system.top" to the
socketWorkerV2Methods set (or change executionPolicy(forV2Method:) to treat
request.method == "system.top" as .socketWorker), remove or retire
socketWorkerSystemTopResponseIfNeeded and any direct .mainActor handling in
processV2Command(_:) so v2 system.top requests are dispatched via
withSocketCommandPolicy and handled by v2SystemTop through the same worker path;
keep an explicit code comment only if you intentionally want main-thread
execution.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@Sources/TerminalController.swift`:
- Around line 1408-1502: The review points out that "system.top" is still
handled on the main actor instead of being folded into the off-main worker
policy; update the routing so "system.top" is treated like other socketWorker V2
methods. Add "system.top" to the socketWorkerV2Methods set (or change
executionPolicy(forV2Method:) to treat request.method == "system.top" as
.socketWorker), remove or retire socketWorkerSystemTopResponseIfNeeded and any
direct .mainActor handling in processV2Command(_:) so v2 system.top requests are
dispatched via withSocketCommandPolicy and handled by v2SystemTop through the
same worker path; keep an explicit code comment only if you intentionally want
main-thread execution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80273261-bc52-45db-a429-f1975f2ad10a

📥 Commits

Reviewing files that changed from the base of the PR and between af2de20 and 05e8fd6.

📒 Files selected for processing (1)
  • Sources/TerminalController.swift

@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 the current code and only fix it if needed.

Inline comments:
In `@Sources/TerminalController.swift`:
- Around line 1458-1464: Validate and reject non-positive timeout_seconds before
calling AuthManager.shared.beginSignInAndAwait: parse the incoming value from
request.params using the v2 numeric-parsing helper (instead of blindly casting
to Double into timeoutSeconds), check that the resulting timeout is > 0, and if
not return an invalid_params error (or clamp to a minimum positive value) rather
than passing it to beginSignInAndAwait; update the logic around timeoutSeconds
and the Task that calls beginSignInAndAwait to only run when the validated
timeout is positive.
🪄 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: 1f7b39b2-4158-494a-a5c0-7eacd5634312

📥 Commits

Reviewing files that changed from the base of the PR and between 05e8fd6 and eae86ef.

📒 Files selected for processing (1)
  • Sources/TerminalController.swift

Comment on lines +1458 to +1464
let timeoutSeconds = (request.params["timeout_seconds"] as? Double) ?? 300
let semaphore = DispatchSemaphore(value: 0)
nonisolated(unsafe) var signedIn = false
Task { @MainActor in
signedIn = await AuthManager.shared.beginSignInAndAwait(
timeout: timeoutSeconds
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject non-positive timeout_seconds values.

A missing timeout falls back to 300, but a present 0 or negative value still gets passed into beginSignInAndAwait. For a socket-exposed timeout parameter, that should return invalid_params (or at least be clamped) instead of letting malformed input change auth behavior.

Suggested fix
-            let timeoutSeconds = (request.params["timeout_seconds"] as? Double) ?? 300
+            let timeoutSeconds: TimeInterval
+            if let rawTimeout = request.params["timeout_seconds"] {
+                guard let seconds = rawTimeout as? NSNumber, seconds.doubleValue > 0 else {
+                    return v2Error(
+                        id: request.id,
+                        code: "invalid_params",
+                        message: "auth.begin_sign_in requires `timeout_seconds` > 0"
+                    )
+                }
+                timeoutSeconds = seconds.doubleValue
+            } else {
+                timeoutSeconds = 300
+            }

Based on learnings: In Swift request/JSON handlers in Sources/TerminalController.swift, prefer the v2 numeric-parsing helpers and clamp/validate timeout inputs as appropriate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 1458 - 1464, Validate and
reject non-positive timeout_seconds before calling
AuthManager.shared.beginSignInAndAwait: parse the incoming value from
request.params using the v2 numeric-parsing helper (instead of blindly casting
to Double into timeoutSeconds), check that the resulting timeout is > 0, and if
not return an invalid_params error (or clamp to a minimum positive value) rather
than passing it to beginSignInAndAwait; update the logic around timeoutSeconds
and the Task that calls beginSignInAndAwait to only run when the validated
timeout is positive.

…tch-crash

# Conflicts:
#	Sources/TerminalController.swift

@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.

♻️ Duplicate comments (1)
Sources/TerminalController.swift (1)

1458-1464: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate timeout_seconds before calling auth wait.

Line 1458 currently defaults missing values, but it still accepts present 0/negative values and forwards them. Please reject invalid values with invalid_params (or clamp to a minimum positive value) before beginSignInAndAwait.

Suggested fix
-        case "auth.begin_sign_in":
-            let timeoutSeconds = (request.params["timeout_seconds"] as? Double) ?? 300
+        case "auth.begin_sign_in":
+            let timeoutSeconds: TimeInterval
+            if let rawTimeout = request.params["timeout_seconds"] {
+                guard let n = rawTimeout as? NSNumber, n.doubleValue > 0 else {
+                    return v2Error(
+                        id: request.id,
+                        code: "invalid_params",
+                        message: "auth.begin_sign_in requires `timeout_seconds` > 0"
+                    )
+                }
+                timeoutSeconds = n.doubleValue
+            } else {
+                timeoutSeconds = 300
+            }
             let semaphore = DispatchSemaphore(value: 0)
             nonisolated(unsafe) var signedIn = false
             Task { `@MainActor` in
                 signedIn = await AuthManager.shared.beginSignInAndAwait(
                     timeout: timeoutSeconds
                 )
                 semaphore.signal()
             }

Based on learnings: In Swift request/JSON handlers in Sources/TerminalController.swift, prefer v2 numeric parsing helpers and validate/clamp timeout inputs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 1458 - 1464, Validate and
reject or clamp the timeout_seconds value before calling
AuthManager.shared.beginSignInAndAwait: parse timeout_seconds from
request.params using the v2 numeric parsing helper, ensure it is a positive
nonzero value (or clamp to a minimum like 1.0), and if invalid respond with an
invalid_params error instead of forwarding 0/negative to beginSignInAndAwait;
update the code around the timeoutSeconds assignment and the Task { `@MainActor`
... beginSignInAndAwait(...) } invocation to use the validated/clamped value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@Sources/TerminalController.swift`:
- Around line 1458-1464: Validate and reject or clamp the timeout_seconds value
before calling AuthManager.shared.beginSignInAndAwait: parse timeout_seconds
from request.params using the v2 numeric parsing helper, ensure it is a positive
nonzero value (or clamp to a minimum like 1.0), and if invalid respond with an
invalid_params error instead of forwarding 0/negative to beginSignInAndAwait;
update the code around the timeoutSeconds assignment and the Task { `@MainActor`
... beginSignInAndAwait(...) } invocation to use the validated/clamped value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe46626d-d3d7-49aa-9eee-39feae200055

📥 Commits

Reviewing files that changed from the base of the PR and between eae86ef and 16aa5f9.

📒 Files selected for processing (1)
  • Sources/TerminalController.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16aa5f9fea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/TerminalController.swift Outdated
let rawWorkspaceId = event.workspaceId,
let workspaceId = v2UUID(["workspace_id": rawWorkspaceId], "workspace_id"),
let tabManager = v2ResolveWorkspaceOwner(workspaceId)
let workspaceId = UUID(uuidString: rawWorkspaceId.trimmingCharacters(in: .whitespacesAndNewlines))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve non-UUID workspace handles in feed prompt side effects

v2ApplyPromptSubmitSideEffects now parses event.workspaceId with UUID(uuidString:), so feed.push UserPromptSubmit events that carry a workspace handle/ref are ignored. This is a regression from the previous v2UUID(...) path, which also resolved handle refs, and it breaks prompt-submit side effects (message preview + iMessage-mode reordering) for valid feed payloads that provide workspace_ref/handle-style IDs (see feedWorkspaceId accepting workspace_ref in CLI/cmux.swift).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in ff5280c. Prompt-submit side effects now resolve workspace handles/refs inside the main-actor bridge before touching TabManager.

— Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@lawrencecchen
lawrencecchen merged commit 5d1b41d into main Apr 30, 2026
21 checks passed
@lawrencecchen
lawrencecchen deleted the feat-repro-feed-dispatch-crash branch April 30, 2026 10:07

This branch was successfully deployed

1 active deployment
Preview – cmux — 7c282c45 Deployed Apr 30, 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