<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Multi-Provider on ICE-ICE-BEAR-BLOG</title><link>https://ice-ice-bear.github.io/tags/multi-provider/</link><description>Recent content in Multi-Provider 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/multi-provider/index.xml" rel="self" type="application/rss+xml"/><item><title>Claude Code Harness Anatomy #6 — Beyond Claude Code: A Retrospective on Building an Independent 7-Crate Harness</title><link>https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-6/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0900</pubDate><guid>https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-6/</guid><description>&lt;img src="https://ice-ice-bear.github.io/" alt="Featured image of post Claude Code Harness Anatomy #6 — Beyond Claude Code: A Retrospective on Building an Independent 7-Crate Harness" /&gt;&lt;h2 id="overview"&gt;Overview
&lt;/h2&gt;&lt;p&gt;This is the final post in the series that systematically dissected Claude Code&amp;rsquo;s TypeScript source across 27 sessions. In Phase 1 we understood the architecture of 100k+ lines of TS code, in Phase 2 we reimplemented core patterns in Rust, and in Phase 3 we designed and built an independent agent harness that overcomes the 8 limitations we discovered. This post covers the limitation analysis, 5 design principles, 7-crate architecture, 61 tests, and a full retrospective of the journey.&lt;/p&gt;
&lt;h2 id="1-8-limitations-of-claude-codes-architecture"&gt;1. 8 Limitations of Claude Code&amp;rsquo;s Architecture
&lt;/h2&gt;&lt;p&gt;From 27 sessions of analysis, we distinguished strengths from limitations. The strengths (AsyncGenerator pipeline, 3-tier concurrency, hook extensibility, CLAUDE.md discovery, MCP support, self-contained tool interface, 7-path error recovery) represent excellent design. However, the following 8 limitations motivated the independent harness:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;#&lt;/th&gt;
 &lt;th&gt;Limitation&lt;/th&gt;
 &lt;th&gt;Source Session&lt;/th&gt;
 &lt;th&gt;Impact&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;React/Ink dependency — heavy TUI&lt;/td&gt;
 &lt;td&gt;S08&lt;/td&gt;
 &lt;td&gt;Unnecessary dependency in headless mode&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;Single provider (effectively Anthropic-only)&lt;/td&gt;
 &lt;td&gt;S01&lt;/td&gt;
 &lt;td&gt;Cannot use OpenAI or local models&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;main.tsx 4,683-line monolith&lt;/td&gt;
 &lt;td&gt;S01&lt;/td&gt;
 &lt;td&gt;CLI/REPL/session mixed in one file&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;Synchronous tool execution (Rust port)&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;No streaming pipelining&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;TS ecosystem-locked plugins&lt;/td&gt;
 &lt;td&gt;S13&lt;/td&gt;
 &lt;td&gt;No language-neutral extensions&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;85 React hooks mixing UI/runtime&lt;/td&gt;
 &lt;td&gt;S08&lt;/td&gt;
 &lt;td&gt;Dual meaning of &amp;ldquo;hook&amp;rdquo;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;7&lt;/td&gt;
 &lt;td&gt;Implicit prompt caching dependencies&lt;/td&gt;
 &lt;td&gt;S10&lt;/td&gt;
 &lt;td&gt;3 cache invalidation paths are implicit&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;8&lt;/td&gt;
 &lt;td&gt;MCP OAuth 2,465-line complexity&lt;/td&gt;
 &lt;td&gt;S12&lt;/td&gt;
 &lt;td&gt;RFC inconsistency is the root cause&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="2-5-design-principles"&gt;2. 5 Design Principles
