# 와치독 .done 경합(race condition) 버그 수정

## 배경
task-1861_2.1+1이 정상 완료 중인데 와치독이 재위임(+2)함.
타임라인:
- 11:21:12 — task-timer end (status=completed)
- 11:21:12 — 와치독 재위임 시작 (task-1861_2.1+2)
- 11:22:54 — .done 파일 생성

## 근본 원인
봇이 timer end → 보고서 작성 → .done 생성 순서로 진행.
timer end 후 PID가 없어지자 와치독이 "죽었다"고 판정.
와치독이 task-timers.json의 status를 확인하지만, timer end와 재위임 사이에 race condition 발생.

## 수정 사항
파일: `/home/jay/workspace/scripts/session-watchdog.sh`

### 수정 1: .done 파일 존재 시 스킵
STALLED 판정 전에 .done 파일 존재 여부 확인:
```bash
# .done 파일이 이미 존재하면 완료 처리 중 → 스킵
DONE_FILE="${WORKSPACE}/memory/events/${TASK_ID}.done"
if [[ -f "$DONE_FILE" || -f "${DONE_FILE}.acked" || -f "${DONE_FILE}.clear" ]]; then
    log "${TASK_ID}: .done 파일 존재 → 완료 처리 중, 재위임 스킵"
    continue
fi
```

### 수정 2: task-timers.json status 재확인
STALLED 판정 직전에 status를 다시 한번 확인:
```bash
# race condition 방지: status 재확인
CURRENT_STATUS=$(jq -r --arg tid "$TASK_ID" '.tasks[$tid].status // "unknown"' "$TIMERS_FILE" 2>/dev/null)
if [[ "$CURRENT_STATUS" != "running" ]]; then
    log "${TASK_ID}: status=${CURRENT_STATUS} (not running) → 스킵"
    continue
fi
```

### 수정 3: 완료 직후 grace period
timer end 후 봇이 .done을 생성할 시간을 주기 위해, timer end 시각 기준 5분 이내면 스킵:
```bash
END_TIME=$(jq -r --arg tid "$TASK_ID" '.tasks[$tid].end_time // ""' "$TIMERS_FILE" 2>/dev/null)
if [[ -n "$END_TIME" && "$END_TIME" != "null" ]]; then
    # end_time이 있으면 이미 완료된 task → 스킵
    log "${TASK_ID}: end_time 존재 → 완료됨, 재위임 스킵"
    continue
fi
```

## 검증 시나리오
1. 봇이 정상 완료 중(timer end → .done 생성 전)일 때 와치독이 재위임하지 않음
2. 봇이 실제로 죽었을 때(PID 없음 + heartbeat 10분+ + .done 없음)는 정상 재위임
3. 기존 재위임 기능 회귀 없음
