<?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/ko/tags/multi-provider/</link><description>Recent content in Multi-Provider on ICE-ICE-BEAR-BLOG</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Mon, 06 Apr 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://ice-ice-bear.github.io/ko/tags/multi-provider/index.xml" rel="self" type="application/rss+xml"/><item><title>Claude Code 하네스 해부학 #6 — Claude Code를 넘어서, 7크레이트 독자 하네스 구축 회고</title><link>https://ice-ice-bear.github.io/ko/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/ko/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 하네스 해부학 #6 — Claude Code를 넘어서, 7크레이트 독자 하네스 구축 회고" /&gt;&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;Claude Code의 TypeScript 소스를 27세션에 걸쳐 체계적으로 해부한 여정의 마지막 포스트다. Phase 1에서 10만줄+ TS 코드의 아키텍처를 이해하고, Phase 2에서 핵심 패턴을 Rust로 재구현한 뒤, Phase 3에서 발견한 8가지 한계점을 극복하는 독자 에이전트 하네스를 설계-구축했다. 이 포스트에서는 한계점 분석, 5가지 설계 원칙, 7크레이트 아키텍처, 61개 테스트, 그리고 전체 여정의 회고를 정리한다.&lt;/p&gt;
&lt;h2 id="1-claude-code-아키텍처의-8가지-한계점"&gt;1. Claude Code 아키텍처의 8가지 한계점
&lt;/h2&gt;&lt;p&gt;27세션의 분석에서 발견한 강점과 한계를 구분했다. 강점(AsyncGenerator 파이프라인, 3-tier 동시성, 훅 확장성, CLAUDE.md 디스커버리, MCP 지원, 자기 완결적 도구 인터페이스, 7가지 에러 복구)은 설계의 우수한 면이다. 그러나 다음 8가지 한계가 독자 하네스의 동기가 되었다:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;#&lt;/th&gt;
 &lt;th&gt;한계점&lt;/th&gt;
 &lt;th&gt;근거 세션&lt;/th&gt;
 &lt;th&gt;영향&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 의존 &amp;ndash; 무거운 TUI&lt;/td&gt;
 &lt;td&gt;S08&lt;/td&gt;
 &lt;td&gt;headless 모드에서 불필요한 의존&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;단일 프로바이더 (실질 Anthropic 전용)&lt;/td&gt;
 &lt;td&gt;S01&lt;/td&gt;
 &lt;td&gt;OpenAI, 로컬 모델 사용 불가&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;main.tsx 4,683줄 모놀리스&lt;/td&gt;
 &lt;td&gt;S01&lt;/td&gt;
 &lt;td&gt;CLI/REPL/세션이 단일 파일에 혼재&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;동기적 도구 실행 (Rust 포트)&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;스트리밍 파이프라이닝 불가&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;TS 생태계에 묶인 플러그인&lt;/td&gt;
 &lt;td&gt;S13&lt;/td&gt;
 &lt;td&gt;언어 중립적 확장 불가&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;85개 React 훅의 UI/런타임 혼재&lt;/td&gt;
 &lt;td&gt;S08&lt;/td&gt;
 &lt;td&gt;&amp;ldquo;hook&amp;rdquo; 용어의 이중 의미&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;7&lt;/td&gt;
 &lt;td&gt;프롬프트 캐싱의 암묵적 의존&lt;/td&gt;
 &lt;td&gt;S10&lt;/td&gt;
 &lt;td&gt;3가지 캐시 무효화 경로가 암묵적&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;8&lt;/td&gt;
 &lt;td&gt;MCP OAuth 2,465줄의 복잡성&lt;/td&gt;
 &lt;td&gt;S12&lt;/td&gt;
 &lt;td&gt;RFC 비일관성이 근본 원인&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="2-5가지-설계-원칙"&gt;2. 5가지 설계 원칙