&lt;/h2&gt;&lt;p&gt;We established 5 core principles to overcome these limitations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principle 1 &amp;ndash; Multi-provider&lt;/strong&gt;: Support Anthropic, OpenAI, and local models (Ollama) through a single abstraction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[async_trait]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;trait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Provider&lt;/span&gt;: &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;: &lt;span class="nc"&gt;ProviderRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProviderError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;available_models&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ModelInfo&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ProviderRequest&lt;/code&gt; is a provider-neutral struct that each implementation converts to its own API format.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principle 2 &amp;ndash; Native async&lt;/strong&gt;: Fully async based on tokio. &lt;code&gt;yield&lt;/code&gt; -&amp;gt; &lt;code&gt;tx.send()&lt;/code&gt;, &lt;code&gt;yield*&lt;/code&gt; -&amp;gt; channel forwarding replaces the AsyncGenerator pattern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principle 3 &amp;ndash; Module separation&lt;/strong&gt;: Conversation engine, tools, hooks, and prompts are each separate crates. No repeating the &lt;code&gt;main.tsx&lt;/code&gt; monolith.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principle 4 &amp;ndash; Language-neutral extensions&lt;/strong&gt;: SKILL.md compatibility + MCP servers as plugin units.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principle 5 &amp;ndash; Full MCP utilization&lt;/strong&gt;: Leveraging not just tools but resources, prompts, and sampling across the full spec.&lt;/p&gt;
&lt;h2 id="3-7-crate-architecture"&gt;3. 7-Crate Architecture
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;graph TD
 CLI["harness-cli&amp;lt;br/&amp;gt;REPL binary"] --&gt; CORE["harness-core&amp;lt;br/&amp;gt;Conversation engine + turn loop"]
 CORE --&gt; PROV["harness-provider&amp;lt;br/&amp;gt;LLM provider abstraction"]
 CORE --&gt; TOOLS["harness-tools&amp;lt;br/&amp;gt;Tool registry + built-in tools"]
 CORE --&gt; HOOKS["harness-hooks&amp;lt;br/&amp;gt;Hook pipeline"]
 CORE --&gt; PROMPT["harness-prompt&amp;lt;br/&amp;gt;CLAUDE.md discovery"]
 CORE --&gt; MCP["harness-mcp&amp;lt;br/&amp;gt;MCP client"]
 MCP --&gt; TOOLS

 style CLI fill:#b3e5fc
 style CORE fill:#fff9c4
 style PROV fill:#c8e6c9
 style TOOLS fill:#c8e6c9
 style HOOKS fill:#c8e6c9
 style PROMPT fill:#c8e6c9
 style MCP fill:#e1bee7&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Core design&lt;/strong&gt;: Only &lt;code&gt;harness-core&lt;/code&gt; depends on other crates. The rest are independent of each other (except &lt;code&gt;harness-mcp&lt;/code&gt; -&amp;gt; &lt;code&gt;harness-tools&lt;/code&gt;). This structure enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Independent &lt;code&gt;cargo test&lt;/code&gt; for each crate&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;harness-core&lt;/code&gt; changes needed when adding providers&lt;/li&gt;
&lt;li&gt;MCP tools implementing the same &lt;code&gt;Tool&lt;/code&gt; trait as built-in tools&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Crate&lt;/th&gt;
 &lt;th&gt;Core Responsibility&lt;/th&gt;
 &lt;th&gt;Test Count&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-provider&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;LLM API calls, SSE parsing, retries&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-tools&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Tool registry, 3-tier concurrency&lt;/td&gt;
 &lt;td&gt;12&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-hooks&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Shell hook execution, deny short-circuit, rewrite chain&lt;/td&gt;
 &lt;td&gt;9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-prompt&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;6-stage CLAUDE.md, SHA-256 deduplication&lt;/td&gt;
 &lt;td&gt;9&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-core&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Conversation engine, &lt;code&gt;StreamingToolExecutor&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-mcp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;JSON-RPC, stdio transport&lt;/td&gt;
 &lt;td&gt;14&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;harness-cli&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;REPL binary&lt;/td&gt;
 &lt;td&gt;&amp;ndash;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="provider-trait--multi-provider"&gt;Provider Trait &amp;ndash; Multi-Provider
