# task-2559+1 plan — in-progress cron 봇 인식 hotfix

## 본질 1줄
PR #114 task-2559 MERGED 후 라이브 환경에서 cron 작업 진행 중 봇이 `active_lead_count=0` 으로 누락되는 결함을 collector 격리 doctrine을 보존한 채 최소 fallback으로 해소한다.

## 근본 원인
`bot_process_collector._classify_lead` (line 222 영역) 은 `_parse_schedule_log(schedule_id)` 결과에 의존한다.
schedule_history/<id>.log JSONL 은 cron 작업이 **종료** 시점에 한 줄씩 기록되므로 작업 **진행 중**에는 파일이 존재하지 않는다.
log 부재 → `_parse_schedule_log` None → 격리 → `get_active_lead_bots()` 빈 list → `collect()` `active_lead_count: 0`.

실측: dev1 cron 봇 PID 2939430 cwd `/home/jay/.cokacdir/workspace/B6F6E56A/` 활성이나 `B6F6E56A.log` 부재.

## 구현 단위 (collector 만, dashboard 다른 파일 변경 0)

### 1. `dashboard/bot_process_collector.py` — line 222 영역 fallback
- `LeadBotProcess` 데이터클래스에 `chat_id_resolution_source: Optional[str]` 필드 추가
  값: `"schedule_log" | "in_progress_fallback" | "dispatch_worktree"` (audit trace)
- `get_active_lead_bots()` → `_classify_lead(pid, cwd, cmd_str, *, username=...)` 로 username 전달
- `_classify_lead` 신규 분기:
  - `_parse_schedule_log` None 이고 `username == "jay"` 면 → in_progress_fallback 경로
    - `chat_id = self.dashboard_chat_id` (회장 chat)
    - `chat_id_resolution_source = "in_progress_fallback"`
    - `display_name = "unknown_cron_bot"` (schedule_id-only fallback)
    - `bot_key = None`, `bot_key_verifier = None`, `dev_id = None`
    - `task_id = self._extract_task_id(cmd_str)` (cmdline 에서 best-effort)
  - log 존재 + `chat_id != dashboard_chat_id` → 여전히 격리 (chat 격리 doctrine 보존)

### 2. fixture `dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json` (신규)
- cron cwd `/home/jay/.cokacdir/workspace/B6F6E56A` 활성 프로세스
- schedule_history/B6F6E56A.log **부재** (fixture 에 `schedule_log_*` key 없음)
- 기대: lead 인식 + `chat_id_resolution_source="in_progress_fallback"`

### 3. test `dashboard/tests/test_bot_process_collector.py` (수정 — 5 케이스 추가)
- `test_in_progress_cron_no_log_fallback` — 핵심 fallback 동작
- `test_in_progress_other_user_isolated` — username≠jay 격리
- `test_in_progress_fallback_does_not_break_chat_isolation_doctrine` — log 존재 + 다른 chat_id 는 fallback 으로 우회되지 않음
- `test_existing_cron_active_records_schedule_log_source` — 기존 fixture audit trace 회귀
- `test_existing_dispatch_worktree_records_dispatch_source` — dispatch 봇 source 박제

## expected_files (8)
1. `dashboard/bot_process_collector.py` (M)
2. `dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json` (+)
3. `dashboard/tests/test_bot_process_collector.py` (M)
4. `memory/plans/tasks/task-2559+1/plan.md` (+)
5. `memory/plans/tasks/task-2559+1/context-notes.md` (+)
6. `memory/plans/tasks/task-2559+1/checklist.md` (+)
7. `memory/reports/task-2559+1.md` (+)
8. `memory/events/task-2559+1.dispatch-decision.json` (+)

## forbidden 8
- PR #98~#114 branch head 변경 0
- scripts/ci.sh / dispatch/ / prompts/team_prompts.py / .github/workflows/ / .env(.keys) 변경 0
- traffic-light-spec.md 변경 0
- anu_v2/ (task-2558/2560/2561) 변경 0
- dashboard/data_loader.py / routes_get.py / server.py 변경 0 (task-2559에서 이미 박제, hotfix는 collector만)
- task-2562 영역 (G4 gate) 변경 0
- 다른 task markers 변경 0
- task-2559 본문 (`memory/reports/task-2559.md` 등) 변경 0

## 검증
- pytest `dashboard/tests/test_bot_process_collector.py` — 22 passed (17 기존 + 5 신규)
- pytest `dashboard/tests/test_traffic_light_layer_signals.py` — 9 passed (회귀 0)
- 라이브 검증: `BotProcessCollector().collect()` → `active_lead_count` ≥ 1 (PR merge 직후)
- chat 격리 doctrine 어셀션 — fixture-driven 테스트로 박제

## 본 hotfix 가 손대지 않는 것
- chat 격리 doctrine (log 존재 시 chat_id 비교 권위 유지)
- spec §9 본문 (이미 박제 완료)
- data_loader / routes_get / server (이미 chat_id_resolution_source 미참조이므로 zero-cost)
- task-2558/2560/2561/2562 영역
