Skip to content

Fix blocking Pi extension hook dispatch - #8693

Merged
austinywang merged 168 commits into
mainfrom
issue-8672-pi-extension-spawnsync-blocking
Jul 28, 2026
Merged

austinywang merged 168 commits into
mainfrom
issue-8672-pi-extension-spawnsync-blocking

Conversation

@austinywang

@austinywang austinywang commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • replace the generated Pi extension's synchronous spawnSync hook path with an asynchronous, serialized spawn dispatcher so Pi's Node event loop remains responsive
  • bound terminal-feed work globally and per importance/session class while preserving same-session ordering, independent-session progress, lifecycle completion, and failure propagation
  • make Feed acceptance authoritative: validate the live workspace/surface owner, return the accepted canonical target atomically, and repair Pi's cached route when a surface moves
  • compose socket, relay-auth, route-resolution, and acknowledgment work under one monotonic operation deadline
  • keep accepted-event publication and UI callbacks outside the Feed commit lock and off the main actor where required, without allowing a timed-out request to mutate the Feed later
  • bound fallback transcript resolution globally across distinct sessions
  • reject blank explicit Pi targets and malformed compacted markers instead of silently routing or falling through

This may also address the similar freeze/queue symptom in #6507, but that report's exact scenario was not reproduced, so this PR does not close it.

Concrete before/after reproduction

Using Pi 0.73.0 with the xAI grok-3-fast streaming provider:

# origin/main generated extension
$ pi --print --no-session "Reply with exactly: HARNESS OK"
stdout: HARNESS OK
stderr: four cmux hook timeout warnings
elapsed: 28.75s

# hooks-disabled control
$ CMUX_PI_HOOKS_DISABLED=1 pi --print --no-session "Reply with exactly: HARNESS OK"
stdout: HARNESS OK
stderr: empty
elapsed: 9.87s

# PR generated extension
$ pi --print --no-session "Reply with exactly: HARNESS OK"
stdout: HARNESS OK
stderr: empty
exit: 0
elapsed: 4.63s

The prior Pi extension and settings were restored byte-for-byte after the run.

Red/green regression evidence

The behavioral Bun harness fails against the original synchronous dispatcher:

origin/main: FAIL: Pi hook dispatch was blocking or concurrent

The final branch passes:

PASS: Pi dispatch stays responsive, serialized, and fails stale surfaces once
PASS: generated Pi extension installs and emits cmux hooks

Earlier hosted red proofs captured ownership and total-deadline failures:

  • 30155091444, commit 6105ca937d: the batch-snapshot race delivered only 5 of 64 payloads.
  • 30155092309, commit b085ba9c05: a slowly drained request exceeded one operation deadline and blank explicit Pi targets were accepted.

The corrected final test-only proof run 30165033218, commit e05f5dd077, keeps the new tests while omitting the corresponding final source fixes. It records the intended failures, including:

positiveTimeoutAcceptanceCallbackCompletesBeforeAcknowledgedCallerReturns()
  callback/publication ordering and acknowledgment expectations failed

positiveTimeoutReturnsAuthoritativeEventWhenMainActorCallbackStalls()
  authoritative acknowledgment expectation failed

blockingAcceptanceCallbackCompletesBeforeResolvedCallerReturns()
  blocking publication ordering expectations failed

distinctSessionFallbackResolutionHasGlobalAdmissionBound()
  fallback scans for distinct sessions must have a global admission bound

The final red/green commit pairs are:

  1. f2ba6498da / 6b152c219a — deadline composition coverage/fix
  2. 8b8083725f / 24c8eecf05 — stalled callback coverage/lock-scope fix
  3. 8288228764 / 0cd0465711 — authoritative handoff coverage/fix
  4. baa6c92bc9 / e4064278ed — cross-session isolation coverage/fix
  5. 84c91727a1 / e1796976c8 — bounded fallback/publication coverage/fix
  6. c378b4fb93 — correct the final test semaphore's synchronization scope

The temporary red-proof branch was deleted after the hosted evidence was captured.

Exact-head verification