&lt;/h3&gt;&lt;p&gt;The existing Rust port&amp;rsquo;s &lt;code&gt;ApiClient&lt;/code&gt; trait was Anthropic-specific (&lt;code&gt;ApiRequest&lt;/code&gt; with Anthropic fields). The &lt;code&gt;Provider&lt;/code&gt; trait accepts a provider-neutral &lt;code&gt;ProviderRequest&lt;/code&gt; that each implementation converts to its own API format. &lt;code&gt;Box&amp;lt;dyn Provider&amp;gt;&lt;/code&gt; enables runtime fallback chains.&lt;/p&gt;
&lt;h3 id="conversationengine--turn-loop"&gt;ConversationEngine &amp;ndash; Turn Loop
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ConversationEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;: &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tool_executor&lt;/span&gt;: &lt;span class="nc"&gt;StreamingToolExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hook_pipeline&lt;/span&gt;: &lt;span class="nc"&gt;HookPipeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt_builder&lt;/span&gt;: &lt;span class="nc"&gt;PromptBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;budget&lt;/span&gt;: &lt;span class="nc"&gt;TokenBudget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of the existing Rust port&amp;rsquo;s &lt;code&gt;ConversationRuntime&amp;lt;C, T&amp;gt;&lt;/code&gt; generic pattern, we use trait objects. The provider must be swappable at runtime (model fallback), and generics fix the type at compile time, lacking flexibility.&lt;/p&gt;
&lt;h3 id="streaming-tool-execution-pipelining"&gt;Streaming Tool Execution (Pipelining)
&lt;/h3&gt;&lt;p&gt;We solved the biggest constraint of the existing Rust port — &amp;ldquo;collect all SSE events then execute tools&amp;rdquo;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When a &lt;code&gt;ContentBlockStop(ToolUse)&lt;/code&gt; event arrives from &lt;code&gt;EventStream&lt;/code&gt;, forward immediately&lt;/li&gt;
&lt;li&gt;After &lt;code&gt;is_concurrency_safe()&lt;/code&gt; check, parallel processing via &lt;code&gt;tokio::spawn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Tool execution proceeds while the API is still streaming&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="4-phase-2-retrospective--extending-the-existing-port"&gt;4. Phase 2 Retrospective &amp;ndash; Extending the Existing Port
&lt;/h2&gt;&lt;p&gt;Before Phase 3&amp;rsquo;s independent harness, we extended the existing &lt;code&gt;rust/&lt;/code&gt; prototype in Phase 2:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Sprint&lt;/th&gt;
 &lt;th&gt;Achievement&lt;/th&gt;
 &lt;th&gt;Core Pattern&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;S14-S15&lt;/td&gt;
 &lt;td&gt;Orchestration module + 3-tier concurrency&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tokio::JoinSet&lt;/code&gt;-based parallel execution&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;S16-S17&lt;/td&gt;
 &lt;td&gt;Tool expansion (19 -&amp;gt; 26)&lt;/td&gt;
 &lt;td&gt;Added Task, PlanMode, AskUser&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;S18-S19&lt;/td&gt;
 &lt;td&gt;Hook execution pipeline&lt;/td&gt;
 &lt;td&gt;stdin JSON, deny short-circuit&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;S20-S21&lt;/td&gt;
 &lt;td&gt;Skill discovery&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/skills/&lt;/code&gt; scan, prompt injection&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most of Phase 2&amp;rsquo;s code was rewritten in Phase 3. However, the &lt;strong&gt;questions discovered during prototyping&lt;/strong&gt; (&amp;ldquo;Why AsyncGenerator?&amp;rdquo;, &amp;ldquo;Why should tools be unaware of the UI?&amp;rdquo;) determined the final design.&lt;/p&gt;
