chore(tui): sweep verified-dead ToolRegistry API surface (#4785)
First dead-code sweep slice for #4785. All in crates/tui/src/tools/registry.rs; 181 net production lines removed (264 incl. orphaned inline tests), 16 #[allow(dead_code)] attributes gone (482 -> 466, budget locked in via --update). Deleted items (zero production callers; verified with rg across crates/** and confirmed by warning-free cargo check/clippy in this binary crate, where rustc flags unreachable pub items): - ToolRegistry::len / is_empty (test-only callers, in-file) - ToolRegistry::execute (test-only; live path is execute_full{,_with_context}) - ToolRegistry::filter_by_capability (only caller: its own test) - ToolRegistry::read_only_tools (only caller: its own test) - ToolRegistry::approval_required_tools (no callers anywhere) - ToolRegistry::approval_suggested_tools (no callers anywhere) - ToolRegistry::set_context / context_mut (no callers anywhere) - ToolRegistry::remove (test-only; live remove_tool also invalidates api_cache) - ToolRegistry::clear (test-only) - ToolRegistryBuilder::with_agent_tools (legacy allow_shell wrapper; comment admitted test-only) - ToolRegistryBuilder::with_full_agent_surface{,_policy} (legacy wrappers; only caller was each other) Attribute-only removals (items have live production callers): - ToolRegistry::names (engine.rs configure_plugin_tools x2) - struct McpToolAdapter (built by with_mcp_tools; all fields read) Orphaned test code deleted: test_registry_remove, test_registry_clear, test_registry_execute, test_registry_execute_unknown_tool, test_filter_by_capability, test_read_only_tools. Tests re-pointed at live APIs instead of deleted shims: remove_tool_invalidates_api_tools_cache (remove_tool), legacy_file_aliases_replay_through_canonical_dispatch (execute_full), three with_agent_tools_* tests rewritten against with_agent_tools_policy(ShellPolicy::None/Full). Gates (isolated CODEWHALE_HOME, CARGO_TARGET_DIR=.../v093-deadcode-sweep-a): - cargo fmt --all -- --check PASS - cargo test -p codewhale-tui --bin codewhale-tui --locked tools::registry 36 passed, 0 failed - ... tools:: 1391 passed, 0 failed - ... core::engine 435 passed, 0 failed (one flaky first-run failure, passed twice on rerun) - cargo clippy -p codewhale-tui --all-features --locked -- -D warnings (+ brief's allow list) PASS - python3 scripts/check-dead-code-budget.py PASS (466 at budget) - python3 scripts/check-source-structure-budget.py PASS (645410 <= 645603; was 645674, i.e. the +71 overage is offset)
This commit is contained in:
@@ -33,7 +33,7 @@ pub struct ToolRegistry {
|
||||
/// Memoised serialised tool catalog. Rebuilt lazily on first
|
||||
/// `to_api_tools` call after a mutation; pinned across reads so the
|
||||
/// description and schema bytes stay byte-stable for DeepSeek's KV
|
||||
/// prefix cache. Invalidated on `register` / `remove` / `clear`.
|
||||
/// prefix cache. Invalidated on `register` / `remove_tool`.
|
||||
api_cache: OnceLock<Vec<Tool>>,
|
||||
}
|
||||
|
||||
@@ -78,43 +78,16 @@ impl ToolRegistry {
|
||||
|
||||
/// Get all registered tool names.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn names(&self) -> Vec<&str> {
|
||||
self.tools.keys().map(std::string::String::as_str).collect()
|
||||
}
|
||||
|
||||
/// Get the number of registered tools.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.tools.len()
|
||||
}
|
||||
|
||||
/// Check if the registry is empty.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.tools.is_empty()
|
||||
}
|
||||
|
||||
/// Get all registered tools.
|
||||
#[must_use]
|
||||
pub fn all(&self) -> Vec<Arc<dyn ToolSpec>> {
|
||||
self.tools.values().cloned().collect()
|
||||
}
|
||||
|
||||
/// Execute a tool by name with the given input.
|
||||
#[allow(dead_code)]
|
||||
pub async fn execute(&self, name: &str, input: Value) -> Result<String, ToolError> {
|
||||
let tool = self
|
||||
.get(name)
|
||||
.ok_or_else(|| ToolError::not_available(format!("tool '{name}' is not registered")))?;
|
||||
|
||||
enforce_tool_authority(name, &input, tool.as_ref(), &self.context)?;
|
||||
let result = tool.execute(input, &self.context).await?;
|
||||
Ok(result.content)
|
||||
}
|
||||
|
||||
/// Execute a tool by name, returning the full `ToolResult`.
|
||||
pub async fn execute_full(&self, name: &str, input: Value) -> Result<ToolResult, ToolError> {
|
||||
let tool = self
|
||||
@@ -319,79 +292,6 @@ impl ToolRegistry {
|
||||
facts
|
||||
}
|
||||
|
||||
/// Filter tools by capability.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn filter_by_capability(&self, capability: ToolCapability) -> Vec<Arc<dyn ToolSpec>> {
|
||||
self.tools
|
||||
.values()
|
||||
.filter(|t| t.capabilities().contains(&capability))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get read-only tools.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn read_only_tools(&self) -> Vec<Arc<dyn ToolSpec>> {
|
||||
self.tools
|
||||
.values()
|
||||
.filter(|t| t.is_read_only())
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get tools that require approval.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn approval_required_tools(&self) -> Vec<Arc<dyn ToolSpec>> {
|
||||
self.tools
|
||||
.values()
|
||||
.filter(|t| t.approval_requirement() == ApprovalRequirement::Required)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get tools that suggest approval.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn approval_suggested_tools(&self) -> Vec<Arc<dyn ToolSpec>> {
|
||||
self.tools
|
||||
.values()
|
||||
.filter(|t| {
|
||||
matches!(
|
||||
t.approval_requirement(),
|
||||
ApprovalRequirement::Suggest | ApprovalRequirement::Required
|
||||
)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Update the context (e.g., when workspace changes).
|
||||
#[allow(dead_code)]
|
||||
pub fn set_context(&mut self, context: ToolContext) {
|
||||
self.context = context;
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the current context.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn context_mut(&mut self) -> &mut ToolContext {
|
||||
&mut self.context
|
||||
}
|
||||
|
||||
/// Remove a tool by name.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn remove(&mut self, name: &str) -> Option<Arc<dyn ToolSpec>> {
|
||||
let removed = self.tools.remove(name);
|
||||
if removed.is_some() {
|
||||
self.invalidate_api_cache();
|
||||
}
|
||||
removed
|
||||
}
|
||||
|
||||
/// Resolve a non-canonical tool name to a registered canonical name.
|
||||
///
|
||||
/// Runs a deterministic ladder against the registered tool names:
|
||||
@@ -448,13 +348,6 @@ impl ToolRegistry {
|
||||
None
|
||||
}
|
||||
|
||||
/// Clear all tools from the registry.
|
||||
#[allow(dead_code)]
|
||||
pub fn clear(&mut self) {
|
||||
self.tools.clear();
|
||||
self.invalidate_api_cache();
|
||||
}
|
||||
|
||||
/// Remove a tool from the registry by name. Returns `true` if the tool
|
||||
/// was present and removed, `false` if no tool with that name existed.
|
||||
pub fn remove_tool(&mut self, name: &str) -> bool {
|
||||
@@ -1237,21 +1130,6 @@ impl ToolRegistryBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Include all agent tools (file tools + shell + note + search).
|
||||
///
|
||||
/// Web and patch tools are NOT registered here — callers must add them
|
||||
/// via `.with_web_tools()` and `.with_patch_tools()` after checking
|
||||
/// feature flags (see `tool_setup.rs`). This prevents double-registration
|
||||
/// when `tool_setup.rs` conditionally registers them on top of
|
||||
/// `with_agent_tools`.
|
||||
#[must_use]
|
||||
#[allow(dead_code)] // legacy allow_shell convenience wrapper; used by tests, prod uses with_agent_tools_policy
|
||||
pub fn with_agent_tools(self, allow_shell: bool) -> Self {
|
||||
self.with_agent_tools_policy(crate::worker_profile::ShellPolicy::from_legacy_allow_shell(
|
||||
allow_shell,
|
||||
))
|
||||
}
|
||||
|
||||
/// Include all agent tools under a typed shell policy.
|
||||
#[must_use]
|
||||
pub fn with_agent_tools_policy(self, shell_policy: crate::worker_profile::ShellPolicy) -> Self {
|
||||
@@ -1330,40 +1208,6 @@ impl ToolRegistryBuilder {
|
||||
builder.with_notify_tool()
|
||||
}
|
||||
|
||||
/// Legacy convenience wrapper for the full child-inherited Agent surface.
|
||||
///
|
||||
/// New production callers should prefer [`Self::with_full_agent_surface_options`]
|
||||
/// so feature/config-gated families (web, patch, memory, vision, etc.)
|
||||
/// stay in parity with the parent Agent-mode registry.
|
||||
///
|
||||
/// `allow_shell` mirrors the session's shell permission. `manager` and
|
||||
/// `runtime` are the sub-agent runtime — children pass through their own
|
||||
/// runtime so grandchildren can spawn within the same depth/cancellation
|
||||
/// envelope.
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_full_agent_surface(
|
||||
self,
|
||||
client: Option<DeepSeekClient>,
|
||||
model: String,
|
||||
manager: super::subagent::SharedSubAgentManager,
|
||||
runtime: super::subagent::SubAgentRuntime,
|
||||
allow_shell: bool,
|
||||
todo_list: super::todo::SharedTodoList,
|
||||
plan_state: super::plan::SharedPlanState,
|
||||
) -> Self {
|
||||
self.with_full_agent_surface_policy(
|
||||
client,
|
||||
model,
|
||||
manager,
|
||||
runtime,
|
||||
crate::worker_profile::ShellPolicy::from_legacy_allow_shell(allow_shell),
|
||||
todo_list,
|
||||
plan_state,
|
||||
)
|
||||
}
|
||||
|
||||
/// Include the full child-inherited Agent surface under resolved
|
||||
/// feature/config options.
|
||||
#[must_use]
|
||||
@@ -1382,30 +1226,6 @@ impl ToolRegistryBuilder {
|
||||
.with_subagent_tools(manager, runtime)
|
||||
}
|
||||
|
||||
/// Legacy typed-shell wrapper for the full child-inherited Agent surface.
|
||||
///
|
||||
/// New production callers should pass resolved [`AgentToolSurfaceOptions`]
|
||||
/// to [`Self::with_full_agent_surface_options`].
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn with_full_agent_surface_policy(
|
||||
self,
|
||||
client: Option<DeepSeekClient>,
|
||||
model: String,
|
||||
manager: super::subagent::SharedSubAgentManager,
|
||||
runtime: super::subagent::SubAgentRuntime,
|
||||
shell_policy: crate::worker_profile::ShellPolicy,
|
||||
todo_list: super::todo::SharedTodoList,
|
||||
plan_state: super::plan::SharedPlanState,
|
||||
) -> Self {
|
||||
let mut options = AgentToolSurfaceOptions::new(shell_policy);
|
||||
options.speech_output_dir = runtime.speech_output_dir.clone();
|
||||
self.with_full_agent_surface_options(
|
||||
client, model, manager, runtime, options, todo_list, plan_state,
|
||||
)
|
||||
}
|
||||
|
||||
/// Include the todo / work-progress tools with a shared `TodoList`.
|
||||
///
|
||||
/// `work_update` is the sole model-visible progress surface (#4132).
|
||||
@@ -1504,7 +1324,6 @@ fn to_snake_case(s: &str) -> String {
|
||||
|
||||
/// Adapter that wraps an MCP tool definition so it can live in the
|
||||
/// unified `ToolRegistry` alongside native tools (§5.B).
|
||||
#[allow(dead_code)]
|
||||
struct McpToolAdapter {
|
||||
name: String,
|
||||
tool: crate::mcp::McpTool,
|
||||
@@ -1707,7 +1526,7 @@ mod tests {
|
||||
|
||||
assert!(registry.contains("test_tool"));
|
||||
assert!(!registry.contains("nonexistent"));
|
||||
assert_eq!(registry.len(), 1);
|
||||
assert_eq!(registry.all().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1970,7 +1789,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_and_clear_invalidate_api_tools_cache() {
|
||||
fn remove_tool_invalidates_api_tools_cache() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
@@ -1980,14 +1799,10 @@ mod tests {
|
||||
let before = registry.to_api_tools();
|
||||
assert_eq!(before.len(), 2);
|
||||
|
||||
let _ = registry.remove("alpha");
|
||||
assert!(registry.remove_tool("alpha"));
|
||||
let after_remove = registry.to_api_tools();
|
||||
assert_eq!(after_remove.len(), 1);
|
||||
assert_eq!(after_remove[0].name, "beta");
|
||||
|
||||
registry.clear();
|
||||
let after_clear = registry.to_api_tools();
|
||||
assert!(after_clear.is_empty(), "cache must clear with the registry");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2028,59 +1843,6 @@ mod tests {
|
||||
assert_eq!(order_a, order_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_remove() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
|
||||
registry.register(make_test_tool("removable"));
|
||||
assert!(registry.contains("removable"));
|
||||
|
||||
let _ = registry.remove("removable");
|
||||
assert!(!registry.contains("removable"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_clear() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
|
||||
registry.register(make_test_tool("tool1"));
|
||||
registry.register(make_test_tool("tool2"));
|
||||
assert_eq!(registry.len(), 2);
|
||||
|
||||
registry.clear();
|
||||
assert!(registry.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_registry_execute() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
|
||||
registry.register(make_test_tool("echo"));
|
||||
|
||||
let result = registry
|
||||
.execute("echo", json!({"message": "hello"}))
|
||||
.await
|
||||
.expect("execute");
|
||||
|
||||
assert_eq!(result, "Echo: hello");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_registry_execute_unknown_tool() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let registry = ToolRegistry::new(ctx);
|
||||
|
||||
let result = registry.execute("nonexistent", json!({})).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
fn scoped_context(workspace: &std::path::Path) -> ToolContext {
|
||||
ToolContext::new(workspace.to_path_buf())
|
||||
.with_tool_authority(
|
||||
@@ -2318,34 +2080,6 @@ mod tests {
|
||||
assert!(registry.contains("custom"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filter_by_capability() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
|
||||
registry.register(make_test_tool("readonly_tool"));
|
||||
|
||||
let readonly = registry.filter_by_capability(ToolCapability::ReadOnly);
|
||||
assert_eq!(readonly.len(), 1);
|
||||
|
||||
let writes = registry.filter_by_capability(ToolCapability::WritesFiles);
|
||||
assert_eq!(writes.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_only_tools() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
let mut registry = ToolRegistry::new(ctx);
|
||||
|
||||
registry.register(make_test_tool("reader"));
|
||||
|
||||
let readonly = registry.read_only_tools();
|
||||
assert_eq!(readonly.len(), 1);
|
||||
assert_eq!(readonly[0].name(), "reader");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_with_web_tools_no_longer_includes_finance() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
@@ -2419,11 +2153,11 @@ mod tests {
|
||||
let registry = ToolRegistryBuilder::new().with_file_tools().build(ctx);
|
||||
|
||||
registry
|
||||
.execute("read_file", json!({"path": "sample.txt"}))
|
||||
.execute_full("read_file", json!({"path": "sample.txt"}))
|
||||
.await
|
||||
.expect("legacy read should execute");
|
||||
registry
|
||||
.execute(
|
||||
.execute_full(
|
||||
"edit_file",
|
||||
json!({"path": "sample.txt", "search": "before", "replace": "after"}),
|
||||
)
|
||||
@@ -2524,37 +2258,37 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_with_agent_tools_includes_finance() {
|
||||
fn test_builder_with_agent_tools_policy_includes_finance() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
|
||||
let registry = ToolRegistryBuilder::new()
|
||||
.with_agent_tools(false)
|
||||
.with_agent_tools_policy(crate::worker_profile::ShellPolicy::None)
|
||||
.build(ctx);
|
||||
|
||||
assert!(registry.contains("finance"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_tools_with_allow_shell_false_excludes_shell_tools() {
|
||||
fn agent_tools_with_shell_policy_none_excludes_shell_tools() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
|
||||
let registry = ToolRegistryBuilder::new()
|
||||
.with_agent_tools(false)
|
||||
.with_agent_tools_policy(crate::worker_profile::ShellPolicy::None)
|
||||
.build(ctx);
|
||||
|
||||
assert!(
|
||||
!registry.contains("exec_shell"),
|
||||
"exec_shell should be excluded when allow_shell is false"
|
||||
"exec_shell should be excluded when the shell policy is None"
|
||||
);
|
||||
assert!(
|
||||
!registry.contains("task_shell_start"),
|
||||
"task_shell_start should be excluded when allow_shell is false"
|
||||
"task_shell_start should be excluded when the shell policy is None"
|
||||
);
|
||||
assert!(
|
||||
!registry.contains("task_shell_wait"),
|
||||
"task_shell_wait should be excluded when allow_shell is false"
|
||||
"task_shell_wait should be excluded when the shell policy is None"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2576,23 +2310,25 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_tools_with_allow_shell_true_includes_shell_tools() {
|
||||
fn agent_tools_with_shell_policy_full_includes_shell_tools() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let ctx = ToolContext::new(tmp.path().to_path_buf());
|
||||
|
||||
let registry = ToolRegistryBuilder::new().with_agent_tools(true).build(ctx);
|
||||
let registry = ToolRegistryBuilder::new()
|
||||
.with_agent_tools_policy(crate::worker_profile::ShellPolicy::Full)
|
||||
.build(ctx);
|
||||
|
||||
assert!(
|
||||
registry.contains("exec_shell"),
|
||||
"exec_shell should be included when allow_shell is true"
|
||||
"exec_shell should be included when the shell policy is Full"
|
||||
);
|
||||
assert!(
|
||||
registry.contains("task_shell_start"),
|
||||
"task_shell_start should be included when allow_shell is true"
|
||||
"task_shell_start should be included when the shell policy is Full"
|
||||
);
|
||||
assert!(
|
||||
registry.contains("task_shell_wait"),
|
||||
"task_shell_wait should be included when allow_shell is true"
|
||||
"task_shell_wait should be included when the shell policy is Full"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"_comment": "Ceiling for `#[allow(dead_code)]` across crates/. This number may go down freely; raising it needs a reviewer to say why in the PR. Regenerate with: python3 scripts/check-dead-code-budget.py --update",
|
||||
"_issue": "https://github.com/Hmbown/CodeWhale/issues/4785",
|
||||
"total": 482,
|
||||
"total": 466,
|
||||
"per_crate": {
|
||||
"config": 2,
|
||||
"tools": 2,
|
||||
"tui": 478
|
||||
"tui": 462
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user