<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tools on ICE-ICE-BEAR-BLOG</title><link>https://ice-ice-bear.github.io/tags/tools/</link><description>Recent content in Tools on ICE-ICE-BEAR-BLOG</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 06 Apr 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://ice-ice-bear.github.io/tags/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>Claude Code Harness Anatomy #3 — The Design Philosophy of 42 Tools, from BashTool to AgentTool</title><link>https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-3/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0900</pubDate><guid>https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-3/</guid><description>&lt;img src="https://ice-ice-bear.github.io/" alt="Featured image of post Claude Code Harness Anatomy #3 — The Design Philosophy of 42 Tools, from BashTool to AgentTool" /&gt;&lt;h2 id="overview"&gt;Overview
&lt;/h2&gt;&lt;p&gt;Claude Code has 42 tools. This post dissects the &amp;ldquo;tools know themselves&amp;rdquo; pattern implemented by the 30+ member &lt;code&gt;Tool.ts&lt;/code&gt; interface, classifies all 42 tools into 8 families, and deep-dives into the most complex ones: BashTool&amp;rsquo;s 6-layer security chain (12,411 lines), AgentTool&amp;rsquo;s 4 spawn modes (6,782 lines), FileEditTool&amp;rsquo;s string matching strategy, MCPTool&amp;rsquo;s empty-shell proxy pattern, and the Task state machine.&lt;/p&gt;
&lt;h2 id="1-tool-interface--tools-know-themselves"&gt;1. Tool Interface &amp;ndash; &amp;ldquo;Tools Know Themselves&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Tool.ts&lt;/code&gt; (792 lines) is the contract for the tool system. The &lt;code&gt;Tool&lt;/code&gt; type (Tool.ts:362-695) that every tool implements consists of &lt;strong&gt;30+ members&lt;/strong&gt; across four domains:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Domain&lt;/th&gt;
 &lt;th&gt;Key Members&lt;/th&gt;
 &lt;th&gt;Role&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Execution contract&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;call()&lt;/code&gt;, &lt;code&gt;inputSchema&lt;/code&gt;, &lt;code&gt;validateInput()&lt;/code&gt;, &lt;code&gt;checkPermissions()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Core tool logic&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Metadata&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;aliases&lt;/code&gt;, &lt;code&gt;searchHint&lt;/code&gt;, &lt;code&gt;shouldDefer&lt;/code&gt;, &lt;code&gt;maxResultSizeChars&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Search and display&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Concurrency/Safety&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;isConcurrencySafe()&lt;/code&gt;, &lt;code&gt;isReadOnly()&lt;/code&gt;, &lt;code&gt;isDestructive()&lt;/code&gt;, &lt;code&gt;interruptBehavior()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Orchestration decisions&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UI rendering&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;renderToolUseMessage()&lt;/code&gt; + 10 more&lt;/td&gt;
 &lt;td&gt;Terminal display&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Why so many members in one interface?&lt;/strong&gt; When the orchestrator (&lt;code&gt;toolExecution.ts&lt;/code&gt;) calls a tool, it can read all metadata directly from the tool object without any external mapping tables. This is the foundation of a plugin architecture where adding a new tool is &lt;strong&gt;self-contained within a single directory&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="toolusecontext--42-fields-of-execution-environment"&gt;ToolUseContext &amp;ndash; 42 Fields of Execution Environment
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ToolUseContext&lt;/code&gt; (Tool.ts:158-300) is the environment context injected during tool execution. Spanning 142 lines, it defines 42 fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;abortController&lt;/code&gt;: Cancellation propagation for the 3-tier concurrency model&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getAppState()&lt;/code&gt;/&lt;code&gt;setAppState()&lt;/code&gt;: Global state access (permissions, todos, teams)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readFileState&lt;/code&gt;: LRU cache-based change detection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contentReplacementState&lt;/code&gt;: Save large results to disk, return summaries only&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tools are not isolated functions — they need access to the harness&amp;rsquo;s entire state. FileReadTool uses the cache to detect changes, AgentTool registers sub-agent state, and BashTool can interrupt sibling processes.&lt;/p&gt;
&lt;h3 id="buildtools-fail-closed-defaults"&gt;buildTool()&amp;rsquo;s fail-closed Defaults
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;buildTool()&lt;/code&gt; (Tool.ts:783) takes a &lt;code&gt;ToolDef&lt;/code&gt; and returns a complete &lt;code&gt;Tool&lt;/code&gt; with defaults filled in. The defaults follow a &lt;strong&gt;fail-closed&lt;/strong&gt; principle (Tool.ts:757-768):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isConcurrencySafe&lt;/code&gt; -&amp;gt; &lt;code&gt;false&lt;/code&gt; (assume unsafe)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isReadOnly&lt;/code&gt; -&amp;gt; &lt;code&gt;false&lt;/code&gt; (assume writes)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a new tool doesn&amp;rsquo;t explicitly declare concurrency/read-only status, it takes the most conservative path (sequential execution, write permission required). &lt;strong&gt;This structurally prevents the bug of accidentally running an unsafe tool in parallel.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="2-42-tools-in-8-families"&gt;2. 42 Tools in 8 Families
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 subgraph safe["isConcurrencySafe: true (10)"]
 direction TB
 R1["FileReadTool"]
 R2["GlobTool / GrepTool"]
 R3["WebFetchTool / WebSearchTool"]
 R4["ToolSearchTool / SleepTool"]
 R5["TaskGetTool / TaskListTool"]
 R6["LSPTool"]
 end

 subgraph unsafe["isConcurrencySafe: false (32)"]
 direction TB
 W1["BashTool 12,411 lines"]
 W2["FileEditTool / FileWriteTool"]
 W3["AgentTool 6,782 lines"]
 W4["MCPTool / SkillTool"]
 W5["Task 5 / Todo"]
 W6["Config / PlanMode / Worktree"]
 end

 subgraph orch["Orchestrator"]
 O["partitionToolCalls()&amp;lt;br/&amp;gt;toolOrchestration.ts"]
 end

 O --&gt;|"Parallel batch"| safe
 O --&gt;|"Sequential execution"| unsafe

 style safe fill:#e8f5e9
 style unsafe fill:#ffebee&lt;/pre&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Family&lt;/th&gt;
 &lt;th&gt;Count&lt;/th&gt;
 &lt;th&gt;Representative Tool&lt;/th&gt;
 &lt;th&gt;Key Characteristic&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Filesystem&lt;/td&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;FileReadTool (1,602 lines)&lt;/td&gt;
 &lt;td&gt;PDF/image/notebook support, token limits&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Execution&lt;/td&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;BashTool (12,411 lines)&lt;/td&gt;
 &lt;td&gt;6-layer security, command semantics&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Agent/Team&lt;/td&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;AgentTool (6,782 lines)&lt;/td&gt;
 &lt;td&gt;4 spawn modes, recursive harness&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Task management&lt;/td&gt;
 &lt;td&gt;7&lt;/td&gt;
 &lt;td&gt;TaskUpdateTool (484 lines)&lt;/td&gt;
 &lt;td&gt;State machine, verification nudge&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MCP/LSP&lt;/td&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;MCPTool (1,086 lines)&lt;/td&gt;
 &lt;td&gt;Empty-shell proxy&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Web/External&lt;/td&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;WebFetchTool (1,131 lines)&lt;/td&gt;
 &lt;td&gt;Parallel safe&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;State/Config&lt;/td&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;ConfigTool (809 lines)&lt;/td&gt;
 &lt;td&gt;Session state changes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Infra/Utility&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;td&gt;SkillTool (1,477 lines)&lt;/td&gt;
 &lt;td&gt;Command-to-tool bridge&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Only 10 of 42 (24%) are parallel-safe, but these 10 are the most frequently called tools (Read, Glob, Grep, Web), so perceived parallelism is higher than the ratio suggests.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="3-bashtool--6-layer-security-chain"&gt;3. BashTool &amp;ndash; 6-Layer Security Chain
