---
task_id: task-2467
type: context
scope: task
created: 2026-05-06
updated: 2026-05-06
status: completed
---

# 맥락 노트: task-2467 — taskctl PR Lifecycle State Machine

**task**: task-2467

---

## 결정 근거

### [핵심 결정 1] Phase A와 B를 분리하지 않고 통합 commit
- **이유**: 회장 명령 직접 인용 — "A와 B를 분리하지 않는다 — state machine만 있고 경로 연결이 없으면 taskctl은 존재하지만 아무도 사용하지 않는 상태가 된다."
- **대안과 기각 이유**: A(상태 머신만) → B(경로 라우팅)으로 분할 시, A 머지 사이에 우회 경로가 그대로 살아 있어 시스템 거버넌스 공백이 생김. 회장이 명시적으로 금지.

### [핵심 결정 2] 기존 11상태와 신규 14+5상태의 union 운영 (backwards-compat)
- **이유**: 운영 중인 task (task-2466 등)가 기존 상태값을 사용하므로 기존 상태 enum을 폐기하면 즉시 모든 task 차단됨. 신규 14+5을 추가하고, `DISPATCHED`/`ACKED`/`GUARD_PASS`는 alias 또는 호환 유지로 처리.
- **매핑**: `GUARD_PASS` ≡ `VERIFIED` (verify 명령은 두 상태 모두 인식). `DISPATCHED`/`ACKED`는 상태로 살리되, 새 task는 `WORKTREE_READY` 사용 권장.
- **대안과 기각 이유**: hard-cutover (모든 task를 새 상태로 마이그레이션) → migration script 필요 + 전체 dispatch.py / chain_manager.py 수정 → 본 task 범위 폭증. 회장 명령 "Phase 분리 금지"와 충돌.

### [핵심 결정 3] bot PAT 부재 시 graceful degradation + ADMIN_OVERRIDE_USED 박제
- **이유**: bot 계정 GitHub PAT는 회장 운영 영역(외부 발급). 본 task는 코드 경로(pr-open with bot token, fallback 분기)까지만 준비. PAT 미발급 시 PR 생성 단계에서 chairman admin override 1회 사용 + audit log 박제 + 후속 task로 명시.
- **drink-your-own-champagne 적용**: bot PAT 발급 후 task-2468부터 100% 자체 흐름 적용. 본 task는 신규 14상태/19전이/9 evidence 등 코어가 자기 자신에게 적용되어 동작함을 증명 (admin override 1회 사용 시 그 자체도 새 머신에서 박제).
- **대안과 기각 이유**: bot PAT 발급을 task에 포함 → 외부 자격 인증 작업이 코딩 task에 묶이면 토큰 발급 지연 시 task 진행 차단 + 회장 검토 필요. 분리가 안전.

### [핵심 결정 4] worktree_manager의 PR/머지 함수는 "차단"이 아니라 "taskctl 라우팅"
- **이유**: 기존 자동화(예: dispatch.py가 worktree_manager.finish --action pr 호출)가 즉시 깨지면 회귀 발생. taskctl로 라우팅하여 호환성 유지.
- **대안과 기각 이유**: 함수 자체 제거 → 호출자(dispatch/노티퍼) 모두 수정 필요 → 범위 폭증. 라우팅 방식이 안전.

---

## 3 Step Why 자문 (Lv.3+ 의무)

- **1st Why** ("왜 이 설계가 필요한가?"):
  → A: PR #31 머지에서 self-approve 구조 결함 + 우회 경로 잔존이 동시 노출됐다. 회장이 1회 admin override를 박제하면서 "이 구조 자체를 시스템으로 강제 차단" 명령. taskctl을 단일 관문으로 만들어야 self-approve와 우회가 구조적으로 불가능해짐.

