quad/.claude/session/2026-08-16-01-subagent-audit-and-claudemd-split.md
qwreey 8aeec7644f
docs: CLAUDE.md 4분할(매 세션 로드 1537→354줄), 워크플로 가짜 초록불 수정
## CLAUDE.md 분할
1537줄이라 (a) 사람이 검토 불가, (b) 공식 권장치(파일당 200줄) 7.7배
초과로 지침 준수도 자체가 저하, (c) 긴 파일 편집 시 에이전트 실수 증가.

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

세션 히스토리 1231줄(전체의 80%)은 그 문서 스스로 "항상 읽을 필요 없음,
base/가 소스"라고 명시해온 색인이라 @import에서 뺐음. 내용 유실 없음
(1537→1647줄, 추가 헤더만큼 증가).

주의: @import는 컨텍스트를 줄이지 않음(전부 로드됨). 분할이 사는 건
사람 검토성 + 편집 정확도 + 파일 단위 자동생성 가능성.
CLAUDE.md 계열의 블록 HTML 주석은 주입 전 제거되므로 지시는 본문에 쓸 것.

## 워크플로 가짜 초록불 수정
첫 실측에서 감사 에이전트 6개 전원 실패했는데 converged:true가 나왔음
(전멸하면 fresh가 비어 "깨끗한 라운드"와 구분 불가). 감사 도구 최악의
실패 모드라 (1) 전멸이면 throw, (2) 반영 에이전트 실패 시 그 발견을
seen에서 빼 다음 라운드가 재시도하도록 수정.

## 부수
- 분할로 깨진 상호참조 20여 곳 정정(병렬 에이전트 3개).
  ref-plan.md:541의 사전 존재 오류(→ pre-implementation-audit 1-5)도 정정.
- doc-check.py: 새 파일 4개를 OURS에 등록(안 하면 깨진 참조가 WARN으로만
  잡힘), is_history()로 session-summary.md를 archive/와 같이 면제.
- doc-include-plan.md: 목적지가 통째로 생성되는 파일이 되면서 양방향
  마커 설계의 절반(목적지 마커)이 불필요해져 단방향 생성으로 단순화.

doc-check.py ERROR 0 유지.

미해결: session-summary.md 자동생성 미착수(91개 세션 파일 마커 삽입 필요),
orphan 인용 3건(modifier-plan.md:536, v1-compat-plan.md:50,
pre-implementation-audit.md:434 — 분할 이전부터 존재).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019HvEKn9f67kkx2nGLG8PsP
2026-08-16 01:35:02 +09:00

15 KiB
Raw Blame History

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건.