&lt;/h2&gt;&lt;p&gt;한계점을 극복하기 위해 5가지 핵심 원칙을 정립했다:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;원칙 1 &amp;ndash; 멀티 프로바이더&lt;/strong&gt;: Anthropic, OpenAI, 로컬 모델(Ollama)을 단일 추상화로 지원.&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;는 프로바이더 중립적 구조체로, 각 구현체가 자신의 API 형식으로 변환한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;원칙 2 &amp;ndash; 네이티브 비동기&lt;/strong&gt;: 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; 채널 전달로 AsyncGenerator 패턴을 대체.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;원칙 3 &amp;ndash; 모듈 분리&lt;/strong&gt;: 대화 엔진, 도구, 훅, 프롬프트를 각각 독립 크레이트로 분리. &lt;code&gt;main.tsx&lt;/code&gt; 모놀리스를 반복하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;원칙 4 &amp;ndash; 언어 중립 확장&lt;/strong&gt;: SKILL.md 호환 + MCP 서버를 플러그인 단위로 활용.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;원칙 5 &amp;ndash; MCP 통합 활용&lt;/strong&gt;: 도구뿐 아니라 리소스, 프롬프트, 샘플링까지 전체 스펙 활용.&lt;/p&gt;
&lt;h2 id="3-7크레이트-아키텍처"&gt;3. 7크레이트 아키텍처
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;graph TD
 CLI["harness-cli&amp;lt;br/&amp;gt;REPL 바이너리"] --&gt; CORE["harness-core&amp;lt;br/&amp;gt;대화 엔진 + 턴 루프"]
 CORE --&gt; PROV["harness-provider&amp;lt;br/&amp;gt;LLM 프로바이더 추상화"]
 CORE --&gt; TOOLS["harness-tools&amp;lt;br/&amp;gt;도구 레지스트리 + 내장 도구"]
 CORE --&gt; HOOKS["harness-hooks&amp;lt;br/&amp;gt;훅 파이프라인"]
 CORE --&gt; PROMPT["harness-prompt&amp;lt;br/&amp;gt;CLAUDE.md 디스커버리"]
 CORE --&gt; MCP["harness-mcp&amp;lt;br/&amp;gt;MCP 클라이언트"]
 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;핵심 설계&lt;/strong&gt;: &lt;code&gt;harness-core&lt;/code&gt;만 다른 크레이트를 의존한다. 나머지는 서로 독립적이다(&lt;code&gt;harness-mcp&lt;/code&gt; -&amp;gt; &lt;code&gt;harness-tools&lt;/code&gt; 제외). 이 구조 덕분에:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 크레이트를 독립적으로 &lt;code&gt;cargo test&lt;/code&gt; 가능&lt;/li&gt;
&lt;li&gt;프로바이더 추가 시 &lt;code&gt;harness-core&lt;/code&gt; 수정 불필요&lt;/li&gt;
&lt;li&gt;MCP 도구가 내장 도구와 동일한 &lt;code&gt;Tool&lt;/code&gt; 트레이트 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;크레이트&lt;/th&gt;
 &lt;th&gt;핵심 책임&lt;/th&gt;
 &lt;th&gt;테스트 수&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 호출, SSE 파싱, 재시도&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;도구 레지스트리, 3-tier 동시성&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;셸 훅 실행, deny short-circuit, rewrite 체인&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단계 CLAUDE.md, SHA-256 중복 제거&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;대화 엔진, &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 트랜스포트&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 바이너리&lt;/td&gt;
 &lt;td&gt;&amp;ndash;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="provider-트레이트--멀티-프로바이더"&gt;Provider 트레이트 &amp;ndash; 멀티 프로바이더