Final pushed HEAD: c378b4fb93a591eef13742435c0d82c2c7c984d2

  • origin/main is an ancestor of HEAD; merge-conflict gate is clean
  • tagged Debug build: pass (issue-8672-route)
  • exact tagged-binary Pi dispatcher harness: pass
  • exact tagged-binary compacted Feed suite: pass
  • exact tagged-binary socket operation deadline suite: pass with ambient cmux socket credentials/context scrubbed
  • strict determinism guard: 0 findings
  • pbxproj test-wiring guard: pass (593 files)
  • project normalization, workspace package grouping, and SwiftPM lockfile policy guards: pass
  • Swift parsing/Python syntax and git diff --check: pass
  • worktree is clean and matches the pushed branch

Exact-head CI run 30164877041:

  • all four app-host unit-test shards: pass (shard 4 passed on rerun after its first attempt failed before checkout because the runner could not resolve github.com)
  • workflow guards, Linux preflight, web/typecheck/database, React/webviews, remote daemon, sidecar, and agent-session resources: pass
  • tests-build-and-lag: the complete test build succeeds, then the repository-wide warning budget fails on four files untouched by this PR (AppDelegate+WindowDock.swift, ContentView.swift, MobileTerminalRenderGridAnchorRegistry.swift, and SidebarGroupHeaderRowView.swift)
  • swift-package-tests: package compilation reaches final link, then fails because the CI Ghostty static library lacks _ghostty_surface_render_grid_json_v2

The latter two failures are current-main/toolchain infrastructure signatures; they are recorded here rather than hidden or represented as green.

Review closeout

Canonical semantic autoreview on exact HEAD returned:

{
  "findings": [],
  "overall_correctness": "patch is correct",
  "overall_confidence": 0.90
}

Greptile's exact-head check succeeded; its substantive summary rates the patch 5/5 and safe to merge. GitHub reports zero unresolved review threads.

The canonical wrapper itself exits nonzero because its static cmux policy phase reports 12 matches. Those matches were individually triaged and intentionally retained:

  • five file-organization matches are private state/queue helpers or tightly namespaced result DTOs whose ownership is local to Feed ingress
  • the lock/semaphore/main-sync matches implement the synchronous socket-to-main-actor bridge: admission locks guard only short state transitions, never wait or execute event work; runtime waits have explicit deadlines; the main-queue hop enters the main-actor Feed store
  • semantic review plus the deadline, cancellation, publication-order, cross-session, and red/green harnesses exercise those boundaries

This is an explicit owner-documented exception, not a claim that the static wrapper exited 0. An actor rewrite or new micro-package is intentionally outside this bug fix.

Localization audit

The changed user-facing surfaces are CLI/socket and feed.push errors. Resources/Localizable.xcstrings parses successfully; all 12 added keys have translated English and Japanese values. Added localized references were cross-checked against the catalog, and the final review commits add no user-facing text.

Closes #8672

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

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

The Pi extension now uses asynchronous serialized dispatch with bounded feeds, explicit target resolution, acknowledged ingestion, live ownership reconciliation, deadline-based socket operations, preserved tool failure status, and expanded Swift, Python, and CI regression coverage.

Changes

Pi extension dispatch and Feed delivery

