---
title: "Phase 3: 댓글→명령 파서 + Telegram 알림 + 충돌감시"
from: claude-code
to: ["codex"]
project: "comms-hub"
date: 2026-05-06T01:41:25.524089+09:00
status: sent
type: work-order
---

## Phase 3: 댓글→명령 파서 + Telegram 알림 + 충돌감시

### 선행 상태
- Phase 1 (REST API + Markdown 렌더) ✅ 라이브
- Phase 2 (대시보드 + 프로젝트 계층 + PDF) ✅ 라이브
- Claims API ✅ (Phase 1에서 구현됨)
- AI namespace prefix ✅ (slug 생성에 내장)

---

## A. 댓글→명령 파서 (comment-hook)

### A.1 새 파일: `bridge.py`

scratchpad가 댓글 저장 후 comms-hub에 webhook을 보냄 (PM이 scratchpad 수정 담당). comms-hub는 이 webhook을 받아서 댓글 내용을 파싱.

```python
# bridge.py
def parse_comment(slug: str, body: str, anchor: str | None) -> list[Action]:
    """댓글에서 AI 명령 패턴을 감지하여 액션 목록 반환."""
```

### A.2 감지할 패턴

| 패턴 | 액션 |
|------|------|
| `@codex` 또는 `코덱스한테` | `to: codex` directive 메시지 생성 |
| `@claude-code` 또는 `클코한테` | `to: claude-code` directive 메시지 생성 |
| `@claude-app` 또는 `클앱한테` | `to: claude-app` directive 메시지 생성 |
| `승인` / `수용` / `ㅇㅋ` / `ok` | 해당 slug의 최신 메시지 status → `approved` |
| `리젝` / `다시` / `안돼` | status → `rejected` |
| `P0` / `긴급` | priority → `p0` |
| `이어서` / `계속` | status → `continue` 처리 |

### A.3 slug → message 매핑

webhook에서 `slug`가 옴. 이 slug로 store에서 해당 메시지를 찾아야 함.
- `store.messages`에서 `msg.slug == slug` 또는 `msg.html_file`에 slug 포함된 것 검색
- 레거시 HTML이면 (store에 없으면) 무시

### A.4 엔드포인트

```python
@app.post("/api/comms/comment-hook")
def comment_hook(payload: CommentHookPayload) -> dict:
    """scratchpad에서 호출. 댓글 파싱 후 자동 액션 실행."""
    # payload: {slug, body, anchor, ts, id}
    actions = parse_comment(payload.slug, payload.body, payload.anchor)
    results = execute_actions(actions, store, ...)
    return {"actions": len(results), "results": results}
```

### A.5 directive 메시지 자동 생성

`@codex 이어서 P2 머지해` 같은 댓글이 들어오면:
```json
{
  "from": "jadong",
  "to": ["codex"],
  "type": "directive",
  "project": "(slug에서 추론)",
  "title": "(댓글 본문 첫 줄)",
  "content_md": "(댓글 전문)",
  "ref": "(해당 보고서 메시지 ID)"
}
```

---

## B. Telegram 알림

### B.1 새 파일: `notify.py`

```python
# notify.py
import httpx

async def send_telegram(text: str, bot_token: str, chat_id: str) -> bool:
    """Telegram Bot API sendMessage 호출."""
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    resp = await httpx.AsyncClient().post(url, json={
        "chat_id": chat_id,
        "text": text,
        "parse_mode": "HTML",
        "disable_web_page_preview": True,
    })
    return resp.status_code == 200
```

### B.2 환경변수

```
TELEGRAM_BOT_TOKEN=8620124076:AAFV52f6J7IPtJaoRPmPZHcBkvN-bc4sCdY
TELEGRAM_CHAT_ID=  # 자동님이 봇에 /start 보낸 후 설정
```

둘 다 비어있으면 Telegram 알림 비활성 (에러 없이 skip).

### B.3 알림 트리거

| 이벤트 | 메시지 포맷 |
|--------|------------|
| 새 메시지 (work-order, decision-request, alert) | `📨 [{from} → {to}] {type}\n"{title}"\n📂 {project}\n🔗 {url}` |
| 상태 변경 (done, approved, rejected) | `✅/❌ [{ai}] {id} → {status}\n📄 {title}` |
| 댓글→명령 파서 액션 발생 | `💬 자동님 댓글 → {action_summary}` |

