# 본진 텔레그램 MCP plugin ENOENT — plist PATH 에 ~/.bun/bin 누락으로 reply 툴 미로드 1시간

- **발생 일자:** 2026-05-28 16:08 KST 직후 (decrypt-run.sh wrap 적용 재기동 시점)
- **가시화:** 2026-05-28 16:10 KST (형님 "코덱스 몇 시 보트 진행" 메시지 reply 안 감)
- **해결 일자:** 2026-05-28 17:14:58 KST (본진 자력 v3 restart 성공, 새 daemon pid 68182 정상 plugin 로드)
- **심각도:** high (텔레그램 양방향 채널 1시간 silent 끊김, 형님 폰에 본진 응답 0)
- **재발 가능성:** medium (decrypt-run.sh wrap / plist 수동 편집 시마다 PATH 상속 끊길 위험)
- **영향 범위:** 🍎 본진 텔레그램 채널 (mcp__plugin_telegram_telegram__reply 툴 부재)

## 증상

16:10 KST 형님 "코덱스 몇 시 보트 진행" → 16:45~46 "왜 멈췄냐 / 멈췄냐" 흐름. 본진 응답 0건 보이는 상태로 약 35분 경과. tmux `claude` 세션은 살아있어서 폴링은 정상 동작 — 형님 메시지가 tmux 까지 도달했지만 reply 가 외부로 못 나감.

가시 증상:
- 본진 turn 에 `mcp__plugin_telegram_telegram__reply` 툴이 안 박힘 (ToolSearch "select:mcp__plugin_telegram_telegram__reply" → No matching deferred tools)
- `/mcp` 명령 → `Failed to reconnect to plugin:telegram:telegram: ENOENT`
- `ps aux | grep "claude --channels"` → daemon pid 47081 정상 running
- bot HTTP API 직접 호출 (curl sendMessage) 은 PASS — 폴링 / 봇 토큰은 문제 없음

## 탐지

16:49:01 KST 본진 자가 진단 (b66d864e 세션):
1. 폴링 데몬 pid 살아있음 확인 (ps)
2. `ToolSearch` 결과 reply 툴 zero matches → plugin init 실패 확정
3. bot HTTP API fallback 으로 형님 폰에 진단 1통 발사 (msg27058)

17:13~17:14 KST WSL 측 병렬 진단 (ebfe5e77 세션):
- 형님 "하이" (msg9010) WSL 채널로 도달
- WSL 이 `ssh mac '/opt/homebrew/bin/tmux capture-pane -t claude -p'` 로 본진 tmux 화면 캡처 → ENOENT 메시지 확인
- 본진 plugins filesystem 검증: installed_plugins.json / marketplace / telegram 0.0.6 cache / bun-node which/version
- 17:15:12 KST 형님께 원인 분석 reply (msg9014): "plist PATH 에 ~/.bun/bin 빠져있던 게 root cause"

## 근본 원인

`com.user.tmux-claude.plist` 의 inline `export PATH` 에 `~/.bun/bin` (= `/Users/user/.bun/bin`) 누락.

```xml
<string>... export PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin && exec /Users/user/infra-config/scripts/decrypt-run.sh --profile telegram /opt/homebrew/bin/claude --channels plugin:telegram@claude-plugins-official</string>
```

- telegram plugin 의 `.mcp.json` 은 `bun run --cwd ${CLAUDE_PLUGIN_ROOT} --shell=bun --silent start` 로 spawn
- `bun` 바이너리 위치 = `/Users/user/.bun/bin/bun` (homebrew/usr 어디에도 없음)
- launchd 가 plist 의 정의된 PATH 외 부모 셸의 `.zshrc` PATH 를 상속 안 함 → MCP server 가 `bun` 못 찾고 ENOENT
- daemon 본체 (`claude --channels`) 는 `/opt/homebrew/bin/claude` 절대경로라 정상 시작 → 폴링 OK, plugin spawn 만 실패 → reply 툴 누락

타이밍: 16:08 KST 에 코덱스(직전 사이클)가 `com.user.tmux-claude.plist` 의 `command` 를 `decrypt-run.sh --profile telegram` wrap 으로 전환 + restart 적용. 이 wrap 변환 자체는 OK 였지만 PATH 라인이 누락된 상태로 plist 가 박혀있던 것을 깨우지 못함 (직전 비교 검증 갭).

## 복구

본진 자력 fix (b66d864e 세션 17:13~17:14 KST):
1. 진단 (~/.claude/plugins/cache/.../telegram/0.0.6/.mcp.json 읽음 → bun spawn 명령 확인)
2. `which bun` 실패 + `~/.bun/bin/bun` 실재 확인
3. plist 의 symlink 추적: `~/Library/LaunchAgents/com.user.tmux-claude.plist` → `/Users/user/claude-automations/launchd/com.user.tmux-claude.plist`
4. Edit: PATH 문자열 prepend `/Users/user/.bun/bin:` (한 라인 surgical change)
5. `/tmp/restart-v3.sh` 발사 — `launchctl bootout` + `pkill -KILL -f "claude --channels"` + `launchctl bootstrap` + tmux session 재생성 검증

17:14:58 KST 새 daemon pid 68182 + tmux `claude` session 재생성 → 새 c8139fdf 세션이 17:15:03 KST 부터 backlog 메시지 처리 시작 → reply 툴 정상 박힘.

