-
Notifications
You must be signed in to change notification settings - Fork 0
Code Review Report
이 문서는 Code-Review 요약 페이지의 상세 동반 보고서다. 5개 관점(유지보수성·안정성· 보안·LLM 친화성·동시작업 대응)에서 코드 전체를 정독 검수한 결과를 모든 발견을 빠짐없이 수록한다(요약본은 일부만 발췌).
-
대상: 지금까지 작성된 코드 전체 —
pytmuxlib/+ 플러그인 + 테스트, 약 64k LOC / 151개.py파일.-
pytmuxlib/*.py약 34k LOC - 플러그인 약 13.4k LOC
- 테스트 약 25k LOC
-
- 목적: 유지보수성·안정성·보안·LLM 친화성·동시작업 대응의 5개 관점에서 정독 검수하고, 각 관점별 개선 가능성을 발견·평가한다.
-
표기 규약:
- 심각도 H / M / L (보안은 Critical / High / Med / Low).
-
파일:라인표기는 검수 시점 기준이며, 이후 코드 변경으로 달라질 수 있다. - [검증됨] = 인용한 코드를 직접 읽어 확인함.
- 5개 관점별로 코드를 독립 정독(코드 직접 읽기 우선) + grep / wc / AST 기반 정량 측정 병행.
- 데이터 흐름 추적으로 read-then-write·fan-out 등 경로별 위험 식별.
- 선행 내부 감사 문서(보안 리뷰 F1
F8, 코드 감사 S1S6, 개선 기회 목록)의 결론과 대조하여 이미 적용된 완화책은 재보고하지 않고, 적용 상태 검증 + net-new 발견에 집중. -
미수행: 동적 익스플로잇, 런타임 벤치마크. 개선 적용 시 게이트는 전체 테스트 스위트 통과
(
python3 tests/run.py)이며 보안 건은 회귀 테스트 추가가 필수. - macOS / 실 PTY / 실 Claude 패널은 driver 검증 불가 영역(환경 제약).
본 보고서의 발견은 후속 스프린트에서 차례대로 수정 완료했다(각 수정 = 회귀 테스트 추가 + 전체 스위트 green). 테스트 수는 약 846 → 874로 증가(874 passed, 0 failed).
| 관점 | 처리한 발견 |
|---|---|
| 보안 | NEW-1 NEST 자동 승격 위조 차단(provenance 토큰 + 비로컬 endpoint 직결 금지) |
| 안정성 | H1 _UnixPty.write EAGAIN · H2 ptyhostclient 콜백 격리 · H3 ConPTY spawn 누수 · H4 _remote_transport 프로세스 reap · H5 재연결 first-status · M1 _on_eof executor · M2 keepalive · M3 스레드 join · M4 복원 부분실패 정리 · M5 원자적 상태 쓰기 · M7 host spawn 로깅 |
| 동시성 | H-1 / H-2 송신 fan-out 직렬화 + 느린 소비자 가드(+M6) · M-1 / M-2 릴레이 직렬화 + autorename 가드 |
| 유지보수성 | 1-3 usage_refresh_sec 영속 버그 + 대칭 테스트 · 1-9 죽은 코드 · 1-4 i18n 한글 노출 · 1-8 오버레이 dedup · 1-7 stale 주석/숫자 · 1-6 harness running_server() 컨텍스트 매니저 |
| LLM 친화성 | 4-2 포인터 주석 + 4-3 루트 온보딩 문서 · 4-B 모델 화이트리스트 외부화 |
- ✅ 1-1 · 4-1 (
client.pyGod 파일 분할): 응집 믹스인 3모듈 추출 —clientconn.py(재접속· 재시작 버전),clientcmd.py(명령),clientio.py(입력·렌더). 거동 불변·위치만 분리(self경유 MRO), import 헤더 원본 복제(over-import +noqa)로 이름 해석 누락 차단. 4017 → 1621줄(−2396), 874 green. - ✅ 4-4 (타입 힌트, 가치 부분): 값 반환 9개 훅에 반환 타입 명시. void 디스패처 16훅은
None반환이 자명하여 zero-signal churn 회피로 제외.
-
1-10 (pyte 경로 폐기) — 재조사로 순손해 확정:
model.py의 화면 버퍼가class _BCEScreen(_BCEMixin, pyte.Screen)로 pyte.Screen 기반이다.vt_parser = native|pyte는 토크나이저 front-end만 바꾸고 Screen back-end는 항상 pyte. 따라서 "pyte 경로 폐기"는 pyte 의존을 제거하지 못하고(model.py가 계속 필요) 동등성 오라클(native ≡ pyte)만 잃는다 — 데드코드 제거 이득 없이 커버리지만 손실. won't-fix 격상. -
1-2 (
_handle_cmd디스패치 테이블) — 재조사 확인: 72개 action 분기, fall-through(공통 broadcast tail) ↔ early-return(자체 응답)가 혼재하고 federation 전처리는 상태 변이 + 조기 탈출이 얽힌다. 단 1건의 fall-through/return 오분류로 명령이 조용히 깨지는데(broadcast 누락/중복) 명령 경로는 driver 검증 불가. 이득은 스타일뿐 → 보류 유지. -
1-5 (
Server베이스 지연 /make_server) — 재조사 확인:class Server(*_SERVER_BASES)는 import 시 1회 합성. 지연화 이득은 kill-server re-exec로 충족되어 0에 가깝고, Server 본문 전체를 팩토리로 떼는 foundational 변경 위험만 큼 → 보류 유지.
권고: 1-2는 명령 경로 driver 검증 수단이 생기기 전엔 보류. 1-10은 pyte.Screen을 자작 Screen으로 교체하지 않는 한 무의미(별개 대형 과제). 1-5는 트리거 부재.
규모(약 64k LOC) 대비 아키텍처 규율은 동급 TUI 프로젝트에서 보기 드물게 높다. 플러그인
경계의 청결함(delete-to-disable 41개 훅 + getattr 가드), 코어의 죽은 코드 부재(TODO/FIXME 0건),
견고한 와이어 프로토콜·VT 파서·IPC 보안 경계, 상시 퍼징, 868개 헤드리스 단위 테스트가 공통적으로
확인된 강점이다. 선행 감사의 완화책(F1F8, S1S6)은 전수 코드에 실재함을 확인했다.
그러나 5개 관점 모두에서 한 가지 근원 부채가 반복된다: 거대 단일 파일 / God 클래스
(client.py 약 3,981줄·129메서드 App, serverio._handle_cmd 113분기, claude-code/servermixin.py
600줄 단일 메서드). 이것이 유지보수 변경 비용·LLM 부분 수정 회귀·동시 편집 충돌 표면을 동시에
키운다. 그리고 신규 발견 중 단 1건의 실재 보안 취약점(NEST 자동 승격 — High)이 있다.
| 관점 | 등급 | 한 줄 요약 |
|---|---|---|
| 유지보수성 | B− (★★★½) | 플러그인 경계·코어 위생 우수 vs God 클래스·테스트 인프라 취약·문서 부채 |
| 안정성 | B | 핵심 IPC/프로토콜/파서/DB 견고 vs PTY-host·federation 경계 하드닝 필요 |
| 보안 | A− | 성숙도 매우 높음. 단 NEST 자동 승격에 net-new High 1건 |
| LLM 친화성 | B+ | 명시 디스패치·집중 훅 계약·풍부한 docstring 우수 vs 거대 파일·스크래핑 결합 |
| 동시작업 대응 | B | 단일 루프 + 동기 구조 변경으로 모델 동시성 견고 vs 송신 fan-out 계층 최약 |
- [보안 H] NEST 자동 승격 endpoint 직결 차단 — 유일한 실재 취약점. 가장 먼저.
-
[안정 H]
_remote_transportssh 프로세스 정리 +LimitOverrunError포착 — 좀비/fd 누수, 단일 최고 ROI. - [동시 H] 송신 fan-out slow-consumer 가드 + per-writer 직렬화 — 멀티 클라이언트 가용성 직결.
-
[유지보수 H]
Server베이스 합성 지연 +harness.running_app()컨텍스트 매니저 — 테스트 거짓 실패·복붙 동시 해소. -
[유지보수/LLM/동시 H] 거대 파일 분할(
client.pyMixin 물리 분리,test_client.py기능 분할) — 3개 관점 부채 동시 완화. - [안정 M] PTY-host/federation read 타임아웃·keepalive 도입 — 'wedge 영구 hang' 구조적 근원 봉합.
-
[A] 플러그인 경계 계약: "디렉토리 삭제 = 조용히 비활성화" 불변식이 코어 전역에서 지켜진다.
코어 → 플러그인은 41개 레지스트리 훅 +
getattr(pane, "_field", 기본)가드(6곳, 예servertree.py:511)로만 닿고, 코어가 플러그인을 직접 import하는 곳은 없다.test_plugin_contract.py가 강제. 이 프로젝트 유지보수성의 최대 강점. [검증됨] - [A−] 코어 위생: pytmuxlib 전수 grep — TODO/FIXME/XXX/HACK/주석 처리 코드 블록 0건.
| ID | 심각도 | 발견 |
|---|---|---|
| 5-1 | H |
client.py God 모듈 |
| 5-2 | H |
serverio._handle_cmd God 함수 |
| 5-3 | H | 코어 옵션 기본값 ↔ 영속 키 손중복 → 실버그 |
| 5-4 | H |
t() 우회 하드코딩 한국어 ~15개 |
| 5-5 | M | 소형 오버레이 3종 구조 복제 |
| 5-6 | M | 죽은/이중 정의 |
| 5-7 | M | 이중 VT 파서 잔재 |
| 5-8 | H | 테스트 _PLUGIN_SERVER_MIXINS import-time 동결 |
| 5-9 | H | 고정 pilot.pause(N) 남용 + 보일러플레이트 복붙 |
| 5-10 | M | 누적 문서 부채 + stale 산문 숫자 |
-
[H]
client.pyGod 모듈 (client.py:2675):PytmuxApp이 팩토리 함수 안 중첩 클래스로, 8개 믹스인(메서드 77개) + 본문(52개/1,306줄)을 MRO로 129개 메서드 끌어모은다. 믹스인 분리는 물리적 위치 분리일 뿐 논리적으로는 God 클래스다. 중첩이라 테스트가build_client_app()을 거쳐야만 접근 가능 → 단위 테스트 곤란.- 영향: 변경 비용·LLM 부분 수정 회귀·동시 편집 충돌 표면이 동시에 커진다.
-
개선/수정:
PytmuxApp을 모듈 레벨 클래스로 승격(생성자 인자화), 디스패치/렌더/입력을 협력 객체로 추출. → 수정 완료(응집 믹스인 3모듈clientconn.py·clientcmd.py·clientio.py추출, 4017 → 1621줄).
-
[H]
serverio._handle_cmdGod 함수 (serverio.py:418, 355줄/80분기/복잡도≈113): 단일 async 메서드가 모든 클라 명령을 if/elif로 처리. 명령별 단위 테스트 불가.-
개선:
_CMD_HANDLERS: dict[str, handler]테이블 디스패치. → 재조사 결과 fall-through/return 혼재 위험으로 보류 유지(상세는 §3).
-
개선:
-
[H] 코어 옵션 기본값 ↔ 영속 키 손중복 → 실버그 (
server.py:108-218로드 vsserverpersist.py:557-588_save_opts): 같은 23개 옵션을 두 파일에 수기 나열. 실버그:server.py:217이usage_refresh_sec를 로드하나_save_opts에 없음 → 사용자가 바꿔도 저장 시 소실, 다음 기동에 600으로 리셋. load ↔ save 대칭 테스트 부재.-
개선/수정:
_OPTS_KEYS = ((key, default, cast), ...)단일 테이블 패턴(드리프트 불가 모범,claude-code/__init__.py:571)을 코어에 적용. → 수정 완료(영속 버그 + 대칭 테스트).
-
개선/수정:
-
[H]
t()우회 하드코딩 한국어 ~15개 → 영어 사용자에게 한글 노출. 한글 누출 테스트 (test_i18n.py:218)는 카탈로그 값만 검사 → 위젯 리터럴 사각지대:claude-code/screens.py:869-900토큰 뷰어 탭바 11개 전부Label("기간")/"세션"/"한도"/"대사",usagelog.py:408,ncd/screen.py:39.- 개선/수정: 위젯/notice 한글 리터럴 금지 소스 리터럴 lint 테스트 추가. → 수정 완료.
-
[M] 소형 오버레이 3종 구조 복제 (~125–165줄): clock/calendar/claude-token-usage-view가
client_tick/오버레이 render 프리앰블/블록 폰트를 글자 단위로 복제(clock/render.py:33-42≈calendar/render.py:65-71≈ usageoverlay.py:29-37).-
개선/수정:
plugins/_overlaybase.py+ 코어clientrender.dim_pane()/draw_block_text()승격. 위험 최저. → 수정 완료(오버레이 dedup).
-
개선/수정:
-
[M] 죽은/이중 정의:
server.py:358 _tab_index_of(호출 0건, 삭제 가능),replay.py:23 _char_cells(clientutil.py:27lru_cache판 복사), SGR 변환 이중 구현(model._rewrite_sgr_tokenvsvtparse._sgr_params_from_raw, 한쪽만 고치면 드리프트). → 죽은 코드 수정 완료. -
[M] 이중 VT 파서 잔재: 기본 native인데 pyte 전용 우회 ~111줄(
model.py:286-332등) 잔존. pyte opt-in 폐기 결정 시 프로덕션 ~140줄 + 테스트 ~150–250줄 삭제 가능. → 재조사 결과 pyte.Screen이 back-end로 항상 필요해 won't-fix(상세는 §3). -
[H] 테스트
_PLUGIN_SERVER_MIXINSimport-time 동결 (server.py:36): import 순간Server베이스를plugins.load()결과로 영구 동결 → 부분 실행 시 claude 믹스인 없이 동결되어 거짓 실패. 곳곳 poison 가드(test_plugin_prompt_history.py:192조기 return)로 PASS/스킵이 불명확.-
개선:
Server베이스 합성을 import-time → 팩토리(make_server()) 시점으로 지연. → 재조사 결과 foundational 변경 위험으로 보류 유지(상세는 §3).
-
개선:
-
[H] 고정
pilot.pause(N)438회 vswait_until18회: 느린 CI에서 거짓 실패, 러너 재시도 2회(run.py:39)가 진짜 회귀를 FLAKY PASS로 무마. 보일러플레이트(srv, task, sock = await server_only()+ teardown) 214회 복붙.-
개선/수정:
harness.py에@asynccontextmanager running_app()→ 복붙을 한 줄로 +wait_until강제. → 수정 완료(harnessrunning_server()컨텍스트 매니저).
-
개선/수정:
-
[H] 누적 문서 부채: 날짜별 교훈 로그 23개가 코드 참조 0건, 고아 다수, 메모리 색인과 중복.
산문 속 숫자 클레임이 stale(
README.md:163"845 passed" 실측 868; 플러그인 개수 표기 불일치;server.py:114주석 "pyte 기본"이 아래default "native"와 모순).- 개선/수정: durable 교훈은 주제별 영속 문서로 승격, 날짜 로그는 archive 격리, 산문 숫자 → 불변식(테스트) 위임. → stale 주석/숫자 수정 완료.
정량: pytmuxlib 전체
except Exception:128곳. 단 대부분 의도적이며 좁게 잡혔다 — 핫패스는_log_error()로 트레이스백 기록(serverio.py:774), 정리 경로는contextlib.suppress, IPC는(OSError, ConnectionError)로 좁힘. 무로깅 광역 except의 실질 갭은 PTY-host 계층·일부 폴백에 집중.
-
와이어 프로토콜 (
protocol.py):MAX_FRAME=64MiB상한,read_msg가 EOF/비-JSON/decode 오류를 None 흡수,clamp_dim가드. 튼튼함. -
VT 파서 (
vtparse.py): 증분 FSM으로 feed 경계 분할·멀티바이트 carry 안전, OSC_OSC_MAX=4096캡, 과다 파라미터 arity 축소 재시도.tests/fuzz상시 퍼징(protocol·ptyhost·clamp·vtparse). -
IPC 보안 경계 (
ipc.py): 토큰·peer-UID·/tmp선점 거부·0600·TOCTOU-free. 견고. -
usagedb 마이그레이션 (
claude-code/usagedb.py:92-110): 단계별sqlite3.Error가드 + 실패 시 user_version 보류(재시도) + 멱등 staged + WAL. 데이터 손상 내성 양호.
| ID | 발견 | 영향 |
|---|---|---|
| H1 |
_UnixPty.write 부분 write/EAGAIN 미처리 |
입력 무음 유실 |
| H2 | PTY-host _read_loop 콜백 예외 광역 흡수 |
단일 패널 예외 → 전 패널 연결 오인 사망 |
| H3 | ConPTY spawn() 실패 시 핸들/프로세스 누수 |
조용한 폴백 + 매번 누수 |
| H4 |
_remote_transport ssh 서브프로세스 reap/cleanup 누락 |
좀비/fd 누수 (단일 최고 ROI) |
| H5 | 자동 재연결이 "hello 성공"을 "병합 성공"으로 오판 | 거짓 "재연결됨" + reader/proc 누적 |
-
[H1]
_UnixPty.write부분 write/EAGAIN 미처리 (pty_backend.py:291-292): fd가 논블로킹 (:202)인데os.write1회 호출 + 반환값 무시. 큰 페이스트/포화 시 입력 무음 유실 또는BlockingIOError가_handle_input(serverio.py:993)에서 무음 드롭. 세 PTY 계층 중 재현 가장 쉬움.-
수정:
memoryview잔여 루프 write + EINTR 재시도 + EAGAIN 시 양보/버퍼링. → 수정 완료.
-
수정:
-
[H2] PTY-host
_read_loop콜백 예외 광역 흡수 (ptyhostclient.py:59-76except Exception: pass): 한 패널 콜백 예외가 루프 break →_handle_lost()→ host는 멀쩡한데 전 패널 연결 죽음으로 오인, 로깅도 0.-
수정: 콜백 per-frame try/except 격리, 진짜 단절(
read_frame→ None)일 때만 break. → 수정 완료.
-
수정: 콜백 per-frame try/except 격리, 진짜 단절(
-
[H3] ConPTY
spawn()실패 시 HPCON/파이프 핸들·프로세스 누수 (conpty.py:529-588+pty_backend.py:625):__init__이 핸들 생성 후spawn()예외 시close()없이 버려짐. 상위 안전망(pty_backend.py:106-112except Exception: pass)이 로깅 0으로 삼킴 → "조용한 폴백 + 매번 누수".-
수정:
cp.spawn(...)try로 감싸 실패 시cp.close()+ 폴백 직전 1회 디버그 로깅. → 수정 완료.
-
수정:
-
[H4]
_remote_transportssh 서브프로세스 reap/cleanup 누락 (serverremote.py:206-246):wait_for(proc.stdout.readline(), 15)TimeoutError 시proc(로컬 변수) kill/wait 안 됨 → ssh 자식 + 3 fd 누수. 핸드셰이크 실패 경로(:222)도proc.kill()후await proc.wait()없어<defunct>회수 실패.readline()의LimitOverrunError(>64KiB 무개행)가 except(:280)에 안 잡혀 attach 태스크 미처리 예외. 단일 최고 ROI 수정.-
수정: try/finally로 모든 실패 경로
proc.kill(); await wait_for(proc.wait(), 2)보장 + except에LimitOverrunError/ValueError추가. → 수정 완료.
-
수정: try/finally로 모든 실패 경로
-
[H5] 자동 재연결이 "hello 성공"을 "병합 성공"으로 오판 (
serverremote.py:382-389): 대화형 attach는_remote_wait_first_status로 실제 탭 도착을 확인하나(serverio.py:442-452),_remote_reconnect_loop는remote_attach()반환값만 보고 그건 hello 송신 직후 True(:300). 업스트림 pty-host wedge 시 매번 "재연결됨" notice + 화면 안 옴 + reader/proc 누적.-
수정: 재연결 성공 기준을
_remote_wait_first_status통과로 강화(대화형 경로와 정합). → 수정 완료.
-
수정: 재연결 성공 기준을
| ID | 발견 | 영향 / 수정 |
|---|---|---|
| M1 |
_on_eof의 reap(block=True)가 이벤트 루프 블로킹 (ptyhost.py:198-213) |
전 패널 I/O 정지 위험 → executor로 이전, 수정 완료 |
| M2 | PTY-host/federation read 타임아웃·keepalive 부재 (ptyhostproto, serverremote.py:503) |
half-open wedge 영구 hang(구조적 근원). ping op 존재 → 앱 keepalive / TCP SO_KEEPALIVE → keepalive, 수정 완료
|
| M3 | owned reader/watcher 스레드 join 부재 (pty_backend.py:716,764) |
teardown 비결정·드문 stale EOF → 스레드 join, 수정 완료 |
| M4 |
restore_resume_state 부분 복원 시 master fd 누수 + except에 TypeError 누락 (serverpersist.py:256-279) |
변조 상태 파일 시 복원 전체 크래시 → 세션 전손. 부분 실패 정리, 수정 완료 |
| M5 | 재시작 상태 파일 비원자 쓰기 (serverpersist.py:131-133) |
execv 직전 사망 시 절반 JSON → 복원 실패. temp + os.replace(토큰/포트 파일은 이미 이 패턴), 수정 완료
|
| M6 | status 브로드캐스트 무추적 create_task(write_frames) 누적 |
§7 H-1과 동일 근원, 동반 수정 완료 |
| M7 |
ensure_connected host spawn 실패 무로깅 + 최대 6초 블로킹 (ptyhostmgr.py:85) |
spawn 로깅, 수정 완료 |
선행 보안 감사·코드 감사의 적용 상태를 실제 코드와 대조 검증 → 문서에 "적용됨"으로 기재된 완화책은 전수 코드에 실재함을 확인. 와이어 JSON 전용(
pickle/eval/exec/os.system/shell=True0건 재확인), subprocess 전수 argv, SQL 파라미터화, 토큰 + peer-UID 인증, 0600/0700 권한, OSC/CSI 하드닝, 런타임 퍼징까지 갖춘 매우 높은 성숙도. 그러나 그 이후 추가된 중첩(NEST) 경로에 net-new 취약점 1건.
위치: serverpty.py:179(_nest_dcs_scan 무조건 호출)·:232-246(ssh; DCS가 pane._ssh_dest
설정), serverremote.py:703-754(_nest_attach_request/_nest_do_attach — tcp:// 접두 시 endpoint
직결)·:185-203(_remote_transport endpoint 분기는 ssh 옵션 가드 미경유), server.py:123
(nest_auto_attach 기본 True).
전제: 패널에서 신뢰 불가 프로그램 출력을 보는 것만으로 성립(위협 모델 #4). 무인가 IPC 접근
불요. remote_allowed_hosts 기본 비어 있음.
시나리오:
-
_nest_dcs_scan은 모든 패널 raw 출력에 무조건 실행(provenance 게이트 없음). - 악성 출력(조작 파일
cat, 손상 원격 셸)이ESC P >|pytmux-ssh;<b64> ESC\DCS를 emit해pane._ssh_dest를 임의값(예:tcp:evil.example:9999)으로 설정. -
nest;REQ self-report도 동일값으로 맞춤. -
_nest_host_match(dest, selfreport)가드는 공격자가 양쪽을 제어하므로 자명 통과. -
_nest_do_attach가tcp:접두라 endpoint 직결 분기 →remote_attach→open_connection("evil.example", 9999)실 아웃바운드 TCP. - 클라가 그 탭을 보면 live 키 입력이 공격자 서버로 릴레이(MITM/피싱).
핵심: 내부 설계 문서는 "attach 인자는 self-report를 쓰지 않고 래퍼가 기록한 pane._ssh_dest만
쓰므로 새 호스트 아웃바운드 불가"라 단언하나, pane._ssh_dest 자체가 신뢰 불가 패널 출력(ssh;
DCS)에서 설정되므로 전제가 거짓. tcp:// 접두는 ssh 옵션 가드(-/공백 거부·allowlist)를 endpoint
분기로 완전 우회한다.
완화 현황(부분): ssh 분기면 옵션 가드가 막으나 endpoint 분기는 미경유. 디바운스(5s)·notice·
nest_auto_attach OFF 스위치는 있으나 기본 ON이고 notice 전 키 입력이 흐를 수 있음.
개선/수정:
- (핵심)
_nest_do_attach에서_ssh_dest발 dest는tcp://로컬 endpoint 직결 금지, 항상 ssh 호스트로만 해석 → ssh 옵션 가드 경유 강제. - (근본)
ssh;DCS provenance 강화: 서버가 panel_env에 심은 nonce를 래퍼가 DCS에 포함, 서버가 검증 → 임의 프로그램이_ssh_dest를 못 심게. 또는 자동 승격 대신 확인 팝업. - (즉효 심층 방어) NEST 자동 승격에도
remote_allowed_hostsallowlist 적용(기본 비어도 endpoint 거부) 또는nest_auto_attach기본 OFF.
→ 수정 완료: provenance 토큰 도입 + 비로컬 endpoint 직결 차단.
토큰 인증·peer-UID(TCP family는 None 반환)·state-dir lstat 거부·0600/0700·ssh 옵션 인젝션 거부(host
분기 한정)·SQL 전수 파라미터화(유일 f-string PRAGMA user_version={int} 안전)·역직렬화 무(pickle/eval/
exec/yaml 0, marshal.load는 로컬 신뢰 입력만)·subprocess 전수 argv·NEST DCS 정규식 {0,8192} 상한
(ReDoS 차단) — 전부 PRESENT.
-
[L] grep 가능성 우수: 동적 디스패치 거의 없음. 명령 라우팅은
_run_command(client.py:1130) 명시 if/elif 76분기 →grep '"split-h"'로 핸들러 직행.getattr454건은 대부분 플러그인 덕타이핑 한 곳(plugins/__init__.py)에 집중. 문자열 메서드 조립 메타프로그래밍 없음. -
[L] 빠른 검증: 868 헤드리스 테스트,
python tests/run.py종료코드 0 = 통과. 56개 중 16개만 드라이버, 나머지 순수 단위 → LLM 회귀 검증에 이상적. -
플러그인 훅 계약 모범:
plugins/__init__.py의Registry가 ~40훅을 한 곳에 docstring과 함께.
-
[H] Claude TUI 영어 스크래핑 결합 (
claude.py:16~591): 핵심 기능(상태/토큰/계정)이 Claude Code CLI의 영어 TUI를 정규식/문자열로 긁는다. 하드코딩 리터럴 20+개: 상태("esc to interrupt"/스피너 글리프✽✢✳✶✷✻), 한도(_LIMIT_BLOCKED_REreached|exceeded|hit),/usage파싱 (Current\s+(session|week)/(\d{1,3})%\s*used), 모델 화이트리스트(Opus|Sonnet|Haiku). 업스트림 문구 변경 시 깨진다.-
단 방어 상당: 계정은 이메일 신호만 신뢰 +
unknown폴백 + ReDoS 상한, 오탐 이력 docstring화,tests/fixtures/claude/실 캡처 7종 회귀망. -
약점: 진짜 한도 차단(
limit.txt) 등 3종은 합성/미해결 → 가장 중요한 한도 경로 실측 미검증. - 개선/수정: 골든 픽스처 정기 대조 회로 차단기, 가능한 곳은 ANSI/화면 모델 구조 신호로 이전, 모델 화이트리스트 카탈로그 외부화, 미해결 픽스처라도 명시 회귀 테스트. → 모델 화이트리스트 외부화 완료.
-
단 방어 상당: 계정은 이메일 신호만 신뢰 +
-
[M] 거대 파일 vs 컨텍스트 윈도우:
test_client.py5,992줄·test_server.py4,064줄·client.py3,981줄. LLM이 일부만 보고 수정 → 회귀. 완화:client.py는 ~14개 명확한 Mixin(grep '^class '로 부분 Read 가능)이라 단일 더미는 아님.-
개선/수정:
test_client.py를 기능별 분할(import 그래프 단순, 저비용·고효과),client.pyMixin 물리 분리. →client.py분할 수정 완료.
-
개선/수정:
-
[M] 동적 합성 ServerClaudeMixin → 정의 위치 grep 불가 (
plugins/__init__.py:163):server.server_scan()실제 구현이claude-code/servermixin.py에 있고 런타임 합성 →Server정의만 보면 "정의 없는 호출"로 보인다.-
개선/수정: 호출 지점 근처에
# 구현: claude-code/servermixin.py:ServerClaudeMixin.server_scan포인터 주석. → 수정 완료.
-
개선/수정: 호출 지점 근처에
-
[M] 루트 온보딩 문서 부재 + 문서 노이즈: 온보딩 진입점이 표준 위치에 없고 정보가 여러 문서에
분산. 대형 핸드오프 문서를 무심코 Read 시 컨텍스트 예산을 통째로 소진. 완화: 메모리 색인이
자산 역할.
-
개선/수정: 루트 온보딩 문서 신설(빌드/테스트 명령, 훅 계약 위치, delete-to-disable 규약, 거대
파일/문서 Read 주의 30~50줄). → 수정 완료(
CLAUDE.md).
-
개선/수정: 루트 온보딩 문서 신설(빌드/테스트 명령, 훅 계약 위치, delete-to-disable 규약, 거대
파일/문서 Read 주의 30~50줄). → 수정 완료(
-
[L] 타입/docstring 커버리지(AST 측정): docstring 52%(858/1,648), 타입 어노테이션 51%(801/1,548).
평균 이상이나 절반 무타입 → 신규 코드 어노테이션 의무화, 특히 훅 시그니처 전수 타입화.
- 수정(부분): 값 반환 9개 훅에 반환 타입 명시. void 디스패처 16훅은 zero-signal churn 회피로 제외.
서버는 단일 스레드 asyncio 루프. 동시성 프리미티브는 PTY 백엔드 경계 4곳뿐(conpty 락 3 +
pty_backend reader/watcher 스레드 + usageprobe 락 1). 서버 비즈니스 로직(model/tree/io/remote)에
락이 전혀 없고 필요도 없다 — reader 스레드는 loop.call_soon_threadsafe로만 펌프
(pty_backend.py:454), 구조 변경 연산(servertree.py split/kill/new_window)은 전부 동기라 원자적.
협조적 스케줄링 덕에 model 동시 변경은 락 없이 안전. 위험은 await가 가르는 read-then-write와
공유 writer의 비직렬화 fan-out에 집중.
-
[A] PTY 스레드 ↔ 루프 경계 (
pty_backend.py:421-475):call_soon_threadsafe전용 진입 + 백프레셔(pause_reader) + 종료 가드. -
[A] usagedb 동시 쓰기 (
usagedb.py:80-90): 서버당 단일 연결 + WAL +busy_timeout=3000+ 소켓별 파일 격리. 한 소켓 = 한 서버 직렬 쓰기 → 경합 없음. -
[A−] ptyhost 소유권 (
ptyhost.py:91-116): last-wins +_PENDING_MAX1MB 링 flush + 서버측 reconnect-storm 가드(serverio.py:273-312).
-
[H-1] Fan-out 송신 per-writer 직렬화 부재 → 프레임 순서 역전 (
serverio.py:253create_task(_send_full(c))외serverremote.py:340/464/578,servertree.py:589등;protocol.py에 write 락 0건): 같은StreamWriter에 flush 루프write_frames와 다중-await_send_full이 동시 미결 →(구)full … (신)delta … (구)full프레임 혼입. 클라는 도착순 적용 + 시퀀스 번호 없음(client.py:3261) → 한 프레임 깨짐(다음 flush 33ms 내 자가 교정, 영구 손상은 아님).-
수정:
ClientConn에asyncio.Lock또는 클라당 outbound 큐. 최소 대안:_send_full을 단일write_frames로 묶어 다중 await 제거. → 수정 완료(송신 fan-out 직렬화).
-
수정:
-
[H-2] Slow/stalled consumer가 flush 루프 전체를 head-of-line 블록 (
serverio.py:399-400for c: await write_frames(c.writer, ...); 송신 경로에wait_for/high-water 0건): 한 클라 TCP 수신 윈도가 포화하면writer.drain()(protocol.py:98) 무기한 대기 → 모든 클라·모든 세션 렌더 정지. 단일 최고 위험.-
수정: 클라별 송신 task 분리 또는
wait_for(write_frames(...), timeout=N)+ high-water 초과 시 느린 소비자 마킹/드롭. → 수정 완료(느린 소비자 가드).
-
수정: 클라별 송신 task 분리 또는
-
[M-1] 원격 입력 릴레이 비직렬화 fire-and-forget (
serverremote.py:622): 빠른 연속 키 + resize 릴레이가 link.writer를 공유하면 순서가 흔들린다. 라우팅 화이트리스트(_REMOTE_RELAY_ACTIONS)는 정합 확인(재보고 아님). H-1의 per-link 직렬화로 동반 해소. → 수정 완료. -
[M-2]
run_in_executor사이 stale 모델 write (servertree.py:572-575_autorename_loop): await suspend 동안kill_window가 끼어들면 제거된 탭에 stale 이름 write(양성 결함, 다음 루프 교정).-
수정: resume 후
tab in sess.tabs and 캡처 pane is active재확인. → 수정 완료(autorename 가드).
-
수정: resume 후
거대 단일 파일(client.py ~3,981줄·serverio.py 1,184줄·model.py 1,323줄·claude
servermixin.py 2,181줄)이 동시 편집 충돌 표면을 키운다 — 병렬 작업이 같은 파일의 다른 영역을
건드려도 버전 관리 edit/commit이 파일 전체를 잡아 미커밋 줄을 동반 게시할 수 있다. 완화는 코드보다
워크플로 게이트(게시 전 diff로 본인 hunk만인지 확인)이며, 코드 차원 추가 완화는 §5·§8의 거대 파일
분할과 동일한 처방이다.
| # | 처방 | 관점 | 심각도 | 효과 |
|---|---|---|---|---|
| 1 | NEST 자동 승격 endpoint 직결 차단 + provenance 강화 | 보안 | H | 유일 실재 취약점 봉합 |
| 2 |
_remote_transport proc try/finally 정리 + LimitOverrunError 포착 |
안정 | H | 좀비/fd 누수 차단(최고 ROI) |
| 3 | 송신 fan-out slow-consumer 타임아웃 + per-writer Lock | 동시 | H | 멀티 클라이언트 가용성 직결 |
| 4 |
Server 베이스 합성 지연(make_server()) + harness.running_app() CM |
유지보수 | H | 테스트 거짓 실패 + 복붙 동시 해소 |
| 5 | 거대 파일 분할(client.py Mixin 물리 분리·test_client.py 기능 분할) |
유지/LLM/동시 | H | 3관점 부채 동시 완화 |
| 6 | PTY-host/federation read 타임아웃·keepalive | 안정 | M | 'wedge 영구 hang' 근원 봉합 |
| 7 | 코어 옵션 _OPTS_KEYS 테이블 통합 |
유지보수 | H |
usage_refresh_sec 영속 버그 해결 |
| 8 |
_UnixPty.write 잔여 루프 + EAGAIN 처리 |
안정 | H | 입력 무음 유실 차단 |
| 9 | 루트 온보딩 문서 신설 + 위젯 한글 리터럴 lint | LLM/유지 | M | 온보딩·i18n 누출 동시 |
| 10 | 날짜 로그 아카이브·durable 교훈 승격, 산문 숫자 → 불변식 | 유지보수 | M | 문서 rot 부채 정리 |
한 줄 결론: 코어 아키텍처(명시 디스패치·집중 훅 계약·단일 루프 동기 구조 변경·견고한 프로토콜/ 파서/IPC·상시 퍼징)는 이미 우수하며 선행 감사 완화책도 실재한다. 남은 부채는 두 갈래로 수렴 한다 — (가) 거대 파일/God 클래스(유지보수·LLM·동시 편집 충돌 동시 악화)와 (나) out-of-process 경계(PTY-host·federation)의 실패 경로·타임아웃·송신 fan-out 견고성. 보안은 NEST 자동 승격 1건만 잡으면 A급을 유지한다. 위 1~5번 착수 시 전 관점이 한 등급씩 올라간다.
소개 · 사용
- Project-Overview
- User-Manual
- Screenshots
- Settings-Popup
- Single-Session-Model
- Tmux-Feature-Comparison
플러그인
Claude Code 플러그인
- Claude-Code-Plugins
- Dev-Guide-claude-code
- Dev-Guide-claude-token-usage-view
- Dev-Guide-claude-prompt-history
- Dev-Guide-claude-resume
- Dev-Guide-claude-disable-feedback
플랫폼 · 성능
품질 · 보안
리뷰·분석 보고서
연혁
기여