aboutsummaryrefslogtreecommitdiff
path: root/src/persist
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 05:30:20 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 05:30:20 +0300
commit226e87306deaba017d36583f51ae34e42b303aad (patch)
tree2a1155689b75007c15d93735aedfa58db7a05edf /src/persist
parent17544c36ee397233e2f7b3319d86e3b517e3b5d0 (diff)
fix: preserve tab identity across restored sessions
Diffstat (limited to 'src/persist')
-rw-r--r--src/persist/restore.rs117
1 files changed, 112 insertions, 5 deletions
diff --git a/src/persist/restore.rs b/src/persist/restore.rs
index 229d565d..7c046698 100644
--- a/src/persist/restore.rs
+++ b/src/persist/restore.rs
@@ -509,11 +509,7 @@ fn restore_tab(
.map(|pane_id| {
PaneLaunchEnv::from_extra(Vec::new()).with_identity(
workspace_id.to_string(),
- format!(
- "{}:t{}",
- workspace_id,
- crate::workspace::encode_public_number(number)
- ),
+ crate::workspace::public_tab_id_for_number(workspace_id, number),
pane_id.to_string(),
)
})
@@ -1312,6 +1308,117 @@ mod tests {
assert_eq!(workspace.next_public_tab_number, 6);
}
+ #[tokio::test]
+ async fn restore_with_gapped_public_tab_numbers_keeps_agent_panel_tab_indices() {
+ let cwd = std::env::current_dir().unwrap();
+ let pane_snap = |id: &str| {
+ (
+ id.parse::<u32>().unwrap(),
+ super::super::snapshot::PaneSnapshot {
+ cwd: cwd.clone(),
+ label: None,
+ agent_name: None,
+ agent_session: None,
+ launch_argv: None,
+ },
+ )
+ };
+ let final_pane = super::super::snapshot::PaneSnapshot {
+ cwd: cwd.clone(),
+ label: Some("planner".into()),
+ agent_name: Some("planner".into()),
+ agent_session: Some(super::super::snapshot::PaneAgentSessionSnapshot {
+ source: "herdr:codex".into(),
+ agent: "codex".into(),
+ kind: crate::agent_resume::AgentSessionRefKind::Id,
+ value: "codex-session".into(),
+ }),
+ launch_argv: None,
+ };
+ let snapshot = SessionSnapshot {
+ version: super::super::snapshot::SNAPSHOT_VERSION,
+ workspaces: vec![WorkspaceSnapshot {
+ id: Some("w1".into()),
+ custom_name: None,
+ identity_cwd: cwd.clone(),
+ worktree_space: None,
+ public_pane_numbers: HashMap::from([(10, 1), (11, 2), (12, 3), (13, 4)]),
+ next_public_pane_number: 5,
+ public_tab_numbers: vec![1, 3, 4, 5],
+ next_public_tab_number: 6,
+ tabs: vec![
+ TabSnapshot {
+ custom_name: None,
+ layout: LayoutSnapshot::Pane(10),
+ panes: HashMap::from([pane_snap("10")]),
+ zoomed: false,
+ focused: Some(10),
+ root_pane: Some(10),
+ },
+ TabSnapshot {
+ custom_name: None,
+ layout: LayoutSnapshot::Pane(11),
+ panes: HashMap::from([pane_snap("11")]),
+ zoomed: false,
+ focused: Some(11),
+ root_pane: Some(11),
+ },
+ TabSnapshot {
+ custom_name: None,
+ layout: LayoutSnapshot::Pane(12),
+ panes: HashMap::from([pane_snap("12")]),
+ zoomed: false,
+ focused: Some(12),
+ root_pane: Some(12),
+ },
+ TabSnapshot {
+ custom_name: None,
+ layout: LayoutSnapshot::Pane(13),
+ panes: HashMap::from([(13, final_pane)]),
+ zoomed: false,
+ focused: Some(13),
+ root_pane: Some(13),
+ },
+ ],
+ active_tab: 3,
+ }],
+ active: Some(0),
+ selected: 0,
+ agent_panel_scope: Default::default(),
+ sidebar_width: None,
+ sidebar_section_split: None,
+ collapsed_space_keys: Default::default(),
+ };
+ let (events, _event_rx) = mpsc::channel(4);
+
+ let (workspaces, terminals, _runtimes) = restore(
+ &snapshot,
+ None,
+ 24,
+ 80,
+ 0,
+ test_restore_shell(),
+ crate::config::ShellModeConfig::NonLogin,
+ false,
+ events,
+ Arc::new(Notify::new()),
+ Arc::new(AtomicBool::new(false)),
+ );
+
+ let workspace = workspaces.first().expect("workspace should restore");
+ let agent_pane = workspace.tabs[3].root_pane;
+ let detail = workspace
+ .pane_details(&terminals)
+ .into_iter()
+ .find(|detail| detail.pane_id == agent_pane)
+ .expect("restored agent pane should be listed");
+
+ assert_eq!(workspace.active_tab, 3);
+ assert_eq!(workspace.tabs[3].number, 5);
+ assert_eq!(detail.tab_idx, 3);
+ assert_eq!(detail.agent_label, "planner");
+ }
+
#[test]
fn legacy_restore_precomputes_missing_public_pane_numbers() {
let cwd = std::env::current_dir().unwrap();