# 2026-08-16 세션 — 코퍼스 감사 서브에이전트/워크플로 신설, CLAUDE.md 4분할

## 발단

사용자 질문 두 갈래로 시작:

1. 문서 툴링(`doc-check.py`)만으로는 **'실수'가 안 잡힌다** — `/code-review`
   같은 걸 핸드오버 전/커밋 전에 에이전트가 스스로 돌게 만들 수 있나?
2. 어렵거나 목적에 더 맞는 게 있다면, 프로젝트 서브에이전트를 구성하고
   CLAUDE.md에서 쓰도록 명시하는 쪽이 나을지. "summary를 실제 파일 한쪽으로
   옮겨서 해결"하는 방식으로는 **단순 언급으로 남는 stale**을 잡기 어렵고
   모든 곳에 적용도 어려워 보인다 — 현실적으로는 에이전트가 유의하고
   서브에이전트가 새 맥락에서 훑는 게 최선 아닌가.

추가로 "다른 사람들은 이런 큰 기술 계획 문서에 에이전트를 어떻게 쓰는지"
검색해서 stale/실수 전파의 현실적 완화책을 알아봐 달라는 요청.

## 조사 결과

### hook으로 code-review를 직접 부를 수는 없음

hook은 **셸 명령만** 실행한다. Claude 스킬이나 서브에이전트를 직접 호출하는
경로가 없다. 대신 실제로 쓰이는 패턴은 **PreToolUse 게이트**:
`git commit`을 매칭해 조건 미충족이면 `deny`하고, **거부 사유 문자열로
"서브에이전트를 먼저 돌려라"는 지시를 돌려주는** 것(imti.co의 "Pre-Commit
Review Gate"). 즉 hook이 직접 부르는 게 아니라, 에이전트가 부르도록
**막아서 유도**한다.

그 글이 남긴 캐비엇도 유효:
- 기준이 느슨하면 서브에이전트가 통과용 형식적 발견을 지어냄("review theater")
- 너무 엄격하면 사소한 걸로 라운드가 5번씩 돌아 게이트가 아끼려던 시간을 까먹음
- 유효성 기준을 **diff 해시**로 잡으면 포매팅만 바뀌어도 재감사가 걸림(브리틀)

**quad엔 diff 해시 기준이 안 맞는다**는 판단: 이 프로젝트의 반복 실패 패턴은
"diff 안"이 아니라 **diff 밖 파일에 남은 stale 참조**다. 기준을 잡는다면
`.claude/**/*.md` 전체 해시여야 함.

### `/code-review`도 스킬이 맞음

marketplace 플러그인의 `code-review.md`를 직접 읽어 확인. 구조는:
Haiku로 자격 검사 → 관련 CLAUDE.md 목록 수집 → **5개 병렬 Sonnet 렌즈**
(CLAUDE.md 준수 / 얕은 버그 스캔 / git blame·히스토리 / 과거 PR 코멘트 /
코드 주석 준수) → 발견마다 Haiku가 **0~100 confidence rubric**으로 채점 →
80 미만 필터링 → 코멘트. false-positive 예시 목록도 프롬프트에 명시돼 있음.

즉 사용자 추측대로 "프론트메터와 자연어로 적절히 적으면 되는" 것이고,
프로젝트 전용으로 다시 만드는 게 타당.

### 학계 자료는 새 게 별로 없었음

"LLM agent drift / stale context mitigation" 검색 결과는 대부분 이 프로젝트가
이미 경험적으로 재발견한 것("신선한 세션/맥락이 stale을 줄인다", "요약 압축",
"주기적 리셋") 수준. 실무에 추가로 얹을 만한 구체 기법은 없었음.

## 만든 것 1 — `.claude/agents/quad-doc-auditor.md`

읽기 전용(`tools: Read, Grep, Glob, Bash`), `model: sonnet`, `memory: project`.

절차를 고정으로 박음: `doc-check.py` 먼저 → 이번에 뒤집힌/확정된 주장 파악
(git diff + 대화 맥락, 없으면 최근 session 파일) → 그 키워드로 코퍼스 전체
grep해서 **부정당하는 본문 문장** 찾기 → archive 이전 여부 → 개수/목록
이중소스 → 인덱스 3레이어 동기화.

설계 판단 두 개:
- **수정 권한 없음.** 발견만 리포트하고 반영은 호출한 세션이 함. 감사와
  수정을 분리하는 게 지금 워크플로우와 맞음.
- **판정은 "확실/의심" 2단계뿐.** `/code-review`의 0~100 rubric은 diff 하나가
  대상일 때 얘기고, 코퍼스 정합성엔 과한 무게라고 봄. "발견 없으면 그대로
  발견 없음이라고 보고하라, 그럴듯해 보이려고 지어내지 마라"를 명시.

`memory: project`를 준 이유: "6개 문서" stale, "8차 세션" 오표기처럼 이
프로젝트에서 **실제로 반복된** 실수 유형을 지금은 사람이 CLAUDE.md 프로즈로
수기 기록 중인데, 그걸 에이전트 자신의 기억으로 축적시키는 게 맞다고 판단.

## 만든 것 2 — `.claude/workflows/quad-handover-audit.js`

사용자 지적이 핵심이었음: **"subagent가 문제를 항상 찾는다고 결정론적으로
보장되는 게 아니다. 메인 에이전트가 여럿 굴려서 커밋 전에 다 잡게 할 수
있나."** 그리고 "시간은 걸려도 괜찮다, 부정확성이 문제다. 멀티패스로 여러 번
굴려야 all consistent가 되기도 한다."

그래서 단발 호출이 아니라 **수렴 루프**로 구성:
- 라운드마다 `quad-doc-auditor`를 **병렬 3회** 독립 실행(서로 뭘 찾았는지
  모르게 — 비결정성을 커버리지로 바꾸는 방식)
- 새 발견(dedup)을 **파일별로 묶어** `general-purpose`가 즉시 반영
- **새 발견 없는 라운드가 연속 2번** 나올 때까지 반복(최대 6라운드 안전판)
- 다음 라운드 감사가 직전 라운드 수정의 검증을 겸함 → 별도 검증 단계 불필요

`git commit`은 **의도적으로 워크플로 밖**에 뒀음 — diff 재검토와 커밋 메시지
작성은 대화형 맥락이 필요.

과거 수동 감사의 발견 추이(8→7→11→9→4→0, 9→2→3→2→0→0)를 근거로 상수를
잡음(`MAX_ROUNDS = 6`).

## 첫 실측 — 실패, 그리고 더 나쁜 건 실패가 성공으로 보고된 것

커밋(`a1c0e44`) 후 바로 돌림. 결과:

```
converged: true, rounds: 2, totalFindingsFixed: 0
failures: parallel[0..2] agent type 'quad-doc-auditor' not found  (×2 라운드)
```

**에이전트 6개 전원 실패했는데 워크플로는 "수렴했다"고 보고했다.**

- 1차 원인은 예상됐던 것: `.claude/agents/`가 **세션 도중** 생긴 디렉토리라
  레지스트리에 없음(공식 문서: 감시자는 세션 시작 시 존재했던 디렉토리만 포함).
- **진짜 문제는 2차 원인**: 모든 패스가 `null`이 되면 `fresh.length === 0`이
  되고, 이게 "깨끗한 라운드"와 코드상 구분이 안 돼서 그대로 dry 카운트가
  올라가 수렴 판정이 났음. **감사 도구가 아무것도 감사하지 않고 통과 도장을
  찍는 것** — 감사 도구로서 최악의 실패 모드.

즉시 가드 두 개 추가:
1. 라운드의 감사 패스가 **전멸하면 throw**(수렴 판정 자체를 못 내게).
   일부만 실패하면 "커버리지가 그만큼 얕음"을 `log`.
2. **반영 에이전트가 실패하면 그 발견들을 `seen`에서 제거** — 안 그러면 다음
   라운드 감사가 같은 문제를 다시 찾아와도 dedup에 걸려 조용히 사라지고,
   안 고쳐진 채로 수렴 판정이 남(같은 클래스의 두 번째 버그).

### 부수 질문 — 서브에이전트 모델이 왜 Opus로 떴나

사용자가 진행 화면에서 `audit-r1-0 Opus 5 · failed`을 보고 물음(정의엔
`model: sonnet`인데). 답: **정의를 못 찾아서**. 공식 해석 순서는
`CLAUDE_CODE_SUBAGENT_MODEL` → 호출별 `model` → **정의의 frontmatter** →
메인 대화 모델인데, 정의가 없으니 3번이 통째로 건너뛰어지고 4번(당시
`/model opus`로 바뀐 Opus 5)으로 떨어진 것. 워크플로는 `opts.model`을 안
넘기므로 정의가 로드되면 `sonnet`이 이겨야 정상 — **재시작 후 첫 실행에서
표시 모델을 확인해야 확정.**

## CLAUDE.md 4분할

사용자 제안: CLAUDE.md가 사람이 검토하기 힘들다. `@name.md`로 flatten되어
컨텍스트에 들어가고 캐싱되니, 세션 히스토리 / 할 일 / 작업 방식+베이스
컨텍스트로 분화하는 게 나을 듯. **"같은 것끼리는 묶는다"가 아주 중요한
지점.** 그리고 `session-summary.md`는 절대 건들지 말라고 해놓고 **아예 전부
자동생성** 되게 하는 게 나을 것 같다.

### 실측 숫자

| 섹션 | 줄 수 | 비중 |
|---|---|---|
| 세션 히스토리 | 1231 | **80%** |
| 지금 할 일 | 116 | 8% |
| 작업 방식 | 92 | 6% |
| 계획 문서 구조 | 63 | 4% |
| 이 프로젝트가 뭔지 | 21 | 1% |
| 언어/모델 관례 | 12 | 1% |
| 합계 | 1537 | |

### 공식 문서에서 확인한 것 (셋 다 중요)

1. **권장치가 파일당 200줄**이고, 초과하면 컨텍스트만 더 쓰는 게 아니라
   **"준수를 줄입니다"** — 사용자가 관찰한 "파일 길어지면 실수 더 하는
   경향"이 문서에도 명시된 알려진 현상. 지금 7.7배 초과.
2. **`@import`는 컨텍스트를 안 줄인다** — "가져온 파일은 여전히 로드되고
   시작 시 컨텍스트 윈도우에 들어갑니다". 분할이 사는 건 사람 검토성 +
   에이전트 편집 정확도 + **파일 단위 자동생성 가능성**이지 토큰이 아님.
3. **블록 HTML 주석은 컨텍스트 주입 전 제거된다** — 사람용 메모를 토큰 0으로
   남길 수 있는 대신, **거기 쓴 지시는 에이전트가 못 본다.**

(3)에서 이 세션이 자기 함정을 밟음: 새 CLAUDE.md의 "새 서술을 여기 직접 쌓지
말 것"을 HTML 주석에 넣었다가, 그게 스트립된다는 걸 문서로 확인하고 본문으로
옮김. 주석엔 "이 주석은 주입 전 제거되니 사람용 메모만 넣을 것"을 남김.

부수 발견: **`.claude/rules/*.md`** 라는 별도 메커니즘이 있음 — `@` 없이 자동
로드되고 `paths:` frontmatter로 **"특정 파일 작업할 때만 로드"**가 됨. 지금은
`src/`가 없어 쓸모없지만, M0 들어가면 "Handler 작성 체크리스트"를
`src/**/Dispatch/*.luau`에만 물리는 식으로 쓸 수 있음.

### 결정 — session-summary는 import 안 함

내가 사용자 안보다 한 발 더 나가는 제안을 함: 세션 히스토리의 고유 가치는
**"어느 세션 파일을 열지 찾는 색인"**뿐이고(그 문서 스스로 "항상 읽을 필요
없음, 지금 유효한 설계는 `base/`가 소스"라고 명시해왔음), 색인은 grep으로
온디맨드 조회하면 되는 종류다 → import 안 걸면 로드가 1537 → ~325줄.

사용자 동의: *"나도 session-summary.md 는 차라리 @ 안 붙이고, 그냥 거기에
있긴 하니까 만약에 선행 맥락이 필요하다면 그 때 가서 느리게 확인하도록
두는게 맞아보임."*

이름은 `code-of-conduct.md`를 제안받았으나 **OSS에서 "기여자 행동강령"이라는
다른 뜻으로 굳어진 말**이라 피하는 게 좋겠다고 제안 → `conventions.md` 채택.

### 최종 구조

```
CLAUDE.md (39줄, 진입점)
├─ @.claude/conventions.md      108줄  언어/모델 관례 + 작업 방식
├─ @.claude/project-context.md   88줄  프로젝트 설명 + 문서 구조
└─ @.claude/todos.md            119줄  지금 할 일
   .claude/session-summary.md  1241줄  ← import 안 함(온디맨드)
```

매 세션 로드: **1537 → 354줄 (77% 감소)**. `session-summary.md`는 상단에
"자동생성 전환 예정, 전환 후엔 직접 편집 금지"를 명시해둠.

## 분할이 깨뜨린 상호참조 정리

20여 곳. 파일 그룹별 병렬 에이전트 3개(`base/` / `research/` /
루트+luau-test+audit)로 정정. 매핑은 `"지금 할 일"`→`todos.md`,
`"반복 조사 금지"`→`project-context.md`, `"세션 히스토리"`→`session-summary.md`,
SAFETY 원칙→`conventions.md`.

**가장 값진 결과는 base/ 담당 에이전트가 내 전제를 반박한 것**: 내가
"`dispatch-core-plan.md:41`의 CLAUDE.md 참조는 기존 stale"이라고 지시에
적어 보냈는데, 실제로는 **줄바꿈에 걸친 `.claude/initreq/tbox/CLAUDE.md`
참조**(tbox의 type-check/constraint-check 분리 원칙 인용)로 완전히 유효한
것이었음. 같은 패턴이 `slot-plan.md:1285`, `ref-plan.md:190`에도 있음.
에이전트가 "다른 에이전트에게 같은 전제를 넘겼다면 정정이 필요하다"까지
보고함 — 지시를 그대로 믿지 않고 검증한 게 정확히 이 감사 구조가 노린 것.

같은 에이전트가 `ref-plan.md:541`의 `위 "지금 할 일" 우선순위1 항목`도
발견 — 분할과 무관한 **원래부터 잘못된 참조**였고(그 "우선순위1"은
`research/pre-implementation-audit.md`의 용어), 진단이 확실해서
`pre-implementation-audit.md` 1-5로 정정.

**추측 수정을 금지한 게 효과가 있었음.** 진짜 orphan 3건은 손대지 않고
보고만 하게 했고, 코퍼스 전체 grep으로 인용된 원칙이 어디에도 없음을 확인:
- `modifier-plan.md:536` — CLAUDE.md의 "드문 오용/가상 미래 요구까지
  방어/최적화하려고 구조를 복잡하게 만들지 않는다" 원칙
- `v1-compat-plan.md:50` — CLAUDE.md에 "이 자동 위임/재렌더 매직은 v2에서
  폐기하기로 확정"이라 기록됐다는 서술
- `pre-implementation-audit.md:434` — CLAUDE.md가 M2/M3/M5에서 quad-debug
  훅 확장 지점을 고려하라고 명시해뒀다는 서술

셋 다 **분할 이전부터** 존재하지 않던 인용이다. 원칙을 복원할지 인용을
버릴지는 사용자 판단 영역이라 남겨둠.

## `doc-check.py` 갱신

1. **새 파일 4개를 `OURS` 정규식에 등록.** 안 하면 `.claude/todos.md`로 가는
   깨진 참조가 ERROR가 아니라 WARN으로만 잡힘 — 일곱 번째 세션에
   `store-semantics.md`로 실제로 당했던 사각지대와 같은 클래스.
2. **`is_history()` 헬퍼 신설** — `archive/`와 `session-summary.md`를 묶어
   절 참조/시한부 주장 검사에서 면제. 둘 다 "그때는 그렇게 적었다"가 정상인
   문서라 현재형 검사를 걸면 안 꺼지는 WARN만 쌓임.

최종 ERROR 0 유지(검사 대상 78 → 82개).

## `doc-include-plan.md` 단순화

이 분할이 그 플랜의 **설계 절반을 불필요하게 만들었음.** 원안은 원본에
`<!--#summary-->`, 인용처에 `<!--#include-->` 두 종류 마커를 두는 양방향
설계였는데, 세션 히스토리가 독립 파일이 되면서 목적지가 "손으로 쓴 파일 속
구간"이 아니라 **통째로 생성되는 파일**이 됨 → 목적지 마커가 필요 없어지고,
id도 불필요(파일명이 곧 순서이자 식별자). "목적지 마커가 손실되면?" 같은
실패 모드도 같이 사라짐. 파일럿 절을 이 정정으로 갱신.

## 남은 것

- **재시작 후 검증 필요**: (a) `/memory`로 `@import` 4개가 실제 로드되는지,
  (b) `quad-doc-auditor`가 레지스트리에 잡히는지, (c) 워크플로에서 그
  에이전트가 frontmatter의 `sonnet`으로 도는지(Opus로 뜨면 `opts.model`
  명시가 필요).
- **`session-summary.md` 자동생성 미착수** — 91개 세션 파일에 마커를 넣고
  생성기를 짜는 마이그레이션이 남음. 그 전까지는 손으로 관리 중이며 파일
  상단에 그렇게 명시해둠.
- 위 orphan 인용 3건.
