
에러는 한 줄도 안 나온다. 하지만 모든 hook이 침묵한다 ── 무지(無印) python을 c3 run으로 근절한 이야기 (v2.51.0)
요약
Claude Code 프레임워크인 C3(Claude Code Conductor)의 v2.51.0 업데이트 내용을 다룹니다. OS별 Python 경로 문제로 인해 Claude Code의 hooks가 침묵하는 결함을 해결하기 위해 'c3 run'이라는 새로운 호출 방식을 도입했습니다.
핵심 포인트
- Claude Code hooks가 실행 환경의 Python 경로 문제로 작동하지 않는 결함 발견
- 절대 경로 기록 대신 console script를 활용한 'c3 run' 호출 방식 도입
- Windows, macOS, Linux 전 OS에서 안정적인 Python 인터프리터 구동 보장
- 팀 단위 git 공유 시 환경 오염을 방지하는 설계 방식 제안
지난 글: https://zenn.dev/satoh_y_0323/articles/829384045d2fed
C3 GitHub:https://github.com/satoh-y-0323/claude-code-conductor /PyPI:https://pypi.org/project/claude-code-conductor/ /공식 문서:https://satoh-y-0323.github.io/claude-code-conductor/
본 기사의 범위: 직접 만든 Claude Code 프레임워크 C3 (Claude Code Conductor)의 v2.50.0과 v2.51.0입니다. X에서의 실제 이용 보고에서 시작하여, '순수 macOS에서는 C3의 hook이 전부 침묵한다'라는 구조적 결함을 스스로 발견하고, c3 run
이라는 python 호출 방식을 신설하여 Windows / macOS / Linux 3 OS에서 구동 보장을 갖추게 된 기록입니다. Claude Code로 hooks를 직접 만들고 배포하는 분들에게는 C3와 관계없이 참고할 만한 결함에 대한 이야기라고 생각합니다.
서론 ── 에러가 날 때보다 무서운 '침묵하며 작동하지 않음'
장애에는 두 종류가 있습니다. 에러를 내뱉으며 멈추는 것과, 에러도 내지 않고 존재 자체를 사라지게 하는 것입니다.
Claude Code의 hooks는 후자에 해당할 수 있습니다. settings.json
에 등록한 hook의 명령어가 실행 환경에서 해결되지 않을 때, 화려한 스택 트레이스는 나오지 않습니다. hook이 하나도 작동하지 않은 채로 세션은 아무 일도 없었던 것처럼 진행됩니다. 세션 기록도, 권한 가드도, 학습 데이터 수집도──프레임워크가 hook에 담아둔 모든 것이, 사용자가 알아차리지 못하는 사이에 사라집니다.
C3 배포물에는 이 시한폭탄이 29군데 있었습니다. 구동자(launcher) 전부가 무지(無印)의 python이었기 때문입니다.
발단은 X의 포스트였다 (v2.50.0)
시작은 C3에 대한 X에서의 실제 이용 보고였습니다. 'Claude Code에서는 작동하지만, Codex에서는 구동 시 에러로 로드되지 않는다'.
조사해 보니, C3가 생성하는 Codex / Cursor용 MCP 설정의 command
이 `
경험한 사람은 hook이 단 한 번도 작동하지 않은 채 '원래 그런 프레임워크인가 보다'라고 생각하며 이탈합니다──불만이라는 형태조차 되지 않는 장애입니다.
「절대 경로를 기록하기」를 그만두고, 호출구를 만들기 (v2.51.0)
솔직하게 생각하면 MCP와 동일한 대처, 즉 settings.json에 sys.executable의 절대 경로를 기록하는 안이 됩니다. 하지만 이것은 반갑지 않습니다. settings.json은 팀 단위로 git 공유되는 배포물이므로, 머신 고유의 절대 경로를 섞으면 리포지토리를 오염시킵니다 (MCP 설정은 각 머신에서 재생성되는 것을 전제로 하는 생성물이기에 허용할 수 있었습니다).
여기서 방침을 바꿨습니다. 경로를 해결해서 기록하는 것이 아니라, 해결된 장소에 입구를 만든다.
"command": "c3 run ${CLAUDE_PROJECT_DIR}/.claude/hooks/session_start.py"
c3는 pip가 설치 시에 만드는 console script (entry point)입니다. 이 부분이 이번 설계의 핵심인데, console script는 실행되는 시점에 'c3를 설치한 올바른 Python 인터프리터 안'에 있습니다. pip가 생성하는 실행기(launcher)에는 PATH에 python이 있는지 여부와 무관하게, 올바른 인터프리터에 도달하는 메커니즘이 처음부터 내장되어 있습니다. 그렇다면 그곳에 스크립트 실행구를 열어두면, 환경별 python 해결 문제는 소멸합니다.
새로 만든 c3 run은 CPython의 호출 규약을 그대로 계승한 3가지 형식입니다.
c3 run script.py [args...] # python script.py 에 상당
c3 run -c "print('ok')" # python -c 에 상당
c3 run -m module [args...] # python -m 에 상당
내용물은 동일 프로세스에서의 runpy 실행입니다 (서브 프로세스에서 python을 찾으러 간다면 본말전도이므로). 미묘하지만 효과적인 고집이 두 가지 있습니다.
- 인자 전달의 투명성: argparse는
--를 포함한 모든 토큰을 가공 없이 전달하는--를 특별 취급하여 삼켜버리기 때문에,run만은 인자 해석을 바이패스하여sys.argv로 그대로 통과시키는 설계로 했습니다. - cwd 임포트의 패리티 (parity):
python -c/-m은 cwd를 import 가능하게 하므로 (sys.path선두의 빈 문자열), 동일한 동작으로 맞췄습니다. "python으로 교체해도 망가지지 않는다"가 교체의 전제 조건입니다.
이로써 settings.json에는 머신 고유 정보가 일절 들어가지 않은 채로, 어떤 OS에서도 실행기가 해결될 수 있게 되었습니다.
「고치는 것」만으로는 부족하다 ── 탐지의 3층 구조
29곳을 치환하고 끝내지는 않았습니다. 이 구멍의 본질은 "망가져도 침묵한다"는 것이기에, 침묵하지 못하게 하는 메커니즘을 3층으로 추가했습니다.
- c3 doctor: settings.json의 hooks / statusLine 실행기를 검사하여, 무인(無印)
c3 doctor의 실행기 검증 결과python이라면 WARN (c3 update를 통한 이행 안내),c3자체가 해결 불가능/실행 불가능하다면 FAIL (exit 1). FAIL의 복구 안내는 "c3 update를 해주세요"가 아니라pip install --force-reinstall입니다 ── 망가진 c3로 c3를 고치라는 순환 안내를 리뷰에서 지적받아 수정했습니다. - lint 테스트: 배포물에 순수
python실행 형태가 혼입되면 테스트가 Red가 되는 검사. 행두뿐만 아니라&&,;, 백틱`,$(단독,&의 후방까지 검출합니다 ( "이번에 바로 치환한 형태를 lint가 놓치고 있었다" ため 단계적으로 확장). - macOS CI 스모크 (Smoke Test): 저는 macOS 실기기를 가지고 있지 않습니다. 이것이 "보이지 않는 장벽"의 근본 원인이기에, GitHub Actions의 macos-latest에서 "wheel을 설치하고
c3 run -c가 동작한다 / 배포된 hook이 실행된다 / statusLine의 문자열이 POSIX sh에서 실행 가능하다"의 3가지 케이스를 돌리는 단발성 workflow를 추가했습니다.
3번째 층이 처음으로 초록색이 된 순간은 꽤 감개무량했습니다. python 명령어가 존재하지 않는 머신에서 C3의 hook이 기동된 첫 번째 기록입니다. 개발 중에는 자신의 세션의 hook이 도중에
c3 run
기동으로 전환되어 계속 움직이는 것을 옆에서 지켜보며 (editable install + hook은 매번 새로운 프로세스이므로, 교체한 순간부터 자신이 첫 사용자가 됨), 도그푸딩 (dogfooding)과 검증이 하나가 되는 듯한 쾌감이 있었습니다.
결말 ── 환경 의존성은 테스트의 전제 조건에도 서식하고 있었다
릴리스하여 PyPI 공개까지 마친 직후, CI의 Tests workflow가 빨간색으로 변했습니다. 원인을 파헤쳐 보니 제품 코드가 아니라, 새로 작성한 테스트 자체가 환경 의존적이었습니다.
"c3는 해결할 수 있지만 python은 없는 PATH"를 재현하는 테스트는, PATH를 "c3가 있는 디렉터리 + 시스템 디렉터리"로 한정하여 만들었습니다. Windows에서는 이것으로 성립합니다. console script는 Scripts\에, python.exe는 그 부모 디렉터리에 있으므로, Scripts\만 PATH에 넣으면 python은 보이지 않습니다. 하지만 POSIX에서는 console script와 python 본체가 같은 bin/에 공존합니다. c3를 PATH에 넣는 순간, 옆에 있는 python도 보여버려서 "python이 없는 환경"의 재현에 실패합니다.
환경 의존성의 구멍을 메우는 기능의, 환경 의존성을 검증하는 테스트가 환경 의존적이었다──라는 것입니다. 수정 방법은 "임시 디렉터리에 c3의 실행 파일만 두고 PATH에 설정하기"였습니다. console script는 올바른 인터프리터(interpreter)로 가는 경로를 스스로 가지고 있으므로, 옆에 python이 없어도 기동됩니다. 테스트의 수정 방법 자체가 이 기능이 해결한 문제의 재연이 되어 있어, 수정하면서 조금 웃음이 났습니다.
참고로 v2.50.0에서 추가한 테스트 하나도 Linux에서 실패했습니다 (Windows 형식 경로의 is_absolute() 판정). 즉, 전날 릴리스부터 CI는 빨간색이었는데, 저는 초록색인 Windows만 보고 있었던 것입니다. "로컬 전체 초록색은 분포의 한 점"이라는 지난 기사의 교훈이, 다음 날 그대로 회수된 형태입니다.
여담 ── 이번에도 E 루프는 순순히 수렴하지 않았다
이번 구현도 C3의 자율 모드(리뷰를 High/Medium 0까지 자동 반복)로 운전했습니다. 결과만 적자면 6회전입니다. 지적 건수는 13→10→1→2→1→0으로 추이하였고, 규정된 상한선인 5회전에 도달했을 때 High/Medium 0에 도달하지 못하여, 설계대로 인간에게 중단 보고가 올라왔습니다. 저의 판정은 "수정 가능하다면 수정". 승인된 연장인 6회째에, bash에 가변 내용을 일절 전달하지 않는 구조 변경을 넣고 나서야 겨우 수렴했습니다.
"수정이 새로운 구멍을 만드는" 연쇄가 4단계나 깊게 파고들어 가는 과정은 그 자체로 하나의 기사가 될 만큼 밀도가 높았기에, 리뷰 루프의 수렴론은 별도의 기사로 미루겠습니다. 여기서는 "상한 도달 → 인간에게 에스컬레이션 (escalation) → 승인을 동반한 연장"이라는 자율 모드의 안전장치가 처음으로 실전에서 발동하여, 설계대로 작동했다는 사실만을 기록해 둡니다.
업무·개인 개발에 활용할 수 있는 점
1. "묵묵히 움직이지 않음"을 장애 모드 중 하나로 설계에 포함하기
에러가 발생하는 장애는 보고가 들어옵니다. 침묵하는 장애는 이탈이 발생합니다. hooks, cron, 에이전트의 자동 기동처럼 "작동하지 않고 있다는 것을 깨달을 기회가 구조적으로 없는" 것은, 진단 명령의 검사 대상에 포함하는 것까지가 구현입니다.
2. python이라고 쓰는 순간, 그것은 사용자 환경에 대한 도박이다
배포물에 python, python3, 혹은 절대 경로 중 무엇을 써도 누군가의 환경에서는 깨집니다. Python 제작 도구를 배포한다면, pip의 console script를 유일한 입구로 만드는 것이 가장 저렴한 해답입니다. entry point는 "올바른 인터프리터로 가는 경로"를 pip가 보장해 주는, 이미 존재하는 인프라입니다.
3. 실기(実機)가 없는 OS는, CI의 단발성 스모크 테스트로 "기동"만이라도 보장하기
풀 매트릭스 (Full matrix) CI는 규모가 크더라도, "설치해서 명령어 하나가 동작한다" 수준의 단발성 workflow라면 30초면 돌아갑니다. 보이지 않는 장벽을 가장 저렴하게 가시화하는 방법입니다.
4. 환경 의존성 버그를 고쳤다면, 그 테스트의 환경 의존성을 의심하기
「이 환경에서는 발생하지 않는다」는 문제의 테스트는, 작성한 사람의 환경에 대한 전제를 조용히 각인시킵니다. Windows의 Scripts\ 분리, POSIX의 bin/ 공존과 같은 레이아웃의 전제는 테스트 코드 안에서는 보이지 않기 쉽습니다. 릴리스 push 후 CI의 결론까지 확인하는 것을 절차에 포함해 두면, 이번처럼 다음 날이 아니라 당일에 알아차릴 수 있습니다.
요약
- X(구 Twitter)의 실제 이용 보고로 시작된 「무인(無印)
python문제」는, MCP(v2.50.0 · 절대 경로화)에서 hooks 전체 29개소(v2.51.0)로 파고들어, pip console script를 python 호출구로 만드는 방식으로c3 run을 통해 구조적으로 해결했습니다. - 단순 치환뿐만 아니라 doctor 검증 · lint · macOS CI 스모크 테스트의 3층 구조로 「조용히 고장 나는」 경로를 차단하여, C3는 처음으로 3개 OS의 실행 보장을 갖추게 되었습니다.
- 결론적으로, 이 기능을 검증하는 테스트 자체가 환경 의존성 때문에 CI를 빨간색(실패)으로 만들었습니다. 환경 의존성은 제품 코드뿐만 아니라, 테스트의 전제와 개발자의 시야 속에도 서식합니다.
v2.50.0 릴리스 노트에 「X 포스트에서 시작된 실제 사례가 당일 완결됨」이라고 적은 다음 날 아침, 그 수정 사항과 동일한 형태의 구멍을 자신의 본진에서 발견하는 것으로 이번 이야기는 시작되었습니다. 외부에서 온 1건의 보고는 대개 빙산의 가장 잘 보이는 모서리입니다. 보고를 해주신 분께 다시 한번 감사드립니다.
C3를 써보고 싶다면 ── 시작하기
pip install claude-code-conductor # C++ 컴파일러 불필요
cd your-project
c3 init # .claude/ 에 에이전트 정의 · skill · hook 이 전개됩니다
그 후 Claude Code에서 /start를 입력하세요. 기존 이용자는 c3 update를 통해 settings.json의 실행기가 c3 run 형식으로 업데이트됩니다 (settings.json을 직접 커스텀하고 있는 경우에는 c3 update --dry-run으로 차분을 확인하세요. 커스텀 설정은 settings.local.json으로 옮겨야 합니다). 어댑터는 Claude / Codex / Cursor / OpenCode 4종을 지원합니다 (c3 init --platform ...). "여기서 막혔다", "이 부분이 이상하다" ── 이번 사례가 바로 그랬듯이, 넘어진 보고가 가장 고맙습니다. Issue, PR, X 어디든 편하게 남겨주세요.
링크
- C3 GitHub: https://github.com/satoh-y-0323/claude-code-conductor
- C3 PyPI: https://pypi.org/project/claude-code-conductor/
- C3 공식 문서: https://y-0323.github.io/claude-code-conductor/
- v2.50.0 릴리스 노트: https://github.com/satoh-y-0323/claude-code-conductor/releases/tag/v2.50.0
- v2.51.0 릴리스 노트: https://github.com/satoh-y-0323/claude-code-conductor/releases/tag/v2.51.0
- 이전 기사 (『내가 직접 쓴 규약에 내가 가장 먼저 걸려들었다』 / C3 v2.49.0): https://zenn.dev/satoh_y_0323/articles/829384045d2fed
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기