fix(cli): say where a workspace id nobody typed came from

`tty7 tab new` in a shell whose workspace has been removed answered:

    no workspace with id 65b90fd4-421f-4cfb-9848-d263c9a80959 on this machine

Nothing in what was typed contains a uuid. `workspace_or_context` falls
back to `$TTY7_WS`, so the id came from the shell — and bare like that it
reads as an internal error rather than something to act on. Every sibling
message in this file carries a hint; this one had none.

Found by running the CLI against a daemon that was not the one my shell
belonged to, which is the same shape as the two cases the comment above
`run --keep` already names: a workspace since removed, or a shell opened
against another machine. That insight was in the source and never reached
the reader.

The id stays whole rather than shortened the way an ambiguity is — it is
the exact string to compare against `echo $TTY7_WS`.
This commit is contained in:
l0ng-ai
2026-08-16 10:42:03 +08:00
parent 66269f7eb2
commit 91c2cac80d
+45 -1
View File
@@ -60,13 +60,33 @@ fn nothing_named(text: &str) -> String {
}
}
/// What to say when an id resolves to nothing.
///
/// Its sibling [`nothing_named`] answers a word someone typed. This one often
/// answers a word nobody typed: [`crate::address::workspace_or_context`] falls
/// back to `$TTY7_WS`, so `tab new` inside a shell whose workspace has since
/// been removed — or a shell opened against another machine — fails with a
/// uuid that appears nowhere in what was typed. Left bare, that reads as an
/// internal error rather than something the reader can act on, which is why
/// the second sentence is here at all.
///
/// The id stays whole rather than shortened the way an ambiguity is: this is
/// the exact string to compare against `echo $TTY7_WS`.
fn no_workspace_with_id(id: &WorkspaceId) -> String {
format!(
"no workspace with id {id} on this machine — `tty7 ls` lists them. If you did not type \
this id, it is the $TTY7_WS of the shell you are in, whose workspace has been removed \
or belongs to another machine"
)
}
pub fn workspace<'m>(machine: &'m Machine, addr: &WorkspaceAddress) -> Result<&'m Workspace> {
match addr {
WorkspaceAddress::Id(id) => machine
.workspaces
.iter()
.find(|ws| ws.id == *id)
.ok_or_else(|| anyhow::anyhow!("no workspace with id {id} on this machine")),
.ok_or_else(|| anyhow::anyhow!("{}", no_workspace_with_id(id))),
WorkspaceAddress::Named(text) => {
let by_name: Vec<_> = machine
.workspaces
@@ -171,6 +191,30 @@ mod tests {
use crate::address::parse_workspace;
use crate::testbed::two_workspace_machine;
/// The reader is holding a uuid they did not type; the message has to say
/// where it came from, not just that it failed.
#[test]
fn an_id_that_resolves_to_nothing_says_where_it_came_from() {
let m = two_workspace_machine();
let missing: WorkspaceId = "0d4e1a54-0000-4000-8000-00000000dead".parse().unwrap();
let err = workspace(&m, &WorkspaceAddress::Id(missing))
.expect_err("no workspace has that id")
.to_string();
assert!(
err.contains(&missing.to_string()),
"whole, to compare: {err}"
);
assert!(
err.contains("`tty7 ls`"),
"a way to see the real ones: {err}"
);
assert!(
err.contains("$TTY7_WS"),
"where an untyped id comes from: {err}"
);
}
#[test]
fn tab_ordinals_number_the_machine_in_tree_order() {
let m = two_workspace_machine();