From 159f509dd606851d6bfd53617680a0db598eea86 Mon Sep 17 00:00:00 2001 From: Hunter B Date: Wed, 3 Jun 2026 19:38:19 -0700 Subject: [PATCH] fix(tui): invalidate fanout card rows on sibling starts --- crates/tui/src/tui/subagent_routing.rs | 2 ++ crates/tui/src/tui/ui/tests.rs | 41 +++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/tui/src/tui/subagent_routing.rs b/crates/tui/src/tui/subagent_routing.rs index 318203b2..60168d1b 100644 --- a/crates/tui/src/tui/subagent_routing.rs +++ b/crates/tui/src/tui/subagent_routing.rs @@ -155,6 +155,7 @@ pub(super) fn handle_subagent_mailbox(app: &mut App, seq: u64, message: &Mailbox { card.claim_pending_worker(&agent_id, AgentLifecycle::Running); app.subagent_card_index.insert(agent_id, idx); + app.bump_history_cell(idx); } else { let mut card = FanoutCard::new( dispatch_kind.unwrap_or("rlm_eval").to_string(), @@ -165,6 +166,7 @@ pub(super) fn handle_subagent_mailbox(app: &mut App, seq: u64, message: &Mailbox let idx = app.history.len().saturating_sub(1); app.last_fanout_card_index = Some(idx); app.subagent_card_index.insert(agent_id, idx); + app.bump_history_cell(idx); } } else { let card = DelegateCard::new(agent_id.clone(), agent_type.clone()); diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 5c916fdd..38d3c650 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -18,7 +18,7 @@ use crate::tui::footer_ui::{ friendly_subagent_progress, render_footer_from, }; use crate::tui::history::{ - ExecCell, ExecSource, GenericToolCell, HistoryCell, ToolCell, ToolStatus, + ExecCell, ExecSource, GenericToolCell, HistoryCell, SubAgentCell, ToolCell, ToolStatus, }; use crate::tui::views::{ModalView, ViewAction}; use crate::working_set::Workspace; @@ -3254,6 +3254,45 @@ fn subagent_token_usage_is_deduped_by_mailbox_sequence() { assert!(app.session.subagent_cost > first); } +#[test] +fn fanout_started_sibling_bumps_existing_card_revision() { + let mut app = create_test_app(); + app.pending_subagent_dispatch = Some("rlm".to_string()); + + handle_subagent_mailbox( + &mut app, + 1, + &crate::tools::subagent::MailboxMessage::Started { + agent_id: "fanout-a".to_string(), + agent_type: "default".to_string(), + }, + ); + + let fanout_idx = app.last_fanout_card_index.expect("fanout card index"); + let initial_revision = app.history_revisions[fanout_idx]; + + handle_subagent_mailbox( + &mut app, + 2, + &crate::tools::subagent::MailboxMessage::Started { + agent_id: "fanout-b".to_string(), + agent_type: "default".to_string(), + }, + ); + + assert_eq!(app.history.len(), 1, "sibling should reuse fanout card"); + assert_ne!( + app.history_revisions[fanout_idx], initial_revision, + "reused fanout card must invalidate its cached transcript rows" + ); + match &app.history[fanout_idx] { + HistoryCell::SubAgent(SubAgentCell::Fanout(card)) => { + assert_eq!(card.worker_count(), 2); + } + cell => panic!("expected fanout card, got {cell:?}"), + } +} + #[test] fn format_token_count_compact_formats_units() { assert_eq!(format_token_count_compact(999), "999");