diff --git a/crates/maestro-daemon/src/session.rs b/crates/maestro-daemon/src/session.rs index 00e9bfb..bfd827f 100644 --- a/crates/maestro-daemon/src/session.rs +++ b/crates/maestro-daemon/src/session.rs @@ -374,7 +374,7 @@ impl SessionManager { .map(wait_for_event_pump); futures_util::future::join_all(drains).await; for (id, _, _) in children { - mark_session_dead(&self.inner, id).await; + mark_session_dead_after_shutdown(&self.inner, id).await; } } @@ -1656,7 +1656,7 @@ async fn run_blocked_work_pass(inner: &Arc) { } } -/// Mark a session whose tmux session has disappeared as [`SessionStatus::Dead`] +/// Mark a session whose backing process has disappeared as [`SessionStatus::Dead`] /// and settle its worktree per the natural-exit rule (#82). /// /// The record, its [`PtyBridge`] and its sidecar all survive, so a client can @@ -1668,9 +1668,24 @@ async fn run_blocked_work_pass(inner: &Arc) { /// alone and stays attached to the dead record for a later forced dismissal /// (#81), which leaves it on disk too. /// -/// Dead sessions do not survive a daemon restart: [`SessionManager::recover`] -/// adopts only what tmux still holds. +/// Terminal sessions that die naturally do not survive a daemon restart: +/// [`SessionManager::recover`] adopts only what tmux still holds. Structured +/// sessions stopped for daemon shutdown retain their sidecars for ACP recovery. async fn mark_session_dead(inner: &Arc, id: Uuid) { + mark_session_dead_with_worktree_settlement(inner, id, true).await; +} + +/// A daemon restart terminates ACP children, but it is not a natural adapter +/// exit: the sidecar is the recovery inventory, including its worktree. +async fn mark_session_dead_after_shutdown(inner: &Arc, id: Uuid) { + mark_session_dead_with_worktree_settlement(inner, id, false).await; +} + +async fn mark_session_dead_with_worktree_settlement( + inner: &Arc, + id: Uuid, + settle_worktree: bool, +) { let flipped = { let mut sessions = inner.sessions.lock().await; match sessions.get_mut(&id) { @@ -1694,17 +1709,19 @@ async fn mark_session_dead(inner: &Arc, id: Uuid) { }; tracing::info!(%id, "session process gone — marking dead"); - if let Some(wt) = &worktree { - if settle_worktree_on_exit(inner, id, wt).await { - // The tree is settled (removed); a stale "in the way" would - // describe something the session no longer holds. - session.blocked_work = None; - let mut sessions = inner.sessions.lock().await; - if let Some(record) = sessions.get_mut(&id) { - record.session.worktree = None; - record.session.blocked_work = None; - record.worktree_path = None; - record.worktree_base_repo = None; + if settle_worktree { + if let Some(wt) = &worktree { + if settle_worktree_on_exit(inner, id, wt).await { + // The tree is settled (removed); a stale "in the way" would + // describe something the session no longer holds. + session.blocked_work = None; + let mut sessions = inner.sessions.lock().await; + if let Some(record) = sessions.get_mut(&id) { + record.session.worktree = None; + record.session.blocked_work = None; + record.worktree_path = None; + record.worktree_base_repo = None; + } } } }