# 와치독 재위임 폭주(Retry Loop) 수정

## 배경
session-watchdog.sh에서 완료된 작업을 계속 재위임하는 폭주 버그 발생.
실제 사례: task-1869_2.2+1 완료 → 와치독이 +2, +3, +4, +5, +6까지 연쇄 재위임.
제이회장님 직접 지적: "워치독 프로세스 문제 있다!!!!!"

## 근본 원인 (분석 완료)

### 원인 1: 재시도(+N) 계열의 원본 완료 미확인
- 78행에서 `${TASK_ID}.done`만 확인 (현재 task_id 기준)
- task-1869_2.2+2는 자기 자신의 .done만 찾지, 원본 +1의 .done은 참조 안 함
- +1이 완료(.done 생성)되어도 +2는 독립 task_id이므로 재위임 대상이 됨

### 원인 2: retry_count 리셋
- dispatch.py가 새 task_id (+N)를 생성하면 task-timers.json에 새 엔트리가 생김
- 새 엔트리는 retry_count=0으로 시작 → MAX_RETRY(2) 제한이 무력화
- 결과: +1→+2→+3→+4→+5→+6 무한 재위임

### 원인 3: 완료된 봇의 heartbeat 부재
- 봇이 작업 완료 후 종료하면 heartbeat도 중단
- 와치독이 다음 사이클에서 "PID 없음 + heartbeat 없음" → STALLED 판정
- 실제로는 정상 완료인데 STALLED로 오판

## 수정 사항 (3건)

### 수정 1: base task .done 체크 추가 (78행 부근)
```
현재 task_id에서 +N 제거한 base_task_id 추출
base_task_id의 모든 +N 변형에 대해 .done/.done.acked/.done.clear 존재 확인
하나라도 있으면 → "이미 완료된 재시도 계열, 스킵"
```

구체적으로:
```bash
# 78행 이후 추가
# 1.1 재시도 계열 완료 체크
BASE_TASK_ID=$(echo "$TASK_ID" | sed 's/+[0-9]*$//')
SIBLING_DONE=false
for DONE_CHECK in "${EVENTS_DIR}/${BASE_TASK_ID}"*.done "${EVENTS_DIR}/${BASE_TASK_ID}"*.done.acked "${EVENTS_DIR}/${BASE_TASK_ID}"*.done.clear; do
    if [[ -f "$DONE_CHECK" ]]; then
        SIBLING_DONE=true
        log "${TASK_ID}: 형제 작업 완료 감지 ($(basename "$DONE_CHECK")) → 재위임 스킵"
        break
    fi
done
if [[ "$SIBLING_DONE" == "true" ]]; then
    # 현재 task도 완료 처리
    python3 "${WORKSPACE}/memory/task-timer.py" end "$TASK_ID" 2>/dev/null || true
    continue
fi
```

### 수정 2: 전체 +N 계열 retry_count 합산 (206행 부근)
```
재위임 전 base_task_id의 모든 +N retry_count 합산
합산값이 글로벌 상한(예: 3)을 초과하면 재위임 거부
```

구체적으로:
```bash
# 206행 교체
# 글로벌 재시도 상한: base task 기준으로 전체 +N 합산
GLOBAL_RETRY_COUNT=0
while IFS= read -r SIBLING_TID; do
    [[ -z "$SIBLING_TID" ]] && continue
    S_RC=$(jq -r --arg tid "$SIBLING_TID" '.tasks[$tid].retry_count // 0' "$TIMERS_FILE" 2>/dev/null)
    GLOBAL_RETRY_COUNT=$((GLOBAL_RETRY_COUNT + S_RC + 1))
done <<< "$(jq -r '.tasks | keys[]' "$TIMERS_FILE" 2>/dev/null | grep "^${BASE_TASK_ID}")"

GLOBAL_MAX_RETRY=3  # base task 전체 기준 최대 재시도
if [[ "$GLOBAL_RETRY_COUNT" -ge "$GLOBAL_MAX_RETRY" ]]; then
    log "${TASK_ID}: 글로벌 재시도 상한 (${GLOBAL_RETRY_COUNT}/${GLOBAL_MAX_RETRY}) 도달 → 재위임 거부"
    STALLED_LIST+=("${TASK_ID}(${TEAM_ID},global-retry-limit)")
    continue
fi
```

### 수정 3: 완료 후 자동 정리
```
task-timer end가 호출된 task (end_time 존재) → STALLED 판정에서 제외
기존 159-163행의 end_time 체크가 이미 있지만, retry 계열에서 +N의 end_time이
task-timer end로 설정되지 않는 경우가 있음 → 확인 필요
```

## 영향 파일
- `/home/jay/workspace/scripts/session-watchdog.sh` — 수정 (3곳)

## 검증 시나리오 (스모크테스트)
1. base task에 .done이 있는 상태에서 +N task가 running → 재위임 스킵 확인
2. +1, +2, +3까지 retry한 상태에서 글로벌 상한(3) 도달 → 재위임 거부 확인  
3. 정상 stalled (base에 .done 없음 + heartbeat 없음) → 기존대로 재위임 정상 동작
4. shellcheck 통과
5. 기존 정상 동작 회귀 없음

## 주의
- bash 스크립트이므로 shellcheck 준수
- 기존 로직 변경 최소화 (추가 위주)
- 수정 후 즉시 커밋