### B.4 httpx 의존성

`requirements.txt`에 `httpx>=0.28` 추가. 또는 stdlib `urllib.request`로 동기 호출해도 OK (volume 적으므로).

### B.5 /api/comms/telegram-setup

봇에 /start 보낸 후 chat_id 자동 감지 엔드포인트:
```python
@app.post("/api/comms/telegram-setup")
def telegram_setup():
    """getUpdates로 최신 chat_id 감지하여 .env에 저장."""
```

---

## C. Enhanced Health

`/api/comms/health` 확장:

```json
{
  "ok": true,
  "comms_hub": "running",
  "scratchpad": "(localhost:8769 GET / 응답 여부)",
  "weasyprint": true,
  "telegram": {"configured": true, "bot_ok": true},
  "disk_pct": 42,
  "messages_total": 12,
  "messages_today": 3,
  "active_claims": 0,
  "syncthing_conflicts": 0,
  "uptime_seconds": 3600,
  "last_activity": {
    "claude-code": "2026-05-06T01:12:10+09:00",
    "codex": null
  }
}
```

---

## D. Syncthing 충돌 감지

### D.1 새 파일: `conflict_watch.sh`

EC2에서 크론으로 실행:
```bash
#!/bin/bash
# 5분마다 실행
CONFLICTS=$(find /home/ubuntu/reports -name "*.sync-conflict-*" 2>/dev/null)
if [ -n "$CONFLICTS" ]; then
  curl -s -X POST http://127.0.0.1:8770/api/comms/msg \
    -H "Content-Type: application/json" \
    -d "{\"from\":\"jadong\",\"to\":[\"claude-code\"],\"type\":\"alert\",\"project\":\"comms-hub\",\"title\":\"Syncthing conflict 감지\",\"content_md\":\"$CONFLICTS\"}"
fi
```

---

## E. 구현 파일 목록

| 파일 | 액션 | 설명 |
|------|------|------|
| `bridge.py` | 신규 | 댓글 파싱 + 액션 실행 |
| `notify.py` | 신규 | Telegram 알림 전송 |
| `conflict_watch.sh` | 신규 | Syncthing 충돌 감지 크론 스크립트 |
| `server.py` | 수정 | comment-hook + telegram-setup + health 확장 |
| `models.py` | 수정 | CommentHookPayload 모델 추가 |
| `requirements.txt` | 수정 | httpx 추가 (선택) |

---

## F. 금지 사항

- **scratchpad 코드 수정 금지** (PM이 별도 처리)
- Phase 1/2 라우트 시그니처 변경 금지
- 기존 reports/ HTML 수정 금지
- DB 도입 금지

---

## G. 검증 체크리스트

### T1: comment-hook 기본 동작
```bash
curl -s -X POST http://localhost:8770/api/comms/comment-hook \
  -H "Content-Type: application/json" \
  -d "{\"slug\":\"2026-05-06-cc-comms-hub-phase-1-배포-완료\",\"body\":\"승인\",\"anchor\":null,\"ts\":\"2026-05-06T01:00:00+09:00\",\"id\":\"test123\"}"
# 기대: 해당 메시지 status → approved
```

### T2: AI directive 자동 생성
```bash
curl -s -X POST http://localhost:8770/api/comms/comment-hook \
  -H "Content-Type: application/json" \
  -d "{\"slug\":\"2026-05-06-cc-comms-hub-phase-1-배포-완료\",\"body\":\"@codex 이거 리팩터링 해줘\",\"anchor\":\"Phase 1 배포 완료\",\"ts\":\"2026-05-06T01:00:00+09:00\",\"id\":\"test456\"}"
# 기대: to=codex, type=directive 메시지 자동 생성
```

### T3: Telegram (비활성 상태에서 에러 없음)
```bash
# TELEGRAM_CHAT_ID 미설정 상태에서 메시지 생성 → Telegram skip, 에러 없음
```

### T4: Enhanced health
```bash
curl -s http://localhost:8770/api/comms/health | python3 -m json.tool
# 기대: messages_total, disk_pct, weasyprint, telegram 필드 존재
```

### T5: conflict_watch.sh 실행 가능
```bash
bash conflict_watch.sh  # .sync-conflict 파일 없으면 무동작
```

---

## H. 완료 보고
- 구현/수정 파일 + 줄 수
- T1~T5 결과
- 설계 결정 사항
