fix: restore workspace unsafe_code=forbid, fix TaskClaim TOCTOU, remove TUI deps

Review fixes for PR #3250:

1. Restore workspace-level unsafe_code = "forbid" (was downgraded to
   "warn" for TUI support). The per-crate [lints.rust] unsafe_code =
   "allow" on rusty-claude-cli already overrides it where needed.

2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write
   with OpenOptions::create_new(true) which atomically fails if the lock
   file already exists, eliminating the race window between the check and
   the write.

3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel,
   libc, unicode-width) and the /tui slash command spec — these belong in
   a future TUI PR and are unused in the current source.

Also updates the provider chain precedence test to reflect the new behavior
where the caller's resolved model is the chain primary.

Co-Authored-By: Claw <noreply@openclaw.ai>
This commit is contained in:
TheArchitectitandClaw authored and clawdbot committed 2026-10-13 00:12:35 +00:00
1 parent fe71ead55f
commit 08cce855b5
5 files changed
+653 -892

No files matched your search

+630 -856
View File
File diff suppressed because it is too large. Load diff
+1 -3
View File
@@ -12,9 +12,7 @@ publish = false
serde_json = "1"
[workspace.lints.rust]
# rusty-claude-cli needs unsafe for TUI stdout suppression (dup/dup2),
# all other crates should avoid it — override per-crate if needed.
unsafe_code = "warn"
unsafe_code = "forbid"
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
+1 -8
View File
@@ -65,13 +65,6 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[
argument_hint: None,
resume_supported: true,
},
SlashCommandSpec {
name: "tui",
aliases: &[],
summary: "Switch to the split-pane TUI dashboard",
argument_hint: None,
resume_supported: true,
},
SlashCommandSpec {
name: "status",
aliases: &[],
@@ -6045,7 +6038,7 @@ mod tests {
assert!(!help.contains("/login"));
assert!(!help.contains("/logout"));
assert!(help.contains("/setup"));
assert_eq!(slash_command_specs().len(), 141);
assert_eq!(slash_command_specs().len(), 140);
assert!(resume_supported_slash_commands().len() >= 39);
}
-6
View File
@@ -13,13 +13,9 @@ path = "src/main.rs"
api = { path = "../api" }
commands = { path = "../commands" }
crossterm = "0.28"
crossbeam-channel = "0.5"
gag = "1"
pulldown-cmark = "0.13"
once_cell = "1"
ratatui = "0.29"
rustyline = "15"
tui-textarea = "0.7"
runtime = { path = "../runtime" }
plugins = { path = "../plugins" }
serde = { version = "1", features = ["derive"] }
@@ -28,8 +24,6 @@ syntect = "5"
tokio = { version = "1", features = ["rt-multi-thread", "signal", "time"] }
tools = { path = "../tools" }
log = "0.4"
libc = "0.2"
unicode-width = "0.2"
[lints.rust]
+21 -19
View File
@@ -2826,9 +2826,6 @@ fn claim_task(task_id: &str, agent_id: &str, team_id: &str) -> Result<bool, Stri
let dir = claims_dir();
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
let lock_path = dir.join(format!("{task_id}.lock"));
if lock_path.exists() {
return Ok(false);
}
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
@@ -2839,20 +2836,22 @@ fn claim_task(task_id: &str, agent_id: &str, team_id: &str) -> Result<bool, Stri
"team_id": team_id,
"claimed_at": ts,
});
// Atomic claim: write to temp file then rename
let tmp_path = dir.join(format!("{task_id}.lock.tmp.{agent_id}"));
std::fs::write(
&tmp_path,
serde_json::to_string_pretty(&entry).map_err(|e| e.to_string())?,
)
.map_err(|e| e.to_string())?;
match std::fs::rename(&tmp_path, &lock_path) {
Ok(()) => Ok(true),
Err(_) => {
// Another agent claimed it first
let _ = std::fs::remove_file(&tmp_path);
Ok(false)
// Atomic claim: create_new fails atomically if the file already exists,
// eliminating the TOCTOU race between exists() and write().
let content = serde_json::to_string_pretty(&entry).map_err(|e| e.to_string())?;
match std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&lock_path)
{
Ok(mut file) => {
use std::io::Write;
file.write_all(content.as_bytes())
.map_err(|e| e.to_string())?;
Ok(true)
}
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(false),
Err(e) => Err(e.to_string()),
}
}
@@ -12490,9 +12489,12 @@ printf 'pwsh:%s' "$1"
.expect("chain with primary override should construct");
// then
assert_eq!(client.chain.len(), 2);
assert_eq!(client.chain[0].model, "grok-3");
assert_eq!(client.chain[1].model, "claude-sonnet-4-6");
// The caller's model is now the primary; fallbacks are appended
// as recovery entries, deduped.
assert_eq!(client.chain.len(), 3);
assert_eq!(client.chain[0].model, "claude-haiku-4-5-20251213");
assert_eq!(client.chain[1].model, "grok-3");
assert_eq!(client.chain[2].model, "claude-sonnet-4-6");
match original_anthropic {
Some(value) => std::env::set_var("ANTHROPIC_API_KEY", value),