Suppress skipped MCP startup updates during review

This commit is contained in:
Charlie Marsh
2026-06-28 10:54:11 -04:00
parent 7807ffc65b
commit df4b4401fb
5 changed files with 41 additions and 2 deletions

View File

@@ -606,6 +606,8 @@ pub(crate) struct ChatWidget {
mcp_startup_pending_next_round: HashMap<String, McpStartupStatus>,
/// Tracks whether the buffered next round has seen any `Starting` update yet.
mcp_startup_pending_next_round_saw_starting: bool,
/// Ignore startup notifications while a review that skipped MCP startup is active.
mcp_startup_updates_suppressed_for_review: bool,
connectors: ConnectorsState,
ide_context: IdeContextState,
plugins_cache: PluginsCacheState,
@@ -1257,6 +1259,7 @@ impl ChatWidget {
self.flush_interrupt_queue();
self.flush_active_cell();
self.review.is_review_mode = false;
self.mcp_startup_updates_suppressed_for_review = false;
self.restore_pre_review_token_info();
self.add_to_history(history_cell::new_review_status_line(
"<< Code review finished >>".to_string(),

View File

@@ -163,6 +163,7 @@ impl ChatWidget {
mcp_startup_allow_terminal_only_next_round: false,
mcp_startup_pending_next_round: HashMap::new(),
mcp_startup_pending_next_round_saw_starting: false,
mcp_startup_updates_suppressed_for_review: false,
connectors: ConnectorsState::default(),
ide_context: IdeContextState::default(),
plugins_cache: PluginsCacheState::default(),

View File

@@ -38,6 +38,10 @@ impl ChatWidget {
status: McpStartupStatus,
complete_when_settled: bool,
) {
if self.mcp_startup_updates_suppressed_for_review {
return;
}
let mut activated_pending_round = false;
let startup_status = if self.mcp_startup_ignore_updates_until_next_start {
// Ignore-mode buffers the next plausible round so stale post-finish
@@ -221,6 +225,7 @@ impl ChatWidget {
self.mcp_startup_allow_terminal_only_next_round = false;
self.mcp_startup_pending_next_round.clear();
self.mcp_startup_pending_next_round_saw_starting = false;
self.mcp_startup_updates_suppressed_for_review = true;
if mcp_startup_owned_status {
self.set_status_header(String::from("Working"));
}

View File

@@ -100,6 +100,34 @@ async fn review_with_args_during_mcp_startup_skips_local_startup_round() {
assert!(chat.mcp_startup_status.is_none());
assert!(chat.bottom_pane.is_task_running());
assert_eq!(chat.status_state.current_status.header, "Working");
handle_entered_review_mode(&mut chat, "current changes");
handle_exited_review_mode(&mut chat);
notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting);
notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting);
assert!(chat.mcp_startup_status.is_some());
}
#[tokio::test]
async fn skipped_mcp_startup_updates_do_not_reopen_or_finish_review_task() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]);
notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting);
chat.dispatch_command_with_args(
SlashCommand::Review,
"check regressions".to_string(),
Vec::new(),
);
assert_matches!(op_rx.try_recv(), Ok(Op::Review { .. }));
notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting);
notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Ready);
notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready);
assert!(chat.mcp_startup_status.is_none());
assert!(chat.bottom_pane.is_task_running());
assert_eq!(chat.status_state.current_status.header, "Working");
}
#[tokio::test]

View File

@@ -12,10 +12,12 @@ impl ChatWidget {
/// Synchronize the bottom-pane "task running" indicator with the current lifecycles.
///
/// The bottom pane only has one running flag, but this module treats it as a derived state of
/// both the agent turn lifecycle and MCP startup lifecycle.
/// the agent turn, review, and MCP startup lifecycles.
pub(super) fn update_task_running_state(&mut self) {
self.bottom_pane.set_task_running(
self.turn_lifecycle.agent_turn_running || self.mcp_startup_status.is_some(),
self.turn_lifecycle.agent_turn_running
|| self.mcp_startup_status.is_some()
|| self.review.is_review_mode,
);
self.refresh_plan_mode_nudge();
self.refresh_status_surfaces();