From 8ef7735c0c54716728bea4afd99216765408ef47 Mon Sep 17 00:00:00 2001 From: asto Date: Mon, 3 Aug 2026 01:32:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(fork):=20=E4=BF=AE=E6=AD=A3=20fim=5Fedit=20?= =?UTF-8?q?=E4=B8=8E=20update=5Fplan=20=E7=9A=84=20capability=20=E6=A0=87?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fim_edit 实际原子写回目标文件(execute 步骤 7 write_atomic), 删除自相矛盾的 ReadOnly 声明;WritesFiles 保留,repo_law 写门 控(repo_law.rs WRITE_TOOLS)依赖其写工具身份,已有注释佐证 - update_plan 只写内存会话计划状态(锁 plan_state 更新快照), 不写文件系统,删除 WritesFiles 声明;ToolCapability 文档语义 明确为 "Tool writes to the filesystem" - 各补一条 capability 回归断言 生产面影响分析:两工具的 approval_requirement 均为显式覆盖 (Suggest/Auto),不走 capabilities 默认推导;is_read_only 前后 取值不变;repo_law 门控按工具名不按 capability;command_palette 标签朝正确方向修正。todo/checklist 系列同为内存状态却声明 WritesFiles,属同类问题,留待后续单独决策。 Signed-off-by: asto --- crates/tui/src/tools/fim.rs | 25 ++++++++++++++++++++++++- crates/tui/src/tools/plan.rs | 20 ++++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/crates/tui/src/tools/fim.rs b/crates/tui/src/tools/fim.rs index 0510a2c3b5..f59dc53ba1 100644 --- a/crates/tui/src/tools/fim.rs +++ b/crates/tui/src/tools/fim.rs @@ -94,8 +94,10 @@ impl ToolSpec for FimEditTool { } fn capabilities(&self) -> Vec { + // Writes the target file atomically (see execute step 7), so ReadOnly + // would be false; repo_law::WRITE_TOOLS also relies on this tool being + // recognized as write-capable. vec![ - ToolCapability::ReadOnly, ToolCapability::WritesFiles, ToolCapability::RequiresApproval, ] @@ -180,3 +182,24 @@ impl ToolSpec for FimEditTool { ToolResult::json(&result).map_err(|e| ToolError::execution_failed(e.to_string())) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fim_edit_declares_write_capability_without_read_only() { + let tool = FimEditTool::new(None, "fim-model".to_string()); + let capabilities = tool.capabilities(); + + assert!( + capabilities.contains(&ToolCapability::WritesFiles), + "fim_edit writes the target file atomically" + ); + assert!( + !capabilities.contains(&ToolCapability::ReadOnly), + "a file-writing tool must not claim ReadOnly" + ); + assert!(!tool.is_read_only()); + } +} diff --git a/crates/tui/src/tools/plan.rs b/crates/tui/src/tools/plan.rs index b1f1ca12f0..68e1ce3c13 100644 --- a/crates/tui/src/tools/plan.rs +++ b/crates/tui/src/tools/plan.rs @@ -570,7 +570,8 @@ impl ToolSpec for UpdatePlanTool { } fn capabilities(&self) -> Vec { - vec![ToolCapability::WritesFiles] + // Mutates only in-memory session plan state; no filesystem writes. + vec![] } fn approval_requirement(&self) -> ApprovalRequirement { @@ -664,9 +665,24 @@ fn string_vec_field(input: &serde_json::Value, field: &str) -> Vec { #[cfg(test)] mod tests { use super::*; - use crate::tools::spec::{ToolContext, ToolSpec}; + use crate::tools::spec::{ToolCapability, ToolContext, ToolSpec}; use serde_json::json; + #[test] + fn update_plan_declares_no_filesystem_write_capability() { + let tool = UpdatePlanTool::new(new_shared_plan_state()); + let capabilities = tool.capabilities(); + + assert!( + !capabilities.contains(&ToolCapability::WritesFiles), + "update_plan mutates only in-memory session plan state" + ); + assert!( + !tool.is_read_only(), + "update_plan still mutates session state" + ); + } + #[test] fn update_plan_description_keeps_work_update_as_primary_progress() { let tool = UpdatePlanTool::new(new_shared_plan_state());