Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.

fix(terminal): backport responsive teardown to 0.64.22 - #1

Merged
vadimzak merged 3 commits into
stable/0.64.22from
fix/stable-teardown
Sep 8, 2026
Merged

vadimzak merged 3 commits into
stable/0.64.22from
fix/stable-teardown

Conversation

@vadimzak

@vadimzak vadimzak commented Sep 8, 2026 •

Copy link
Copy Markdown
Owner

Terminal close on cmux 0.64.22 keeps the main thread available while native workers shut down.

Why: M deadlocked: main joined I/O, I/O joined its reader, and the reader waited for the full main-thread mailbox.
Risk: med — native cleanup moves off main; callback userdata remains retained until it finishes.
Needs you: Release signing setup before installing this backport.
Unverified: Release packaging is blocked: neither P nor M has a code-signing identity.

Changed

  • Backport upstream commit 77ab34b from upstream PR #9358, reusing the existing teardown coordinator.
  • Backport its bounded, surface-scoped regression gate and main-thread responsiveness test.

Checks

  • Regression failed on original stable code; all 15 focused teardown/lifetime tests pass with the fix.
  • SwiftPM reports the known archive-name diagnostic; results satisfy upstream CI's explicit exception.
  • Tagged Debug build passed; 20 live closes under mouse-mode output passed, each followed by a responsive main-thread RPC. Workloads exited; worker counts returned to the original terminal only.

Review contract

Request: "do it" — approved stable backport, isolated validation, then M/P rollout.
Non-goals / Removed: blocking native free on main; unrelated upstream changes excluded.
Depends on: unchanged pinned GhosttyKit 6143bac; existing teardown coordinator.
Surface: test-only C gate functions; no production API/config changes.
Size: code +9/−8 · tests +130/−0

Rollout

  • After Release signing and validation, install on M; verify sends and shutdowns, then P.
  • Rollback: restore the preserved vendor app bundle and restart Orc.

Certainty

Problem 98 · Plan 95 · Impl 98 — fixes the captured cycle; Release signing remains outstanding.

Session

codex-2b59230d-7f2b-4db8-821d-447a05c87abc@vz-kooply-mac-3

vadimzak and others added 2 commits September 8, 2026 15:05
Backport the bounded, surface-scoped regression from upstream cmux PR manaflow-ai#9358. Fails on v0.64.22 before the production fix.
@vadimzak vadimzak added cr-in-progress Code review in progress (temporary; auto-removed when the CR ends) and removed cr-in-progress Code review in progress (temporary; auto-removed when the CR ends) labels Sep 8, 2026

@vadimzak vadimzak left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review — full · head 00f2a1d

claude-7db4eeaa-f8ab-413f-a876-2b20b3fedd5e@vz-kooply-mac-3 · claude / claude-fable-5-1 · effort high

Scope inspected: head 00f2a1d1ce vs base ddd4a01bc5 (stable/0.64.22). Production delta is 9+/8− in TerminalSurface+RuntimeLifecycle.swift; the rest is test-only (Swift test + C stub gate).

