---
task_id: task-2388
type: context
scope: task
created: 2026-05-03
updated: 2026-05-03
status: in-progress
---

# 맥락 노트: task-2388 — Phase ε dispatch.py 모듈 분리

**task**: task-2388

---

## 3 Step Why 자문 (Lv.4 필수)

### 1st Why: "왜 이 분리 설계가 필요한가?"
**A 답변**: dispatch.py가 4336줄 monolithic이 되어 누적된 부채 임계점 도달:
- task-2367 ID 충돌 3차례 (task-2380이 fix했지만 함수 분포가 흩어져 추적 어려움)
- 메모리 피드백 위반 사고 다수 (allowed_resources 파싱, retry 흐름 등)
- 단위 테스트 어려움 (전역 상태 다수, mock 비용 ↑)
- 회장 4 목표 #1 "한번에 에러없이" 달성 위해 모듈 단위 검증 필수

### 2nd Why: "왜 6 모듈이 최선의 접근인가?"
**B 답변**: 회귀 보존 필수 4 영역(task-2380 task_id, task-2374 metadata, task-2386 prompt, task-2387 retry)에 + audit + core를 더한 6 모듈은:
- 각 영역이 독립 회귀 보존 단위 = 단위 테스트 정밀화 가능
- audit.py가 가장 큰 모듈(~1900줄)이 되지만, 모두 "외부 입력 검증" 카테고리로 응집도 높음 (allowed_resources, capability, affected_files, warnings, team routing 모두 dispatch 전 검증)
- core.py가 dispatch/cancel/main + composite/PRD를 담당 (제어 흐름)
- 더 잘게 쪼개면(7+) 의존성 그래프 복잡 ↑, import overhead ↑
- 5개로 합치면 audit/core 응집도 낮음 = 분리 의미 감소

**대안 (기각)**:
- 7 모듈 (bot_pool 별도): bot_pool은 audit과 강결합(가용성 검증 → reservation), 분리 시 cyclic import 위험. 기각.
- 8 모듈 (composite/PRD 별도): dispatch() 본체와 호출 흐름이 직접 연결, 분리 시 함수 시그니처 노출 증가. 기각.
- 4 모듈 (task_id+metadata 통합): task-2380 fix와 task-2374 metadata는 시점/잠금 의미 다름 (task_id는 카운터 락, metadata는 frontmatter 파싱). 통합 시 복잡도 ↑. 기각.

### 3rd Why: "왜 6 모듈이 다른 대안보다 나은가?"
**C 답변**: 6 모듈이 회귀 보존 ≅ 모듈 경계 1:1 매핑을 달성:
- task-2380 → task_id.py (3 함수, 172줄, 변경 전혀 없이 mechanical 이동)
- task-2387 → retry.py (2 함수, 54줄)
- task-2386 → prompt.py (3 함수, 149줄)
- 외부 테스트 호환은 `dispatch/__init__.py` re-export로 일괄 해결 (한 곳에서 관리 = 변경 추적 쉬움)
- core/audit이 큰 것은 회귀 보존 영역이 아니라 일반 dispatch 흐름 + 검증 헬퍼이므로 단일 책임 위반 아님
- 본 task의 핵심 KPI("회귀 0")가 모듈 경계와 일치 = 일관성 있음

**A-B-C 일관성 검증**: 통과
- A (4 영역 부채) → B (4 영역 + audit + core = 6) → C (회귀 보존 단위 = 모듈 경계)
- 논리 chain 일관됨

---

## 결정 근거

### dispatch.py 호환 shim 유지 결정
- 외부 호출자가 `python3 dispatch.py --team ...` 사용 (사용자 매뉴얼 + 기존 cron + 외부 docs)
- Python은 같은 디렉토리에 `dispatch.py` 파일 + `dispatch/` 패키지 공존 시 import는 패키지 우선, 스크립트 실행은 .py 파일
- 따라서 dispatch.py를 30줄 shim으로 유지하면 두 호출 방식 모두 동작
- 대안: dispatch.py 삭제 + `python3 -m dispatch` → 외부 호출자 깨짐 (기각)

### `__init__.py` re-export 전략
- 외부 테스트(11건)가 `from dispatch import _is_insuro_server_change` 등 사용
- forbidden_paths에 외부 테스트 다수 포함 → 수정 불가
- 따라서 모든 public/private 함수를 `__init__.py`에서 re-export 필수
- 함수 이름 충돌 없음 (각 모듈에서 unique)

### 모듈 의존성 그래프 (cyclic import 회피)
```
_state.py        — 상수, 의존 없음
   ↓
task_id.py       — _state.py만 의존
retry.py         — _state.py만 의존  
prompt.py        — _state.py만 의존
   ↓
audit.py         — _state, retry, task_id, prompt 의존 가능 (단방향)
   ↓
core.py          — 모든 모듈 의존 (단방향)
   ↓
__init__.py      — 모든 모듈 re-export
```
- Cyclic import 0 보장: core가 audit 부르고 audit가 core 안 부름

---

## 참조 자료

- task 명세: `memory/tasks/task-2388.md`
- task-2380 task-counter 4-layer fix: `memory/reports/task-2380.md`, `tests/dev1/test_dispatch_counter_fix.py`
- task-2374 옵션 D frontmatter 파서 ('이참나' 작성): `memory/reports/task-2374.md`
- task-2386 슬림 prompt: `memory/reports/task-2386.md`, `prompts/team_prompts.py`
- task-2387 status 가드: `memory/reports/task-2387.md`, dispatch.py:762
- 외부 dispatch 테스트 (호환 검증):
  - `tests/dev1/test_dispatch_counter_fix.py` ⚠ forbidden — 재실행만
  - `tests/test_dispatch_phase_warn.py`
  - `tests/test_dispatch_insuro_reload.py`
  - `tests/test_dispatch_auto_inject.py`
  - `tests/test_validate_composite.py`
  - `tests/test_dispatch_platform_rules.py`
  - `tests/test_affected_files_overlap.py`
  - `tests/test_quality_gates_integration.py`
  - `tests/test_dispatch_gate.py`
  - `prompts/test_consistency.py`
  - `tests/test_task_2352_cancel.py`
  - `teams/dev1/tests/test_dispatch_counter_sync.py`

---

## 주의사항

1. **dispatch.py와 dispatch/ 패키지 공존**: Python 우선순위 검증 필수. import time 체크 (`python3 -c "import dispatch; print(dispatch.__file__)"`)
2. **자기 참조 절대 금지**: 본 task 작업 중 dispatch.py 호출 시도 X (worktree 환경에서도 마찬가지)
3. **외부 테스트 무수정**: forbidden_paths의 모든 테스트는 새 패키지로 100% 호환되어야 함
4. **Module-level 부수효과 검사**: `_sync_bot_settings()`, `org_loader` 호출 등이 import 시점 vs runtime 시점에서 실행되는지 확인. 변경 없이 위치 이동만 수행.
5. **build_prompt vs _build_team_prompt**: `_build_team_prompt`는 `prompts.team_prompts`에서 import한 것 (build_prompt as 이름변경). prompt.py에서도 같은 import 유지.
6. **flock + atomic increment**: task-2380의 핵심 안전 메커니즘. 이동 시 한 줄도 변경 금지.
7. **logger 객체**: 각 모듈에서 `from utils.logger import get_logger; logger = get_logger(__name__)` 일관 적용
