fix(cmux-tui): a new owner never ends a shell the holder still runs (an adopting row re-attaches, a failed re-attach keeps the shell, only ended rows are closed) (QUIT P0)

Merged
LA
6d ago
+226-17

What

QUIT P0 (Lawrence 2026-11-01: "after quitting and reopening, i lose all the workspaces and terminals"). In holder mode (cmux-tui __holder, on every channel), a new owner's startup pass ended the person's shell in two cases:

  1. A terminal whose registry row was not exactly running (a previous owner that stopped mid-adoption: a version handoff at the app's reopen, a crash) was recorded ended (adopt_from_holder_or_end), and then
  2. release_unclaimed_holder_terminals sent CLOSE (2 s hang-up) to EVERY holder terminal no surface re-attached, a terminal with no registry row too. A failed re-attach ended the shell the same way.

Now:

  • A row left adopting re-attaches like a running one.
  • A re-attach that fails while the holder lists the terminal and its row is still running or adopting keeps the row and the shell (adopt_on_holder's failure paths now detach the shell instead of closing it), and the tab shows adopting (PendingTerminal::Adopting), not a live terminal that takes no input. The next start re-attaches it (the failed attach already took the holder's listed entry, so this process cannot retry it).
  • After the pass the holder ends only terminals whose row exited or was tombstoned (the shell exited, or the person closed the tab). A terminal with no registry row stays held, on purpose: a registry that comes back thin (the P0 itself) reads "no row" for every terminal with no read error, so "absent = ended" would close every shell in exactly the failure this fixes. A visible "Running in background" list (pty-holder.md section 5) is a follow-up.

Tests (red first, fleet cargo on macOS, scripts/ci/cmux-tui-rust-check.sh test <filter>)

  • holder_terminals a_restart_reattaches_a_holder_terminal_whose_row_was_left_adopting (2 holder cat terminals, SIGKILL the daemon, set one row adopting, restart: both shells kept by pid, screens kept, input works).
    • First branch fix/p0-quit-reopen-holder: red 3e9146edeef, step 6dfde043d7436ab48f80fb89 (aws-m4pro-2): FAILED at holder_terminals.rs:175, the terminal came back as "— session restored (previous process ended; it ran /bin/cat) —" with a NEW shell: the base ends the person's shell. Green 6c746e5cffd, step 7e88d4e215446ab8f43257dc (aws-m4pro-4): 1 test run, 1 passed.
  • holder_terminals a_restart_closes_only_holder_terminals_whose_row_ended (a tombstoned row's shell is closed; a terminal with no registry row keeps its shell).
  • This branch: red 48fac40093d step b54edea31a2b8a815cab67d5 (aws-m4pro-2): the adopting test FAILED at holder_terminals.rs:175 as on the first branch (a second test there failed at its own set-up, a foreign-key refusal of a row DELETE; that test was replaced). Head cc4b006e88e (after the review FIX): step 7d7f0cd255c5c7ad5dcd5579 (aws-m4pro-9, macOS, filter a_restart_): a_restart_reattaches_a_holder_terminal_whose_row_was_left_adopting PASS, a_restart_closes_a_holder_terminal_whose_row_was_tombstoned_and_keeps_the_rest PASS, all holder_terminals PASS; one acpmux test (durable_agents restart_reattach) failed once and passed on its rerun, not in this diff. Compile-check of cc4b006e88e: 1e94d277757362b79298f099 passed (CmuxNext, app, cmux-tui).
  • Clippy step 6298c55e7fe041d7e44d0356 stopped on a BASE red: acpmux/src/hub/processes.rs:256 redundant closure (feat base autofix 704abda602b), fixed on feat by #30009 (55361f16bf0). Clippy for this PR's crates: step 2ab060fade549d7c4c993c40 (aws-m4pro-6) on scratch ref ffe8515e4c3 (this head merged with feat, which has #30009; never merged): clippy -D warnings PASS (cmux-tui-rust-check summary (mode clippy): clippy pass).
  • Not covered by a test: the failed re-attach branch (no seam to inject an attach error without a test-only hook).

Risk

risk:high (lead 01:2xZ): this decides whether a person's running shell ends at a daemon start. Kept shells are user data. Needs an independent review receipt at the exact head and a green compile of the head.

Owners

PL-daemon holder (plans/cmux-next/pty-holder.md): told in STATUS. DAEMON SKEW owner (a619d54a5cbae4d7d) read the diff: no conflict with its host-mode design.

Changelog

  • Fixed: a reopen after an update or a daemon restart no longer ends terminals whose re-attach was interrupted or failed.

Observed / Not run

  • Observed: red and green of the first test above (fleet logs); prepush gate (no new violations); rustfmt --check.
  • Not run yet: a journey on a build with this fix.

Open follow-ups (DAEMON SKEW design review)

  • A Retry for a tab held as adopting (needs a fresh holder list); a UI and End path for no-row held shells; a seam to test the failed re-attach branch; a future holder ADOPT handoff must never end a held shell.