# task-2559+1 보고서 — in-progress cron 봇 인식 hotfix

> 작성: 2026-05-13 (dev3 Dagda — cron 직접 발사 1회, task-2559 follow-up)
> 본질: PR #114 task-2559 라이브 결함 즉시 hotfix (회장 §명시 2026-05-12 Tier 1)
> 박제 근거: bot_process_collector.py line 222 영역 fallback path
> 레벨: Lv.2 (dashboard hotfix, task-2559 follow-up)

---

## 본질 1줄

PR #114 MERGED 후 라이브 환경에서 진행 중 cron 봇이 `active_lead_count=0` 으로 누락되는 결함을 collector 격리 doctrine 을 보존한 채 최소 fallback 으로 해소했다. 9개월간 반복된 신호등 오류 영구 해소.

---

## 근본 원인

`bot_process_collector._classify_lead` (line 216 영역) → `_parse_schedule_log(schedule_id)` 결과에 의존. schedule_history `<id>.log` JSONL 은 cron 작업 **종료 시점**에 한 줄씩 append 되므로 **진행 중**에는 파일이 존재하지 않는다. 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` 부재.

---

## 구현 요약

### 수정 (단일 파일)
- `dashboard/bot_process_collector.py`
  - `LeadBotProcess.chat_id_resolution_source: Optional[str]` 필드 추가 (`"schedule_log" | "in_progress_fallback" | "dispatch_worktree"` audit trace)
  - `to_dict()` 에 신규 필드 포함
  - `get_active_lead_bots()` 가 proc dict 의 `username` 을 `_classify_lead` 에 전달
  - `_classify_lead(*, username=None)` 시그니처 확장
  - cron 분기 fallback path 추가:
    - `_parse_schedule_log` None + `username == "jay"` + cron cwd 정규식 매칭 → 회장 chat 소유 봇으로 인정
    - `chat_id = self.dashboard_chat_id`, `chat_id_resolution_source = "in_progress_fallback"`
    - `display_name = "unknown_cron_bot"` (schedule_id-only fallback — bot_key/verifier 부재)
  - log 존재 + chat_id 불일치 분기는 **별도** 분기로 분리 — 격리 doctrine 보존
  - 정상 path 에 `chat_id_resolution_source = "schedule_log"`, dispatch path 에 `"dispatch_worktree"` 박제

### 신규 fixture
- `dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json`
  - cron cwd `/home/jay/.cokacdir/workspace/B6F6E56A` username=jay 활성
  - schedule_log_<id> key 부재 (log 부재 시나리오)
  - expected.lead_chat_id_resolution_sources=["in_progress_fallback"]

### 테스트 추가 (5건)
`dashboard/tests/test_bot_process_collector.py::TestScenarioInProgressCronNoLog`
1. `test_in_progress_cron_no_log_fallback` — 핵심 fallback 5단 assert (log 부재 sanity / lead 인식 / display_name / chat_id_resolution_source / verifier None / collect snapshot 정합)
2. `test_in_progress_other_user_isolated` — username≠jay 격리 어셀션
3. `test_in_progress_fallback_does_not_break_chat_isolation_doctrine` — log 존재 + 다른 chat_id 는 fallback 으로 우회되지 않음
4. `test_existing_cron_active_records_schedule_log_source` — 기존 fixture audit trace 회귀
5. `test_existing_dispatch_worktree_records_dispatch_source` — dispatch 봇 source 박제

---

## chat 격리 doctrine 보존 어셀션

| 시나리오 | 동작 | 박제 위치 |
|----------|------|-----------|
| log 존재 + chat_id 일치 | 인정 + `source="schedule_log"` | `_classify_lead` cron 분기 (기존) |
| log 존재 + chat_id 불일치 | **격리** (회장 §명시) | `_classify_lead` cron 분기 별도 (기존) |
| log 부재 + username==jay | 인정 + `source="in_progress_fallback"` | `_classify_lead` cron 분기 신규 (본 hotfix) |
| log 부재 + username≠jay | **격리** | `_classify_lead` cron 분기 신규 (본 hotfix) |

회귀 박제: `test_in_progress_fallback_does_not_break_chat_isolation_doctrine` + `test_other_chat_filtered` (기존) + `test_in_progress_other_user_isolated`.

---

## 검증 결과

### pytest
- `dashboard/tests/test_bot_process_collector.py` — **22 passed** (17 기존 + 5 신규)
- `dashboard/tests/test_traffic_light_layer_signals.py` — **9 passed** (회귀 0)

### 정적 검증
- `python3 -m py_compile dashboard/bot_process_collector.py` PASS

### 회귀 sanity (dashboard suite)
- 383 passed / 8 failed / 2 skipped — failure 8건 전부 main pre-existing baseline (test_composite_status 5건, test_refine_api 2건, test_server 1건). 내 변경 무관 (origin/main 4fffc55b 에서도 동일 8건 fail).

### 라이브 검증 (worktree 에서 실측)
```
$ python3 -c "from bot_process_collector import BotProcessCollector; snap=BotProcessCollector().collect(); print(snap['active_lead_count']); [print(l['cwd'], l['display_name'], l['chat_id_resolution_source']) for l in snap['leads']]"
active_lead_count: 2
  /home/jay/.cokacdir/workspace/B6F6E56A unknown_cron_bot in_progress_fallback
  /home/jay/.cokacdir/workspace/1D5F6B13 unknown_cron_bot in_progress_fallback