&lt;/h3&gt;&lt;p&gt;기존 Rust 포트의 &lt;code&gt;ApiClient&lt;/code&gt; 트레이트는 Anthropic 전용이었다(&lt;code&gt;ApiRequest&lt;/code&gt;에 Anthropic 필드). &lt;code&gt;Provider&lt;/code&gt; 트레이트는 프로바이더 중립적 &lt;code&gt;ProviderRequest&lt;/code&gt;를 받아 각 구현체가 자신의 API 형식으로 변환한다. &lt;code&gt;Box&amp;lt;dyn Provider&amp;gt;&lt;/code&gt;로 런타임에 폴백 체인 구현 가능.&lt;/p&gt;
&lt;h3 id="conversationengine--턴-루프"&gt;ConversationEngine &amp;ndash; 턴 루프
&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;기존 Rust 포트의 &lt;code&gt;ConversationRuntime&amp;lt;C, T&amp;gt;&lt;/code&gt; 제네릭 패턴 대신 트레이트 객체를 사용한다. 런타임에 프로바이더를 교체할 수 있어야 하고(모델 폴백), 제네릭은 컴파일 타임에 타입이 고정되므로 유연성이 부족하다.&lt;/p&gt;
&lt;h3 id="스트리밍-도구-실행-파이프라이닝"&gt;스트리밍 도구 실행 (파이프라이닝)
&lt;/h3&gt;&lt;p&gt;기존 Rust 포트의 가장 큰 제약인 &amp;ldquo;모든 SSE 이벤트를 수집 후 도구 실행&amp;quot;을 해결했다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;EventStream&lt;/code&gt;에서 &lt;code&gt;ContentBlockStop(ToolUse)&lt;/code&gt; 이벤트 도착 시 즉시 전달&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_concurrency_safe()&lt;/code&gt; 검사 후 &lt;code&gt;tokio::spawn&lt;/code&gt;으로 병렬 처리&lt;/li&gt;
&lt;li&gt;API가 아직 스트리밍 중인 동안 도구 실행 병행&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="4-phase-2-회고--기존-포트-확장"&gt;4. Phase 2 회고 &amp;ndash; 기존 포트 확장
&lt;/h2&gt;&lt;p&gt;Phase 3의 독자 하네스 이전에, Phase 2에서 기존 &lt;code&gt;rust/&lt;/code&gt; 프로토타입을 확장했다:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;스프린트&lt;/th&gt;
 &lt;th&gt;성과&lt;/th&gt;
 &lt;th&gt;핵심 패턴&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;오케스트레이션 모듈 + 3-tier 동시성&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tokio::JoinSet&lt;/code&gt; 기반 병렬 실행&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;S16-S17&lt;/td&gt;
 &lt;td&gt;도구 확장 (19 -&amp;gt; 26개)&lt;/td&gt;
 &lt;td&gt;Task, PlanMode, AskUser 추가&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;S18-S19&lt;/td&gt;
 &lt;td&gt;훅 실행 파이프라인&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;스킬 디스커버리&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/skills/&lt;/code&gt; 스캔, 프롬프트 주입&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Phase 2의 코드 대부분은 Phase 3에서 재작성되었다. 그러나 &lt;strong&gt;프로토타입 과정에서 발견한 질문들&lt;/strong&gt;(&amp;ldquo;왜 AsyncGenerator인가?&amp;rdquo;, &amp;ldquo;왜 도구가 UI를 몰라야 하는가?&amp;quot;)이 최종 설계를 결정했다.&lt;/p&gt;
