# Phase 3: 오케스트레이터 코어 (Layer 3 MVP) 구현

## 태스크 ID: task-906.1
## 한정위임: 설계 → 구현 → 테스트 → 보고까지 완료

---

## 배경

Phase 1 (task-902.1, 1팀)과 Phase 2 (task-903.1, 2팀)가 모두 완료됨.
- Phase 1: notify-completion.py requests.post 교체, find_done_file 팀 기반 매칭, done-watcher.sh fallback
- Phase 2: pipeline_validator.py, token_ledger.py, event_bus.py 구현 완료 (pytest 32건 통과)

Phase 3는 Phase 2의 보안 모듈을 활용하여 auto_orch.py 메인 오케스트레이터를 구현하는 핵심 단계.

## 설계서 참조

- **통합 구현 계획서**: `memory/specs/automation-impl-plan.md` (Phase 3 섹션)
- **자동화 에이전트 설계서**: `memory/specs/automation-agent-spec.md`
- **.done 감지 설계서**: `memory/specs/done-polling-spec.md`
- **Phase 2 보고서**: `memory/reports/task-903.1.md`

## 구현 범위

### 1. `orchestrator/auto_orch.py` — 메인 오케스트레이터 (~300 LOC)

CLI 인터페이스:
- `--scan`: 트리거 스캔 + 진행 중 파이프라인 스텝 관리 (systemd timer의 매 tick 호출)
- `--run <pipeline_id>`: 수동 파이프라인 시작
- `--status`: 실행 중 파이프라인 + 팀 점유 상태 조회
- `--list`: 등록된 파이프라인 목록 (pipelines/*.yaml 스캔)
- `--validate <file>`: YAML 검증만 수행

핵심 로직:
```
scan 모드 동작:
1. 전역 flock 획득 (/tmp/auto-orch.lock, LOCK_EX|LOCK_NB) → 실패 시 즉시 종료
2. event_bus.consume_events() → 새 .done 이벤트 수집
3. 진행 중 파이프라인 상태 확인 (orchestrator/state/*.json)
4. 대기 스텝 중 팀 사용 가능 여부 확인 (TeamLock)
5. 실행 조건 충족 시: dispatch.py 호출 (shell=False, subprocess.run)
6. state/*.json 갱신 + health.json 갱신
7. flock 해제
```

### 2. `orchestrator/team_lock.py` — 팀별 뮤텍스 (~50 LOC)

- `TeamLock(team_id)`: fcntl.flock 기반 팀별 락
- `is_team_available(team_id)`: 비블로킹 LOCK_NB 시도 → 즉시 반환
- 락 파일 경로: `orchestrator/locks/<team_id>.lock`
- 팀이 작업 중이면 해당 팀에 새 작업 배정 안 함

### 3. 상태 관리

- `orchestrator/state/<pipeline_id>.json`: 파이프라인 실행 상태
  ```json
  {
    "pipeline_id": "marketing-dev",
    "status": "running",
    "current_step": "step-2-implement",
    "steps": [
      {"id": "step-1-design", "status": "completed", "task_id": "task-100.1"},
      {"id": "step-2-implement", "status": "running", "task_id": "task-101.1", "team": "dev1-team"},
      {"id": "step-3-verify", "status": "pending"}
    ],
    "started_at": "2026-03-24T10:00:00",
    "updated_at": "2026-03-24T11:30:00"
  }
  ```
- `orchestrator/health.json`: 매 tick 갱신
  ```json
  {
    "last_tick": "2026-03-24T11:30:00",
    "active_pipelines": 1,
    "errors_last_hour": 0,
    "version": "1.0"
  }
  ```

### 4. systemd 설정

- `~/.config/systemd/user/auto-orch.timer`:
  - OnUnitActiveSec=30s
  - AccuracySec=1s
  - Persistent=false

- `~/.config/systemd/user/auto-orch.service`:
  - Type=oneshot
  - ExecStart=/usr/bin/python3 /home/jay/workspace/orchestrator/auto_orch.py --scan
  - TimeoutStartSec=25s
  - MemoryMax=512M
  - KillMode=process
  - EnvironmentFile=/home/jay/workspace/.env.keys

⚠️ systemd enable/start는 하지 마세요. 등록만 해둡니다.

### 5. Phase 2 모듈 활용

- `pipeline_validator.validate()`: YAML 로드 시 검증
- `token_ledger.can_start()`: 파이프라인 시작 전 토큰 한도 확인
- `token_ledger.record_start()`: 시작 기록
- `event_bus.consume_events()`: .done 이벤트 원자적 소비
- `injection_guard.check_content()`: task_desc 인젝션 차단

## 수정 금지 파일 (충돌 방지)

- `scripts/` 디렉토리 전체 (Phase 1 영역)
- `dashboard/` 디렉토리 전체 (5팀 영역)
- `utils/injection_guard.py` (참조만, 수정 금지)

## 검증 기준

1. `--scan` dry-run 단위 테스트 (mock dispatch, mock event_bus)
2. `--validate` 유효/무효 YAML 검증
3. `--status` / `--list` 출력 형식
4. TeamLock 동시 접근 테스트 (multiprocessing으로 2프로세스 → 1개만 획득)
5. 전역 flock 중복 실행 방지 테스트
6. state/*.json crash recovery (비정상 종료 후 재시작 시 상태 복구)
7. dispatch.py 호출 시 shell=False 확인
8. pyright 0 에러, pytest 전건 통과, black+isort 준수

## 산출물 체크리스트

- [ ] orchestrator/auto_orch.py (~300 LOC)
- [ ] orchestrator/team_lock.py (~50 LOC)
- [ ] orchestrator/tests/test_phase3.py (테스트)
- [ ] ~/.config/systemd/user/auto-orch.timer
- [ ] ~/.config/systemd/user/auto-orch.service
- [ ] orchestrator/health.json (초기 파일)
- [ ] 보고서: memory/reports/task-906.1.md