# 와치독 사망 원인 판정 — ai_trace.log 통합

## 개요
cokacdir의 `~/.cokacdir/debug/ai_trace.log`를 와치독 사망 원인 판정의 추가 소스로 통합한다.
현재 telegram JSONL 로그만으로는 정확한 사망 원인을 판별하기 어려운데, ai_trace.log에는 모든 봇 세션의 START/EXEC/STREAM/FINAL 이벤트가 상세히 기록되어 있다.

## ai_trace.log 포맷 분석

```
[2026-04-13 14:12:27.294] [EXEC] provider=claude, model=Some("claude-opus-4-6"), prompt_len=57, system_prompt_len=4060, history_len=12, session=Some("08a61ffb-..."), path=/home/jay/.cokacdir/workspace/autoset
[2026-04-13 14:12:35.492] [EXEC] completed OK
[2026-04-13 14:12:37.103] [STREAM] Done: result_len=380, full_response_len=380, session_id=Some("08a61ffb-...")
[2026-04-13 14:12:37.104] [FINAL] full_response_len=380, last_confirmed_len=0, remaining_len=380
```

### 핵심 이벤트
- `[START]` — 세션 시작 (chat_id, user_text, from_queue)
- `[EXEC]` — 실행 시작 (provider, model, prompt_len, system_prompt_len, history_len, session, path)
- `[EXEC] completed OK` — 정상 완료
- `[STREAM]` — 스트리밍 응답 (result_len, session_id)
- `[FINAL]` — 최종 결과 (full_response_len)

### 사망 원인 판별 기준

| ai_trace 패턴 | 사망 원인 | 설명 |
|---|---|---|
| `[START]` 후 `[EXEC]` 없음 | `start_failure` | 세션 시작 자체 실패 |
| `[EXEC]` 후 `completed OK` 없음 | `exec_crash` | 실행 중 크래시 |
| `history_len` > 100 + 실패 | `context_limit` | 컨텍스트 윈도우 초과 |
| `[EXEC]` 에러 메시지 포함 | `api_error` | API 호출 에러 |
| 정상 완료 후 새 START 없음 | `session_ended` | 정상 종료 (재위임 불필요) |

## 수정 대상

### 1. session-watchdog.sh — 사망 원인 판정 블록 확장

**현재 위치**: 사망 원인 분석 블록 (BOT_LOG grep 후)

**수정 방향**: 기존 BOT_LOG(telegram) 판정 후, ai_trace.log에서 추가 판정

```bash
# --- ai_trace.log 기반 추가 판정 ---
AI_TRACE_LOG="/home/jay/.cokacdir/debug/ai_trace.log"
if [[ "$DEATH_REASON" == "unknown" && -f "$AI_TRACE_LOG" ]]; then
    # SCHEDULE_ID에 대응하는 system_prompt 파일의 path로 필터링
    if [[ -n "$SCHEDULE_ID" && "$SCHEDULE_ID" != "null" ]]; then
        TRACE_PROMPT_FILE=$(grep -rl "$SCHEDULE_ID" /home/jay/.cokacdir/system_prompt_* 2>/dev/null | head -1)
        if [[ -n "$TRACE_PROMPT_FILE" ]]; then
            TRACE_PATH=$(dirname "$TRACE_PROMPT_FILE" 2>/dev/null || echo "")
            # ai_trace에서 해당 경로의 최근 로그 추출 (최근 50줄에서 검색)
            TRACE_LINES=$(tail -200 "$AI_TRACE_LOG" | grep "$TRACE_PATH\|$SCHEDULE_ID" 2>/dev/null || true)
        fi
    fi
    
    # 팀 workspace 경로로 fallback 필터링
    if [[ -z "$TRACE_LINES" ]]; then
        TEAM_SHORT="${TEAM_ID%-team}"
        TEAM_PATH="teams/${TEAM_SHORT}"
        TRACE_LINES=$(tail -200 "$AI_TRACE_LOG" | grep "$TEAM_PATH" 2>/dev/null || true)
    fi
    
    if [[ -n "$TRACE_LINES" ]]; then
        # 패턴 매칭으로 사망 원인 판별
        if echo "$TRACE_LINES" | grep -q "\[START\]" && ! echo "$TRACE_LINES" | grep -q "\[EXEC\].*provider"; then
            DEATH_REASON="start_failure"
        elif echo "$TRACE_LINES" | grep -q "\[EXEC\].*provider" && ! echo "$TRACE_LINES" | grep -q "completed OK"; then
            DEATH_REASON="exec_crash"
        elif echo "$TRACE_LINES" | grep -q "history_len=[0-9]\{3,\}"; then
            # history_len 3자리 이상 (100+) → context_limit 가능성
            DEATH_REASON="context_limit"
        fi
    fi
fi
```