- **2nd Why** ("왜 A가 최선의 접근인가?"):
  → B: 대안은 (1) 인적 정책 (체크리스트), (2) PR 템플릿 + 봇 코멘트 (수동 검사), (3) Branch protection rule만 운영. 모두 우회 가능. taskctl state machine + checksum + evidence 9종 박제는 코드 레벨에서 우회 경로를 막고 외부 audit 가능성을 보장. 회장 청사진 §10/§11이 같은 방향(evidence-based merge_policy).

- **3rd Why** ("왜 B가 다른 대안보다 나은가?"):
  → C: GitHub Actions 워크플로우만으로 강제 (대안 4)도 가능하지만, GitHub 측 변경에 종속되고 워크플로우 외 명령(예: 로컬 git push origin main, 직접 gh pr merge)은 차단 못함. taskctl은 로컬 + CI 양쪽에서 동일 evidence를 검증하므로 단일 진실 원천(SSoT). 또한 admin override audit log를 시스템이 자동 박제하므로 사후 감사 가능.

★ A-B-C 일관성: 시스템 거버넌스 공백 방지 → 코드 레벨 강제 → SSoT + audit 가능성 → 모두 회장 박제 청사진과 일치. 설계 통과.

---

## 참조 자료

- 회장 통합 명령: `memory/tasks/task-2467.md` §3 (회장 인용 [1]~[10])
- 청사진 4층 자동화: `memory/system_bot_orchestration_blueprint_260506.md`
- 감시견 안전 원칙: `memory/feedback_daemon_safety_principles_260506.md`
- 회장 admin override 박제 (선례): `memory/orchestration-audit/task-2465.jsonl`
- 회장 박제 보고서 (변경 금지): `memory/reports/task-2465.md`
- 현재 taskctl 코어: `scripts/taskctl.py` (1000줄, 11상태)
- 우회 경로 (제거 대상): `scripts/worktree_manager.py:830-1043` (action="pr")
- anu_confirm_bot 라우팅 현황: `scripts/anu_confirm_bot/main.py:122-138` (이미 taskctl로 일부 라우팅됨)
- finish-task.sh 머지 진입: `scripts/finish-task.sh:446-447` (worktree_manager.finish --action auto 경유)

---

## 주의사항

- **★ 회장 절대 룰**: PR #29 / #30 / #31 절대 변경 금지. 변경 발견 시 즉시 task FAIL.
- **★ task-2465 / task-2466 결과 read-only**: 회장 박제 보존.
- **★ GEMINI_API_KEY 도입 절대 금지**: 회장 명시 — Gemini App review만 사용. evaluate_gate는 GitHub API로 review 데이터만 fetch.
- **★ Phase A와 B 분리 commit 금지**: 단일 PR에 통합. 회장 명령.
- **★ 우회 경로 일부만 막고 일부 유지 금지**: 100% 차단 또는 task FAIL.
- **★ self-approve 구조 차단 후에도 단발성 admin override는 chairman 전용**: 시스템에서 자동 차단하지 않음 (chairman의 의도적 1회 사용 보장). 단 audit log 박제 + cap 강제.
- **★ Codex 사전 검증 sanitize**: 코드/명세를 외부 AI에 보내기 전 PII 마스킹 (주민번호/연락처/API key/계좌/증권번호). taskctl 코드에는 PII 가능성 거의 없으나 `BOT_GITHUB_TOKEN` 같은 PAT 관련 코드가 sanitize 대상.
- **★ 백업 cron 금지**: 본 task의 위임 완료는 dispatch.py + cron status=ok 직접 확인.
- **★ 위임 후 중간 정정 100% 무효**: 회장 격상 룰. 본 task 시작 후 회장 추가 지시는 별도 task로.

---

## Codex 사전 검증 박제 (2026-05-06 05:17, source=codex_companion)

**결과**: pass=false, risks=6 (critical=3, high=2, medium=1)

**Codex 지적 사항** (모두 본 task가 해소해야 할 baseline — 명세에 모두 반영):

