<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Prompt-Assembly on ICE-ICE-BEAR-BLOG</title><link>https://ice-ice-bear.github.io/ko/tags/prompt-assembly/</link><description>Recent content in Prompt-Assembly 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/prompt-assembly/index.xml" rel="self" type="application/rss+xml"/><item><title>Claude Code 하네스 해부학 #4 — 런타임 훅 26+이벤트와 CLAUDE.md 6단계 디스커버리</title><link>https://ice-ice-bear.github.io/ko/posts/2026-04-06-harness-anatomy-4/</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-4/</guid><description>&lt;img src="https://ice-ice-bear.github.io/" alt="Featured image of post Claude Code 하네스 해부학 #4 — 런타임 훅 26+이벤트와 CLAUDE.md 6단계 디스커버리" /&gt;&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;Claude Code에서 &amp;ldquo;hook&amp;quot;이라는 단어는 &lt;strong&gt;두 가지 완전히 다른 시스템&lt;/strong&gt;을 가리킨다. 런타임 훅(&lt;code&gt;toolHooks.ts&lt;/code&gt; + &lt;code&gt;utils/hooks.ts&lt;/code&gt;)은 도구 실행 전후에 셸 스크립트를 실행하는 보안/확장 파이프라인이고, React 훅(&lt;code&gt;hooks/*.ts&lt;/code&gt; 85개+)은 터미널 UI의 상태 관리 코드다. 이 구분을 놓치면 Rust 재구현 범위를 85배 오판하게 된다. 이번 포스트에서는 런타임 훅의 PreToolUse/PostToolUse 파이프라인과 &lt;code&gt;resolveHookPermissionDecision()&lt;/code&gt;의 보안 불변식, 85개 React 훅의 9개 카테고리 분류, 그리고 CLAUDE.md 6단계 디스커버리와 토큰 버짓 관리를 분석한다.&lt;/p&gt;
&lt;h2 id="1-런타임-훅-vs-react-훅--핵심-구분"&gt;1. 런타임 훅 vs React 훅 &amp;ndash; 핵심 구분
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;차원&lt;/th&gt;
 &lt;th&gt;런타임 훅 (toolHooks.ts + utils/hooks.ts)&lt;/th&gt;
 &lt;th&gt;React 훅 (hooks/*.ts)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;실행 주체&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;child_process.spawn()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;React 렌더 사이클&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;구성 방법&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;settings.json &lt;code&gt;hooks&lt;/code&gt; 필드, 셸 커맨드&lt;/td&gt;
 &lt;td&gt;소스코드 &lt;code&gt;import&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;실행 시점&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;도구 사용 전/후, 세션 시작 등 26+ 이벤트&lt;/td&gt;
 &lt;td&gt;컴포넌트 마운트/업데이트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;사용자 정의&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;가능 &amp;ndash; 사용자가 셸 스크립트 등록&lt;/td&gt;
 &lt;td&gt;불가능 &amp;ndash; 내부 코드&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;결과 형태&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;JSON stdout (allow/deny/ask/rewrite)&lt;/td&gt;
 &lt;td&gt;React state 변경&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Rust 재구현&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;필수 &amp;ndash; 도구 실행 파이프라인의 핵심&lt;/td&gt;
 &lt;td&gt;불필요 &amp;ndash; TUI 전용&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="2-pretooluse-파이프라인--7가지-yield-변형"&gt;2. PreToolUse 파이프라인 &amp;ndash; 7가지 yield 변형
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;runPreToolUseHooks()&lt;/code&gt;(toolHooks.ts:435-650)는 AsyncGenerator로 설계되어 있다. 도구 실행 전에 호출되며 다음 yield 타입들을 방출한다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;message&lt;/code&gt;&lt;/strong&gt;: 진행 상황 메시지 (훅 시작/에러/취소)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;hookPermissionResult&lt;/code&gt;&lt;/strong&gt;: allow/deny/ask 결정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;hookUpdatedInput&lt;/code&gt;&lt;/strong&gt;: 입력 재작성 (권한 결정 없이 입력만 변경)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;preventContinuation&lt;/code&gt;&lt;/strong&gt;: 실행 중단 플래그&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;stopReason&lt;/code&gt;&lt;/strong&gt;: 중단 사유 문자열&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;additionalContext&lt;/code&gt;&lt;/strong&gt;: 모델에 전달할 추가 컨텍스트&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;stop&lt;/code&gt;&lt;/strong&gt;: 즉시 중단&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;왜 AsyncGenerator인가?&lt;/strong&gt; 훅은 여러 개가 순차 실행되고, 각 훅의 결과가 다음 처리에 영향을 미친다. Promise 체이닝은 최종 결과만 반환하고, 이벤트 이미터는 타입 안전성이 없다. AsyncGenerator는 호출자가 각 결과를 소비하면서 중간에 중단할 수 있는 유일한 패턴이다.&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart TD
 subgraph "PreToolUse 파이프라인"
 A["toolExecution.ts&amp;lt;br/&amp;gt;도구 호출 시작"]
 B["runPreToolUseHooks()&amp;lt;br/&amp;gt;toolHooks.ts:435"]
 C["getMatchingHooks()&amp;lt;br/&amp;gt;utils/hooks.ts:1603"]
 D["settings.json hooks&amp;lt;br/&amp;gt;이벤트+패턴 매칭"]
 E["spawn() 셸 커맨드&amp;lt;br/&amp;gt;stdin: JSON, stdout: 결과"]
 F["HookResult 파싱&amp;lt;br/&amp;gt;allow / deny / ask / rewrite"]
 end

 subgraph "권한 해석"
 G["resolveHookPermission&amp;lt;br/&amp;gt;Decision()&amp;lt;br/&amp;gt;toolHooks.ts:332"]
 H{"훅 결과?"}
 I["allow: checkRule&amp;lt;br/&amp;gt;BasedPermissions()&amp;lt;br/&amp;gt;규칙이 훅을 오버라이드"]
 J["deny: 즉시 거부"]
 K["ask: canUseTool()&amp;lt;br/&amp;gt;사용자 프롬프트"]
 end

 subgraph "도구 실행"
 L["tool.call()"]
 end

 subgraph "PostToolUse"
 M["runPostToolUseHooks()&amp;lt;br/&amp;gt;결과 변환 / 차단"]
 end

 A --&gt; B --&gt; C --&gt; D --&gt; E --&gt; F --&gt; G --&gt; H
 H --&gt;|"allow"| I
 H --&gt;|"deny"| J
 H --&gt;|"ask"| K
 I --&gt;|"규칙 통과"| L
 L --&gt; M&lt;/pre&gt;&lt;h3 id="resolvehookpermissiondecision--allow--bypass"&gt;resolveHookPermissionDecision &amp;ndash; allow != bypass
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;resolveHookPermissionDecision()&lt;/code&gt;(toolHooks.ts:332-433)의 핵심 불변식: &lt;strong&gt;훅의 &lt;code&gt;allow&lt;/code&gt;가 settings.json의 deny/ask 규칙을 바이패스하지 않는다&lt;/strong&gt; (toolHooks.ts:325-327).&lt;/p&gt;
&lt;p&gt;처리 로직 3단계:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1단계 &amp;ndash; allow 처리&lt;/strong&gt; (toolHooks.ts:347-406):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hookResult.behavior === &amp;#39;allow&amp;#39;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; checkRuleBasedPermissions() 호출
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; null -&amp;gt; 규칙 없음, 훅 허용 통과
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; deny -&amp;gt; 규칙이 훅을 오버라이드 (보안 우선!)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; ask -&amp;gt; 사용자 프롬프트 필요
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;왜 allow가 bypass하지 않는가?&lt;/strong&gt; 이것은 의도적 보안 결정이다. 외부 셸 스크립트가 &lt;code&gt;{&amp;quot;decision&amp;quot;:&amp;quot;allow&amp;quot;}&lt;/code&gt;를 반환한다고 해서 &lt;code&gt;settings.json&lt;/code&gt;의 &lt;code&gt;deny&lt;/code&gt; 규칙을 무시하면, 악의적 훅이 보안 정책을 우회할 수 있다. &lt;strong&gt;규칙은 항상 훅보다 우선한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2단계 &amp;ndash; deny&lt;/strong&gt; (toolHooks.ts:408-411): 즉시 거부, 추가 검사 없음.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3단계 &amp;ndash; ask/없음&lt;/strong&gt; (toolHooks.ts:413-432): &lt;code&gt;canUseTool()&lt;/code&gt; 호출로 사용자 프롬프트.&lt;/p&gt;
&lt;h3 id="26개-이상의-이벤트-유형"&gt;26개 이상의 이벤트 유형
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;getMatchingHooks()&lt;/code&gt;(utils/hooks.ts:1603-1682)이 훅 매칭을 담당한다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;도구 이벤트&lt;/strong&gt;: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세션 이벤트&lt;/strong&gt;: SessionStart, SessionEnd, Setup&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;에이전트 이벤트&lt;/strong&gt;: SubagentStart, SubagentStop, TeammateIdle&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;작업 이벤트&lt;/strong&gt;: TaskCreated, TaskCompleted&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;시스템 이벤트&lt;/strong&gt;: Notification, ConfigChange, FileChanged, InstructionsLoaded&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;컴팩트 이벤트&lt;/strong&gt;: PreCompact, PostCompact&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;입력 이벤트&lt;/strong&gt;: UserPromptSubmit, Elicitation, ElicitationResult&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;중단 이벤트&lt;/strong&gt;: Stop, StopFailure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;매칭된 훅들은 &lt;strong&gt;순차 실행&lt;/strong&gt;되며, 하나가 deny하면 이후 훅은 실행되지 않는다.&lt;/p&gt;
&lt;h2 id="3-85개-react-훅--9개-카테고리-분류"&gt;3. 85개 React 훅 &amp;ndash; 9개 카테고리 분류
&lt;/h2&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;mindmap
 root(("TS 훅 시스템"))
 런타임 훅
 toolHooks.ts 651줄
 PreToolUse
 PostToolUse
 PostToolUseFailure
 utils/hooks.ts ~5000줄
 26+ 이벤트 유형
 셸 스폰
 비동기 프로토콜
 React 훅 85+
 권한 3개
 useCanUseTool
 PermissionContext
 UI 입력 11개
 useTextInput
 useVimInput
 useTypeahead
 UI 디스플레이 11개
 useVirtualScroll
 useDiffData
 상태/설정 12개
 useSettings
 useSessionBackgrounding
 통합/원격 12개
 useRemoteSession
 useReplBridge
 기능 20개
 useVoice
 useSwarm
 useTasks
 알림 16개
 notifs/ 디렉토리
 도구/키바인딩 5개
 useMergedTools
 추가 5+개
 fileSuggestions
 useManagePlugins&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;Rust 재구현&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;/td&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;부분 (브릿지)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useCanUseTool&lt;/code&gt; (203줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UI 입력&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useTextInput&lt;/code&gt; (529줄), &lt;code&gt;useVimInput&lt;/code&gt; (316줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UI 디스플레이&lt;/td&gt;
 &lt;td&gt;11&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useVirtualScroll&lt;/code&gt; (721줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;상태/설정&lt;/td&gt;
 &lt;td&gt;12&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useSessionBackgrounding&lt;/code&gt; (158줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;통합/원격&lt;/td&gt;
 &lt;td&gt;12&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useRemoteSession&lt;/code&gt; (605줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;기능/알림&lt;/td&gt;
 &lt;td&gt;20&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useVoice&lt;/code&gt; (1,144줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;알림/배너&lt;/td&gt;
 &lt;td&gt;16&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;notifs/&lt;/code&gt; 디렉토리&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;도구/키바인딩&lt;/td&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;useMergedTools&lt;/code&gt; (44줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;추가&lt;/td&gt;
 &lt;td&gt;5+&lt;/td&gt;
 &lt;td&gt;불필요&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;fileSuggestions&lt;/code&gt; (811줄)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;핵심&lt;/strong&gt;: Rust가 재구현해야 하는 것은 &lt;code&gt;toolHooks.ts&lt;/code&gt;(651줄) + &lt;code&gt;utils/hooks.ts&lt;/code&gt;(~5,000줄)의 런타임 파이프라인뿐이다. 85개 React 훅 15,000줄+은 범위 밖이다.&lt;/p&gt;
&lt;h2 id="4-claudemd-6단계-디스커버리"&gt;4. CLAUDE.md 6단계 디스커버리
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;claudemd.ts&lt;/code&gt;(1,479줄)의 &lt;code&gt;getMemoryFiles()&lt;/code&gt;(L790-1074)는 6단계 계층으로 CLAUDE.md를 로드한다:&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. Managed&lt;/td&gt;
 &lt;td&gt;조직 정책&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/etc/claude-code/CLAUDE.md&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;최저&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2. User&lt;/td&gt;
 &lt;td&gt;개인 습관&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;, &lt;code&gt;~/.claude/rules/*.md&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3. Project&lt;/td&gt;
 &lt;td&gt;프로젝트 규칙&lt;/td&gt;
 &lt;td&gt;cwd에서 루트까지 &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.claude/rules/*.md&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4. Local&lt;/td&gt;
 &lt;td&gt;로컬 오버라이드&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;CLAUDE.local.md&lt;/code&gt; (gitignore)&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5. AutoMem&lt;/td&gt;
 &lt;td&gt;자동 메모리&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;MEMORY.md&lt;/code&gt; 엔트리포인트&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6. TeamMem&lt;/td&gt;
 &lt;td&gt;팀 메모리&lt;/td&gt;
 &lt;td&gt;조직 간 동기화&lt;/td&gt;
 &lt;td&gt;최고&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;왜 이 순서인가?&lt;/strong&gt; 파일 주석(L9)이 명시한다: &amp;ldquo;Files are loaded in reverse order of priority.&amp;rdquo; LLM은 프롬프트 후반부에 더 주의를 기울이므로, 가장 구체적인 지시(Local &amp;gt; Project &amp;gt; User &amp;gt; Managed)가 &lt;strong&gt;마지막에 위치&lt;/strong&gt;한다. 이것은 CSS specificity가 아니라 &lt;strong&gt;LLM 주의 편향&lt;/strong&gt;을 활용한 설계다.&lt;/p&gt;
&lt;h3 id="디렉토리-상향-탐색과-중복-방지"&gt;디렉토리 상향 탐색과 중복 방지
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;originalCwd&lt;/code&gt;에서 파일시스템 루트까지 올라간 뒤 &lt;code&gt;dirs.reverse()&lt;/code&gt;로 &lt;strong&gt;루트부터 아래로&lt;/strong&gt; 처리한다 (L851-857). 모노레포에서 상위 &lt;code&gt;CLAUDE.md&lt;/code&gt;가 먼저 로드되고 하위 프로젝트의 &lt;code&gt;CLAUDE.md&lt;/code&gt;가 그 위에 오는 효과를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;워크트리 중복 방지&lt;/strong&gt; (L868-884): git 워크트리가 메인 레포 내부에 중첩되면 동일 &lt;code&gt;CLAUDE.md&lt;/code&gt;가 두 번 로드되는 것을 &lt;code&gt;isNestedWorktree&lt;/code&gt; 검사로 방지한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;@include 지시자&lt;/strong&gt; (L451-535): 마크다운 토큰을 렉싱하여 코드 블록 내부의 &lt;code&gt;@path&lt;/code&gt;는 무시하고, 텍스트 노드의 &lt;code&gt;@path&lt;/code&gt;만 재귀적으로 해석한다. 최대 깊이 5.&lt;/p&gt;
&lt;h2 id="5-시스템사용자-컨텍스트-분리--dual-memoize-캐시"&gt;5. 시스템/사용자 컨텍스트 분리 &amp;ndash; dual-memoize 캐시
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;context.ts&lt;/code&gt;(189줄)는 시스템 프롬프트를 &lt;strong&gt;두 개의 독립적인 컨텍스트&lt;/strong&gt;로 분리한다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;getSystemContext()&lt;/code&gt;&lt;/strong&gt; (L116): git 상태, 캐시 브레이커&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;getUserContext()&lt;/code&gt;&lt;/strong&gt; (L155): CLAUDE.md 머지 문자열, 현재 날짜&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;왜 둘로 나누었는가?&lt;/strong&gt; Anthropic API의 프롬프트 캐싱 전략 때문이다. git 상태(세션 고정)와 CLAUDE.md(파일 변경 시에만 무효화)의 캐시 수명이 다르므로, &lt;code&gt;cache_control&lt;/code&gt;을 서로 다르게 적용해야 한다. 두 함수 모두 &lt;code&gt;memoize&lt;/code&gt;로 감싸져 세션 내 한 번만 실행된다.&lt;/p&gt;
&lt;h3 id="3가지-캐시-무효화-경로"&gt;3가지 캐시 무효화 경로
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;setSystemPromptInjection()&lt;/code&gt; (context.ts:29): 양쪽 캐시 모두 클리어&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clearMemoryFileCaches()&lt;/code&gt; (claudemd.ts:1119): 메모리 파일만 클리어&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resetGetMemoryFilesCache()&lt;/code&gt; (claudemd.ts:1124): 메모리 파일 클리어 + &lt;code&gt;InstructionsLoaded&lt;/code&gt; 훅 발화&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 분리는 워크트리 전환(리로드 불필요)과 실제 리로드(compaction 후)를 구분하기 위함이다.&lt;/p&gt;
&lt;h2 id="6-토큰-버짓--응답-연속-결정"&gt;6. 토큰 버짓 &amp;ndash; 응답 연속 결정
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;tokenBudget.ts&lt;/code&gt;(93줄)의 &lt;code&gt;checkTokenBudget()&lt;/code&gt;은 &lt;strong&gt;프롬프트 크기가 아닌 응답 연속 여부&lt;/strong&gt;를 제어한다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;COMPLETION_THRESHOLD = 0.9 -- 90% 미만이면 계속
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DIMINISHING_THRESHOLD = 500 -- 3회+ 연속, 매번 500토큰 미만 -&amp;gt; 수확 체감
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if (!isDiminishing &amp;amp;&amp;amp; turnTokens &amp;lt; budget * 0.9) -&amp;gt; continue
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if (isDiminishing || continuationCount &amp;gt; 0) -&amp;gt; stop with event
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;else -&amp;gt; stop without event
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;왜 0.9인가?&lt;/strong&gt; 모델이 버짓 근처에서 요약을 시작하는 경향이 있다. 90%에서 멈추면 &amp;ldquo;마무리 요약&amp;quot;을 하지 않고 작업을 계속하게 만든다. &lt;code&gt;nudgeMessage&lt;/code&gt;가 명시적으로 &amp;ldquo;do not summarize&amp;quot;를 지시한다.&lt;/p&gt;
&lt;p&gt;수확 체감 감지는 모델이 반복적 패턴에 빠지는 것을 방지한다. &lt;strong&gt;서브에이전트는 즉시 stop&lt;/strong&gt; (L51) &amp;ndash; 자체 버짓을 갖지 않는다.&lt;/p&gt;
&lt;h2 id="rust-대조"&gt;Rust 대조
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;관점&lt;/th&gt;
 &lt;th&gt;TS&lt;/th&gt;
 &lt;th&gt;Rust&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;훅 이벤트 유형&lt;/td&gt;
 &lt;td&gt;26개+&lt;/td&gt;
 &lt;td&gt;PreToolUse, PostToolUse 2개&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;훅 실행&lt;/td&gt;
 &lt;td&gt;비동기 AsyncGenerator&lt;/td&gt;
 &lt;td&gt;동기 &lt;code&gt;Command::output()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;훅 결과&lt;/td&gt;
 &lt;td&gt;7가지 yield 변형 + JSON&lt;/td&gt;
 &lt;td&gt;Allow/Deny/Warn 3가지 (exit code)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;입력 수정&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;hookUpdatedInput&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;불가능&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;allow != bypass&lt;/td&gt;
 &lt;td&gt;보장됨&lt;/td&gt;
 &lt;td&gt;미구현 (보안 취약)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CLAUDE.md&lt;/td&gt;
 &lt;td&gt;6단계 디스커버리&lt;/td&gt;
 &lt;td&gt;4후보 per dir&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;@include&lt;/td&gt;
 &lt;td&gt;재귀적, 깊이 5&lt;/td&gt;
 &lt;td&gt;미지원&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;토큰 버짓&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkTokenBudget()&lt;/code&gt; 3가지 결정&lt;/td&gt;
 &lt;td&gt;없음&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;프롬프트 캐시&lt;/td&gt;
 &lt;td&gt;memoize + 3가지 무효화&lt;/td&gt;
 &lt;td&gt;매번 빌드&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="인사이트"&gt;인사이트
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;hook&amp;quot;의 이중 의미가 아키텍처의 가장 큰 혼동원이다&lt;/strong&gt; &amp;ndash; 85개 React 훅은 Rust 재구현 범위에 포함되지 않는다. 런타임 훅(~5,600줄)만이 포팅 대상이다. 그러나 이 런타임 엔진은 26개 이벤트 유형, 비동기 프로토콜(&lt;code&gt;{&amp;quot;async&amp;quot;:true}&lt;/code&gt; 백그라운드 전환), 프롬프트 요청(stdin/stdout 양방향)까지 포함하는 복잡한 시스템이다. &amp;ldquo;훅&amp;quot;이라는 단어의 범위를 정확히 파악하는 것이 범위 산정의 출발점이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md의 &amp;ldquo;마지막이 더 강하다&amp;rdquo; 패턴은 LLM 주의 편향의 의도적 활용이다&lt;/strong&gt; &amp;ndash; 6단계 계층 로딩(Managed -&amp;gt; User -&amp;gt; Project -&amp;gt; Local -&amp;gt; AutoMem -&amp;gt; TeamMem)에서 가장 구체적인 지시가 프롬프트 끝에 위치하여 가장 강한 영향력을 갖는다. 이것은 아키텍처적 깔끔함이 아니라 &lt;strong&gt;API 프롬프트 캐싱 적중률 최적화 + LLM 행동 특성&lt;/strong&gt;의 교차점에서 나온 설계다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;resolveHookPermissionDecision()&lt;/code&gt;의 &amp;ldquo;allow != bypass&amp;rdquo; 불변식은 보안의 핵심이다&lt;/strong&gt; &amp;ndash; 현재 Rust hooks.rs는 exit code만으로 allow/deny를 판단한다. JSON 결과 파싱과 &lt;code&gt;checkRuleBasedPermissions&lt;/code&gt; 후속 검사를 구현하지 않으면, 악의적 훅이 deny 규칙을 우회할 수 있는 보안 취약점이 생긴다. 편의 자동화와 보안 정책의 경계를 명확히 하는 것이 훅 시스템의 근본 과제다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;다음 포스트: &lt;a class="link" href="https://ice-ice-bear.github.io/posts/2026-04-06-harness-anatomy-5/" &gt;#5 &amp;ndash; MCP 서비스와 플러그인 스킬 확장 생태계&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>