```

이전(PR #114) 동일 명령 → `active_lead_count: 0`. hotfix 후 진행 중 봇 2건이 정확히 인식된다.

### forbidden path
- 0건 hit. scripts/ci.sh, dispatch/, team_prompts.py, .github/workflows/, .env*, anu_v2/, traffic-light-spec.md, dashboard/data_loader.py, dashboard/routes_get.py, dashboard/server.py, task-2562 영역, task-2559 본문 전부 변경 없음
- PR #98~#114 branch head 변경 0건

### effective diff (1 변경 + 7 신규 = 8)
- M `dashboard/bot_process_collector.py`
- + `dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json`
- M `dashboard/tests/test_bot_process_collector.py`
- + `memory/plans/tasks/task-2559+1/plan.md`
- + `memory/plans/tasks/task-2559+1/context-notes.md`
- + `memory/plans/tasks/task-2559+1/checklist.md`
- + `memory/reports/task-2559+1.md`
- + `memory/events/task-2559+1.dispatch-decision.json`
- effective diff == expected_files (dispatch_decision.json authoritative)

---

## 금지 9건 — 전 항목 준수

1. ✅ task-2559 본문 변경 0 (memory/reports/task-2559.md / memory/plans/tasks/task-2559/* / memory/events/task-2559.* unchanged)
2. ✅ chat 격리 doctrine 위반 0 — fixture+test 회귀로 박제
3. ✅ 회장 수동 `/gemini review` 0
4. ✅ BOT 계정 `/gemini review` 0 (Gemini 자동 chain 또는 owner_trigger_only_capability 만)
5. ✅ close/reopen 0
6. ✅ force push / rebase / admin override 0
7. ✅ long polling 0 (anu_v2/polling_policy.py 상수 doctrine 상속)
8. ✅ task-2558/2560/2561/2562 영역 mutate 0
9. ✅ dashboard 다른 파일 (data_loader / server / routes_get) 변경 0 — collector만 hotfix

---

## 라이브 검증 시나리오 (PR merge 후 어셀션 protocol)

1. `python3 -c "import sys; sys.path.insert(0,'dashboard'); from bot_process_collector import BotProcessCollector; snap=BotProcessCollector().collect(); print('active_lead_count:', snap['active_lead_count'])"`
2. 진행 중 cron 봇 (예: dev1 B6F6E56A) 1개 이상이면 출력 ≥ 1 어셀션
3. dashboard `/api/bot-process-snapshot` 호출 → leads[].chat_id_resolution_source 필드 박제 확인
4. dashboard `/api/member-status` 호출 → 해당 dev팀 팀장 working 신호 정합

---

## ESCALATED 매핑 (해당 없음)

본 task 는 모든 완료 조건 8 을 충족하며 ESCALATED 없이 finalize 가능.

---

## 본질 / 최종 원칙

task-2559 라이브 환경 결함 즉시 hotfix. collector 격리 doctrine 유지하면서 in-progress cron 봇 fallback 만 추가. 최소 범위 (collector + fixture + test만). 9개월 반복 지적 신호등 오류 영구 해소 완성.
