# 자동화 오케스트레이터 통합 구현 — 체크리스트
> 버전: 1.0 | 작성일: 2026-03-24 | 작성자: 오딘 (2팀장) | 에이전트 미팅 만장일치 합의

---

## Phase 1: .done 감지 인프라 (Layer 2)

### 완료 조건
- [ ] P1-1. notify-completion.py의 `send_telegram_notification()`이 `requests.post()` 직접 API로 교체됨
- [ ] P1-1b. activity-watcher.py의 `send_telegram_notification()`이 `requests.post()` 직접 API로 교체됨 (cokacdir 사용 0건)
- [ ] P1-2. 교체된 함수가 Bot Token을 환경변수에서만 로드 (하드코딩 0건)
- [ ] P1-3. 타임아웃 10초 + 재시도 3회 (지수 백오프) 구현
- [ ] P1-4. 성공 시 `.done.notified` 마커를 O_EXCL 원자적으로 생성
- [ ] P1-5. activity-watcher.py `find_done_file`이 팀 기반 매칭으로 수정됨 (`get_active_task_for_team()` 구현 포함: task-timers.json에서 team_id 일치 + status=running인 task_id 반환)
- [ ] P1-5b. activity-watcher.py `check_already_notified()`가 로그 파일 스캔 → `.done.notified` 마커 파일 존재 확인으로 교체됨
- [ ] P1-6. activity-watcher.py `BOT_TEAM_MAP`에 dev1-dev8 전체 포함
- [ ] P1-7. activity-watcher.service가 systemd user service로 등록 + enable + start (Restart=on-failure, StartLimitIntervalSec=60s, StartLimitBurst=3 설정 포함)
- [ ] P1-8. done-watcher.sh에 미알림 .done 직접 Telegram 알림 로직 추가
- [ ] P1-9. done-watcher.sh에 flock 동시 실행 방지 적용
- [ ] P1-10. done-watcher.sh의 `touch` → O_EXCL 원자적 생성으로 복구

### 테스트 항목
- [ ] T1. 단일 팀 완료 → finish-task.sh 실행 → Telegram 메시지 1건 수신 (즉시)
- [ ] T2. 3팀 동시 완료 → 총 수신 Telegram 메시지 수 ≤ 3건, task_id별 최대 1건, 중복 0건
- [ ] T3-a. Layer 1 실패(ANU_BOT_TOKEN 미설정) → done-watcher 30초 이내 알림
- [ ] T3-b. Layer 1 실패(Telegram API mock 장애) → done-watcher 30초 이내 알림
- [ ] T3-c. notify-completion.py 예외 발생 → done-watcher 30초 이내 알림
- [ ] T4. 서버 재부팅 후 → systemctl --user restart → 모든 서비스 자동 재시작
- [ ] T5. Telegram API 장애 → 로컬 로그 기록 + 복구 후 재시도
- [ ] T6. symlink .done → 처리 거부, 로그 기록
- [ ] T7. 16팀 동시 완료 → 배치 알림 정상 처리
- [ ] T8. done-watcher + activity-watcher 동시 실행 → 중복 알림 0건 (TOCTOU 방어)

### 보안 검증
- [ ] S1-1. Bot Token이 프로세스 목록(ps aux)에 노출되지 않음 (curl 미사용, requests 사용 확인)
- [ ] S1-2. .done 파일이 symlink일 때 처리 거부됨
- [ ] S1-3. O_EXCL로 생성된 마커 파일이 경쟁 조건에서 중복 생성되지 않음
- [ ] S1-4. done-watcher.sh에서 task_id가 Python 인라인 코드에 직접 삽입되지 않음 (환경변수 전달 방식 또는 별도 Python 스크립트로 분리)
- [ ] S1-5. done-watcher.sh 에스컬레이션 경로의 cokacdir 사용이 의도적 허용임을 맥락노트에 명시됨

---

## Phase 2: 보안 기반 (Layer 3 전제 조건)

