From ffaf110957c3061cd8c7f8bd91dd11818ef3dec9 Mon Sep 17 00:00:00 2001 From: Hunter B Date: Sat, 6 Jun 2026 10:34:49 -0700 Subject: [PATCH] fix(tui): advance tab restore counters --- crates/tui/src/tui/tab/delegator.rs | 35 +++++++++++++++ crates/tui/src/tui/tab/group.rs | 33 ++++++++++++++ crates/tui/src/tui/tab/manager.rs | 67 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/crates/tui/src/tui/tab/delegator.rs b/crates/tui/src/tui/tab/delegator.rs index 806a55a3..9b88557b 100644 --- a/crates/tui/src/tui/tab/delegator.rs +++ b/crates/tui/src/tui/tab/delegator.rs @@ -364,6 +364,17 @@ impl TaskDelegator { self.next_id += 1; format!("delegation_{}", id) } + + pub(crate) fn advance_next_id_past_existing_tasks(&mut self) { + let max_seen = self + .pending_tasks + .iter() + .filter_map(|task| task.task_id.strip_prefix("delegation_")) + .filter_map(|suffix| suffix.parse::().ok()) + .max() + .unwrap_or(0); + self.next_id = self.next_id.max(max_seen + 1); + } } impl Default for TaskDelegator { @@ -539,4 +550,28 @@ mod tests { delegator.prune_results(10); assert_eq!(delegator.results_for_tab(to).len(), 3); } + + #[test] + fn test_advance_next_id_after_restore() { + let mut delegator = TaskDelegator::new(); + delegator.pending_tasks.push(DelegationTask::new( + "delegation_42".to_string(), + TabId::new(1), + TabId::new(2), + "restored".to_string(), + Priority::Normal, + )); + + delegator.advance_next_id_past_existing_tasks(); + let new_id = delegator + .create_delegation( + TabId::new(1), + TabId::new(2), + "fresh".to_string(), + Priority::Normal, + ) + .unwrap(); + + assert_eq!(new_id, "delegation_43"); + } } diff --git a/crates/tui/src/tui/tab/group.rs b/crates/tui/src/tui/tab/group.rs index 715214d8..14095dce 100644 --- a/crates/tui/src/tui/tab/group.rs +++ b/crates/tui/src/tui/tab/group.rs @@ -239,6 +239,17 @@ impl TabGroupManager { self.next_id += 1; format!("group_{}", id) } + + pub(crate) fn advance_next_id_past_existing_groups(&mut self) { + let max_seen = self + .groups + .keys() + .filter_map(|id| id.strip_prefix("group_")) + .filter_map(|suffix| suffix.parse::().ok()) + .max() + .unwrap_or(0); + self.next_id = self.next_id.max(max_seen + 1); + } } impl Default for TabGroupManager { @@ -347,4 +358,26 @@ mod tests { assert!(mgr.group_of(tab1).is_none()); assert!(mgr.tab_to_group.get(&tab1).is_none()); } + + #[test] + fn test_advance_next_id_after_restore() { + let mut mgr = TabGroupManager::new(); + mgr.groups.insert( + "group_7".to_string(), + TabGroup { + id: "group_7".to_string(), + name: "Restored".to_string(), + color: GroupColor::Blue, + tab_ids: Vec::new(), + created_at: Utc::now(), + }, + ); + + mgr.advance_next_id_past_existing_groups(); + let new_id = mgr.create_group("Fresh".to_string(), GroupColor::Green); + + assert_eq!(new_id, "group_8"); + assert!(mgr.groups.contains_key("group_7")); + assert!(mgr.groups.contains_key("group_8")); + } } diff --git a/crates/tui/src/tui/tab/manager.rs b/crates/tui/src/tui/tab/manager.rs index 61f280bd..7bf7fb2f 100644 --- a/crates/tui/src/tui/tab/manager.rs +++ b/crates/tui/src/tui/tab/manager.rs @@ -272,6 +272,7 @@ impl TabManager { }; self.delegator.pending_tasks.push(task); } + self.delegator.advance_next_id_past_existing_tasks(); // Restore groups AFTER tabs so tab_ids can reference real tabs for g in &state.groups { @@ -287,6 +288,7 @@ impl TabManager { self.groups.tab_to_group.insert(*tab_id, g.id.clone()); } } + self.groups.advance_next_id_past_existing_groups(); if let Some(idx) = state.active_tab_index { if idx < self.tabs.len() { @@ -997,4 +999,69 @@ mod tests { assert_eq!(restored_tasks[0].status, DelegationStatus::InProgress); assert_eq!(restored_tasks[0].task_id, task_id); } + + #[test] + fn test_restore_advances_delegation_ids() { + use super::super::Priority; + + let mut manager = TabManager::new(); + let from = manager + .create_tab("Source".to_string(), TabType::Chat) + .unwrap(); + let to = manager + .create_tab("Target".to_string(), TabType::Chat) + .unwrap(); + + let restored_id = manager + .delegate_task(from, to, "restored".to_string(), Priority::Normal) + .unwrap(); + assert_eq!(restored_id, "delegation_1"); + + let snapshot = manager.snapshot(); + let mut restored = TabManager::new(); + restored.restore_from_snapshot(&snapshot); + + let fresh_id = restored + .delegate_task(from, to, "fresh".to_string(), Priority::Normal) + .unwrap(); + assert_eq!(fresh_id, "delegation_2"); + } + + #[test] + fn test_restore_advances_group_ids() { + use super::super::group::GroupColor; + + let mut manager = TabManager::new(); + let tab = manager + .create_tab("Grouped".to_string(), TabType::Chat) + .unwrap(); + let restored_group = manager.create_group("Restored".to_string(), GroupColor::Blue); + assert_eq!(restored_group, "group_1"); + assert!(manager.assign_tab_to_group(tab, &restored_group)); + + let snapshot = manager.snapshot(); + let mut restored = TabManager::new(); + restored.restore_from_snapshot(&snapshot); + + let fresh_group = restored.create_group("Fresh".to_string(), GroupColor::Green); + assert_eq!(fresh_group, "group_2"); + assert!( + restored + .groups() + .all_groups() + .iter() + .any(|g| g.id == "group_1") + ); + assert!( + restored + .groups() + .all_groups() + .iter() + .any(|g| g.id == "group_2") + ); + assert_eq!( + restored.tab_group(tab).map(|g| g.id.as_str()), + Some("group_1") + ); + } }