1. **CRITICAL — 상태 모델 차이**: 현재 11상태 MVP, 명세 14+5. 특히 `cmd_merge()`가 MERGED→DONE 즉시 전이 (회장 §3.4 위반). → 명세 §6.5에 `taskctl done` 분리 박제됨. 구현 단계에서 `MERGED→DONE` 자동 전이 코드 제거.
2. **CRITICAL — pr-open / approve 미구현**: `cmd_pr_open()`이 PR번호만 받음 (bot token 미사용), `cmd_approve()`에 self-approve 차단 없음. → 명세 §6.1, §6.2에 박제됨.
3. **CRITICAL — finish-task.sh 우회**: worktree_manager finish --action auto + .merge-done/.done 직접 생성. → pr-lifecycle-spec §8에 BLOCKED→.done.blocked 분기 박제됨.
4. **HIGH — worktree_manager.py:1006 잔존**: `gh pr merge` 직접 호출. → 명세 §7에 차단 박제됨.
5. **HIGH — TASKCTL_BYPASS 누구나 가능**: `--admin` 개념 미구현. → 명세 §6.4에 chairman 자격 검증 + cap + audit log 박제됨. 추가 보강: TASKCTL_BYPASS는 ADMIN_OVERRIDE_USED 박제 필수, `--admin` 우선 권장.
6. **MEDIUM — 테스트 부재**: 기존 test_taskctl.py가 새 설계 방어 못함. → checklist Phase 3 박제됨.

**Codex 추가 제안 반영**:
- TOCTOU 방지: `approve`와 `merge` 직전 외부 상태 재조회 + `head_sha` 고정 → 명세에 추가 박제 (state.evidence.head_sha == branch HEAD 검사).
- monotonic transition id + append-only log → transitions 배열에 `seq` 필드 추가.
- file lock (concurrency) → state 파일은 단일 봇 전용, lock 파일은 start_task_guard에서 이미 생성.

**판정**: 본 task의 산출물이 이 6가지 risk를 모두 해소함. 구현 완료 후 codex_gate_check 재실행하여 PASS 받기 목표 (단, 본 task 자체는 거대한 시스템 변경이므로 일부 medium 위험은 후속 task로 넘어갈 수 있음 — 그 경우 명시적으로 박제).

**Sanitize 게이트 통과**: 본 task의 코드/명세에 PII 없음. `BOT_GITHUB_TOKEN`은 환경변수 이름만 노출 (값은 코드에 하드코딩되지 않음, .env.keys 로딩 헬퍼만 정의).

---

## 위임 / 라우팅 매핑 (스바로그/벨레스 작업)

### 스바로그 (백엔드, sonnet) Phase 1 — taskctl.py 확장
구체 위임 4요소 (plan.md 마이크로태스크 단위):
- 대상 파일: `scripts/taskctl.py` (단일 파일 in-place edit)
- 변경 내용: 상태 enum 확장 / 신규 명령 9개 / evidence 9종 / admin override + audit
- 테스트 방법: `python3 scripts/taskctl.py status task-2467 --machine` + pytest tests/taskctl/
- 커밋 메시지: `feat(taskctl): 14+5 state machine + evidence 9종 + admin override`

### 스바로그 Phase 2 — 우회 경로 제거
- 대상 파일: `scripts/worktree_manager.py`, `scripts/finish-task.sh`, `scripts/anu_confirm_bot/main.py`
- 변경 내용: 직접 `gh pr create`/`gh pr merge` 제거 → taskctl 호출로 변경
- 테스트 방법: `grep -rn "gh pr merge"` 등 3종
- 커밋 메시지: `refactor(routing): worktree_manager/finish-task/anu_confirm_bot → taskctl 단일 라우팅`

### 벨레스 (테스터, sonnet) Phase 3 — 테스트
- 대상 파일: `tests/state_machine/`, `tests/taskctl/` (신설 6개)
- 변경 내용: 20+ 케이스 (전이/lifecycle/evidence/self-approve/admin override/grep audit)
- 테스트 방법: `pytest tests/state_machine/ tests/taskctl/ -v`
- 커밋 메시지: `test(taskctl): state machine 전이 + lifecycle + evidence 9종 + admin override`
