dispatch.py 재발 방지 로직 구현 (task-86.1)

## 배경
task-75.1에서 3팀 봇D 수신 실패 발생. 근본 원인: cokacdir 세션 스폰 실패 + 동일 팀 동시 dispatch 경합.
상세 조사 보고서: /home/jay/workspace/memory/reports/task-85.1.md

## 수정 대상
- /home/jay/workspace/dispatch.py
- /home/jay/workspace/memory/task-timer.py (필요 시)

## 구현 항목 4건

### 1. dispatch 전 팀 가용성 확인
- dispatch.py에서 위임 실행 전 task-timers.json을 확인
- 해당 팀에 status=running인 작업이 있으면 경고 출력 후 중단
- --force 옵션 추가: 강제 위임 시에만 running 팀에도 dispatch 가능
- 출력: {"status":"error","message":"dev3-team에 running 작업(task-XX.X)이 있습니다. --force로 강제 위임 가능"}

### 2. 이중 위임 방지 중복 검사
- dispatch 시 task description의 핵심 키워드(또는 task_id)가 이미 running/reserved 상태에 있는지 확인
- 동일 작업이 이미 진행 중이면 경고 출력 후 중단
- 완전 동일 description 매칭이 아니라, task_id 기반으로 검사 (아누가 task-timer start 시 부여한 ID)

### 3. reserved 상태 타임아웃 감지
- task-timer.py에 check-reserved 서브커맨드 추가
- 사용법: python3 task-timer.py check-reserved --timeout 300
- reserved 상태가 5분(300초) 이상 지속된 작업을 찾아 출력
- 출력: {"status":"warning","stale_tasks":[{"task_id":"...","reserved_at":"...","age_seconds":...}]}
- 아누가 .done 확인 시점에 같이 호출하면 됨

### 4. 빈 workspace 모니터링 (경량)
- dispatch.py에 --verify 옵션 추가 (dispatch 후 검증)
- dispatch 완료 후 cron에 등록된 schedule 시각 + 2분 뒤에 해당 workspace가 비어있는지 확인하는 로직은 너무 복잡하므로,
- 대신: dispatch 결과에 cron_id를 포함하고, 아누가 나중에 cokacdir --cron-list로 해당 cron이 소비됐는지 확인할 수 있게 정보 제공
- dispatch 출력에 추가: "cron_id": "XXXXX", "expected_execution": "2026-03-02 15:00:00"

## 주의사항
- dispatch.py의 기존 위임 흐름 깨뜨리지 말 것
- task-timer.py의 기존 start/end/log 기능 건들지 말 것
- 에러/경고 메시지도 JSON 형식 유지
- 테스트: 각 시나리오별 동작 확인

## 완료 후
- 보고서: /home/jay/workspace/memory/reports/task-86.1.md
- task-timer end: python3 /home/jay/workspace/memory/task-timer.py end task-86.1