The docs read as OpenAI's Codex CLI: a getting-started page titled for it, `codex --enable skills` and `code sandbox macos` for commands that do not exist, defaults given as gpt-5.1-codex-max/openai when they are zen5-coder/hanzo, a Homebrew page describing a formula we do not publish. Removed three tui-stream-chunking notes and the execpolicy page: they document modules and a subcommand that live only in the vendored upstream copy. There is no execpolicy subcommand and no .codexpolicy loader in code-rs; the page told users to run something the binary does not have. Rewrote tui-alternate-screen against the source. It described an auto/always/never enum and a --no-alt-screen flag; the shipped behavior is a tui.alternate_screen bool with Ctrl+T, which replays the transcript into scrollback on the way out — a better answer to Zellij than the one written down. Genuine openai-provider documentation stays.
3.9 KiB
Exit and shutdown flow (tui)
This document describes how exit, shutdown, and interruption work in the Rust TUI (code-rs/tui).
It is intended for anyone reasoning about future exit/shutdown changes.
This doc replaces earlier separate history and design notes. High-level history is summarized below; full details are captured in PR #8936.
Terms
- Exit: end the UI event loop and terminate the process.
- Shutdown: request a graceful agent/core shutdown (
Op::Shutdown) and wait forShutdownCompleteso cleanup can run. - Interrupt: cancel a running operation (
Op::Interrupt).
Event model (AppEvent)
Exit is coordinated via a single event with explicit modes:
AppEvent::Exit(ExitMode::ShutdownFirst)- Prefer this for user-initiated quits so cleanup runs.
AppEvent::Exit(ExitMode::Immediate)- Escape hatch for immediate exit. This bypasses shutdown and can drop in-flight work (e.g., tasks, rollout flush, child process cleanup).
App is the coordinator: it submits Op::Shutdown and it exits the UI loop only when
ExitMode::Immediate arrives (typically after ShutdownComplete).
User-triggered quit flows
Ctrl+C
Priority order in the UI layer:
- Active modal/view gets the first chance to consume (
BottomPane::on_ctrl_c).- If the modal handles it, the quit flow stops.
- When a modal/popup handles Ctrl+C, the quit shortcut is cleared so dismissing a modal cannot accidentally prime a subsequent Ctrl+C to quit.
- If the user has already armed Ctrl+C and the 1 second window has not expired, the second Ctrl+C triggers shutdown-first quit immediately.
- Otherwise,
ChatWidgetarms Ctrl+C and shows the quit hint (ctrl + c again to quit) for 1 second. - If cancellable work is active (streaming/tools/review),
ChatWidgetsubmitsOp::Interrupt.
Ctrl+D
- Only participates in quit when the composer is empty and no modal is active.
- On first press, show the quit hint (same as Ctrl+C) and start the 1 second timer.
- If pressed again while the hint is visible, request shutdown-first quit.
- With any modal/popup open, key events are routed to the view and Ctrl+D does not attempt to quit.
Slash commands
/quit,/exit,/logoutrequest shutdown-first quit without a prompt, because slash commands are harder to trigger accidentally and imply clear intent to quit.
/new
- Uses shutdown without exit (suppresses
ShutdownComplete) so the app can start a fresh session without terminating.
Shutdown completion and suppression
ShutdownComplete is the signal that core cleanup has finished. The UI treats it as the boundary
for exit:
ChatWidgetrequestsExit(Immediate)onShutdownComplete.Appcan suppress a singleShutdownCompletewhen shutdown is used as a cleanup step (e.g.,/new).
Edge cases and invariants
- Review mode counts as cancellable work. Ctrl+C should interrupt review, not quit.
- Modal open means Ctrl+C/Ctrl+D should not quit unless the modal explicitly declines to handle Ctrl+C.
- Immediate exit is not a normal user path; it is a fallback for shutdown completion or an emergency exit. Use it sparingly because it skips cleanup.
Testing expectations
At a minimum, we want coverage for:
- Ctrl+C while working interrupts, does not quit.
- Ctrl+C while idle and empty shows quit hint, then shutdown-first quit on second press.
- Ctrl+D with modal open does not quit.
/quit//exit//logoutquit without prompt, but still shutdown-first.- Ctrl+D while idle and empty shows quit hint, then shutdown-first quit on second press.
History (high level)
The TUI has historically mixed "exit immediately" and "shutdown-first" across quit gestures, largely due to incremental changes and regressions in state tracking. This doc reflects the current unified, shutdown-first approach. See PR #8936 for the detailed history and rationale.