&lt;/h2&gt;&lt;p&gt;BashTool is not a simple shell executor. Because &lt;strong&gt;arbitrary code execution&lt;/strong&gt; is an inherent risk, more than half of its 12,411 lines are security layers.&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TB
 A["Model: Bash call"] --&gt; B{"validateInput"}
 B --&gt;|"sleep pattern blocked"| B1["Return error"]
 B --&gt;|"Pass"| C{"6-layer security chain"}

 subgraph chain["Security chain"]
 C1["1. bashSecurity.ts&amp;lt;br/&amp;gt;2,592 lines -- command structure analysis"]
 C2["2. bashPermissions.ts&amp;lt;br/&amp;gt;2,621 lines -- rule matching"]
 C3["3. readOnlyValidation.ts&amp;lt;br/&amp;gt;1,990 lines -- read-only determination"]
 C4["4. pathValidation.ts&amp;lt;br/&amp;gt;1,303 lines -- path-based security"]
 C5["5. sedValidation.ts&amp;lt;br/&amp;gt;684 lines -- sed-specific security"]
 C6["6. shouldUseSandbox.ts&amp;lt;br/&amp;gt;153 lines -- sandbox decision"]
 C1 --&gt; C2 --&gt; C3 --&gt; C4 --&gt; C5 --&gt; C6
 end

 C --&gt; chain
 chain --&gt; D{"allow / ask / deny"}
 D --&gt;|"allow"| E["runShellCommand()"]
 D --&gt;|"ask"| F["Request user approval"]
 D --&gt;|"deny"| G["Denied"]
 E --&gt; H["Result processing&amp;lt;br/&amp;gt;interpretCommandResult()&amp;lt;br/&amp;gt;trackGitOperations()"]

 style chain fill:#fff3e0&lt;/pre&gt;&lt;p&gt;Each layer handles a different threat:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;bashSecurity.ts&lt;/strong&gt; (2,592 lines): Blocks command substitution (&lt;code&gt;$()&lt;/code&gt;, &lt;code&gt;`&lt;/code&gt;), Zsh module-based attacks. Key: &lt;strong&gt;only metacharacters in unquoted contexts are classified as dangerous&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bashPermissions.ts&lt;/strong&gt; (2,621 lines): Rule-based allow/deny/ask. &lt;code&gt;stripAllLeadingEnvVars()&lt;/code&gt; + &lt;code&gt;stripSafeWrappers()&lt;/code&gt; strip wrappers to extract the actual command&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;readOnlyValidation.ts&lt;/strong&gt; (1,990 lines): If read-only, then &lt;code&gt;isConcurrencySafe: true&lt;/code&gt; — parallel execution allowed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pathValidation.ts&lt;/strong&gt; (1,303 lines): Per-command path extraction rules for path safety judgment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sedValidation.ts&lt;/strong&gt; (684 lines): sed&amp;rsquo;s &lt;code&gt;w&lt;/code&gt; and &lt;code&gt;e&lt;/code&gt; flags can write files/execute arbitrary code — blocked separately&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;shouldUseSandbox.ts&lt;/strong&gt; (153 lines): Final isolation decision&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Command semantics&lt;/strong&gt; (&lt;code&gt;commandSemantics.ts&lt;/code&gt;): &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;diff&lt;/code&gt; return exit code 1 as a normal result, not an error. The &lt;code&gt;COMMAND_SEMANTICS&lt;/code&gt; Map defines per-command interpretation rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rust porting implications&lt;/strong&gt;: Either reproduce all 6 layers wholesale, or simplify to sandbox-only. Skipping intermediate layers creates security holes.&lt;/p&gt;