&lt;h2 id="5-61-tests-and-the-mockprovider-pattern"&gt;5. 61 Tests and the MockProvider Pattern
&lt;/h2&gt;&lt;p&gt;All crates are independently testable. &lt;code&gt;MockProvider&lt;/code&gt; enables verifying the conversation engine&amp;rsquo;s full turn loop without actual API calls:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSE&lt;/span&gt; &lt;span class="n"&gt;parsing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deny&lt;/span&gt; &lt;span class="n"&gt;short&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rewrite&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeouts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt; &lt;span class="n"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="n"&gt;deduplication&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;turn&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;harness&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;RPC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;listing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="6-how-phase-1-2-lessons-shaped-the-design"&gt;6. How Phase 1-2 Lessons Shaped the Design
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 subgraph Phase1["Phase 1 -- Understanding"]
 direction TB
 P1A["S02: AsyncGenerator chain"]
 P1B["S05: 42-tool classification"]
 P1C["S08: Runtime vs React hooks"]
 P1D["S10: 6-stage CLAUDE.md"]
 P1E["S12: MCP connection management"]
 P1F["S13: Skills = prompts"]
 end

 subgraph Phase3["Phase 3 -- Independent Harness"]
 direction TB
 P3A["EventStream + mpsc channels"]
 P3B["Tool trait + 3-tier"]
 P3C["HookPipeline (runtime only)"]
 P3D["PromptAssembler separation"]
 P3E["harness-mcp stdio"]
 P3F["SKILL.md compatible"]
 end

 P1A --&gt;|"yield -&gt; tx.send()"| P3A
 P1B --&gt;|"fail-closed defaults"| P3B
 P1C --&gt;|"scope reduction"| P3C
 P1D --&gt;|"cache splitting"| P3D
 P1E --&gt;|"implemented without SDK"| P3E
 P1F --&gt;|"text injection"| P3F

 style Phase1 fill:#e1f5fe
 style Phase3 fill:#fff3e0&lt;/pre&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Lesson&lt;/th&gt;
 &lt;th&gt;Source&lt;/th&gt;
 &lt;th&gt;Design Impact&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;StreamingToolExecutor&lt;/code&gt; 4-stage state machine&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;Async implementation in &lt;code&gt;harness-core&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;QueryDeps&lt;/code&gt; callback DI&amp;rsquo;s type safety limits&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;Trait object DI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6-layer Bash security chain&lt;/td&gt;
 &lt;td&gt;S06&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;check_permissions()&lt;/code&gt; + hook separation&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Agent = recursive harness instance&lt;/td&gt;
 &lt;td&gt;S06&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ConversationEngine&lt;/code&gt; reuse&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ApiClient&lt;/code&gt; sync trait blocks pipelining&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Provider&lt;/code&gt; async trait&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Deny short-circuit + Rewrite chaining&lt;/td&gt;
 &lt;td&gt;S09&lt;/td&gt;
 &lt;td&gt;Identical pattern in &lt;code&gt;HookPipeline&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SHA-256 content hash outperforms path hash&lt;/td&gt;
 &lt;td&gt;S11&lt;/td&gt;
 &lt;td&gt;Content hash in &lt;code&gt;harness-prompt&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="7-top-10-architecture-patterns-learned"&gt;7. Top 10 Architecture Patterns Learned