### 완료 조건
- [ ] P2-1. `utils/injection_guard.py`에서 high/critical 위협 감지 시 하드블록 (차단) 적용
- [ ] P2-2. `pipeline_validator.py` 구현 완료 — 10개 검증 항목 전부 동작
  - [ ] schema_version 존재 + 지원 버전
  - [ ] gates 최소 1개 이상
  - [ ] token_budget 존재 + 양수
  - [ ] blast_radius 유효값
  - [ ] allowed_teams 비어있지 않음
  - [ ] validate_dag() 통과 (Kahn's algorithm)
  - [ ] scan_secrets() 통과
  - [ ] target_team ∈ allowed_teams
  - [ ] depends_on 참조 유효성
  - [ ] task_desc injection_guard 통과
  - [ ] inject_context.source 경로가 WORKSPACE_ROOT 하위인지 검증 (path traversal 방지)
- [ ] P2-3. `token_ledger.py` 구현 완료
  - [ ] DAILY_HARD_LIMIT = 1,000,000 tokens
  - [ ] MAX_CONCURRENT_PIPELINES = 3
  - [ ] MAX_RETRIES_PER_STEP = 2
  - [ ] MAX_PIPELINE_STARTS_PER_DAY = 20
- [ ] P2-4. `event_bus.py` 구현 완료
  - [ ] POSIX rename()으로 단일 소비 보장
  - [ ] FileNotFoundError → 정상 스킵 (다른 소비자 선점)

### 테스트 항목
- [ ] T2-1. pipeline_validator: 순환 DAG YAML → 거부 + 에러 메시지
- [ ] T2-2. pipeline_validator: gates 누락 YAML → 거부
- [ ] T2-3. pipeline_validator: 시크릿 패턴 포함 YAML → quarantine/ 격리
- [ ] T2-4. injection_guard: 프롬프트 인젝션 패턴 → 하드블록 (차단)
- [ ] T2-5. token_ledger: DAILY_HARD_LIMIT 초과 요청 → 거부
- [ ] T2-6. token_ledger: MAX_CONCURRENT_PIPELINES 초과 → 대기
- [ ] T2-7. event_bus: 2개 프로세스 동시 소비 → 1개만 성공 (TOCTOU 테스트)

### 보안 검증
- [ ] S2-1. injection_guard 하드블록이 high/critical 위협에서 실제로 실행을 중단하는지 확인
- [ ] S2-2. scan_secrets()가 AWS_KEY, PRIVATE_KEY, password= 등 주요 패턴을 감지하는지 확인
- [ ] S2-3. validate_dag()가 자기 참조, 상호 참조, 간접 순환을 모두 감지하는지 확인

---

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

### 완료 조건
- [ ] P3-1. `auto_orch.py --scan` 동작: pipelines/*.yaml 스캔 → 트리거 평가 → 디스패치
- [ ] P3-2. `auto_orch.py --run <id>` 동작: 특정 파이프라인 수동 시작
- [ ] P3-3. `auto_orch.py --status` 동작: 실행 중 파이프라인 JSON 출력
- [ ] P3-4. `auto_orch.py --list` 동작: 등록된 파이프라인 목록
- [ ] P3-5. `auto_orch.py --validate <f>` 동작: YAML 검증만 수행
- [ ] P3-6. TeamLock 구현: fcntl.flock LOCK_EX|LOCK_NB, 락 실패 시 스킵
- [ ] P3-7. chain.py + dispatch.py subprocess 호출에 shell=False 강제
- [ ] P3-8. shlex.quote() 적용: 모든 사용자 입력 이스케이프
- [ ] P3-9. 상태 파일 정합성: crash 후 재시작 시 orphan 상태 정리
- [ ] P3-10. systemd timer + service 파일 작성 및 등록
  - service: Type=oneshot, TimeoutStartSec=25s, MemoryMax=512M, KillMode=process
  - timer: AccuracySec=1s, Persistent=false
  - auto_orch.py 시작 시 /tmp/auto-orch.lock에 fcntl.flock(LOCK_EX|LOCK_NB) 전역 락 획득
- [ ] P3-11. 3종 트리거 구현: event(.done), schedule(cron 표현식), manual(--run)
- [ ] P3-12. gates/ 디렉토리 + .approved 파일 기반 게이트 검증

### 테스트 항목
- [ ] T3-1. dry-run: 테스트 파이프라인 YAML → 트리거 평가 → 디스패치 시뮬레이션
- [ ] T3-2. TeamLock: 2개 프로세스 동시 접근 → 1개 거부
- [ ] T3-3. 상태 복구: state/ 파일에 stale 상태 → --scan에서 감지 + 정리
- [ ] T3-4. 타임아웃: 스텝 timeout_minutes 초과 → Telegram 알림 + 상태 갱신
- [ ] T3-5. MAX_RETRIES: 스텝 2회 실패 → 파이프라인 중단 + 알림

### 보안 검증
- [ ] S3-1. shell=False: subprocess.run() 호출에서 shell=True 사용 0건
- [ ] S3-2. shlex.quote(): 외부 입력이 명령줄에 포함되는 모든 경우에 적용
- [ ] S3-3. TeamLock: 락 파일이 locks/ 디렉토리에만 생성됨 (path traversal 방지)
- [ ] S3-4. gates/: .approved 파일의 uid가 jay 사용자인지 검증

---

## Phase 4: Layer 2↔3 통합

### 완료 조건
- [ ] P4-1. event_bus.py가 `memory/events/*.done` 스캔 → `orchestrator/incoming/` 복사 → 원자적 소비
  - Layer 2가 .done → .done.notified로 rename한 경우에도 event_bus.py가 해당 이벤트를 처리하는 메커니즘 확정 (스캔 패턴 확장 OR .done.orch-pending 별도 마커 도입 OR .done.notified 스캔 포함)
- [ ] P4-2. notify-completion.py가 .done 원본을 삭제하지 않음 (기존 동작 확인)
  - done-watcher.sh의 .done.processing → .done.notified 경로에서 원본 보존 방식 확정 (Phase 4 착수 전 옵션 A/B 결정 필수)
- [ ] P4-3. Layer 2 알림과 Layer 3 오케스트레이션이 동일 .done에 대해 독립 작동
- [ ] P4-4. chain_manager.py 체인과 auto_orch 파이프라인 네임스페이스 분리

### 통합 테스트 시나리오
- [ ] I4-1. 일반 작업 완료: .done → Layer 2 알림 (즉시) + Layer 3 이벤트 소비 (30초 내)
- [ ] I4-2. 체인 중간 Phase: .done → chain_manager.py next (Layer 2) + event_bus 이벤트 수신 (Layer 3, 파이프라인 매치 없으면 무시)
- [ ] I4-3. auto_orch 파이프라인 스텝 완료: .done → 파이프라인 다음 스텝 dispatch (Layer 3) + 알림 (Layer 2)
- [ ] I4-4. 아누 수동 dispatch + auto_orch 동시 접근: 같은 팀 → TeamLock이 후발 거부
- [ ] I4-5. 16팀 동시 완료 burst: 알림 + 오케스트레이션 모두 정상 처리

---

## Phase 5: 토큰 회계 + 모니터링

### 완료 조건
- [ ] P5-1. token_ledger.py가 task-timers.json 참조하여 일일 실제 사용량 추정
- [ ] P5-2. health.json 매 tick 갱신 (활성 파이프라인, 토큰, 오류 수)
- [ ] P5-3. 토큰 80% 도달 시 Telegram 경고 발송
- [ ] P5-4. 스텝 타임아웃 시 Telegram 알림 + state/ 갱신
- [ ] P5-5. `auto_orch.py --status`에 토큰 사용량 포함

### 테스트 항목
- [ ] T5-1. DAILY_HARD_LIMIT 80% 도달 시뮬레이션 → Telegram 경고 확인
- [ ] T5-2. health.json 내용 유효성 검증 (last_tick, active_pipelines 등)
- [ ] T5-3. task-timers.json ↔ token_ledger.json 정합성 교차 검증

---

## Phase 6: 파이프라인 YAML + 유스케이스

### 완료 조건
- [ ] P6-1. 최소 1개 파이프라인 YAML 작성 (marketing-dev-pipeline)
- [ ] P6-2. pipeline_validator 전건 통과
- [ ] P6-3. `auto_orch.py --validate` 성공
- [ ] P6-4. dry-run 모드로 전체 흐름 시뮬레이션 성공

### 테스트 항목
- [ ] T6-1. 파이프라인 YAML 문법 유효성
- [ ] T6-2. 템플릿 파일 존재 확인
- [ ] T6-3. dry-run 결과 로그 검토

---

## 공통 보안 검증 항목

- [ ] SEC-1. 모든 Python 파일: pyright 0 에러
- [ ] SEC-2. 모든 Python 파일: black + isort 포맷팅 준수
- [ ] SEC-3. 환경변수에 시크릿 하드코딩 0건
- [ ] SEC-4. subprocess.run()에서 shell=True 사용 0건 (신규 코드)
- [ ] SEC-5. 모든 파일 경로 조작에 path traversal 방어 (../ 차단)
- [ ] SEC-6. 로키 6대 보안 기준 전부 구현 확인
- [ ] SEC-7. 펜리르 P0 4건 전부 대응 확인
- [ ] SEC-8. activity-watcher.service 및 done-watcher.sh의 EnvironmentFile 최소 키 검토 완료 (.env.done-watcher 분리 후속 태스크 발행 확인)

---

## 공통 테스트 항목

- [ ] TEST-1. pytest 전건 통과 (관련 테스트 파일)
- [ ] TEST-2. pyright 0 에러 (변경 파일)
- [ ] TEST-3. black + isort 준수 (변경 파일)
- [ ] TEST-4. 기존 테스트 회귀 없음

---

*체크리스트 초안 종료 — 에이전트 미팅 검토 대기*