&lt;h2 id="4-agenttool--4-spawn-modes"&gt;4. AgentTool &amp;ndash; 4 Spawn Modes
&lt;/h2&gt;&lt;p&gt;AgentTool is less of a &amp;ldquo;tool&amp;rdquo; and more of an &lt;strong&gt;agent orchestrator&lt;/strong&gt;. The key: &lt;code&gt;runAgent()&lt;/code&gt; recursively calls the harness&amp;rsquo;s &lt;code&gt;query()&lt;/code&gt; loop. Child agents receive the same tools, API access, and security checks as the parent.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Mode&lt;/th&gt;
 &lt;th&gt;Trigger&lt;/th&gt;
 &lt;th&gt;Context Sharing&lt;/th&gt;
 &lt;th&gt;Background&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Synchronous&lt;/td&gt;
 &lt;td&gt;Default&lt;/td&gt;
 &lt;td&gt;None (prompt only)&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Async&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;run_in_background: true&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;None&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Fork&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;subagent_type&lt;/code&gt; omitted&lt;/td&gt;
 &lt;td&gt;Full parent context&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Remote&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;isolation: &amp;quot;remote&amp;quot;&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;None&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="fork-sub-agents--byte-identical-prefix"&gt;Fork Sub-agents &amp;ndash; Byte-Identical Prefix
