# .done 알림 시스템 구현: Telegram Bot API 직접 호출

## 배경
3회 연속 알림 실패. 에이전트 미팅 + 3사이클 재검토 완료.
- 미팅 기록: `memory/meetings/2026-03-14-done-notification-direct-telegram.md`
- 3사이클 검토 보고서: `memory/reports/task-561.1.md` (코드 스케치 포함, 반드시 읽을 것)

## 근본 원인
1. `cokacdir --cron`은 Telegram 메시지가 아니라 **새 AI 세션 생성** → 아누 비활성 시 무용
2. "선점 후 실패" 버그: `.done.clear`를 먼저 생성 → 알림 전송 실패해도 재시도 불가
3. done-watcher.sh도 동일한 선점-실패 구조

## 토큰 결정 (확정)
- **ANU_BOT_TOKEN 사용** (이미 `.env.keys`에 추가 및 테스트 완료)
- GROUP_CHAT_BOT_TOKEN은 다른 봇(단톡방 봇)이라 아누 1:1 채팅에 전송 불가
- chat_id: `6937032012`

## 구현 범위

### 1. `/home/jay/workspace/scripts/notify-completion.py` 수정

#### 1-A. `send_direct_telegram()` 함수 추가
```python
def send_direct_telegram(message: str) -> bool:
    """Telegram Bot API 직접 호출 (retry 3회, 지수 백오프)"""
    import requests
    import time

    bot_token = os.environ.get("ANU_BOT_TOKEN")
    if not bot_token:
        print("[FATAL] ANU_BOT_TOKEN 환경변수 미설정", file=sys.stderr)
        return False

    chat_id = "6937032012"
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"

    for attempt in range(3):
        try:
            resp = requests.post(url, json={
                "chat_id": chat_id,
                "text": message,
                "parse_mode": "HTML"
            }, timeout=10)
            if resp.status_code == 200:
                return True
            if resp.status_code == 429:
                retry_after = resp.json().get("parameters", {}).get("retry_after", 5)
                time.sleep(retry_after)
                continue
        except Exception:
            pass
        time.sleep(2 ** attempt)
    return False
```

#### 1-B. `wake_anu_session()` 수정
- cokacdir --cron 호출 **전부 제거**
- `send_direct_telegram()` 호출로 교체
- 메시지 포맷: `"✅ {task_id} 완료\n보고서: /home/jay/workspace/memory/reports/{task_id}.md"`

#### 1-C. `send_telegram_notification()` 수정
- 동일하게 cokacdir 제거 → `send_direct_telegram()` 호출

#### 1-D. else 분기 (line 217~235) 순서 변경 — 핵심!
**변경 전** (선점 후 전송):
```python
# .done.clear 먼저 생성 → wake_anu_session()
fd = os.open(str(done_clear_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
...
wake_anu_session(...)
```

**변경 후** (전송 후 선점):
```python
# 1. 먼저 전송
message = f"✅ {args.task_id} 완료\n보고서: /home/jay/workspace/memory/reports/{args.task_id}.md"
sent = send_direct_telegram(message)

# 2. 전송 성공 시에만 .done.clear 생성
if sent:
    try:
        fd = os.open(str(done_clear_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
        os.write(fd, json.dumps({"source": "notify-completion", "task_id": args.task_id}).encode())
        os.close(fd)
    except FileExistsError:
        pass  # 다른 프로세스가 이미 처리 — 중복 알림 허용
else:
    print(f"[WARN] Telegram 전송 실패. .done.clear 미생성 → L3 재시도 대기", file=sys.stderr)
```

#### 1-E. chain 중간 Phase 분기 (line 165~215) 순서 변경
**변경 전**: `create_done_clear()` → dispatch → telegram
**변경 후**: dispatch → telegram → 성공 시 `create_done_clear()`

구체적으로:
1. `.done.clear` 존재 확인만 (line 170의 `create_done_clear()` 호출을 존재 확인으로 변경)
2. `dispatch_next_phase()` 먼저 호출
3. `send_direct_telegram()` 호출 (cokacdir 대신)
4. dispatch 성공 + telegram 성공 시에만 `.done.clear` 생성
5. dispatch 실패 시 `.done.clear` 미생성 → 재시도 허용 (기존 삭제 로직 불필요해짐)

#### 1-F. `get_anu_key()` 함수 — 제거 또는 유지
- cokacdir 호출이 완전 제거되면 `COKACDIR_KEY_ANU`가 불필요
- 하지만 하위 호환성 위해 함수는 남기되, 호출하지 않도록 수정
- `args.anu_key` 파라미터도 유지 (향후 확장용)

### 2. `/home/jay/workspace/scripts/done-watcher.sh` 수정