Production change (verified sound):

  • teardownSurface() no longer runs ghostty_surface_free inside Task { @MainActor in … }. It now hands the pointer plus callback/manual-IO/tee userdata to runtimeTeardown.enqueueRuntimeTeardown(...), identical to the existing deinit path in TerminalSurface.swift:714 and to upstream commit 77ab34b3 (manaflow-ai#9358, merged).
  • Coordinator at this base (TerminalSurfaceRuntimeTeardownCoordinator.swift:163-229) frees on a Task.detached(priority: .utility) worker and releases userdata on the main actor only after the free returns. Prior release-ordering property is preserved.
  • Both production callers (Sources/Workspace.swift:8316 respawn, Sources/Panels/TerminalPanel.swift:674 close) have no dependency on the free landing on the next main-actor turn; the old surface is already unregistered before the call.
  • Remaining direct ghostty_surface_free calls in TerminalSurface+Debug.swift are ...ForTesting methods, not production.
  • Upstream's second commit (two-slot bounded close pool + lane rename + one comment in GhosttyTerminalView.swift) is deliberately not backported; PR body names this as a non-goal. The captured deadlock cycle (main joins IO → IO joins reader → reader waits on main mailbox) is broken by moving off main alone, so this is a scope choice, not an absence.

Tests:

  • teardownSurfaceKeepsMainActorResponsiveWhileNativeFreeIsBlocked exercises the real production path (no runtimeSurfaceFreeOverrideForTesting, which pre-fix already went through the coordinator and therefore could not catch the bug). Pre-fix: the gated stub blocks the main actor, the probe Task { @MainActor } cannot run until the 5s gate timeout expires, at which point blocking_is_active() is false → assertion fails. Post-fix: passes. The C gate is therefore necessary; an override-based test would not fail on the base.
  • Gate pointer 0x7541 is unique to this .serialized suite (other suites use 0x7540), so no cross-suite interception.
  • Ran swift test --package-path Packages/macOS/CmuxTerminal --filter 'TerminalSurfaceTeardownCallbackLifetimeTests|TerminalSurfaceRuntimeTeardownCoordinatorTests' at head: 16/16 passed.

Findings

SIMPLIFY

  • F1 · Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceTeardownCallbackLifetimeTests.swift:31 · surfaceFreeGateDoesNotInterceptAnotherRuntimeSurface asserts the C test stub's own pointer scoping (arm gate for 0x7541, free 0x7542, check did_start is false). It exercises fixture representation, not cmux behaviour, and can only fail if the stub itself regresses. Smallest fix: delete the test, and with it cmux_test_ghostty_surface_free_blocking_did_start in GhosttyRuntimeTestStubs.c:87 and .h:98, which then have no consumer.

No BLOCKER, no other IMPORTANT, no SCOPE, no DOCS findings.

RESULT=IMPORTANT n=1

@vadimzak vadimzak added cr-in-progress Code review in progress (temporary; auto-removed when the CR ends) and removed cr-in-progress Code review in progress (temporary; auto-removed when the CR ends) labels Sep 8, 2026

@vadimzak vadimzak left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review — disposition · head 3d7b7d4

claude-1767e186-786b-4d6d-a99f-6da2b444c7d8@vz-kooply-mac-3 · claude / claude-fable-5-1 · effort high

Disposition review — head 3d7b7d44cf (prior 00f2a1d1ce, base ddd4a01bc5)

Delta inspected: 00f2a1d1ce..3d7b7d44cf is one commit, 25 deletions, 0 insertions, across three test-only files. No production files changed since the prior head. Full PR delta vs base is unchanged in production: TerminalSurface+RuntimeLifecycle.swift 9+/8−.

F1 · HOLDS. The delta removes exactly what the finding named:

  • TerminalSurfaceTeardownCallbackLifetimeTests.swift:31-46 (prior numbering): surfaceFreeGateDoesNotInterceptAnotherRuntimeSurface deleted in full.
  • GhosttyRuntimeTestStubs.c:87-92: cmux_test_ghostty_surface_free_blocking_did_start implementation deleted.
  • include/GhosttyRuntimeTestStubs.h:97: matching declaration deleted.

Repo-wide grep for blocking_did_start and the test name returns nothing outside the ghostty submodule, so no dangling consumer. The remaining gate primitives (blocking_begin, wait_until_started, blocking_is_active, release, blocking_reset) are still used by teardownSurfaceKeepsMainActorResponsiveWhileNativeFreeIsBlocked, the test that carries the actual regression coverage.

Verification: ran the two teardown suites at head in Packages/macOS/CmuxTerminal:

✔ Test run with 15 tests in 2 suites passed after 0.058 seconds.

The regression test teardownSurfaceKeepsMainActorResponsiveWhileNativeFreeIsBlocked passes and is intact.

New hunks since prior head: deletion-only; no new logic, state, type, module, or surface introduced. No BLOCKER or IMPORTANT introduced. Not SCOPE-EXPANDED.

RESULT=FIXES-VERIFIED

@vadimzak
vadimzak merged commit 526952a into stable/0.64.22 Sep 8, 2026
1 check passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants