{
  "schema": "task.dispatch_decision.v1",
  "task_id": "task-2559+1",
  "title": "in-progress cron 봇 인식 hotfix — bot_process_collector fallback",
  "team": "dev3",
  "lead": "Dagda",
  "issued_at_kst": "2026-05-13",
  "owner_directive": "회장 §명시 2026-05-12 Tier 1 — task-2559 PR #114 MERGED 후 라이브 환경에서 진행 중 cron 봇 누락 결함 즉시 hotfix.",
  "level": "Lv.2",
  "track": "task-2559 follow-up (hotfix, replacement chain 외 — task-2559 본문 보존)",
  "base_sha": "4fffc55b7431c739d0e86c97320d1c4be98e9374",
  "base_ref": "origin/main",
  "branch": "task/task-2559plus1-dev3",
  "predecessor_pr": 114,
  "predecessor_merge_commit": "ea242814",
  "predecessor_classification": "MERGED — 라이브 환경 결함 보충 hotfix",
  "no_long_polling_required": true,
  "long_polling_doctrine_inheritance": {
    "policy_module": "anu_v2/polling_policy.py",
    "constants_enforced": {
      "MAX_SINGLE_SLEEP_SECONDS": 900,
      "FIRST_TIMEOUT_SECONDS": 1800,
      "MAX_RECHECKS": 1,
      "NORMAL_WAIT_MIN_SECONDS": 300,
      "NORMAL_WAIT_MAX_SECONDS": 900
    },
    "exit_after_recheck": true,
    "note": "task-2554+2 OWNER_TRIGGER_ONLY_CAPABILITY doctrine 1:1 상속"
  },
  "expected_files": [
    "dashboard/bot_process_collector.py",
    "dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json",
    "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"
  ],
  "expected_files_count": 8,
  "implementation_spec_authoritative_source": "this file (dispatch_decision.json) — task.md 와 audit json 보다 우선",
  "implementation_spec": {
    "fix_1_collector_fallback": {
      "file": "dashboard/bot_process_collector.py",
      "location": "_classify_lead (line 216 영역 cron 분기)",
      "issue": "_parse_schedule_log None 시 (log 부재 또는 파싱 실패) 전부 격리 — in-progress cron 봇 누락",
      "fix": "log_meta None + username=='jay' + cron cwd 매칭 시 in_progress_fallback path 박제 (chat_id=DASHBOARD_CHAT_ID, source='in_progress_fallback', display_name='unknown_cron_bot')",
      "preserved": "chat 격리 doctrine — log 존재 + chat_id 불일치 시 격리 유지 (별도 분기)"
    },
    "fix_2_audit_trace_field": {
      "file": "dashboard/bot_process_collector.py",
      "location": "LeadBotProcess dataclass",
      "issue": "chat_id 산정 경로 audit 부재 — fallback 사용 여부 가시성 0",
      "fix": "chat_id_resolution_source: Optional[str] 필드 추가 ('schedule_log' | 'in_progress_fallback' | 'dispatch_worktree')"
    },
    "fix_3_regression_fixture": {
      "file": "dashboard/tests/fixtures/bot_process_cron_in_progress_no_log.json",
      "scenario": "cron cwd 활성 + schedule_history/<id>.log 부재 시나리오",
      "expected": "active_lead_count=1, chat_id_resolution_source='in_progress_fallback'"
    },
    "fix_4_regression_tests": {
      "file": "dashboard/tests/test_bot_process_collector.py",
      "test_class": "TestScenarioInProgressCronNoLog",
      "test_methods": [
        "test_in_progress_cron_no_log_fallback",
        "test_in_progress_other_user_isolated",
        "test_in_progress_fallback_does_not_break_chat_isolation_doctrine",
        "test_existing_cron_active_records_schedule_log_source",
        "test_existing_dispatch_worktree_records_dispatch_source"
      ]
    }
  },
  "chain_policy": "code-changing 재발 시 자동 task-2559+2 발행 X — OWNER_DECISION_REQUIRED",
  "forbidden_paths": [
    "PR #98~#114 branch 변경 0",
    "scripts/ci.sh / dispatch/ / prompts/team_prompts.py / .github/workflows/ / .env(.keys) 변경 0",
    "traffic-light-spec.md 변경 0",
    "anu_v2/ 변경 0 (task-2558/2560/2561 영역)",
    "dashboard/data_loader.py / routes_get.py / server.py 변경 0 (task-2559 본문 보존)",
    "task-2562 영역 (G4 gate) 변경 0",
    "다른 task markers 변경 0",
    "task-2559 본문 (memory/reports/task-2559.md / memory/plans/tasks/task-2559/*) 변경 0",
    "main 직접 push 0"
  ],
  "forbidden_9_actions": [
    "task-2559 본문 변경 0 (이미 MERGED)",
    "chat 격리 doctrine 위반 0 (log 존재 + 다른 chat_id 는 여전히 격리)",
    "회장 수동 /gemini review 0",
    "BOT 계정 /gemini review 0",
    "close/reopen 0",
    "force push / rebase / admin override 0",
    "long polling 0",
    "task-2558/2560/2561/2562 영역 mutate 0",
    "dashboard 다른 파일 (data_loader / server / routes_get) 변경 0 — collector만 hotfix"
  ],
  "completion_conditions": [
    "PR MERGED, mergedBy = app/jeon-jonghyuk-taskctl-bot",
    "in-progress cron 봇 fallback 작동 어셀션 (test_in_progress_cron_no_log_fallback PASS)",
    "chat 격리 doctrine 보존 어셀션 (test_in_progress_fallback_does_not_break_chat_isolation_doctrine PASS)",
    "pytest regression PASS (5 신규 + 17 기존 = 22 collector test + 9 traffic-light = 31 PASS)",
    "effective diff == expected_files (8 파일)",
    "forbidden path 0",
    "CI 11 checks all SUCCESS",
    "라이브 검증 — PR 머지 직후 진행 중 cron 봇 active_lead_count > 0 어셀션"
  ],
  "live_verification_protocol": {
    "command": "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']); [print('  ', l['kind'], l['cwd'], l['display_name'], l['chat_id_resolution_source']) for l in snap['leads']]\"",
    "assertion": "active_lead_count >= 1 AND any(lead.chat_id_resolution_source == 'in_progress_fallback') OR all leads source = 'schedule_log'",
    "executed_in_worktree": true,
    "result_active_lead_count": 2,
    "result_evidence": "dev1 PID 2939430 cwd /home/jay/.cokacdir/workspace/B6F6E56A → in_progress_fallback; self PID 2964816 cwd /home/jay/.cokacdir/workspace/1D5F6B13 → in_progress_fallback"
  }
}
