# task-1740.1 완료 보고서 — Session Watchdog Phase 3

**S**: Session Watchdog Phase 1-2가 완료되어 heartbeat 기반 2분 주기 감지, Circuit Breaker(백오프/재실패 감지), 동시 재위임 상한(3건), 집계 알림이 정상 동작 중이다. (task-1710.1, task-1715.1)

**C**: 실전 운영에서 3가지 미구현 항목이 발견되었다: (1) dispatch 직후 heartbeat 파일 생성 전 watchdog이 STALLED로 오판 (task-1736.1 사례), (2) 죽은 세션의 cokacdir 스케줄 미정리로 새 작업 충돌 (task-1731.1 사례), (3) 재위임 시 서브넘버링이 retry_count에 의존하여 동기화 오류 발생.

**Q**: 초기 유예 기간, 재위임 전 정리, task_id 기반 서브넘버링으로 watchdog 오판율을 0으로 낮출 수 있는가?

**A**: 3가지 기능 모두 구현 완료. 전체 49/49 테스트 통과 (기존 36건 회귀 0건 + 신규 13건). session-watchdog.sh 234줄 → 294줄 (+60줄).

---

## 구현 내용

### 1. 초기 유예 기간 (Grace Period)
- `GRACE_PERIOD=600` 상수 추가 (10분)
- `.done` 체크 이후, PID 추적 이전에 `start_time` 기반 유예 판정 삽입
- task-timers.json의 `start_time` (ISO 8601)을 `date -d`로 epoch 변환하여 비교
- 600초 미만이면 `log + continue` (STALLED 오판 방지)
- 엣지 케이스: start_time 없음/null/파싱 실패 → 유예 미적용 (기존 로직으로 진행)

### 2. 재위임 전 /stop 정리
- `get_cokacdir_key()` 함수 추가: team_id → `COKACDIR_KEY_*` 환경변수 매핑
  - `dev*-team` → `COKACDIR_KEY_DEV{N}` (간접 변수 참조 `${!var}`)
  - `anu-direct` → `COKACDIR_KEY_ANU`
- 재위임 전 3단계 정리:
  1. `cokacdir --cron-remove $SCHEDULE_ID` (스케줄 제거)
  2. `kill $STALE_PID` (잔존 프로세스 종료, system_prompt 해시 기반)
  3. `task-timer.py end $TASK_ID` (타이머 정리)
- 모든 명령에 `|| true` 방어 (set -e 안전)

### 3. 서브넘버링 수정
- 기존: retry_count 기반 (`SUB_NUM=$((NEW_RETRY + 1))`) — 동기화 오류 위험
- 변경: task_id에서 직접 추출 (`CURRENT_SUB=$(echo "$TASK_ID" | grep -o '\.[0-9]*$' | tr -d '.')`)
- `CURRENT_SUB` 비숫자 시 기본값 1 처리
- task-1736.1 → task-1736.2, task-1736.5 → task-1736.6 확인

---

## 산출물 파일

- `/home/jay/workspace/scripts/session-watchdog.sh` (수정: 234줄 → 294줄, +60줄)
- `/home/jay/workspace/scripts/tests/test_session_watchdog.sh` (수정: TC12-TC14 + 헬퍼 3개 추가)

---

## 테스트 결과

```
결과: 49/49 (0건 실패)
```

- TC1~TC11 (Phase 1-2 기존): 전체 통과 (회귀 없음)
- TC12 (Phase 3 유예): 3건 통과 (grace_skip, proceed, start_time 없음)
- TC13 (Phase 3 정리): 6건 통과 (key 매핑 4건 + 코드 존재 확인 2건)
- TC14 (Phase 3 서브넘버링): 4건 통과 (task-1736.1→.2, .5→.6, task-100.1→.2, task-999.99→.100)
- `bash -n session-watchdog.sh`: 문법 OK

---

## 셀프 QC 체크리스트

- [x] 1. 영향 파일: session-watchdog.sh, test_session_watchdog.sh (2개)
- [x] 2. 엣지 케이스: start_time null/파싱실패 → proceed, CURRENT_SUB 비숫자 → 1, unknown team → 빈 key → cron-remove 스킵
- [x] 3. 작업 지시 3항목 모두 구현 완료
- [x] 4. 보안: cokacdir key 환경변수 로드 (하드코딩 없음), kill || true 방어
- [x] 5. 테스트 49/49 전체 통과
- [x] 6. 발견 이슈 3건 모두 직접 해결 (아래)
- [x] 7. SOLID/DRY: get_cokacdir_key 함수로 매핑 로직 분리
- [x] 8. 인터페이스: systemd timer 내부 스크립트 (외부 API 변경 없음)
- [x] 9. 이미지 작업 없음 (N/A)
- [x] 10. CLAUDE.md 확인: 시스템 스크립트 (팀 CLAUDE.md 변경 없음)

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **GRACE_ELAPSED 변수명 충돌** — heartbeat 체크의 `ELAPSED`와 충돌 방지를 위해 `GRACE_ELAPSED` 사용
   - 상세: line 89에서 `GRACE_ELAPSED=$((NOW - START_EPOCH))` 적용
2. **서브넘버링 CURRENT_SUB 비숫자 방어** — `grep -o` 결과가 빈 문자열일 수 있음
   - 상세: line 209에서 `if ! [[ "$CURRENT_SUB" =~ ^[0-9]+$ ]]; then CURRENT_SUB=1; fi` 추가
3. **set -u 환경에서 COKACDIR_KEY_ANU 미설정 방어** — `${COKACDIR_KEY_ANU:-}` 패턴 사용
   - 상세: line 37에서 기본값 빈 문자열 처리

---

## 모델 사용 기록

- 토르(Thor) / session-watchdog.sh 수정 3항목 (유예+정리+서브넘버링) / sonnet
- 헤임달(Heimdal) / TC12-TC14 테스트 작성 / sonnet
- 오딘(Odin, 팀장) / 설계, 검토, 통합, QC, 보고서 / opus

## 세션 통계
- 총 도구 호출: 10회

### 수정 파일 목록
- /home/jay/workspace/scripts/session-watchdog.sh: 5회 (Edit)
- /home/jay/workspace/scripts/tests/test_session_watchdog.sh: 3회 (Edit)
- /home/jay/workspace/memory/reports/task-1740.1.md: 1회 (Write)
- /home/jay/workspace/memory/tasks/task-1740.1.md: 1회 (dispatch)

### 도구 사용 현황
- Edit: 8회
- Write: 1회
- dispatch: 1회

