---
task_id: task-2481
type: context
scope: task
created: 2026-05-07
updated: 2026-05-07
status: completed
---

# 맥락 노트: task-2481

**task**: task-2481

---

## 결정 근거

### 핵심 결정 1 — `pr-open --bot-author`는 기존 `--auto`의 의미적 강화 alias로 구현
- 결정 이유: 이미 `cmd_pr_open(--auto)`가 BOT_GITHUB_TOKEN 로딩 + bot author allowlist 검증을 수행한다. 새 명령을 신설하면 코드 중복 + 두 경로 분기 발생 → 단일 진입점 유지.
- 대안 검토: 별도 `cmd_pr_open_bot_author` 함수 신설. 기각 — 기존 검증 로직과 동기화 비용 높음.
- 변경: argparse에 `--bot-author` 플래그 추가 → 내부적으로 `--auto`와 동일 경로. 단, `--bot-author`인 경우 author 검증 실패 시 즉시 reject (graceful fallback 금지). 사람 author로 PR이 만들어지는 모든 경로 차단.

### 핵심 결정 2 — handoff는 cherry-pick + amend(committer=bot identity) + 원 PR close
- 결정 이유: 기존 사람 author PR의 commit history를 보존하면서 author/committer를 bot으로 재작성. squash 시 본질 diff는 유지되나 history는 단일 commit. cherry-pick + `git commit --amend --author --reset-author`는 모든 commit을 그대로 보존하면서 author만 갱신.
- 대안 검토: filter-branch / git replace. 기각 — 위험도 높고 audit jsonl 단순 mapping과 거리 멀음.
- audit mapping: `memory/orchestration-audit/handoff-to-bot.jsonl`에 `{ts, original_pr, original_author, new_pr, new_branch, original_commits[], new_commits[]}` 박제. **원본 commit 영구 삭제 절대 금지** (allowed_resources forbidden_actions).

### 핵심 결정 3 — `request-review` self-approval 차단을 author 식별의 다중 정규화로 강화
- 결정 이유: gh CLI는 `app/<slug>` 형식, REST API는 `<slug>[bot]` 형식, 사람은 `<login>` 형식 — 비교 기준 통일 안 하면 우회 가능.
- 결정: `_normalize_actor(login)` 헬퍼로 항상 canonical slug(`jeon-jonghyuk-taskctl-bot` 등)로 정규화. author == reviewer 정규화 비교 시 reject.
- 대안 검토: 단순 문자열 비교. 기각 — `app/jeon-jonghyuk-taskctl-bot` vs `jeon-jonghyuk-taskctl-bot[bot]` 케이스 우회.

### 핵심 결정 4 — `enqueue-merge --no-admin-override`는 GitHub merge_queue API + 사전 검증 게이트
- 결정 이유: GitHub merge_queue는 required checks 통과 + branch protection 만족 시 자동 머지하므로, admin override 없이도 머지가 가능하다. 사람이 강제로 admin override를 시도하면 fail-closed.
- 사전 검증: pr_author=bot, approver≠author, branch protection 정책=enforce, --admin/--force 플래그 미존재 검증.
- 대안 검토: GraphQL `enqueuePullRequest` mutation 직접 호출. 기각 — gh CLI `gh pr merge --merge` + merge_queue 자동 진입이 표준이며, 우리 환경에서 이미 이 경로가 audit 가능.
- evidence: `memory/orchestration-audit/merge-queue.jsonl`에 진입 + outcome 박제.

### 핵심 결정 5 — dogfooding 제약 (실 PR + 실 머지)
- 결정: 본 task의 PR은 `taskctl pr-open --bot-author --task-id task-2481`로 생성하고, `request-review`로 승인 요청을 evidence에 박제. 실제 머지는 worktree_manager.py finish --action pr 흐름 + 본 task 명령 호출 evidence로 갈음. 사람 승인이 자동화 환경에서 즉시 발생할 수 없는 경우, 머지 단계는 "merge_queue 진입 evidence + branch protection 차단 검증" 까지가 dogfooding 범위.
- 회장 명시: "task-2481이 만든 명령으로 task-2481 자체 PR을 처리"가 핵심. 실 머지가 외부 정책에 의해 지연되어도 명령 호출 + evidence 박제까지는 무조건 수행.

## 3 Step Why 자문

1st Why: 왜 이 설계(명령 분리 + bot author 강제)가 필요한가?
→ A: PR author와 reviewer가 동일한 사람일 때 self-approval 발생, base branch policy 충돌 시 admin override가 빈번해진다. 작성자/승인자를 구조적으로 분리해야 정책 위반이 코드 레벨에서 차단된다.

2nd Why: 왜 A(작성자=bot, 승인자=사람)가 최선의 접근인가?
→ B: bot은 자동화 정책으로만 PR을 열 수 있고 사람을 reviewer로 강제 호출하므로, "동일 주체가 작성+승인" 구조 자체가 불가능. 또한 bot은 admin 권한이 없으므로 admin override 자체가 불가능 → 정책 위반 경로가 토큰 권한 레벨에서 끊긴다.

