diff --git a/crates/openshell-tui/src/app.rs b/crates/openshell-tui/src/app.rs index 4b218a37cf..b7ad3e436f 100644 --- a/crates/openshell-tui/src/app.rs +++ b/crates/openshell-tui/src/app.rs @@ -1216,11 +1216,16 @@ impl App { pub fn cycle_workspace(&mut self) { if self.all_workspaces { - self.all_workspaces = false; - self.current_workspace = "default".to_string(); + // Leaving "all" scope: return to the first known named workspace. + // The workspace list is server-provided and membership-scoped, so + // it is not guaranteed to contain (or start with) "default"; only + // enter named scope when at least one workspace is known. + if let Some(first) = self.workspace_names.first() { + self.all_workspaces = false; + self.current_workspace = first.clone(); + } } else if self.workspace_names.is_empty() { self.all_workspaces = true; - self.current_workspace = "default".to_string(); } else { let current_idx = self .workspace_names @@ -1232,7 +1237,6 @@ impl App { } _ => { self.all_workspaces = true; - self.current_workspace = "default".to_string(); } } } @@ -4112,6 +4116,36 @@ mod tests { assert_eq!(app.current_workspace, "default"); } + #[tokio::test] + async fn cycle_workspace_without_default_stays_on_real_workspaces() { + // The workspace list is server-provided and membership-scoped, so a + // non-admin principal may have no "default" workspace visible. + let mut app = test_app(); + app.workspace_names = vec!["team-a".to_string(), "team-b".to_string()]; + app.current_workspace = "team-a".to_string(); + app.all_workspaces = false; + + // Collect every named-scope selection across several full cycles. Each + // named scope must be a workspace that actually exists in the list; + // the cursor must never land on a phantom "default". + let mut named_scopes = Vec::new(); + for _ in 0..8 { + if !app.all_workspaces { + assert!( + app.workspace_names.contains(&app.current_workspace), + "cycled to '{}', which is not a known workspace", + app.current_workspace + ); + named_scopes.push(app.current_workspace.clone()); + } + app.cycle_workspace(); + } + + // Both real workspaces must be reachable by repeated cycling. + assert!(named_scopes.iter().any(|w| w == "team-a")); + assert!(named_scopes.iter().any(|w| w == "team-b")); + } + // -- selected_sandbox_workspace ---------------------------------------- #[test]