fix: notice a stopped cmux-tui owner while connected (cx-jniyz.4)

Merged
TE
23d ago
+95-19

With the app connected to its local cmux-tui owner, an owner that stops answering (SIGSTOP, or wedged in its main loop) went unnoticed. In the cx-jniyz.1 dogfood, no not-connected banner appeared even after 200 s. A stopped process keeps its socket open, so the connection never sees EOF. The only liveness check, the identify heartbeat, ran only for bridged connections.

Change

  • BridgeHeartbeat becomes ConnectionHeartbeat, with the same behavior: identify every interval, and N misses in a row close the transport.
  • DaemonConnectionConfiguration gets heartbeat (15 s) and heartbeatMisses (2), plus an opt-in localHeartbeat (default false). Only the app's main local connection (DaemonService.localConfiguration) turns it on, so page relays and short-lived connections add no idle wakeups. The new heartbeat is listed in plans/cmux-next/idle-wakeups.md.
  • A stopped owner is now noticed in about 30โ€“45 s. The connection closes, the store goes .disconnected, and the existing not-connected banner shows. When the owner answers again, the reconnect loop or RetryWake reconnects.

There are no new visuals; the existing banner now appears when it should. None of the files #20765 touches change.

Verification

  • Red first, as a separate commit: CmuxNextDaemonTests.StoppedOwnerTests (live daemon). It SIGSTOPs the owner by its exact pid, expects .disconnected within 20 s, then SIGCONTs it and expects a reconnect within 30 s.
    • Red, commit 1a506fa6, fleet job 147f959c12022130358105aa: 346 suites passed; only StoppedOwnerTests failed.
    • Green, head fcbac8b2, job a1d0d0aeacb878da7bb1858a: aStoppedOwnerShowsDisconnectedAndReconnectsWhenItResumes() passed in 4.7 s. The step's exit 1 is the same-tree skip guard on two unrelated acpmux suites.
    • Between red and green, the test changed only in the field rename (bridgeHeartbeat* โ†’ heartbeat*) and the localHeartbeat = true line, both of which came with the fix.
  • Release compile on fcbac8b2, job 4a7ba555d3a49121483354bc: passed.
  • Local checks: check-app-size, check-concurrency and crash_ratchet pass, and check-no-godfiles adds nothing new. The conventions lint fails only on the base's BuiltInAdBlockerSetting.

Not verified in a real app yet. The capture to make: with the App Store open, kill -STOP <owner pid>; the not-connected banner should appear within about 45 s and clear after kill -CONT.

Changelog

Fixed: cmux NEXT shows cmux-tui as not connected when its owner stops responding while connected

๐Ÿค– Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes the app keeping cmux-tui shown as connected when its owner stops responding while connected. A stopped or wedged owner keeps its socket open, so the connection never sees EOF; the identify heartbeat only ran for bridged connections. The app's main local connection now runs that heartbeat (every 15 s, 2 unanswered asks close the transport), so a stopped owner shows not connected in about 30โ€“45 s and reconnects once it answers again.

  • BridgeHeartbeat is renamed ConnectionHeartbeat; the opt-in localHeartbeat limits it to the app's main connection, so page relays and short-lived connections add no idle wakeups.
  • New StoppedOwnerTests SIGSTOPs the owner by pid, expects the store to turn disconnected, then SIGCONTs and expects a reconnect.
  • The schemas/defaults/cmux-next.json regeneration (adding key.home.switchView) is a base-branch merge artifact, unrelated to this fix.