Layer / File(s) Summary
Dispatcher, bounded feeds, and lifecycle orchestration
CLI/CMUXCLI+PiExtensionSource*.swift
Adds serialized asynchronous command execution, bounded session queues, terminal compaction, cancellation, context snapshots, target caching, resume-binding flows, completion draining, and ordered shutdown cleanup.
Compacted Feed routing and Pi target resolution
CLI/CMUXCLI+PiCompactedFeed.swift, CLI/PiCompactedFeedEventExpander.swift
Expands compacted terminal events, validates Pi targets and acknowledgments, and bounds untrusted Feed payloads.
Socket deadline and CLI refactoring
CLI/cmux.swift
Uses continuous-clock deadline budgeting for socket and relay operations and enforces bounded Pi Feed stdin ingestion.
Acknowledged ingestion and ingress ordering
Sources/Feed/*, Sources/TerminalController*.swift
Adds live ownership reconciliation, acknowledged batch ingestion, authoritative item IDs, received/completed publication, and ordered bounded ingress delivery.
Tool failure status and event metadata
Packages/macOS/.../Workstream/*, Sources/CmuxEventPublishing.swift, Sources/AppDelegate*.swift
Preserves tool failure status, publishes surface and error metadata, and uses direct workspace-map lookup.
Regression coverage and integration
tests/*, cmuxTests/*, cmux.xcodeproj/project.pbxproj, Resources/Localizable.xcstrings, .github/workflows/ci.yml, web/tests/*
Adds dispatch, routing, acknowledgment, ownership, deadline, privacy, installation, and Feed-ordering coverage with corresponding build, localization, CI, and web-test updates.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: lawrencecchen, azooz2003-bit


Important

Pre-merge checks failed

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

❌ Failed checks (6 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error PR adds production semaphores, NSLock, DispatchQueue.main.sync, and semaphore.wait in FeedCoordinator and new FeedIngress* types, violating the blocking-runtime rule. Replace blocking waits/locks with actor-owned state or async callbacks/timers; avoid main.sync and semaphore.wait in shipped Swift.
Cmux Swift Concurrency ❌ Error FeedIngressDeliveryLane adds a custom background DispatchQueue plus DispatchSemaphore-based serialization for feed ingress, a new legacy async pattern under cmux control. Rewrite the feed-ingress scheduler as an actor/async pipeline; keep DispatchQueue only at unavoidable OS/UI callback boundaries.
Cmux Swift Package Boundaries ❌ Error Sources/Feed/FeedCoordinator.swift adds reusable feed-ingestion/lane/encoding logic in the app target, not a small SwiftPM package boundary. Extract FeedCoordinator/FeedIngressDeliveryLane/FeedSocketEncoding into a small SwiftPM package (e.g. CmuxFeedCore) and expose a narrow protocol/value API.
Cmux User-Facing Error Privacy ❌ Error Production Pi hook warnings print JSON with source:"cmux-pi-extension" and session_id fields to stderr, exposing internal names and session IDs. Remove internal source/session_id from console-warn payloads; keep only generic user-facing messages and move diagnostics to internal logs/telemetry.
Cmux Full Internationalization ❌ Error New Swift strings are localized, but each added xcstrings key only has en/ja while the catalog already includes 20 locales, so existing locales are missing. Add matching translations for every locale already present in Resources/Localizable.xcstrings (or reduce the catalog locale set in the same PR).
Cmux Architecture Rethink ❌ Error Production Swift adds NSLock/DispatchSemaphore and setTimeout deadline-drain queues in core runtime, matching the forbidden locks/delayed-dispatch patterns. Replace the new lane/semaphore/deadline machinery with a smaller invariant-preserving fix, or confine synchronization to test-only/platform-bridge code.
Out of Scope Changes check ⚠️ Warning The PR also changes web/tests/client-config-env.test.ts, which is unrelated to the Pi dispatch fix and its linked objectives. Move the web env test update to a separate PR or remove it unless it is required for the Pi dispatch fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.37% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (17 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement async Pi dispatch, preserve ordering, warn once on stale surfaces, and keep healthy sessions working, matching #8672.
Cmux Swift Actor Isolation ✅ Passed Production Swift adds explicit MainActor hops/locks for store/UI access; new @unchecked Sendable boxes are confined to synchronous or locked use.
Cmux Browser Automation Off-Main ✅ Passed The commit only changes Sources/Feed/FeedCoordinator.swift here; no browser-automation routing or mainActor/socketWorkerMethods code was touched.
Cmux Expensive Synchronous Load ✅ Passed The new app-launch call uses SharedLiveAgentIndex.shared.scheduleRefreshIfStale(), and that cache refresh loads RestorableAgentSessionIndex in Task.detached off-main.
Cmux Cache Substitution Correctness ✅ Passed workspacesById is refreshed on every tabs change, and the new lookups fall back to live scans when missing, so no unhandled cold/stale cache swap appears in a snapshot/history path.
Cmux No Hacky Sleeps ✅ Passed PASS: New timers in the Pi dispatcher are bounded cancellation/deadline timeouts; non-Swift sleeps are only in tests/CI, which the rule allows.
Cmux Algorithmic Complexity ✅ Passed New scans are bounded (feed batches ≤64, feed queue ≤32/64), and larger workspace lookups moved to dictionary access, so no unbounded hot-path regression.
Cmux Swift @Concurrent ✅ Passed No changed Swift file adds a nonisolated async function without @concurrent, and the new UI-sensitive paths use explicit MainActor/DispatchQueue.main hops.
Cmux Swiftpm Lockfiles ✅ Passed PASS: The xcodeproj diff only adds source wiring; there are no .gitignore or Package.resolved diffs, and scripts/check-package-resolved-policy.py reports OK.
Cmux Swift Logging ✅ Passed No new runtime logging was added: targeted Swift files show no added print/debugPrint/dump/NSLog/Logger usage, and existing CLI output is unchanged.
Cmux Swiftui State Layout ✅ Passed No SwiftUI view/state/layout changes found; the only changed file is backend FeedCoordinator.swift, so the rule isn’t triggered.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR-range diff has no NSWindow/NSPanel/WindowGroup or cmuxAuxiliaryWindowIdentifiers changes; only main-window routing/feed code was touched, which is allowed.
Cmux Source Artifacts ✅ Passed All 39 changed paths are intentional source/config/tests/localization files; no temp/build/cache/log/artifact paths appeared in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The only production diff is a type annotation in Sources/Feed/FeedCoordinator.swift; no new debug/test seam or wrapper accessor was added, and existing #if DEBUG hooks predate the PR.
Cmux No Ambient Global State ✅ Passed PASS: New behavior is scoped to CMUXCLI, SocketClient, FeedCoordinator, TerminalController, and concrete types; no new file-scope APIs/singletons/namespace-only types were added.
Title check ✅ Passed The title is concise and accurately reflects the main change: replacing blocking Pi extension dispatch.
Description check ✅ Passed The description is detailed and includes summary, reproduction, and verification evidence, but it omits the template's Testing, Demo Video, Review Trigger, and Checklist sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8672-pi-extension-spawnsync-blocking

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.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the Pi extension's blocking spawnSync hook dispatch with an asynchronous, serialized spawn-based dispatcher that preserves ordering while keeping the Node event loop responsive. It also introduces a new FeedIngressDeliveryLane that bounds in-flight Feed ingress globally and per importance class, makes Feed acceptance authoritative by reconciling events against the live workspace/surface map before insertion, and linearizes acknowledged delivery so the socket caller's response is not reported until transcript/EventBus publication completes.

  • Async Pi dispatch (CMUXCLI+PiExtensionSourceDispatch.swift): replaces spawnSync with an async PiCmuxCommandDispatcher that serializes per session, caps pending/active counts with compaction, and adds bounded drain-wait with timeout before lifecycle hooks proceed.
  • Feed ingress delivery lane (FeedIngressDeliveryLane.swift, FeedIngressSynchronousResult.swift): typed bounded scheduler with per-key ordering, priority burst control, and a commit-before-signal DispatchSemaphore bridge that lets stalled publication fall back to the committed value within the socket deadline.
  • Authoritative Feed acceptance (FeedCoordinator+DeliveryTarget.swift): resolves and rehouses claimed workspace/surface events against the live ownership map on the main actor before store insertion, canonicalizing routes for moved surfaces.

Confidence Score: 5/5

Safe to merge. All six issues raised in prior review rounds have documented fixes and regression harnesses covering the delivery-lane commit/timeout mutual exclusion, semaphore bound, publication ordering, surface-detection narrowing, waiter-registration race, and relay deadline budget.

The FeedIngressSynchronousResult state machine correctly makes timeout and commit mutually exclusive via stateLock, remainingIngressTime includes attosecond precision, waiter registration is inside result.commit so it cannot observe a timed-out caller, and isSurfaceResolutionFailure is narrowed to exit code 69. No new correctness or isolation issues were found after reading the full production diff.

Files Needing Attention: No files require special attention. The most structurally complex new files (FeedIngressDeliveryLane.swift, FeedCoordinator.swift, CMUXCLI+PiExtensionSourceDispatch.swift) are backed by the strongest test coverage in the PR.

Important Files Changed

Filename Overview
CLI/CMUXCLI+PiExtensionSourceDispatch.swift New async PiCmuxCommandDispatcher: replaces spawnSync with spawn, adds per-session serialization, bounded queues, priority promotion for terminal commands, compacted overflow, and drain-with-deadline before lifecycle hooks. Surface-unavailability detection narrowed to exit code 69 only.
Sources/Feed/FeedIngressDeliveryLane.swift New bounded delivery scheduler: NSLock-guarded admission, per-key ordering via activeDeliveryKeys, priority burst control, synchronous session-critical overflow via perform(), and zero-wait capacity classes.
Sources/Feed/FeedIngressSynchronousResult.swift Commit-before-signal semaphore bridge: NSLock-protected state machine makes timeout and commit mutually exclusive, allows caller to return committed value even when publication is still in-flight.
Sources/Feed/FeedCoordinator.swift Refactored ingestBlocking into ingestBlockingWithOutcome with two-phase blocking model, waiter registration moved inside result.commit for race safety, onAccepted publication linearized before acknowledgment in the normal path.
Sources/Feed/FeedCoordinator+DeliveryTarget.swift New authoritative delivery-target resolution: validates live workspace/surface ownership on the main actor before store insertion, rehouses events to canonical target UUIDs.
Sources/TerminalController+FeedAcknowledgment.swift New acknowledged batch ingestion path: resolves delivery target, ingests atomically on main actor inside result.commit, publishes EventBus received+completed per item before signaling the socket caller.
CLI/CMUXCLI+PiCompactedFeed.swift New Swift-side compacted feed validation: sanitizes Pi PostToolUse payload to structural metadata, validates acknowledgment for both singular item_id and plural item_ids.
CLI/CMUXCLI+PiExtensionSourcePart1.swift Adds boundedPiFeedInput, projectPiFeedValue, utf8Prefix, snapshotContext; removes module-level sessionStates global and converts state helpers to explicit Map parameter.
CLI/CMUXCLI+PiExtensionSourcePart2.swift Wires async dispatcher into all hook handlers, adds per-session feedDeliveryFailed tracking, documents terminal-feed failure suppression invariant.
Sources/TerminalController.swift v2FeedPush updated to validate exclusive event/events shape, dispatch batches to v2IngestAcknowledgedFeedEvents, route single events through v2IngestFeedEvent; decode error no longer leaks raw Error.
cmuxTests/FeedCoordinatorIngressTests.swift New Swift 6 strict-concurrency ingress test suite covering delivery-lane ordering, zero-wait bounding, session-critical overflow, and commit-before-timeout mutual exclusion.
tests/test_pi_extension_dispatch.py New 2100-line behavioral Bun harness covering responsiveness, serialization, session isolation, feed cancellation, compaction, backlog bounding, ownership, and stale surface detection.
web/tests/feedback-route.test.ts Removes stale env mock no longer needed by this test; no production behavior change.

Sequence Diagram

sequenceDiagram
    participant Pi as Pi Extension Node
    participant Disp as PiCmuxCommandDispatcher
    participant CLI as cmux CLI
    participant Lane as FeedIngressDeliveryLane
    participant Result as FeedIngressSynchronousResult
    participant Main as Main Actor
    participant Store as WorkstreamStore

    Pi->>Disp: enqueueFeed(key, command)
    Disp->>Disp: scheduleFeed(sessionId)
    Disp->>CLI: spawn feed.push args
    CLI->>Lane: performAcceptedEventDelivery(events, timeout)
    Lane->>Result: create SynchronousResult
    Lane-->>Lane: executionQueue.async delivery
    Note over Lane,Result: Socket worker blocks on result.wait(timeout)
    Lane->>Main: DispatchQueue.main.sync
    Main->>Result: result.commit resolveDeliveryTarget + ingestRevalidated
    Result-->>Result: "state = .committed"
    Main->>Store: store.ingest(event)
    Main-->>Lane: onAcceptedOnMainActor callback
    Lane->>Lane: onAccepted EventBus received+completed
    Lane->>Result: result.complete semaphore.signal
    Result-->>CLI: wait() returns committed value
    CLI-->>Pi: status acknowledged item_id
    CLI->>Disp: exit 0 or surface unavailable
    Disp->>Disp: rememberSurfaceTarget or discardFeedForSession
Loading

Reviews (82): Last reviewed commit: "Fix Feed callback test synchronization s..." | Re-trigger Greptile

Comment thread CLI/CMUXCLI+PiExtensionSourceDispatch.swift Outdated
@austinywang
austinywang force-pushed the issue-8672-pi-extension-spawnsync-blocking branch from a69effb to bdf76b9 Compare July 22, 2026 23:14
@austinywang
austinywang force-pushed the issue-8672-pi-extension-spawnsync-blocking branch 2 times, most recently from a21f92e to 8df86a0 Compare July 22, 2026 23:29

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
CLI/CMUXCLI+PiExtensionSourcePart2.swift (1)

210-240: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

sendFeed swallows genuine command failures without any warning.

sendHook, ensureResumeBinding, and clearResumeBinding all call warn(...) when a dispatched command fails for a reason other than surfaceUnavailable. sendFeed's .catch(() => {}) discards every failure unconditionally, so a real, non-"not_found" feed failure (bad payload, subprocess crash, etc.) produces zero diagnostic output — unlike every other dispatch path in this file.

🩹 Proposed fix to warn on genuine feed failures
   void runCmux(
     ["hooks", "feed", "--source", "pi", "--event", eventName],
     cwd,
     JSON.stringify(payload),
     context,
     "feed",
-  ).catch(() => {});
+  ).then((result) => {
+    if (!result.ok && !result.surfaceUnavailable) {
+      warn(context, "cmux feed command failed", {
+        eventName,
+        status: result.status,
+        stderr_available: result.stderr.trim().length > 0,
+        error_available: result.error !== undefined,
+      });
+    }
+  }).catch(() => {});
🤖 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 `@CLI/CMUXCLI`+PiExtensionSourcePart2.swift around lines 210 - 240, Update
sendFeed so its runCmux rejection handler suppresses only expected
surface-unavailable/not-found failures and calls warn(...) for genuine feed
command failures, matching the handling used by sendHook, ensureResumeBinding,
and clearResumeBinding. Preserve the existing early returns and payload/dispatch
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 `@CLI/CMUXCLI`+PiExtensionSourceDispatch.swift:
- Around line 46-59: Replace the broad stdout/stderr text matching used by
isSurfaceResolutionFailure with a specific structured signal from the hook
command or CLIError, and classify failures using that signal (plus any required
structured status). Update both the failure handling around surfaceState and the
corresponding logic at lines 154-160 so dispatch is disabled only for explicitly
identified surface-resolution failures, not generic CLI errors containing
matching text.

In `@tests/test_pi_extension_dispatch.py`:
- Around line 71-380: Split the four scenario blocks in main() into separate
check_*() -> int helpers: responsiveness, feed backlog, timeout serialization,
and stale surface. Move each scenario’s setup, execution, assertions, and
failure returns into its helper, then have main() retain dependency setup,
temporary-directory management, helper invocation, and final success reporting
while preserving all existing return codes and diagnostics.
- Around line 20-42: Extract the shared Pi extension installation and override
logic from install_extension in tests/test_pi_extension_dispatch.py into
tests/claude_teams_test_utils.py alongside resolve_cmux_cli, preserving its
return path and error handling; update
tests/test_pi_extension_dispatch.py#L20-L42 to call the helper, and replace the
inline override-copy block in tests/test_pi_extension_install.py#L94-L96 with
the same helper call.

---

Outside diff comments:
In `@CLI/CMUXCLI`+PiExtensionSourcePart2.swift:
- Around line 210-240: Update sendFeed so its runCmux rejection handler
suppresses only expected surface-unavailable/not-found failures and calls
warn(...) for genuine feed command failures, matching the handling used by
sendHook, ensureResumeBinding, and clearResumeBinding. Preserve the existing
early returns and payload/dispatch 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: cba1e288-e786-4efd-957b-7661f8686fda

📥 Commits

Reviewing files that changed from the base of the PR and between 5220e7e and bdf76b9.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+PiExtensionSource.swift
  • CLI/CMUXCLI+PiExtensionSourceDispatch.swift
  • CLI/CMUXCLI+PiExtensionSourcePart1.swift
  • CLI/CMUXCLI+PiExtensionSourcePart2.swift
  • cmux.xcodeproj/project.pbxproj
  • tests/test_pi_extension_dispatch.py
  • tests/test_pi_extension_install.py

Comment thread CLI/CMUXCLI+PiExtensionSourceDispatch.swift
Comment thread tests/test_pi_extension_dispatch.py Outdated
Comment thread tests/test_pi_extension_dispatch.py
@austinywang
austinywang force-pushed the issue-8672-pi-extension-spawnsync-blocking branch from 8df86a0 to 3791cff Compare July 22, 2026 23:41
Comment thread CLI/CMUXCLI+PiExtensionSourceDispatch.swift Outdated
@austinywang
austinywang force-pushed the issue-8672-pi-extension-spawnsync-blocking branch from 3791cff to 20f2dea Compare July 23, 2026 00: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 `@tests/test_pi_extension_dispatch.py`:
- Around line 296-299: Replace the fixed-duration waits in the cancellation and
completion harnesses around session_shutdown and the completion flow with
deadline-bounded polling of the relevant log predicate. Await a real completion
signal, such as observing the expected five “hooks feed” calls and stable
cancellation output, before allowing the process to exit and performing Python
assertions; retain a timeout so failures terminate deterministically.
🪄 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: 0f7b27c3-29d9-49d9-9b36-f5c2fe64d61e

📥 Commits

Reviewing files that changed from the base of the PR and between bdf76b9 and ffd6294.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+PiExtensionSource.swift
  • CLI/CMUXCLI+PiExtensionSourceDispatch.swift
  • CLI/CMUXCLI+PiExtensionSourcePart1.swift
  • CLI/CMUXCLI+PiExtensionSourcePart2.swift
  • cmux.xcodeproj/project.pbxproj
  • tests/test_pi_extension_dispatch.py
  • tests/test_pi_extension_install.py

Comment thread tests/test_pi_extension_dispatch.py

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

Caution

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

⚠️ Outside diff range comments (1)
tests/test_pi_extension_dispatch.py (1)

233-401: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split check_feed_lifecycle into per-scenario helpers.

This function packs two independent scenarios — queued-feed cancellation on session_shutdown and terminal-lifecycle completion ordering/backlog-cancellation — into one ~168-line function, unlike every other check_* function in this file which owns exactly one scenario (a pattern this file already adopted after a prior complexity-driven refactor of main()). Splitting into e.g. check_feed_cancellation() and check_completion_order() would keep the file consistent and easier to extend.

🤖 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 `@tests/test_pi_extension_dispatch.py` around lines 233 - 401, Split
check_feed_lifecycle into two independent helpers: check_feed_cancellation for
the session_shutdown queued-feed cancellation scenario and
check_completion_order for terminal completion ordering and backlog
cancellation. Move each scenario’s setup, execution, assertions, and failure
reporting into its respective helper, then update the caller to invoke both
helpers while preserving their existing return-code 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.

Outside diff comments:
In `@tests/test_pi_extension_dispatch.py`:
- Around line 233-401: Split check_feed_lifecycle into two independent helpers:
check_feed_cancellation for the session_shutdown queued-feed cancellation
scenario and check_completion_order for terminal completion ordering and backlog
cancellation. Move each scenario’s setup, execution, assertions, and failure
reporting into its respective helper, then update the caller to invoke both
helpers while preserving their existing return-code behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba35d01e-805d-41d5-bfc4-15ee8823bca1

📥 Commits

Reviewing files that changed from the base of the PR and between 342df5c and ff64934.

📒 Files selected for processing (2)
  • CLI/CMUXCLI+PiExtensionSourceDispatch.swift
  • tests/test_pi_extension_dispatch.py

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Caution

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

⚠️ Outside diff range comments (2)
CLI/CMUXCLI+PiExtensionSourceDispatch.swift (1)

54-60: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve terminal state after promotion to the priority queue.

Line 54 only checks pendingFeedCommands, but Line 75 removes terminal entries into an unkeyed queue and Lines 134-145 dequeue them without retaining their key. A late start for that key is then accepted and can execute after completion—even after finishFeedForSession resolves. Track terminal keys while queued/active, or reject feeds for a session once finishing begins.

Also applies to: 71-76, 134-145

🤖 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 `@CLI/CMUXCLI`+PiExtensionSourceDispatch.swift around lines 54 - 60, Preserve
terminal state for commands promoted from pendingFeedCommands into the priority
queue: update the feed-dispatch flow around pendingFeedCommands,
finishFeedForSession, and queue dequeue handling to retain terminal keys or
reject new feeds once a session begins finishing. Ensure late non-terminal
events for a terminal or finishing key cannot be accepted or executed after
completion, while preserving normal ordering for valid commands.
tests/test_pi_extension_dispatch.py (1)

233-401: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider splitting check_feed_lifecycle into two focused helpers.

This function still runs two logically distinct scenarios (queued-feed cancellation on shutdown, and completion-order/overlap validation on terminal lifecycle) in one ~170-line body, flagged as high complexity. This mirrors the same maintainability concern already raised and fixed for main() in a prior review pass — splitting into check_feed_cancellation_on_shutdown() and check_feed_completion_order() would keep the same precedent consistent across the file.

🤖 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 `@tests/test_pi_extension_dispatch.py` around lines 233 - 401, Split
check_feed_lifecycle into two focused helpers:
check_feed_cancellation_on_shutdown for the cancellation scenario and
check_feed_completion_order for completion ordering and overlap validation. Move
each scenario’s setup, execution, assertions, and failure reporting into its
helper, then have check_feed_lifecycle invoke both and return failure
immediately when either fails, preserving existing 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.

Outside diff comments:
In `@CLI/CMUXCLI`+PiExtensionSourceDispatch.swift:
- Around line 54-60: Preserve terminal state for commands promoted from
pendingFeedCommands into the priority queue: update the feed-dispatch flow
around pendingFeedCommands, finishFeedForSession, and queue dequeue handling to
retain terminal keys or reject new feeds once a session begins finishing. Ensure
late non-terminal events for a terminal or finishing key cannot be accepted or
executed after completion, while preserving normal ordering for valid commands.

In `@tests/test_pi_extension_dispatch.py`:
- Around line 233-401: Split check_feed_lifecycle into two focused helpers:
check_feed_cancellation_on_shutdown for the cancellation scenario and
check_feed_completion_order for completion ordering and overlap validation. Move
each scenario’s setup, execution, assertions, and failure reporting into its
helper, then have check_feed_lifecycle invoke both and return failure
immediately when either fails, preserving existing behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d86f50d1-0787-4b50-bd70-1b13a4792c51

📥 Commits

Reviewing files that changed from the base of the PR and between 342df5c and ff64934.

📒 Files selected for processing (2)
  • CLI/CMUXCLI+PiExtensionSourceDispatch.swift
  • tests/test_pi_extension_dispatch.py

@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

…on-spawnsync-blocking

# Conflicts:
#	CLI/cmux.swift
#	Packages/iOS/CmuxMobileTerminalKit/Tests/CmuxMobileTerminalKitTests/TerminalDockKeyboardTransitionPlannerTests.swift
#	cmux.xcodeproj/project.pbxproj
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.

Pi extension: spawnSync hook dispatch blocks Pi's event loop → empty streamed responses; stale CMUX_SURFACE_ID in nested terminals pays ~2s per event

1 participant