WSL 측 기여: 같은 root cause 진단을 SSH 경유 병렬 도달 + 형님께 별 채널 reply (msg9014). 실제 fix 액션은 본진이 수행.

## 재발방지

1. **글로벌 룰 — 본진 telegram plugin 이 죽으면 ToolSearch + HTTP API fallback 패턴**: reply 툴이 turn 에 안 박혀있으면 (ToolSearch zero) 즉시 bot HTTP API curl 으로 형님께 진단 1통 발사. 본 사이클 16:49 KST 패턴이 표준. 메모리에 박을 것 (`feedback_telegram_plugin_enoent_http_fallback`).

2. **plist PATH 검증 자동화 (deferred)** — `com.user.tmux-claude.plist` 의 PATH 라인에 `~/.bun/bin` 포함 확인. 후보 = launchd plist 가 정의한 PATH 와 `~/.zshrc`/login shell PATH diff 비교 health check. 본진 launchd plist 수동 편집 (코덱스 wrap, decrypt-run, profile 정리 등) 후 자동 verify 단계 추가. 본 사이클은 별 task 로 분리 (구현 비용 30분+).

3. **plist 수동 편집 후 cold-restart 검증 의무** — `launchctl kickstart -k` 만 쓰면 기존 프로세스 살아있으면 새 plist 안 읽음 (사이클 17:05 v1 sticky issue). `launchctl bootout` + `pkill -KILL -f "claude --channels"` + `launchctl bootstrap` 의 v3 패턴이 표준. 이미 본진 자력 fix 흐름에서 검증됨.

4. **direct fallback 메커니즘 표준화 (deferred)** — bot HTTP API curl 호출을 wrapper 스크립트로 박아 본진 reply 툴 부재 시 즉시 호출 가능. 본 사이클은 inline curl 으로 즉응했지만, 재발 시 더 빠른 fallback 가능. 별 task.

5. **재발 시 본 이슈 참조 + 즉시 v3 패턴 재발사** — 동일 ENOENT 패턴 잡히면 진단 단계 skip, plist PATH 검증 + v3 restart 발사 1동작.

6. **✅ bun 을 `/opt/homebrew/bin` 에 심링크 — 근본 차단 (2026-06-05 박음, 본 계열 3회차 재발 후)** — `ln -s ~/.bun/bin/bun /opt/homebrew/bin/bun`. telegram MCP 의 spawn 명령(`bun run ...`)이 PATH 에서 bun 을 찾는데, bun 이 `~/.bun/bin` 에만 있던 게 본 이슈 계열(2026-05-14·05-28·06-05) 3회 재발의 단일 root cause. claude prefix 가 homebrew(`/opt/homebrew`)라 `/opt/homebrew/bin` 은 정규(plist)·비정규(바이너리 이전/수동 임시 재시작) 어떤 재기동 경로에서도 PATH 에 거의 항상 존재 → 심링크 1개로 bun 탐지 보장. 검증: `env -i PATH=/opt/homebrew/bin:/usr/bin:/bin bash -c 'command -v bun'` → `/opt/homebrew/bin/bun` PASS. 기존 #2(plist PATH 검증)는 정규 경로만 커버했으나 본 심링크는 경로 무관 차단. 가역(심링크라 `rm /opt/homebrew/bin/bun` 으로 복원). 재발 시 이 심링크부터 `ls -la /opt/homebrew/bin/bun` 확인.

## 재발 이력

- 2026-06-05 09:34 KST: claude 바이너리를 `~/.npm-global` → `/opt/homebrew` prefix 로 이전(자동업뎃 정상화)한 뒤 세션을 비정규 경로로 임시 재기동하는 과정에서 동일 telegram MCP "Failed to connect" 재발. **이번엔 plist PATH 에 이미 `~/.bun/bin` 포함**(2026-05-28 예방 #2 적용 상태)이었으나, 바이너리 이전 중 임시 재기동 환경이 plist 정규 PATH 를 안 거쳐 bun 미탐지. 형님이 `/mcp` → reconnect 수동 복구(09:36~39 KST). 교훈: 기존 plist PATH 예방은 launchd **정규 기동 경로만** 보장 — claude 바이너리 prefix 이전/수동 임시 재시작 경로는 커버 못함. → 근본 차단으로 재발방지 #6(bun `/opt/homebrew/bin` 심링크) 박음.

## 사이드 노트

- 본진 자체 16:49 KST 진단 → 17:14 KST fix 까지 약 25분 소요. 형님 폰 침묵 = 16:10 ~ 17:14 KST 약 1시간 (재기동 후 backlog 처리 포함).
- 형님 인식: "wsl 에서 복구해줬어" — 실제는 본진 자력 fix + WSL 병렬 진단 reply 둘 다 17:14~17:15 동시간에 진행. WSL reply (msg9014) 가 형님 폰에 17:15:13 KST 도달했고 본진 c8139fdf 첫 reply 가 17:16:34 KST 도달. WSL 진단 채널이 형님 입장에서는 가시적 "복구해줬다" 신호.
- 이 사고로 직전 사이클 핸드오프가 stale carry 박음 — c8139fdf 세션의 next-cycle.md 가 "본진 정상 흐름" 만 기록하고 16:08~17:14 plugin ENOENT 1시간 갭 누락. 재발방지로 핸드오프 carry 작성 시 ToolSearch reply 부재 자동 verify 추가 검토 (별 task).