&lt;/h3&gt;&lt;p&gt;Forks &lt;strong&gt;inherit the parent&amp;rsquo;s full conversation context&lt;/strong&gt;. To share prompt cache, all fork children are designed to produce byte-identical API request prefixes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tool use results replaced with placeholders&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FORK_BOILERPLATE_TAG&lt;/code&gt; prevents recursive forking&lt;/li&gt;
&lt;li&gt;Model kept identical (&lt;code&gt;model: 'inherit'&lt;/code&gt;) — different models cause cache misses&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="memory-system-agentmemoryts"&gt;Memory System (agentMemory.ts)
&lt;/h3&gt;&lt;p&gt;Per-agent persistent memory is managed across 3 scopes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;user&lt;/strong&gt;: &lt;code&gt;~/.claude/agent-memory/&amp;lt;type&amp;gt;/&lt;/code&gt; — user-global&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;project&lt;/strong&gt;: &lt;code&gt;.claude/agent-memory/&amp;lt;type&amp;gt;/&lt;/code&gt; — project-shared (VCS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;local&lt;/strong&gt;: &lt;code&gt;.claude/agent-memory-local/&amp;lt;type&amp;gt;/&lt;/code&gt; — local-only&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5-fileedittool--partial-replacement-pattern"&gt;5. FileEditTool &amp;ndash; Partial Replacement Pattern
&lt;/h2&gt;&lt;p&gt;FileEditTool (1,812 lines) performs &lt;strong&gt;&lt;code&gt;old_string&lt;/code&gt; -&amp;gt; &lt;code&gt;new_string&lt;/code&gt; patches&lt;/strong&gt; rather than full file writes. The model doesn&amp;rsquo;t need to output the entire file, saving tokens and enabling diff-based review.&lt;/p&gt;
&lt;p&gt;Matching strategy:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exact string matching&lt;/strong&gt;: &lt;code&gt;fileContent.includes(searchString)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quote normalization&lt;/strong&gt;: Convert curly quotes -&amp;gt; straight quotes and retry, with &lt;code&gt;preserveQuoteStyle()&lt;/code&gt; preserving the original style&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uniqueness validation&lt;/strong&gt;: Fails if &lt;code&gt;old_string&lt;/code&gt; is not unique in the file (unless &lt;code&gt;replace_all&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Concurrency protection&lt;/strong&gt;: The &lt;code&gt;readFileState&lt;/code&gt; Map stores per-file last-read timestamps. During editing, it compares against the on-disk modification time to detect external changes. This is why the &amp;ldquo;Read before Edit&amp;rdquo; rule is enforced in the prompt.&lt;/p&gt;
&lt;h2 id="6-mcptool--empty-shell-proxy"&gt;6. MCPTool &amp;ndash; Empty-Shell Proxy
&lt;/h2&gt;&lt;p&gt;MCPTool (1,086 lines) is where &lt;strong&gt;a single tool definition represents hundreds of external tools&lt;/strong&gt;. At build time it&amp;rsquo;s an empty shell; at runtime, &lt;code&gt;mcpClient.ts&lt;/code&gt; clones and overrides it per server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// MCPTool.ts:27-51 -- core methods have &amp;#34;Overridden in mcpClient.ts&amp;#34; comments
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// replaced at runtime with &amp;#39;mcp__serverName__toolName&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;call() {&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// replaced at runtime with actual MCP call
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The UI collapse classification (&lt;code&gt;classifyForCollapse.ts&lt;/code&gt;, 604 lines) uses 139 SEARCH_TOOLS and 280+ READ_TOOLS names to determine whether an MCP tool is a read/search operation. Unknown tools are not collapsed (conservative approach).&lt;/p&gt;
&lt;h2 id="7-task-state-machine--agent-ipc"&gt;7. Task State Machine &amp;ndash; Agent IPC
&lt;/h2&gt;&lt;p&gt;TaskUpdateTool (406 lines) state flow: &lt;code&gt;pending -&amp;gt; in_progress -&amp;gt; completed&lt;/code&gt; or &lt;code&gt;deleted&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Key behaviors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto-assign owner&lt;/strong&gt;: Current agent name is automatically assigned on &lt;code&gt;in_progress&lt;/code&gt; transition&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verification nudge&lt;/strong&gt;: After 3+ tasks completed without a verification step, recommends spawning a verification agent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message routing&lt;/strong&gt; (SendMessageTool 917 lines): By name, &lt;code&gt;*&lt;/code&gt; broadcast, &lt;code&gt;uds:path&lt;/code&gt; Unix domain socket, &lt;code&gt;bridge:session&lt;/code&gt; remote peer, agent ID resume&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Task/SendMessage are not simple utilities but the &lt;strong&gt;inter-process communication (IPC)&lt;/strong&gt; foundation of the multi-agent system.&lt;/p&gt;
&lt;h2 id="ts-vs-rust-comparison"&gt;TS vs Rust Comparison
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Aspect&lt;/th&gt;
 &lt;th&gt;TS (42 tools)&lt;/th&gt;
 &lt;th&gt;Rust (10 tools)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Tool definition&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Tool&lt;/code&gt; interface + &lt;code&gt;buildTool()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ToolSpec&lt;/code&gt; struct + &lt;code&gt;mvp_tool_specs()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Input schema&lt;/td&gt;
 &lt;td&gt;Zod v4 + &lt;code&gt;lazySchema()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;serde_json::json!()&lt;/code&gt; direct JSON Schema&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Concurrency declaration&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;isConcurrencySafe(parsedInput)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;None — sequential execution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Permission check&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkPermissions()&lt;/code&gt; -&amp;gt; &lt;code&gt;PermissionResult&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;PermissionMode&lt;/code&gt; enum&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UI rendering&lt;/td&gt;
 &lt;td&gt;10+ render methods (React/Ink)&lt;/td&gt;
 &lt;td&gt;None&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MCP integration&lt;/td&gt;
 &lt;td&gt;MCPTool + &lt;code&gt;inputJSONSchema&lt;/code&gt; dual path&lt;/td&gt;
 &lt;td&gt;None&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Size comparison&lt;/td&gt;
 &lt;td&gt;~48,000 lines (tool code only)&lt;/td&gt;
 &lt;td&gt;~1,300 lines (single lib.rs)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Key gap&lt;/strong&gt;: The Rust port only implements the execution contract (&lt;code&gt;call&lt;/code&gt; equivalent); concurrency declarations, permission pipeline, UI rendering, and lazy-loading optimizations are all missing.&lt;/p&gt;
&lt;h2 id="insights"&gt;Insights
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security is a chain, not a single checkpoint&lt;/strong&gt; &amp;ndash; BashTool&amp;rsquo;s 6 layers each handle different threats. bashSecurity handles command structure, bashPermissions handles rule matching, pathValidation handles path safety. If any link in this chain is missing, an attack surface opens. Combined with the fail-closed principle, the conservative strategy of &amp;ldquo;block when uncertain&amp;rdquo; permeates the entire system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Agents are recursive harness instances&lt;/strong&gt; &amp;ndash; The fact that AgentTool&amp;rsquo;s &lt;code&gt;runAgent()&lt;/code&gt; recursively calls the harness&amp;rsquo;s &lt;code&gt;query()&lt;/code&gt; loop means &amp;ldquo;agent&amp;rdquo; is not a separate system but &lt;strong&gt;a different configuration of the same harness&lt;/strong&gt;. It swaps only the tool pool while reusing the same security, hooks, and orchestration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Only 10 of 42 tools are concurrency-safe, yet perceived parallelism is high&lt;/strong&gt; &amp;ndash; The 10 tools representing only 24% of the total (Read, Glob, Grep, Web, LSP) happen to be the most frequently called. This asymmetry demonstrates the practical value of the 3-tier concurrency model. &lt;code&gt;buildTool()&lt;/code&gt;&amp;rsquo;s fail-closed default (&lt;code&gt;isConcurrencySafe: false&lt;/code&gt;) forms the safety boundary, structurally preventing new tool developers from incorrectly declaring concurrency safety.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Next post: &lt;a class="link" href="https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-4/" &gt;#4 &amp;ndash; Runtime Hooks: 26+ Events and CLAUDE.md 6-Stage Discovery&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>