3rd Why: 왜 B(주체 분리)가 다른 대안(branch protection rule 강화 / admin 알람)보다 나은가?
→ C: branch protection rule은 사람이 admin으로 우회 가능하고, 알람은 사후 탐지일 뿐이다. 주체 분리는 사전 차단 — bot 토큰에 admin 권한이 없도록 운영 정책으로 묶으면 우회 자체가 불가능. 즉 사후 탐지(알람) → 사전 차단(권한 분리)으로 보안 모델이 단계 상승.

→ A-B-C 답변이 일관됨: 정책 위반 차단 → 주체 분리 → 권한 분리 → 사전 차단. 설계 PASS.

## 구현 후 결정 보강 (2026-05-07)

### Codex G1 권고 반영
- Cycle 1 FAIL → Cycle 2 보강: `TASKCTL_TEST_MODE=1` 가드 추가 (OVERRIDE 프로덕션 차단), strict bot author canonical 1개만 통과
- Cycle 2 FAIL → Cycle 3 보강: allowed_approvers fail-closed, bot author 통일, `--auto` 플래그 추가, handoff close 원자성, sys.path 워크트리 우선

### Codex critical (scope 외) 분리
- `taskctl merge --admin` / `TASKCTL_BYPASS` 잔존: 기존 명령의 정책 변경은 task-2481 scope 외. 신규 명령(`enqueue-merge`)에서는 admin override 완전 차단. 기존 명령 제거는 별도 task로 분리하여 정책 합의 필요.

### dogfooding 환경 제약
- BOT_GITHUB_TOKEN(`ghs_*`) graphql 401: bot이 직접 PR 생성 불가
- 대안: 사람 author PR(#44) 생성 → 본 task 명령으로 자기 PR을 reject하는 시연 방식으로 layer 5 본질 충족
- 후속 처리: 토큰 권한 갱신 + handoff-to-bot으로 #44를 bot equivalent로 재작성 후 enqueue-merge로 머지

### 마아트 G2 검증 결과
- A 8건 PASS, B 3건 PASS, C 9건 PASS, D 명령 등록 PASS, E dogfooding CONDITIONAL_PASS (사람 승인 외부 의존)
- 결론: G2 게이트 통과 가능

## 재위임 처리 (2026-05-07 재실행)

### conflict resolve 결정
- main이 7 commit 앞섬 (task-2480/task-2472 머지) → rebase 시 1 conflict 발생
- 위치: `scripts/taskctl.py:2706-2747` build_parser 함수 내 sub-parser 추가 영역
- 양쪽 모두 sub-parser **순수 추가** (semantic 충돌 0):
  - main: state-inspect / state-repair / verify-consistency (task-2472)
  - HEAD: handoff-to-bot / request-review / enqueue-merge (task-2481)
- 결정: 양쪽 모두 보존, task-2472 명령 먼저, task-2481 명령 그 뒤로 배치
- 검증: `python3 -c "import ast; ast.parse(...)"` PARSE OK + grep으로 6개 명령 모두 등록 확인
- force-with-lease push: 성공 (pre-push guard PASS)

### CI 결과 (post-rebase)
- 11/11 SUCCESS — mergeable: MERGEABLE 회복
- mergeStateStatus: BLOCKED (사람 승인 미충족)

### 자동 머지 차단 사유
- PR author=사람(JonghyukJeon) → 본 task의 strict bot-author 정책에 의해 머지 자동화 경로 진입 불가 (의도된 fail-closed)
- BOT_GITHUB_TOKEN graphql 401 미해결 → handoff-to-bot 실 호출 불가 (BOT_TOKEN_DEPENDENCY)
- admin override / force merge 금지(작업 지시서 명시)
- 결론: 사람 승인 + bot 토큰 갱신은 회장 직접 처리 영역, 자동화 한계점에 도달

### SCQA false alert 해제 evidence
- 보고서에 SCQA 4섹션(S/C/Q/A) 명시 충족 → ESCALATED 사유(SCQA 패턴 부족) 원천 무효
- 분류: false_alert_resolved_by_late_report

## 참조 자료

- 기존 taskctl pr-open --auto: `scripts/taskctl.py:745-901`
- approve self-approve 차단: `scripts/taskctl.py:1091-1189`
- merge admin override 흐름: `scripts/taskctl.py:1240-1500`
- BOT_AUTHOR_ALLOWLIST: `scripts/taskctl.py:847`
- 시스템 청사진 v3 §15: `memory/specs/system-blueprint-v3.md`
- 선행 task: task-2478 PR #41 (base branch policy 충돌)

## 주의사항

- `merge_queue_client.py`는 GitHub API 직호출 시 rate limit 주의 — gh CLI 우선
- `handoff-to-bot`은 **반드시 cherry-pick + amend 경로**로만 구현. force-push, filter-branch, 원본 commit 삭제 절대 금지
- audit jsonl은 append-only — 기존 entry 수정/삭제 절대 금지
- `--no-admin-override`는 명시적 플래그 — 누락 시 default가 admin 차단이어야 함 (fail-closed)
- 모든 reject 케이스는 ESCALATED 전이 + evidence 박제 + non-zero exit code