### 2. watchdog-deaths.jsonl — 필드 추가

기존 필드에 `trace_info` 필드를 선택적으로 추가:
```json
{
  "task_id": "task-1772.5",
  "team": "dev2-team", 
  "reason": "exec_crash",
  "timestamp": "2026-04-13T12:13:51+09:00",
  "heartbeat_age_s": 0,
  "trace_info": "EXEC started, no completed OK found"
}
```

JSONL 기록 시 TRACE_INFO 변수가 비어있지 않으면 추가:
```bash
TRACE_INFO=""
if [[ -n "$TRACE_LINES" ]]; then
    # 마지막 EXEC 라인에서 핵심 정보 추출
    LAST_EXEC=$(echo "$TRACE_LINES" | grep "\[EXEC\]" | tail -1 | sed 's/.*\[EXEC\] //' | head -c 200)
    if [[ -n "$LAST_EXEC" ]]; then
        TRACE_INFO="$LAST_EXEC"
    fi
fi

# JSONL에 trace_info 포함
if [[ -n "$TRACE_INFO" ]]; then
    echo "{\"task_id\":\"${TASK_ID}\",\"team\":\"${TEAM_ID}\",\"reason\":\"${DEATH_REASON}\",\"timestamp\":\"$(date -Iseconds)\",\"heartbeat_age_s\":${DEATH_HB_AGE_S},\"trace_info\":\"${TRACE_INFO}\"}" >> "$DEATHS_LOG"
else
    echo "{\"task_id\":\"${TASK_ID}\",\"team\":\"${TEAM_ID}\",\"reason\":\"${DEATH_REASON}\",\"timestamp\":\"$(date -Iseconds)\",\"heartbeat_age_s\":${DEATH_HB_AGE_S}}" >> "$DEATHS_LOG"
fi
```

### 3. constants.json — ai_trace 경로 설정 추가

```json
{
  "watchdog": {
    "ai_trace_log_path": "/home/jay/.cokacdir/debug/ai_trace.log",
    "ai_trace_tail_lines": 200,
    ...기존 설정
  }
}
```

session-watchdog.sh에서 jq로 로드:
```bash
AI_TRACE_LOG=$(jq -r '.watchdog.ai_trace_log_path // "/home/jay/.cokacdir/debug/ai_trace.log"' "$CONSTANTS_FILE" 2>/dev/null)
AI_TRACE_TAIL=$(jq -r '.watchdog.ai_trace_tail_lines // 200' "$CONSTANTS_FILE" 2>/dev/null)
```

## 검증 시나리오

1. **start_failure 감지**: ai_trace.log에 `[START]`만 있고 `[EXEC]`가 없는 패턴이 있을 때, watchdog-deaths.jsonl에 `"reason":"start_failure"`가 기록되면 성공
2. **exec_crash 감지**: `[EXEC]` 시작 후 `completed OK`가 없는 패턴이 있을 때, `"reason":"exec_crash"`가 기록되면 성공
3. **기존 fallback 유지**: ai_trace.log가 없거나 빈 경우, 기존 BOT_LOG 기반 판정이 그대로 동작하면 성공
4. **trace_info 필드**: watchdog-deaths.jsonl에 trace_info가 포함되고 유효한 JSON이면 성공
5. **constants.json 연동**: ai_trace_log_path를 변경했을 때 해당 경로에서 읽으면 성공
6. **회귀 테스트**: `bash scripts/tests/test_session_watchdog.sh` 89/89 전건 통과