# [메타인프라] dispatch spawn 검증 결선 — false-OK 감지

## 배경 (근본원인, ANU 규명 260813)
`dispatch.py`가 cron 등록 성공만으로 `status:ok`를 반환하고 **봇이 실제 spawn됐는지 검증하지 않는다**.
- `_verify_bot_spawn`(terminal_state_classifier.py §5.2.1)은 구현돼 있으나 **dispatch 실행 경로에 호출부 0건(미결선)**
- 봇 시작 시 `spawn-confirmed marker`(events/{task_id}.spawn-confirmed-*.json)를 내는 로직도 **미작동**(마커 전무)
- 결과: cron 전달 채널이 간헐 silent-drop하면 봇 미spawn인데 dispatch는 여전히 `ok` = **false-OK**. dispatch가 이를 구분 못함(실증: task-2940 정상 spawn vs MC-0279 미spawn을 동일하게 ok 처리).

## 목표 (surgical, 1차 = 감지+표면화만)
dispatch가 **false-OK를 감지해 status에 명시하고 handoff marker를 박제**한다.
⚠️ **자동 재전송(봇별 key fallback)은 이번 범위 아님**(무한루프/중복 위험 → 2차 별도). 이번엔 "조용히 ok로 넘어가는 것"만 차단.

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "dispatch.py"
    - "scripts/harness/v36/terminal_state_classifier.py"
    - "dispatch/**"
    - "team_prompts.py"
    - "tests/**"
  forbidden_paths:
    - ".env"
    - ".env.*"
    - ".github/**"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "python3 -c"
  merge_policy: "local_only"
  ttl_hours: 12
```

## 구현
### 1. dispatch.py — `_verify_bot_spawn` 결선
- cron 등록(현재 status:ok 반환하는 지점) **직후**, 봇 spawn 검증을 호출한다.
- `_verify_bot_spawn(task_id, expected_bot_id, child_pid=None)` 사용(cron 방식은 child_pid 없음 → marker 기반 판정).
  - return `SPAWNED` → 정상, status 유지.
  - return `DISPATCH_FALSE_OK` (marker 없음+child 없음) → **result["status"]="DISPATCH_FALSE_OK"** + `write_handoff_marker`로 disk 박제(사유 spawn_false_ok).
  - return `TIMEOUT_BOT_ALIVE_BUT_NO_MARKER` → result에 경고 필드 기록(status는 ok 유지, 봇은 살아있으므로).
- ⚠️ **비차단·무손상**: import 실패(`_verify_bot_spawn is None`)나 예외 시 기존 동작(ok 반환) 유지 — 검증이 dispatch를 죽이면 안 됨.
- ⚠️ 15초 대기가 dispatch 반환을 지연시키므로, timeout은 `FAILURE_CALLBACK_2712_SPAWN_TIMEOUT_SEC` env로 조정 가능하게 이미 돼 있음. 기본 15초 유지하되, 과도하면 근거와 함께 보고에서 조정 제안.

### 2. 봇 시작 시 spawn-confirmed marker 생성
- 봇 세션이 시작될 때(team_prompts.py의 봇 부팅/시작 프롬프트 또는 봇 시작 스크립트) `memory/events/{task_id}.spawn-confirmed-<ts>.json` 를 생성한다.
- 최소 내용: `{task_id, bot_id, pid, ts}`. 이게 있어야 §1의 `_verify_bot_spawn`이 SPAWNED를 판정할 수 있다.
- 기존 봇 시작 흐름을 **최소 침습**으로 건드린다(다른 로직 변경 금지).

### 3. 테스트
- 기존 회귀 유지: `tests/regression/test_progress_watcher_gate_2729.py`, `test_ci_watcher_lifecycle_2729p0a.py` PASS.
- 신규: `_verify_bot_spawn` 결선 단위테스트 — (a) marker 있으면 SPAWNED (b) marker 없고 child 없으면 DISPATCH_FALSE_OK로 status 표시 (c) import None/예외 시 기존 ok 무손상.
- spawn-confirmed marker 생성 단위테스트.

## 검증
- pytest 전체 그린. 기존 dispatch 정상 경로(봇 정상 spawn) status:ok 무손상 확인.
- false-OK 시나리오(marker 미생성 강제) → status=DISPATCH_FALSE_OK + handoff marker 생성 확인.

## 완료 보고 (.done + 보고서)
- 변경 파일 diff / 결선 위치 / 신규 테스트 결과 / 기존 회귀 PASS / 무손상(import 실패·예외 시 ok 유지) 확인
- ⚠️ 자동 재전송 미포함(1차 범위) 명시
- callback envelope: task_id·changed_files·test_result·collector=ANU (UTF-8 ≤3900 bytes)
