Allow root turn suspension with live descendants (#54414)

## Why

Live descendants previously blocked suspension of an unfinished root turn, requiring callers to stop and unload them before recovering the root elsewhere.

## What changed

Allow `suspend_turn_and_shutdown` to suspend the root and initiate shutdown of all sessions sharing its local agent runtime, fencing new starts. Remove `SuspendTurnOutcome::HasLiveDescendants`.

Success confirms that the root has stopped and its history writer is flushed and closed; descendant cleanup completes separately. Callers must finish tree shutdown or fence and terminate the tree's persistence writers before transferring ownership. Interrupted descendants are not automatically recovered.

## Testing

Extend integration coverage to verify child shutdown, recovery under the original root turn ID without restarting children, and separate reporting of child writer failures.

GitOrigin-RevId: 5adc240ae6e34654209154ee9324621a2bd604d5
This commit is contained in:
Michael Zeng
2026-10-15 20:57:39 +00:00
committed by copyberry
parent 97ebf2d94d
commit 341de32c33
4 changed files with 125 additions and 73 deletions
+7 -6
View File
@@ -521,14 +521,15 @@ impl CodexThread {
/// Stops the active unfinished root turn without recording TurnAborted or
/// TurnComplete, so another worker can recover its original turn ID.
///
/// Suspension is refused while a currently loaded descendant exists. Past
/// descendants do not prevent recovery, and concurrent descendant admission
/// is not sealed. Queued user input and outstanding approval, elicitation,
/// or server-request waiters remain best effort and may be discarded.
/// After stopping the root turn, fences new starts and initiates shutdown of all sessions
/// sharing its local agent runtime. Descendant turns are interrupted and are not automatically
/// recovered. Queued user input and outstanding approval, elicitation, or server-request
/// waiters remain best effort and may be discarded.
///
/// The session processes an accepted request even if its caller disconnects.
/// Callers must not transfer ownership until suspension succeeds, which
/// requires stopping execution, flushing history, and closing its writer.
/// Success confirms the root's execution has stopped, its history is flushed, and its writer
/// is closed. It does not wait for descendant cleanup. Before transferring ownership,
/// callers must also finish tree shutdown or fence and terminate the tree's persistence writers.
pub async fn suspend_turn_and_shutdown(&self) -> CodexResult<SuspendTurnOutcome> {
if self.session_source.is_non_root_agent() {
return Err(CodexErr::UnsupportedOperation(