Claude Code 세션에 다는 정책 엔진. 재귀 스폰, 예산 소진, 반복 루프 같은 에이전트 폭주를 실행되기 전에 차단한다.
이름 그대로다: 퓨즈(fuse)를 단 관문(gate). 과부하가 나면 집 전체가 타기 전에 두꺼비집의 퓨즈가 먼저 끊어진다 — 그 퓨즈를 에이전트 실행 관문에 단다. 세션이 정책 상한을 넘는 순간, 해당 실행만 그 자리에서 끊긴다.
2026년 6월, Claude Code에서 서브에이전트가 재귀로 50단계까지 스스로를 복제하며 5분 만에 약 400만 토큰을 태운 사례가 보고됐다(anthropics/claude-code#68619, 아직 OPEN). 같은 시기 AI 에이전트가 프로덕션 DB를 삭제한 사건들도 잇따랐다 — Cursor 에이전트가 Railway API 호출로 DB와 백업을 수 초 만에 지운 건과, Replit 에이전트가 코드 프리즈 중 무승인 파괴를 일으켜 CEO가 공개 사과한 건은 서로 다른 사건이다(언론이 혼동해 보도한 전례가 있어 구분해 적는다 — AI Incident DB #1152, #1469).
피해자들의 결론은 한결같다: 프롬프트에 적은 규칙은 제안일 뿐이다. 강한 제약이 필요하면 시스템 수준의 경계가 필요하다. 모니터링 도구는 많지만 전부 표시 전용이다 — fusegate는 표시하지 않고 강제한다.
fusegate init 한 번으로 프로젝트에 훅 4종이 걸리고, 이후 모든 툴 실행 직전(PreToolUse)에
정책을 평가한다.
| 규칙 | 내용 | 스코프 |
|---|---|---|
depth |
재귀 스폰 깊이 상한 (기본 1 — 아래 실측 근거) | 세션 |
concurrent_session |
세션 내 동시 활성 서브에이전트 수 상한 | 세션 |
concurrent_global |
프로젝트 전역(모든 세션 합산) 동시 수 상한 | 전역 |
budget_session |
세션 토큰 예산 — 추정 기반(트랜스크립트 파싱) | 세션 |
budget_subagent |
서브에이전트 단위 토큰 예산(추정 기반) | 에이전트 |
repeat |
동일 툴 호출의 연속 반복 상한 | 세션 |
- enforce / warn 모드 — 규칙별로 고를 수 있다. enforce는 차단하고, warn은 차단 없이 모델이 관측 가능한 경고를 주입해 자가 교정을 유도한다.
- 예산 위반은 확장만 차단 — Agent 스폰만 막고 Bash·편집 등은 통과시켜, 진행 중 작업의 마무리(커밋·정리)를 잃지 않게 한다.
- 차단 메시지가 재시도를 막는다 — 차단 시 모델에게 "재시도하거나 다른 방법으로 우회하지 말 것, 정지하고 사용자에게 보고할 것"을 지시한다. 거부→재스폰 패턴(#68619의 악화 경로)을 막는 실측된 문구다.
- fail-open — 엔진이 어떤 식으로 고장 나도(정책 문법 오류, DB 손상, 버그) 세션은 절대 막히지 않는다. 통과시키고, 경고하고, 기록한다.
- 위반 텔레메트리 — 모든 판정이
violations테이블과events.jsonl에 남는다(차단, 경고, 킬스위치 통과, 엔진 장애 통과를 구분). - 관측 —
fusegate status(터미널)와fusegate dashboard(127.0.0.1 전용, 완전 읽기 전용 로컬 대시보드)로 위반, 활성 에이전트, 예산 추정을 조회한다. - 킬스위치 —
FUSEGATE_DISABLE=1하나뿐이다. 끄는 행위는 항상 명시적이고 기록된다. 몰래 우회하는 경로는 만들지 않았다.
| 위반 목록 | 타임라인 | 예산 |
|---|---|---|
![]() |
![]() |
![]() |
Claude Code에는 이미 전역 env var 상한이 있다: CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS,
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH, CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION. 쓰고 있다면
계속 쓰면 된다 — fusegate는 이 변수들을 건드리지 않고, 둘 다 설정돼 있으면 보수적인 쪽이 먼저
걸린다.
fusegate의 가치는 상한의 유무가 아니라 정책 계층이다: 전역 고정 노브가 아닌 프로젝트별 정책 파일, 규칙별 enforce/warn 선택, 예산과 반복 같은 상한 밖 규칙, 그리고 무엇이 언제 왜 차단됐는지의 위반 텔레메트리 — 벤더가 버그를 고치고 상한을 늘려도 프로젝트마다 다른 "정책"은 남는다.
재귀 스폰 폭주(#68619 서사)를 가드 없음 / depth=2 / depth=1 세 조건으로 대조 재현했다.
n=1 실측이며 모든 수치는 추정치다 — 재현 스크립트와 원본 증거는
measurements/에 있다(안전 상한 내장).
| 스폰 수 | 차단 | 중단 지점 | 토큰(추정)* | 비용(추정, CLI 보고) | |
|---|---|---|---|---|---|
| 가드 없음 | 4 | 0 | 자연 종료(안전 상한 내 관측) | 783,109 | $0.19 |
| depth=2 enforce | 5 | 0 | 자연 종료 — 차단 실패 | 1,040,701 | $0.25 |
| depth=1 enforce | 1 | 1 | 정책 차단 | 310,464 | $0.14 |
* 토큰 수치는 stream-json usage 4개 카테고리 합산이라 캐시 재사용(cache_read)이 포함된 누적 usage다. 신규 처리량이나 절감률 계산의 근거로 읽지 말 것 — 비용 감각은 CLI 보고 USD가 더 정확하다(두 추정 소스의 차이는 measurements/ 문서 참조).
이 실측의 핵심 발견은 차단 성공이 아니라 depth=2의 차단 실패다. 부모-자식 페어링 필드가
훅 페이로드에 없어 깊이를 동시 활성 수로 근사하는데, 부모가 먼저 종료되는 순차 사슬은 이
근사에 걸리지 않는다. 재귀 재스폰을 실제로 막는 설정은 depth=1뿐이었고, 기본값이 1인
이유다. 이 한계는 문서에 공개돼 있다(docs/tracking/findings.md).
pip install fusegate # PyPI: https://pypi.org/project/fusegate/
cd <보호할 프로젝트>
fusegate init # .fusegate/ 생성 + .claude/settings.json 훅 병합(백업, 멱등)정책은 .fusegate/policy.toml 하나로 조정한다(편집 즉시 적용):
[fusegate]
mode = "enforce" # 전역 기본: "enforce" | "warn"
[rules.depth]
limit = 1 # 규칙 테이블을 지우면 그 규칙은 비활성
[rules.budget_session]
limit_tokens = 2_000_000 # 추정 기반
# 모든 규칙에 mode = "warn" 오버라이드 가능일시 해제: FUSEGATE_DISABLE=1. 위반 확인: .fusegate/events.jsonl.
- 예산은 추정이다. 트랜스크립트에 기록된 usage를 파싱한 누적치라 실제 청구와 다를 수 있고, 아직 기록되지 않은 사용량만큼 뒤처질 수 있다. 과금 명세로 쓰지 말 것.
- 깊이는 근사다. 훅 페이로드에 부모-자식을 잇는 필드가 없어 동시 활성 수로 근사한다. limit=1은 정확히 동작하지만("메인의 직속 스폰만 허용"), 2 이상은 순차 사슬형 폭주를 놓칠 수 있다.
- CLI 2.1.215 실측 스냅샷 기반이다. 훅 계약(이벤트, 필드명, 차단 지점)은 버전업 시 재검증이 필요하다.
- fail-open이 철학이다. 게이트가 죽으면 보호도 사라진다 — 대신 세션은 절대 막히지 않고, 장애는 반드시 기록·경고로 드러난다.


