Exit early when session initialization fails (#11908)

Summary
- wait for the initial session startup loop to finish and handle exit
before waiting for the first message in fresh sessions
- propagate AppRunControl::Exit to return immediately when
initialization fails
This commit is contained in:
jif-oai
2026-02-17 11:22:30 +00:00
committed by GitHub
parent fcf16e97a6
commit 5ae84197b2

View File

@@ -978,6 +978,27 @@ impl App {
self.refresh_status_line();
}
fn should_wait_for_initial_session(session_selection: &SessionSelection) -> bool {
matches!(
session_selection,
SessionSelection::StartFresh | SessionSelection::Exit
)
}
fn should_handle_active_thread_events(
waiting_for_initial_session_configured: bool,
has_active_thread_receiver: bool,
) -> bool {
has_active_thread_receiver && !waiting_for_initial_session_configured
}
fn should_stop_waiting_for_initial_session(
waiting_for_initial_session_configured: bool,
primary_thread_id: Option<ThreadId>,
) -> bool {
waiting_for_initial_session_configured && primary_thread_id.is_some()
}
#[allow(clippy::too_many_arguments)]
pub async fn run(
tui: &mut tui::Tui,
@@ -1068,6 +1089,8 @@ impl App {
let status_line_invalid_items_warned = Arc::new(AtomicBool::new(false));
let enhanced_keys_supported = tui.enhanced_keys_supported();
let wait_for_initial_session_configured =
Self::should_wait_for_initial_session(&session_selection);
let mut chat_widget = match session_selection {
SessionSelection::StartFresh | SessionSelection::Exit => {
let init = crate::chatwidget::ChatWidgetInit {
@@ -1252,6 +1275,7 @@ impl App {
let mut thread_created_rx = thread_manager.subscribe_thread_created();
let mut listen_for_threads = true;
let mut waiting_for_initial_session_configured = wait_for_initial_session_configured;
let exit_reason = loop {
let control = select! {
@@ -1264,7 +1288,10 @@ impl App {
} else {
None
}
}, if app.active_thread_rx.is_some() => {
}, if App::should_handle_active_thread_events(
waiting_for_initial_session_configured,
app.active_thread_rx.is_some()
) => {
if let Some(event) = active {
app.handle_active_thread_event(tui, event).await?;
} else {
@@ -1291,6 +1318,12 @@ impl App {
AppRunControl::Continue
}
};
if App::should_stop_waiting_for_initial_session(
waiting_for_initial_session_configured,
app.primary_thread_id,
) {
waiting_for_initial_session_configured = false;
}
match control {
AppRunControl::Continue => {}
AppRunControl::Exit(reason) => break reason,
@@ -2915,6 +2948,76 @@ mod tests {
Ok(())
}
#[test]
fn startup_waiting_gate_is_only_for_fresh_or_exit_session_selection() {
assert_eq!(
App::should_wait_for_initial_session(&SessionSelection::StartFresh),
true
);
assert_eq!(
App::should_wait_for_initial_session(&SessionSelection::Exit),
true
);
assert_eq!(
App::should_wait_for_initial_session(&SessionSelection::Resume(PathBuf::from(
"/tmp/restore"
))),
false
);
assert_eq!(
App::should_wait_for_initial_session(&SessionSelection::Fork(PathBuf::from(
"/tmp/fork"
))),
false
);
}
#[test]
fn startup_waiting_gate_holds_active_thread_events_until_primary_thread_configured() {
let mut wait_for_initial_session =
App::should_wait_for_initial_session(&SessionSelection::StartFresh);
assert_eq!(wait_for_initial_session, true);
assert_eq!(
App::should_handle_active_thread_events(wait_for_initial_session, true),
false
);
assert_eq!(
App::should_stop_waiting_for_initial_session(wait_for_initial_session, None),
false
);
if App::should_stop_waiting_for_initial_session(
wait_for_initial_session,
Some(ThreadId::new()),
) {
wait_for_initial_session = false;
}
assert_eq!(wait_for_initial_session, false);
assert_eq!(
App::should_handle_active_thread_events(wait_for_initial_session, true),
true
);
}
#[test]
fn startup_waiting_gate_not_applied_for_resume_or_fork_session_selection() {
let wait_for_resume = App::should_wait_for_initial_session(&SessionSelection::Resume(
PathBuf::from("/tmp/restore"),
));
assert_eq!(
App::should_handle_active_thread_events(wait_for_resume, true),
true
);
let wait_for_fork = App::should_wait_for_initial_session(&SessionSelection::Fork(
PathBuf::from("/tmp/fork"),
));
assert_eq!(
App::should_handle_active_thread_events(wait_for_fork, true),
true
);
}
#[tokio::test]
async fn enqueue_thread_event_does_not_block_when_channel_full() -> Result<()> {
let mut app = make_test_app().await;