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

**작성**: 헤르메스 (dev1-team 팀장)
**작성일**: 2026-03-14

---

## SCQA

**S**: .done 알림 시스템이 `cokacdir --cron` (새 AI 세션 생성)을 통해 완료 알림을 전송하고 있으며, "선점 후 전송" 패턴으로 `.done.clear`를 먼저 생성한 뒤 알림을 전송하고 있다.

**C**: 3회 연속 알림 실패 발생. 원인: (1) cokacdir는 Telegram 메시지가 아닌 새 AI 세션을 생성하여 아누 비활성 시 무용, (2) `.done.clear`를 먼저 선점하므로 전송 실패 시 재시도 불가 (L1, L3 모두 동일 버그).

**Q**: Telegram Bot API 직접 호출 + "전송 후 선점" 패턴으로 알림 누락을 0건으로 줄일 수 있는가?

**A**: `send_direct_telegram()` 함수 (retry 3회, 지수 백오프) 도입 + 전송 성공 시에만 `.done.clear` 생성으로 전환. E2E 5개 시나리오 x 3회 연속 성공 검증 완료. 단위 테스트 30건 전체 통과, pyright 에러 0건.

---

## 생성/수정 파일 목록

- **수정**: `/home/jay/workspace/scripts/notify-completion.py` — cokacdir 완전 제거, Telegram 직접 호출, 전송 후 선점 패턴
- **수정**: `/home/jay/workspace/scripts/done-watcher.sh` — 선점 제거, curl 직접 전송, 전송 후 선점
- **수정**: `/home/jay/workspace/tests/test_notify_completion.py` — cokacdir mock → requests mock, 30건 테스트
- **수정**: `/home/jay/workspace/tests/test_dispatch.py` — `test_notify_completion_timeout` → `test_notify_completion_send_failure_no_exit`로 교체
- **생성**: `/home/jay/workspace/scripts/notify-completion.py.bak` — 변경 전 백업
- **생성**: `/home/jay/workspace/scripts/done-watcher.sh.bak` — 변경 전 백업

---

## 주요 변경 상세

### 1. notify-completion.py

- `send_direct_telegram(message)` 함수 추가: `ANU_BOT_TOKEN` 환경변수로 Telegram Bot API 직접 호출, 3회 retry, 지수 백오프, 429 rate limit 대응
- `wake_anu_session()`: cokacdir 제거 → `send_direct_telegram()` 호출 (시그니처 유지)
- `send_telegram_notification()`: 동일하게 `send_direct_telegram()` 위임 (시그니처 유지)
- `get_anu_key()`: `sys.exit(1)` → fallback `"c119085addb0f8b7"` 반환 (하위 호환)
- **else 분기 (일반/마지막 Phase)**: `exists()` 확인 → 전송 → 성공 시에만 `.done.clear` 생성
- **chain 중간 Phase**: dispatch → 전송 → 둘 다 성공 시에만 `.done.clear` 생성

### 2. done-watcher.sh

- O_EXCL 선점 로직 완전 제거
- `.done.clear` 존재 확인만 수행 (exists check only)
- `curl`로 Telegram API 직접 호출 (`ANU_BOT_TOKEN` 환경변수)
- HTTP 200 응답 시에만 `.done.clear` 생성
- 실패 시 `.done.clear` 미생성 → 다음 30초 주기에 재시도

### 3. 테스트

- `TestSendDirectTelegram` 5건 신규: 성공, 토큰 미설정, 3회 재시도, 429 retry_after, 예외 처리
- `TestWakeAnuSession` 2건 교체: `send_direct_telegram` 호출 확인
- `TestSendTelegramNotification` 2건 교체
- `TestMain` 7건 갱신: tmpdir 기반, `send_direct_telegram` mock 사용
- `TestDispatchExecution` 2건 갱신
- `test_dispatch.py` 1건 교체: `test_notify_completion_send_failure_no_exit`

---

## 테스트 결과

### 단위 테스트
- `test_notify_completion.py`: 30/30 PASS (0.13s)
- `test_dispatch.py::TestSubprocessTimeout`: 5/5 PASS

### 전체 테스트 회귀
- 전체: 850 PASS, 2 FAIL (본 작업 범위 외)
- 범위 외 실패: `test_task522.py` 2건 (dashboard server stats 관련, 기존 실패)

### E2E 테스트 (3회 연속)

| 시나리오 | 라운드 1 | 라운드 2 | 라운드 3 |
|----------|---------|---------|---------|
| 1. L1 정상 경로 | PASS | PASS | PASS |
| 2. L3 Fallback | PASS | PASS | PASS |
| 3. 전송 실패 → .done.clear 미생성 | PASS | PASS | PASS |
| 4. 중복 알림 허용 | PASS | PASS | PASS |
| 5. 일반 작업 경로 | PASS | PASS | PASS |

### pyright
- `notify-completion.py`: 에러 0건, 경고 0건

---

## 발견 이슈 (3건)

1. **test_main_chain_mid_phase WORKSPACE_ROOT 미패치**: 테스트가 실제 파일시스템의 `.done.clear` 파일에 영향받아 SKIP됨. tmpdir 패치로 수정.
2. **test_dispatch.py 회귀**: `test_notify_completion_timeout`가 기존 `sys.exit(1)` 동작 기대 → 새 동작(exit 없음)에 맞게 `test_notify_completion_send_failure_no_exit`로 교체.
3. **black 포매팅 미적용**: 테스트 파일에 black 포매팅 적용 필요 → 적용 완료.

---

## 보안 검증

- 토큰 하드코딩: `grep -n "BOT_TOKEN" notify-completion.py done-watcher.sh` 결과 — `os.environ.get("ANU_BOT_TOKEN")` 및 `${ANU_BOT_TOKEN}` 환경변수 참조만 존재
- 로그 출력: 토큰이 포함된 URL 로깅 없음 (HTTP 상태코드만 로깅)
- git tracked: 토큰 포함 파일 없음

---

## 완료 기준 체크

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

---

## QC 자동 검증 결과

```json
{
  "task_id": "task-563.1",
  "checks": {
    "pyright_check": "PASS (0 errors, 0 warnings)",
    "tdd_check": "PASS (테스트 파일 + 구현 파일 모두 존재)",
    "data_integrity": "PASS",
    "style_check": "PASS (black + isort 적용 완료)"
  }
}
```

⚠️ 기존 테스트 실패 2건 (본 작업 범위 외): `test_task522.py::test_simple_http_server_stats`, `test_task522.py::test_index_html_server_stats_listener`