**변경 전**:
```bash
# O_EXCL로 done.clear 원자적 선점 → notify-completion.py 호출
if python3 -c "O_EXCL .done.clear 생성" 2>/dev/null; then
    python3 notify-completion.py "$task_id"  # ← .done.clear 이미 존재 → SKIP!
fi
```

**변경 후**:
```bash
# .done.clear 존재 확인만 (생성하지 않음)
[ -f "$clear_file" ] && continue

# 60초 미만이면 L1에게 기회 양보
file_age=$(( $(date +%s) - $(stat -c %Y "$done_file") ))
[ "$file_age" -lt 60 ] && continue

echo "[$(date -Iseconds)] L3 감지: $task_id → 직접 전송" >> "$LOG"
source /home/jay/workspace/.env.keys 2>/dev/null || true

# 직접 Telegram API 호출 (curl)
RESULT=$(curl -s -w "%{http_code}" -o /dev/null \
    -X POST "https://api.telegram.org/bot${ANU_BOT_TOKEN}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "{\"chat_id\":\"6937032012\",\"text\":\"⚠️ [L3 Fallback] ${task_id} 완료 알림 (L1 미전송 감지)\nreport: /home/jay/workspace/memory/reports/${task_id}.md\"}" 2>/dev/null)

if [ "$RESULT" = "200" ]; then
    # 전송 성공 후에만 .done.clear 생성
    python3 -c "
import os, sys
try:
    fd = os.open('$clear_file', os.O_CREAT | os.O_EXCL | os.O_WRONLY)
    os.write(fd, b'{\"source\":\"done-watcher\",\"task_id\":\"$task_id\"}')
    os.close(fd)
except FileExistsError:
    pass
" 2>/dev/null
    echo "[$(date -Iseconds)] L3 전송 성공 + .done.clear 생성: $task_id" >> "$LOG"
else
    echo "[$(date -Iseconds)] L3 전송 실패 (HTTP $RESULT), 다음 주기 재시도: $task_id" >> "$LOG"
fi
```

### 3. Rollback 준비
- 변경 전 백업 필수:
  ```bash
  cp notify-completion.py notify-completion.py.bak
  cp done-watcher.sh done-watcher.sh.bak
  ```

## E2E 테스트 (필수, 3회 연속 성공)

### 테스트 1: L1 정상 경로
1. `echo '{}' > memory/events/test-e2e-1.done`
2. `source .env.keys && python3 scripts/notify-completion.py test-e2e-1`
3. 검증: Telegram 수신 확인 (curl로 getUpdates) + `.done.clear` 생성 확인
4. 정리: `rm -f memory/events/test-e2e-1.done*`

### 테스트 2: L3 Fallback 경로
1. `touch -d "3 minutes ago" memory/events/test-e2e-2.done`
2. done-watcher 실행 (또는 30초 대기)
3. 검증: Telegram 수신 + `.done.clear` 생성
4. 정리

### 테스트 3: 전송 실패 시 .done.clear 미생성
1. 환경변수에 잘못된 토큰 임시 설정
2. `python3 scripts/notify-completion.py test-e2e-3`
3. 검증: `.done.clear`가 **없어야** 함
4. 올바른 토큰 복원 후 재전송 → 성공 확인

### 테스트 4: 중복 알림 허용
1. `.done` 생성 + `.done.clear` 없는 상태에서 L1, L3 둘 다 실행
2. Telegram 2건 수신 확인 → 정상 (누락보다 중복이 안전)

### 테스트 5: chain 중간 Phase
1. 체인 설정 후 `.done` 생성
2. dispatch 성공 + Telegram 알림 수신 + `.done.clear` 생성 확인

**3회 연속 성공 필수. 테스트 통과 전 "완료" 선언 금지.**

## 기존 테스트 유지
- `tests/test_notify_completion.py` 기존 테스트도 수정 필요 (cokacdir mock → requests mock)
- 기존 842건 테스트 회귀 없어야 함

## 보안 규칙
- ANU_BOT_TOKEN을 스크립트에 하드코딩 절대 금지
- `os.environ.get("ANU_BOT_TOKEN")` 또는 `$ANU_BOT_TOKEN` 환경변수로만 참조
- 로그에 토큰/URL 출력 시 토큰 부분 마스킹
- git tracked 파일에 토큰 포함 금지

## 완료 기준
- [ ] notify-completion.py에서 cokacdir 호출 완전 제거
- [ ] "전송 후 선점" 패턴 적용 (else 분기 + chain 분기 모두)
- [ ] done-watcher.sh에서 선점-실패 버그 제거
- [ ] E2E 테스트 5개 시나리오 × 3회 연속 성공
- [ ] 기존 test_notify_completion.py 테스트 수정 + PASS
- [ ] 기존 전체 테스트 회귀 없음
- [ ] 보안: 토큰 하드코딩 없음, 로그 마스킹