&lt;h2 id="5-61개-테스트와-mockprovider-패턴"&gt;5. 61개 테스트와 MockProvider 패턴
&lt;/h2&gt;&lt;p&gt;모든 크레이트가 독립적으로 테스트 가능하다. &lt;code&gt;MockProvider&lt;/code&gt;를 통해 실제 API 호출 없이 대화 엔진의 전체 턴 루프를 검증한다:&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="err"&gt;테스트&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSE&lt;/span&gt; &lt;span class="err"&gt;파싱&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;재시도&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;스트림&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="err"&gt;테스트&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;레지스트리&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;동시성&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;실행&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="err"&gt;테스트&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deny&lt;/span&gt; &lt;span class="err"&gt;단락&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rewrite&lt;/span&gt; &lt;span class="err"&gt;체인&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;타임아웃&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="err"&gt;테스트&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;단계&lt;/span&gt; &lt;span class="err"&gt;디스커버리&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;해시&lt;/span&gt; &lt;span class="err"&gt;중복제거&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="err"&gt;테스트&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;턴&lt;/span&gt; &lt;span class="err"&gt;루프&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;도구&lt;/span&gt; &lt;span class="err"&gt;호출&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;최대&lt;/span&gt; &lt;span class="err"&gt;반복&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="err"&gt;테스트&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="err"&gt;초기화&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;도구&lt;/span&gt; &lt;span class="err"&gt;목록&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-phase-1-2-교훈이-설계에-반영된-방식"&gt;6. Phase 1-2 교훈이 설계에 반영된 방식
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 subgraph Phase1["Phase 1 -- 이해"]
 direction TB
 P1A["S02: AsyncGenerator 체인"]
 P1B["S05: 42개 도구 분류"]
 P1C["S08: 런타임 vs React 훅"]
 P1D["S10: 6단계 CLAUDE.md"]
 P1E["S12: MCP 연결 관리"]
 P1F["S13: 스킬 = 프롬프트"]
 end

 subgraph Phase3["Phase 3 -- 독자 하네스"]
 direction TB
 P3A["EventStream + mpsc 채널"]
 P3B["Tool 트레이트 + 3-tier"]
 P3C["HookPipeline (런타임만)"]
 P3D["PromptAssembler 분리"]
 P3E["harness-mcp stdio"]
 P3F["SKILL.md 호환"]
 end

 P1A --&gt;|"yield -&gt; tx.send()"| P3A
 P1B --&gt;|"fail-closed 기본값"| P3B
 P1C --&gt;|"범위 축소"| P3C
 P1D --&gt;|"캐시 분할"| P3D
 P1E --&gt;|"SDK 없이 구현"| P3E
 P1F --&gt;|"텍스트 주입"| P3F

 style Phase1 fill:#e1f5fe
 style Phase3 fill:#fff3e0&lt;/pre&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;교훈&lt;/th&gt;
 &lt;th&gt;출처&lt;/th&gt;
 &lt;th&gt;설계 반영&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단계 상태 기계&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;harness-core&lt;/code&gt;에 async 구현&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;QueryDeps&lt;/code&gt; 콜백 DI의 타입 안전성 한계&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;트레이트 객체 DI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6층 Bash 보안 체인&lt;/td&gt;
 &lt;td&gt;S06&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;check_permissions()&lt;/code&gt; + 훅 분리&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;에이전트 = 하네스 재귀 인스턴스&lt;/td&gt;
 &lt;td&gt;S06&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ConversationEngine&lt;/code&gt; 재사용&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ApiClient&lt;/code&gt; 동기 트레이트가 파이프라이닝 차단&lt;/td&gt;
 &lt;td&gt;S03&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Provider&lt;/code&gt; async 트레이트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Deny 단락 + Rewrite 체이닝&lt;/td&gt;
 &lt;td&gt;S09&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;HookPipeline&lt;/code&gt; 동일 패턴&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SHA-256 콘텐츠 해시가 경로 해시보다 우수&lt;/td&gt;
 &lt;td&gt;S11&lt;/td&gt;
 &lt;td&gt;&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"&gt;7. 학습한 아키텍처 패턴 TOP 10
&lt;/h2&gt;&lt;p&gt;27세션에서 추출한 핵심 아키텍처 패턴:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AsyncGenerator/Stream 파이프라인&lt;/strong&gt;: 스트리밍 LLM 응답의 핵심 추상화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3-tier 도구 동시성&lt;/strong&gt;: ReadOnly/Write/Dangerous 분류로 안전성과 성능 균형&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ToolSpec + ToolResult 이원화&lt;/strong&gt;: 메타데이터(LLM 전달용)와 실행 결과 분리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;훅 체인 실행&lt;/strong&gt;: deny short-circuit, rewrite 체인, 독립 post-hook 변환&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;6단계 프롬프트 디스커버리&lt;/strong&gt;: managed -&amp;gt; user -&amp;gt; project -&amp;gt; local 오버라이드&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP 어댑터 패턴&lt;/strong&gt;: 외부 프로토콜 도구를 내부 Tool 트레이트로 통일&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider 추상화&lt;/strong&gt;: 동일 인터페이스로 Anthropic/OpenAI 교체 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSE 점진적 파싱&lt;/strong&gt;: 네트워크 청크를 이벤트 프레임으로 조립&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MockProvider 테스트&lt;/strong&gt;: 사전 정의 이벤트 시퀀스로 엔진 동작 검증&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;스킬 = 프롬프트&lt;/strong&gt;: 복잡한 플러그인 시스템 대신 텍스트 주입으로 충분&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="8-전체-여정-회고"&gt;8. 전체 여정 회고
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Phase&lt;/th&gt;
 &lt;th&gt;세션&lt;/th&gt;
 &lt;th&gt;핵심 산출물&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 1 &amp;ndash; 이해&lt;/td&gt;
 &lt;td&gt;S00-S13&lt;/td&gt;
 &lt;td&gt;14개 분석 문서, Rust 프로토타입&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 2 &amp;ndash; 재구현&lt;/td&gt;
 &lt;td&gt;S14-S21&lt;/td&gt;
 &lt;td&gt;오케스트레이션, 26 도구, 훅, 스킬&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Phase 3 &amp;ndash; 독자 하네스&lt;/td&gt;
 &lt;td&gt;S22-S27&lt;/td&gt;
 &lt;td&gt;7크레이트 워크스페이스, 61+ 테스트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Claude Code는 &lt;strong&gt;프롬프트 엔지니어링 런타임&lt;/strong&gt;이다. 핵심 루프가 메시지를 조립하고, 도구 시스템이 세상과의 상호작용 능력을 부여하고, 권한 시스템이 경계를 설정한다. CLAUDE.md가 컨텍스트를 주입하고, MCP가 외부를 통합하며, 훅과 에이전트가 자동화/위임을 가능케 하고, 플러그인/스킬이 사용자 확장 플랫폼으로 전환한다.&lt;/p&gt;
