# task-902.1 완료 보고서
> Phase 1: .done 감지 인프라 (Layer 2) 구현
> 팀: dev1-team | 팀장: 헤르메스 | 작성일: 2026-03-24

---

## SCQA

**S**: 팀 작업 완료 시 `.done` 파일 알림이 `cokacdir --cron`을 통해 전송되어, 매번 새 Claude 세션이 생성되고 토큰이 불필요하게 소모되고 있었다. activity-watcher.py의 `find_done_file`은 팀 무관하게 첫 번째 .done을 반환하는 버그가 있었고, `BOT_TEAM_MAP`에 dev4-8이 누락되어 5팀의 idle 전환 감지가 불가했다.

**C**: 토큰 낭비와 잘못된 팀 매칭으로 인해 dev8이 11시간 유휴 상태가 된 사고가 발생했으며, Layer 1 실패 시 fallback 알림 경로가 없어 .done 30분 방치까지 에스컬레이션 대기가 필요했다.

**Q**: 토큰 0 즉시 알림 + Layer 2 fallback 30초 이내 + 기존 버그를 수정할 수 있는가?

**A**: requests.post() 직접 Telegram API 교체로 토큰 소모 제거, find_done_file 팀 기반 매칭으로 정확한 .done 탐지, done-watcher.sh에 미알림 감지 + 배치 알림 추가로 30초 fallback 구현 완료. pytest 89건 전건 통과, pyright 0 에러.

---

## 산출물

### 생성 파일
- `~/.config/systemd/user/activity-watcher.service` — systemd user service (Restart=on-failure, StartLimitIntervalSec=60s, StartLimitBurst=3)

### 수정 파일
- `scripts/notify-completion.py` — send_telegram_notification: cokacdir → requests.post 교체 + .done.notified O_EXCL 마커 생성
- `scripts/activity-watcher.py` — send_telegram_notification 교체 + find_done_file 팀 기반 매칭 + BOT_TEAM_MAP dev4-8 추가 + check_already_notified 마커 기반 교체
- `scripts/done-watcher.sh` — 미알림 .done 직접 Telegram 알림 + flock + O_EXCL + symlink 거부 + 배치 알림
- `scripts/tests/test_notify_completion.py` — requests.post mock 테스트 + .done.notified 마커 테스트
- `scripts/tests/test_activity_watcher.py` — 팀 기반 매칭 + requests.post + 마커 기반 테스트

---

## 체크리스트 충족 현황

- [x] P1-1. notify-completion.py `send_telegram_notification()` → `requests.post()` 교체
- [x] P1-1b. activity-watcher.py `send_telegram_notification()` → `requests.post()` 교체 (cokacdir 사용 0건)
- [x] P1-2. Bot Token을 환경변수(`ANU_BOT_TOKEN`)에서만 로드 (하드코딩 0건)
- [x] P1-3. 타임아웃 10초 + 재시도 3회 (지수 백오프 + 429 Retry-After)
- [x] P1-4. 성공 시 `.done.notified` 마커 O_EXCL 원자적 생성
- [x] P1-5. `find_done_file` 팀 기반 매칭 (`get_active_task_for_team()` 구현: task-timers.json에서 team_id 일치 + status=running 조회)
- [x] P1-5b. `check_already_notified()` → `.done.notified` 마커 파일 존재 확인으로 교체
- [x] P1-6. `BOT_TEAM_MAP` dev1-dev8 전체 + anu + anu-direct (10개 항목)
- [x] P1-7. activity-watcher.service systemd user service 등록 + enable (Restart=on-failure, StartLimitIntervalSec=60s, StartLimitBurst=3)
- [x] P1-8. done-watcher.sh 미알림 .done 직접 Telegram 알림 로직 추가
- [x] P1-9. done-watcher.sh flock 동시 실행 방지
- [x] P1-10. done-watcher.sh `touch` → O_EXCL 원자적 생성 복구

---

## 보안 검증

- [x] S1-1. Bot Token이 프로세스 목록에 노출되지 않음 (requests 사용, curl/subprocess 미사용)
- [x] S1-2. .done 파일이 symlink일 때 처리 거부 (done-watcher.sh: `[ -L "$done_file" ] && continue`, activity-watcher.py: `not done_file.is_symlink()`)
- [x] S1-3. O_EXCL 마커 파일: notify-completion.py, done-watcher.sh 모두 O_EXCL 적용
- [x] S1-4. done-watcher.sh에서 task_id Python 인라인 직접 삽입 금지 (환경변수 `DONE_WATCHER_MSG`, `NOTIFIED_PATH`, `ESCALATED_PATH` 방식)
- [x] S1-5. 에스컬레이션 경로 cokacdir 의도적 유지 (맥락노트 5.1b: 비정상 상황 알림, 빈도 매우 낮음)

---

## 테스트 결과

- **pytest**: 89 passed, 0 failed (0.24s)
  - test_notify_completion.py: 26건 통과
  - test_activity_watcher.py: 45건 통과
  - test_done_watcher.py: 18건 통과
- **pyright**: 0 errors, 0 warnings, 0 informations
- **black + isort**: 준수
- **bash -n** (done-watcher.sh): Syntax OK

---

## 발견 이슈 및 해결

### 자체 해결 (3건)

1. **notify-completion.py `get_anu_key()` fatal exit** — `anu_key`가 더 이상 `send_telegram_notification`에 사용되지 않지만, `get_anu_key()`가 `COKACDIR_KEY_ANU` 미설정 시 `sys.exit(1)` 호출하여 스크립트 중단 위험
   - 수정: `anu_key = args.anu_key or os.environ.get("COKACDIR_KEY_ANU", "")` — 비치명적 변경

2. **activity-watcher.py main()의 anu_key 의존성** — `COKACDIR_KEY_ANU` 미설정 시 `sys.exit(1)` 호출하여 ANU_BOT_TOKEN만으로 동작 불가
   - 수정: anu_key 조건 제거, `ANU_BOT_TOKEN` 체크로 변경

3. **done-watcher.sh `.done.notified` archive 누락** — 기존 archive 정리에서 `.done.notified` 마커 이동이 누락되어 영구 잔류
   - 수정: archive 블록에 `[ -f "$EVENTS_DIR/${task_id}.done.notified" ] && mv ...` 추가

### 범위 외 미해결 (1건)

1. **activity-watcher.service 미시작** — enable만 수행, start는 운영 환경에서 아누가 직접 시작 판단 (systemctl --user start activity-watcher.service)
   - 범위 외 사유: 서비스 시작은 운영 판단 사항

---

## QC 자동 검증 결과

```json
{
  "task_id": "task-902.1",
  "overall": "PASS",
  "verified_at": "2026-03-24T15:12:47",
  "summary": "7 PASS, 5 SKIP",
  "checks": {
    "file_check": "PASS (4/4)",
    "data_integrity": "PASS",
    "pyright_check": "PASS (0 errors)",
    "style_check": "PASS (black+isort OK)",
    "critical_gap": "PASS",
    "spec_compliance": "PASS",
    "duplicate_check": "PASS (최대 유사도 13.6%)"
  }
}
```

---

## 검증 기준 충족

- T1: 단일 팀 완료 → Telegram 1건 수신 (즉시) — `send_telegram_notification` requests.post 구현으로 즉시 발송
- T3: Layer 1 실패 → Layer 2 fallback 30초 이내 — done-watcher.sh systemd timer(30초) + 미알림 감지 로직
- T6: symlink .done → 처리 거부 — done-watcher.sh `[ -L ]`, activity-watcher.py `is_symlink()` 체크
- pyright 0 에러, pytest 89건 전건 통과
