나중에 저는 다음과 같은 내용을 자동으로 포함하고 컴팩트 기능을 투명하게 향상시키는 플러그인을 만들었습니다.
https://x.com/u1/status/2073742408555343968
Claude Code 의 Compact 가 실제로 작동하는 방식
먼저, Claude Code 의 컴팩트는 두 가지 경로를 통해 실행됩니다:
- 수동 컴팩트 (/compact): 사용자가 명시적으로 실행합니다. 언제든지 실행할 수 있습니다.
- 자동 컴팩트: 세션 컨텍스트 사용량이 한계에 도달했을 때 (실제로는 약 90-95%) 자동으로 실행됩니다. 사용자가 의도한 정확한 순간이 아니라, 갑자기 압축되었다는 것을 깨닫게 되는 턴 경계에서 실행됩니다.
두 경로 모두 내용은 동일합니다: 이전 대화 기록을 LLM 에 보내 자연어 요약을 만들고, 요약 + 최근 턴 + 시스템 프롬프트를 새로운 컨텍스트로 재구성합니다. 요약은 "무엇을 했는지"에 대한 내러티브 기반 설명이 되며, 결과적으로 "왜 그 선택을 했는지", "어떤 계획이 거부되었는지", "현재 어떤 단계에 있는지"와 같은 의사 결정 구조가 약화됩니다. 또한 압축이 한 번 실행되면 되돌릴 수 없는 작업이므로 압축 이전의 원시 로그로 돌아갈 수 없습니다.
훅 관점에서 보면 압축은 PostCompact 이벤트로 포착할 수 있습니다. 그러나 PostCompact 훅은 additionalContext 를 반환할 수 없기 때문에 압축 직후 에이전트에 직접 명령을 주입할 경로가 없습니다. 명령을 삽입하려면 이후의 UserPromptSubmit 이벤트를 거쳐야 합니다. 이러한 제약 조건이 이 글의 훅 디자인을 결정합니다.
표준 Compact 의 문제점
Claude Code 의 /compact 는 대화 기록을 요약하여 그 결과를 새 세션의 초기 컨텍스트로 배치합니다. 요약 자체는 합리적이지만, "앞으로 무엇을 할지에 대한 지침"보다는 "과거의 작업 기록"에 가깝습니다. 결과적으로 압축 후 에이전트는 다음과 같은 오해에 빠지기 쉽습니다:
- "작업 지침"과 "작업 기록"의 경계를 잃음. 요약에 "플랜 A 가 고려되었다"고 나와 있으면 에이전트는 실제로 채택된 플랜 B 대신 플랜 A 를 구현하기 시작할 수 있습니다.
- 검증 단계와 구현 단계를 혼동함. "배포 전에 실제 기기에서 검증하라"는 전제가 요약에서 빠지고, 에이전트가 다음 턴에 파괴적인 배포 작업을 뛰어넘을 수 있습니다.
- 이미 기각된 잘못된 접근 방식을 다시 제안함. 요약에 접근 방식이 "시도되었다"고 기록될 수 있지만 "거부된 이유"는 기록되지 않을 수 있습니다.
- 플랜 모드, 워커 위임, 작업 트리와 같은 "세션 상태"를 잃음. 에이전트가 플랜 모드를 벗어난 상태에서 작업을 계속하거나 tmux 에서 실행 중인 워커의 존재를 잊고 직접 작업을 시작할 수 있습니다.
실제로 발생한 사고 유형
지난주 내 세션을 되돌아보면 컴팩트 직후에 최소 4가지 패턴을 관찰했습니다. 모두 "작업 지침"과 "작업 기록"의 분리 실패에 뿌리를 두고 있는 것으로 보입니다:
- 검증 전 배포: "파일을 배치하기 전에 기기에서 동작을 검증"하는 데 동의했지만, 에이전트는 압축 후 요약에서 완료 이미지만 보고 검증을 건너뛰고 대상을 덮어썼습니다.
- 기각된 단축 경로 재실행: 요약에 실패한 단축 경로 접근 방식이 "시도된 절차"로 기록되었기 때문에 에이전트가 이를 다시 실행하여 동일한 실패를 반복했습니다.
- 설계 원칙 망각: "대상 저장소 대신 소스 저장소를 편집하라"와 같이 세션 중에 수립된 원칙이 요약에서 누락되어 에이전트가 단기적으로는 작동하지만 재현 불가능한 직접 편집을 했습니다.
- 작업 목표 오해: 압축 전에 수정된 테스트의 특정 목적이 사라져 에이전트가 주변 조사로 방향을 틀고 완전히 다른 라인의 작업을 시작했습니다.
이러한 현상은 동일한 세션 내에서 여러 번 발생했으며, 각 컴팩트 후에 다른 유형의 오해가 나타났습니다. 저는 이것을 고립된 사고가 아닌 표준 컴팩트 사양의 구조적 결함으로 봅니다.
제가 만든 것
(1) compact-prep 스킬
사용자가 /compact 를 입력하기 전에 명시적으로 실행하는 슬래시 명령어로 구현된 스킬입니다. 요약에 포함되지 않을 가능성이 높은 "의사 결정 구조"와 "세션 상태"를 추출하여 고정 경로 (${TMPDIR}/claude-compact-state/<session_id>.md) 에 특정 형식의 상태 파일로 저장합니다.
저장되는 항목에는 요약으로 포착하기 어려운 다음 항목들이 포함됩니다:
- 활성 플랜 (플랜 파일 경로 및 현재 단계)
- 작업 목록 요약 (진행 중인 작업 및 보충 사항)
- 세션 결정 (채택된 계획 / 기각된 계획 / 기각 이유)
- 제약 사항 및 장애 요소
- 워커 토폴로지 (tmux-bridge 창 / 역할 / 할당)
- 편집 중인 파일 (저장되지 않았거나 검증되지 않은 변경 사항에 대한 메모)
- 복구 노트 (압축 후 자신에게 보내는 편지)
핵심 설계 포인트는 "기계적 강제성"입니다. session_id 가 없으면 파일 생성을 방지하는 하드 게이트, 누락을 감지하기 위한 고정 헤더가 있는 강제 함수, 상태 손상을 방지하기 위한 제한된 허용 도구를 사용합니다.
(2) 2단계 훅: PostCompact + UserPromptSubmit
(1)에서 상태 파일을 작성해도 압축 후 에이전트가 이를 읽지 않으면 의미가 없습니다. PostCompact 는 additionalContext 를 반환할 수 없기 때문에 2단계 구성을 사용했습니다:
- PostCompact 훅이 session_id 로 마커 파일을 작성합니다 (명령어 없이, 압축이 발생했음을 기록만 함).
- 다음 UserPromptSubmit 훅이 마커를 감지하면 additionalContext 를 통해 "플랜 파일 읽기 / 상태 파일 읽기 / 작업 목록 확인 / 요약의 다음 단계를 가설로 취급 / 플랜 모드가 비활성화된 경우 재진입 확인" 명령을 주입하고 마커를 삭제합니다.
Claude Code 에는 훅 간 공유 상태 메커니즘이 없기 때문에 파일 시스템의 마커가 유일한 통신 경로입니다. 이렇게 하면 각 훅이 단일 책임으로 가볍게 유지됩니다. 일반 턴에서는 test -f 검사 한 번의 오버헤드만 있습니다.
(3) 60% 알림 (자동 컴팩트 방지)
이전 도구들은 사용자가 수동으로 /compact-prep → /compact 를 실행한다고 가정합니다. 그러나 자동 컴팩트는 경고 없이 실행되어 이 흐름을 우회합니다. 자동 컴팩트가 먼저 실행되면 상태 파일이 저장되지 않고 원시 로그가 요약으로 압축되어 복구 자료가 손실됩니다.
해결책은 자동 컴팩트가 실행되기 전에 사용자가 수동으로 준비를 실행할 수 있는 상태를 만드는 것입니다. 이것이 60% 알림의 목적입니다.
60%인 이유는?
- 충분한 여유 시간: 자동 컴팩트는 약 90-95%에서 실행됩니다. 60%는 30%의 여유를 제공하여 사용자가 다음 턴에서 자동 컴팩트가 실행되기 전에 경고를 볼 수 있도록 합니다.
- 중단점에 도달할 여유: 60%에서는 하위 작업을 완료하고 상태를 저장하기 전에 30%의 용량이 남아 있습니다.
- 기계적 트리거: 컨텍스트 소비를 인지하지 못하고 집중하고 있을 때 강제 함수 역할을 합니다.
- 1M 컨텍스트 가정: 200K 컨텍스트에서는 60%가 너무 작습니다. 저는 1M 컨텍스트 (Opus 4.7) 를 사용하는데, 60%는 약 600K 토큰으로 작업을 마치고 준비를 실행하기에 충분한 여유 공간을 제공합니다.
구현에는 임계값에서 경고 마커를 작성하는 statusline 훅과 이를 감지하여 /compact-prep 실행을 제안하는 UserPromptSubmit 훅이 포함됩니다.
결과
이제 세션이 약 10번의 압축 후에도 논리적 붕괴 없이 안정적으로 유지됩니다!
- 압축으로 인한 작업 손실이 거의 없습니다.
- 기각된 아이디어의 재제안이 사라졌습니다.
- 플랜 모드가 유지됩니다.
- tmux 에서 여러 워커를 실행할 때 토폴로지를 잊어버리는 일이 없습니다.
참고: 구현 코드
다음은 실제 스킬 및 훅 코드입니다. 표준 Claude Code 디렉토리 (~/.claude/) 에 복사하여 작동합니다.
A: compact-prep 스킬 본문
~/.claude/skills/compact-prep/SKILL.md 에 배치합니다.
1---2name: compact-prep3description: |4 Before executing /compact in Claude Code, save the current session state to a temporary state file.5 MANDATORY TRIGGERS: /compact-prep, compact-prep, compression preparation.6 DO NOT TRIGGER: recovery after compact, normal progress reports, plan creation.7strict_procedure: true8argument-hint: "[recovery notes]"9allowed-tools: Read Write Bash(~/.claude/scripts/get-session-id.sh *) Bash(mkdir *) Bash(date *) Bash(pwd)10---1112# compact-prep1314Saves session state that is hard to keep in compression summaries to15`${TMPDIR}/claude-compact-state/${SESSION_ID}.md` before `/compact`.1617## Strict procedure profile1819- Strictness: strict-procedure.20- Hard gates: Stop if session_id cannot be retrieved.21- Forcing function: Fixed path and mandatory heading check after saving.22- Completion receipt: Report file path, saved items, and guidance to run `/compact`.2324## Procedure25261. Get session_id via `~/.claude/scripts/get-session-id.sh`.272. Set path to `${TMPDIR:-/tmp}/claude-compact-state/${SESSION_ID}.md`.283. Check TaskList, active plan, tmux-bridge, and editing files.294. Save headings in order: # Compact Prep State, ## Active Plan, ## Current Phase, ## TaskList Summary, ## Session Decisions, ## Constraints and Blockers, ## Worker Topology, ## Editing Files, ## Recovery Notes.305. Verify all headings exist.316. Tell user: "Preparation complete. Please execute `/compact`."
B: 복구 훅 (2단계)
B-1: PostCompact 훅
~/.claude/hooks/compaction-recovery.sh
B-2: UserPromptSubmit 훅 (주입)
~/.claude/hooks/userpromptsubmit-compaction-recovery.sh
C: 60% 알림
C-1: statusline 훅 임계값 분기
~/.claude/hooks/statusline.sh 에 추가되었습니다.
C-2: UserPromptSubmit 훅 (리마인더)
~/.claude/hooks/userpromptsubmit-compact-prep-reminder.sh
D: settings.json 등록
~/.claude/settings.json 에 훅을 추가합니다.





