# task-2569+1 — dispatch.py UnboundLocalError hotfix 보고서

**팀**: 5팀 (마르둑) — 대체 resolver
**Lv**: 2 / **우선순위**: critical (긴급 hotfix)
**완료일**: 2026-05-13
**상위 task**: task-2569 (RC-4 영역 regression)

---

## 1. 요약 (한 줄)

`dispatch/__init__.py` 함수 내부 중복 `import subprocess` 4건 제거 → top-level (line 30) import 단일화 → UnboundLocalError 해소 → regression 3건 박제.

## 2. 근본 원인 (1줄)

Python LEGB rule: 함수 안 `import X` 는 X 를 함수 전체의 local 로 묶음 → top-level `import subprocess` 가 가려져 line 3508 `subprocess.run(...)` 호출이 local import (line 3590, 4101) 보다 위에서 실행되어 `UnboundLocalError`.

## 3. 수행 내역

### TODO-1. 함수 내부 중복 `import subprocess` 4건 제거 — DONE

| Before line | 위치 컨텍스트 | 처리 |
|---|---|---|
| 1855 | retry meta task md write 후 intent-to-add | local import 1줄 삭제 |
| 2816 | task md 신규 write 후 intent-to-add | local import 1줄 삭제 |
| 3590 | task file write (skip 가드 후) intent-to-add | local import 1줄 삭제 (line 3508 UnboundLocalError 직접 원인) |
| 4101 | cancel 시 stop marker 추가 후 intent-to-add | local import 1줄 삭제 |

검증:
- `grep -n "import subprocess" dispatch/__init__.py` → **line 30 단 1건만 남음** (top-level)
- `python3 -m py_compile dispatch/__init__.py` → PASS
- `python3 -c "from dispatch import dispatch"` → PASS
- `inspect.signature(dispatch.dispatch)` → 22개 파라미터 정상 노출
- `dispatch.subprocess is stdlib_subprocess` → True

### TODO-2. dispatch.py smoke regression 추가 — DONE

`tests/regression/test_dispatch_smoke_2569_plus_1.py` 신규 (3 케이스):

1. `test_regression_1_dispatch_import_smoke` — `from dispatch import dispatch` 통과 + 함수 심볼 callable 확인
2. `test_regression_2_no_local_subprocess_import_in_functions` — AST 로 함수 / 메서드 내부 `import subprocess` 노드 0건 박제 (재발 시 즉시 실패)
3. `test_regression_3_subprocess_resolves_to_stdlib_at_module_level` — `dispatch.subprocess is stdlib subprocess`

pytest 결과:
```
tests/regression/test_dispatch_smoke_2569_plus_1.py::test_regression_1_dispatch_import_smoke PASSED
tests/regression/test_dispatch_smoke_2569_plus_1.py::test_regression_2_no_local_subprocess_import_in_functions PASSED
tests/regression/test_dispatch_smoke_2569_plus_1.py::test_regression_3_subprocess_resolves_to_stdlib_at_module_level PASSED
========================= 3 passed in 0.16s =========================
```

부수적으로, fixture 는 `tests/dispatch/__init__.py` 빈 패키지가 진짜 `dispatch` 패키지를 가리는 import shadow 를 막기 위해 `importlib.util.spec_from_file_location` 으로 파일 경로 강제 결합. (이 사고는 첫 실행 때 실제로 발견되어 fixture 보강함)

### TODO-3. 재발 방지 — DONE

`test_regression_2_no_local_subprocess_import_in_functions` 가 AST 로 함수 내부 `import subprocess` 패턴을 0건 박제 → 향후 누군가 동일 패턴을 추가하면 CI 가 즉시 실패. (광범위 lint hook 도입은 회장 §명시 "광범위 refactor 금지" 에 저촉되어 보류.)

## 4. 복구 검증 (task-2570 dry-run 갈음)

회장 §명시 "task-2570 본작업 수행 금지 (lock_sha 영역)" 에 따라 실제 `dispatch(team_id="dev?", task_desc=...)` 호출은 새 위임 workflow 를 trigger 할 수 있어 회피. 대신 다음 4단 smoke 로 갈음:

1. `python3 -m py_compile dispatch/__init__.py` PASS
2. `from dispatch import dispatch` PASS
3. `inspect.signature(dispatch.dispatch)` → 22 파라미터 정상
4. AST 박제: 함수 내부 local subprocess import 0건

UnboundLocalError 의 직접 원인 (LEGB shadow) 이 코드 레벨에서 제거되었고, 이를 재현하는 코드 경로(`subprocess.run(timer_cmd, ...)` line 3508 등)가 정상 모듈-수준 `subprocess` 를 결합함을 module attribute identity 로 박제.

## 5. 회장 §명시 금지선 준수

- ❌ task-2570 본작업 미수행 (lock_sha 영역 무손실)
- ❌ task-2569 전체 revert 미수행 (RC-4 intent-to-add 4 호출은 그대로, local import 만 제거)
- ❌ 광범위 refactor 미수행
- ❌ cleanup / stash / quarantine / post-merge evidence 영역 미수정
- ❌ dispatch.py 외 파일 확장 없음 (regression test 신규 1건만)
- ❌ force push / rebase / admin override / manual merge 없음
- ❌ 다그다 (dev3) 재투입 없음

## 6. 산출물

- `dispatch/__init__.py` — 함수 내부 `import subprocess` 4건 제거 (-4 lines)
- `tests/regression/test_dispatch_smoke_2569_plus_1.py` — 신규 (3 케이스)
- `memory/reports/task-2569+1.md` — 본 보고서

## 7. Lessons (1줄)

함수 내부에 stdlib `import` 를 추가할 때, 동일 모듈이 top-level 에 이미 import 돼 있으면 LEGB shadow 로 함수 전체가 silent UnboundLocalError 후보가 된다 — top-level 재사용이 정답.

## 8. Escalation (회장 결정 요청)

본 hotfix 산출물은 worktree `.worktrees/task-2569+1-dev5/` 에 완성/검증
완료. commit/push 단계에서 기존 시스템 상태로 인한 차단 2건 발견:

1. **main repo 직접 commit 차단** — `[BLOCKED] main direct commit prohibited`.
   정상 흐름이며, task pattern branch (`task/task-2569+1-dev5`) + 별도 worktree 로
   회피 완료.

2. **start_task_guard #7 차단** — `메인 워크스페이스 HEAD가 origin/main과 불일치`
   - main HEAD: `6b082dab` (task-2569 머지 commit, unpushed)
   - origin/main: `92320afa` (task-2568 머지 직후)
   - 즉 task-2569 자체가 main repo 에 머지되었지만 origin 에 push 되지 않은
     상태. 본 task-2569+1 의 책임 범위 밖이며, push 가 task-2569 머지를
     원격에 박제하는 행위라 회장 §명시 ("자동 fix 금지, 신규 finding escalate")
     에 따라 회장 결정을 요청.

회장 결정 옵션:
- (a) main 의 task-2569 머지 commit (`6b082dab`) push 권한 위임 → 가드 #7 통과 후
  본 hotfix commit + finish-task.sh 재실행 가능
- (b) task-2569 정합성 별도 점검 후 가드 #7 우회 권한 위임
- (c) 다른 처리 지시