&lt;/h2&gt;&lt;p&gt;Core architecture patterns extracted from 27 sessions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AsyncGenerator/Stream pipeline&lt;/strong&gt;: The core abstraction for streaming LLM responses&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3-tier tool concurrency&lt;/strong&gt;: ReadOnly/Write/Dangerous classification balances safety and performance&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ToolSpec + ToolResult duality&lt;/strong&gt;: Separating metadata (for LLM) from execution results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hook chain execution&lt;/strong&gt;: Deny short-circuit, rewrite chain, independent post-hook transforms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;6-stage prompt discovery&lt;/strong&gt;: Managed -&amp;gt; user -&amp;gt; project -&amp;gt; local overrides&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP adapter pattern&lt;/strong&gt;: Unifying external protocol tools into the internal Tool trait&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider abstraction&lt;/strong&gt;: Swapping Anthropic/OpenAI behind the same interface&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSE incremental parsing&lt;/strong&gt;: Assembling network chunks into event frames&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MockProvider testing&lt;/strong&gt;: Verifying engine behavior with predefined event sequences&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills = prompts&lt;/strong&gt;: Text injection sufficient instead of complex plugin systems&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="8-full-journey-retrospective"&gt;8. Full Journey Retrospective
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Phase&lt;/th&gt;
 &lt;th&gt;Sessions&lt;/th&gt;
 &lt;th&gt;Key Deliverables&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 1 &amp;ndash; Understanding&lt;/td&gt;
 &lt;td&gt;S00-S13&lt;/td&gt;
 &lt;td&gt;14 analysis documents, Rust prototype&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 2 &amp;ndash; Reimplementation&lt;/td&gt;
 &lt;td&gt;S14-S21&lt;/td&gt;
 &lt;td&gt;Orchestration, 26 tools, hooks, skills&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 3 &amp;ndash; Independent Harness&lt;/td&gt;
 &lt;td&gt;S22-S27&lt;/td&gt;
 &lt;td&gt;7-crate workspace, 61+ tests&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Claude Code is a &lt;strong&gt;prompt engineering runtime&lt;/strong&gt;. The core loop assembles messages, the tool system grants the ability to interact with the world, and the permission system sets boundaries. CLAUDE.md injects context, MCP integrates external systems, hooks and agents enable automation/delegation, and plugins/skills transform it into a user extension platform.&lt;/p&gt;
&lt;h3 id="future-directions"&gt;Future Directions
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;True streaming: Processing SSE byte streams chunk by chunk&lt;/li&gt;
&lt;li&gt;Permission system: Per-tool user approval workflows&lt;/li&gt;
&lt;li&gt;MCP SSE transport: HTTP SSE support beyond stdio&lt;/li&gt;
&lt;li&gt;Token budget integration: Automatic context window budget management&lt;/li&gt;
&lt;li&gt;Multi-turn agent mode: Autonomous iteration + breakpoint system&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="insights"&gt;Insights
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Good abstractions emerge at boundaries&lt;/strong&gt; &amp;ndash; Provider trait, Tool trait, HookRunner trait. Every core abstraction is a trait defining module boundaries. The existing Rust port&amp;rsquo;s &lt;code&gt;ConversationRuntime&amp;lt;C, T&amp;gt;&lt;/code&gt; generics provide strong compile-time guarantees but had limitations for scenarios like swapping providers at runtime or dynamically registering MCP tools. &lt;code&gt;Box&amp;lt;dyn Provider&amp;gt;&lt;/code&gt; + &lt;code&gt;Box&amp;lt;dyn Tool&amp;gt;&lt;/code&gt; trait objects buy runtime flexibility at a minor vtable cost. Relative to LLM API latency (hundreds of ms to seconds), the vtable overhead is immeasurable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The value of prototypes lies in questions, not code&lt;/strong&gt; &amp;ndash; Most of Phase 1-2&amp;rsquo;s prototype code was rewritten in Phase 3. But questions like &amp;ldquo;Why AsyncGenerator?&amp;rdquo;, &amp;ldquo;Why should tools be unaware of UI?&amp;rdquo;, and &amp;ldquo;Why doesn&amp;rsquo;t allow bypass?&amp;rdquo; determined the final design. The act of reading 100k lines of code is not the answer itself — the &lt;strong&gt;design intent (the why)&lt;/strong&gt; discovered during reading is the true deliverable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Most of the TS code&amp;rsquo;s complexity is defensive lines&lt;/strong&gt; &amp;ndash; Permission layers, frontmatter parsing, deduplication, symlink prevention. These aren&amp;rsquo;t features — they&amp;rsquo;re defenses. Rust can guarantee some of this at compile time through its type system and ownership model, but runtime policies like filesystem security and user config precedence must be implemented explicitly. The 27 sessions were the process of mapping these defensive lines, and that map guided the independent harness&amp;rsquo;s design.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Series complete. The full analysis documents are available at the &lt;a class="link" href="https://github.com/lsr/claw-code" target="_blank" rel="noopener"
 &gt;claw-code repository&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>