diff --git a/Cargo.toml b/Cargo.toml index 34f54f3..d770b15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" readme = "README.md" # Own version (decoupled from workspace.version): data-beans evolves its CLI / # QC API independently of the shared utility crates. -version = "0.10.1" +version = "0.10.2" rust-version = "1.91" # Ship only the sources, README and license: nothing else in the working tree # (data stores, saved name lists, scratch files) can end up on crates.io. @@ -97,7 +97,7 @@ ratatui = { version = "0.30", default-features = false, features = ["crossterm"] # Detachable sessions for the views; no pictures, so no graphics. legume-tui = { version = "0.3.2", optional = true, default-features = false, features = ["clap", "logs"] } # Its `tui` view (`data-beans mcp watch`) comes on with data-beans' `tui`. -legume-mcp = { version = "0.3", optional = true } +legume-mcp = { version = "0.4", optional = true } legume-plot = { version = "0.3.2", optional = true } base64 = { version = "0.22", optional = true } @@ -106,5 +106,5 @@ base64 = { version = "0.22", optional = true } # tests/detach_session.rs. legume-tui = { version = "0.3.2", default-features = false, features = ["testing"] } # tests/mcp_server.rs follows a server through the watch pane. -legume-mcp = { version = "0.3", features = ["tui"] } +legume-mcp = { version = "0.4", features = ["tui"] } ratatui = { version = "0.30", default-features = false, features = ["crossterm"] } diff --git a/src/mcp.rs b/src/mcp.rs index eb08b43..7dea194 100644 --- a/src/mcp.rs +++ b/src/mcp.rs @@ -489,7 +489,7 @@ impl Arguments { /// `out`, or, when the agent names none, `{name}{ext}` in today's /// results folder, `-2`, `-3`… added to `name` (before `ext`) while - /// `taken` says a name is in use. + /// `taken` says a name is in use, or a running job writes it. fn out_or( &self, cx: &Call, @@ -508,7 +508,7 @@ impl Arguments { k => dir.join(format!("{name}-{k}{ext}")), }) .map(|p| p.to_string_lossy().into_owned()) - .find(|p| !taken(p)) + .find(|p| !held(cx, &[p]) && !taken(p)) .context("no free name in the results folder") } @@ -518,7 +518,8 @@ impl Arguments { self.out_or(cx, (name, ""), |c| { backend_target(c).map_or(true, |t| { let stem = strip_backend_suffix(&t.effective); - being_written(stem).is_some() + held(cx, &[&t.effective, &t.found]) + || being_written(stem).is_some() || outputs_beside(stem).map_or(true, |v| !v.is_empty()) }) }) @@ -664,6 +665,12 @@ fn fresh(path: &str) -> Result<()> { /// a command has made its outputs, only this says they are spoken for. static WRITING: Mutex> = Mutex::new(Vec::new()); +/// Whether a running job writes one of `paths`, however spelled. +fn held(cx: &Call, paths: &[&str]) -> bool { + let paths: Vec = paths.iter().map(PathBuf::from).collect(); + cx.ensure_free(&paths).is_err() +} + /// Whether two stems name some file alike: one is the other, or a file /// named after it. fn overlap(a: &str, b: &str) -> bool {