&lt;h3 id="향후-방향"&gt;향후 방향
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;진정한 스트리밍: SSE 바이트 스트림을 청크 단위로 처리&lt;/li&gt;
&lt;li&gt;권한 시스템: 도구별 사용자 승인 워크플로우&lt;/li&gt;
&lt;li&gt;MCP SSE 전송: stdio 외에 HTTP SSE 지원&lt;/li&gt;
&lt;li&gt;토큰 예산 통합: 컨텍스트 윈도우 예산 자동 관리&lt;/li&gt;
&lt;li&gt;멀티턴 에이전트 모드: 자율 반복 + 중단점 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="인사이트"&gt;인사이트
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;좋은 추상화는 경계에서 나온다&lt;/strong&gt; &amp;ndash; Provider 트레이트, Tool 트레이트, HookRunner 트레이트. 모든 핵심 추상화는 모듈 간 경계를 정의하는 트레이트다. 기존 Rust 포트의 &lt;code&gt;ConversationRuntime&amp;lt;C, T&amp;gt;&lt;/code&gt; 제네릭은 컴파일 타임 보장이 강하지만, 런타임에 프로바이더를 교체하거나 MCP 도구를 동적 등록하는 시나리오에서 한계가 있었다. &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; 트레이트 객체가 약간의 vtable 비용으로 런타임 유연성을 확보한다. LLM API 지연시간(수백 ms~수 s) 대비 vtable 비용은 측정 불가능한 수준이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;프로토타입의 가치는 코드가 아니라 질문이다&lt;/strong&gt; &amp;ndash; Phase 1-2의 프로토타입 코드 대부분은 Phase 3에서 재작성되었다. 그러나 &amp;ldquo;왜 AsyncGenerator인가?&amp;rdquo;, &amp;ldquo;왜 도구가 UI를 몰라야 하는가?&amp;rdquo;, &amp;ldquo;왜 allow가 bypass하지 않는가?&amp;rdquo; 같은 질문들이 최종 설계를 결정했다. 10만줄 코드를 읽는 행위 자체가 답이 아니라, 읽으면서 발견하는 **설계 의도(왜)**가 진정한 산출물이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TS 코드의 복잡성은 대부분 방어선이다&lt;/strong&gt; &amp;ndash; 권한 계층, 프론트매터 파싱, 중복 제거, symlink 방지. 이것들은 기능이 아니라 방어선이다. Rust는 타입 시스템과 소유권 모델로 일부를 컴파일타임에 보장할 수 있지만, 파일시스템 보안과 사용자 설정 우선순위 같은 런타임 정책은 명시적으로 구현해야 한다. 27세션은 이 방어선의 지도를 그리는 과정이었고, 그 지도가 독자 하네스의 설계를 안내했다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;시리즈 완결. 전체 분석 문서는 &lt;a class="link" href="https://github.com/lsr/claw-code" target="_blank" rel="noopener"
 &gt;claw-code 리포지토리&lt;/a&gt;에서 확인할 수 있다.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>