# Context: GEMINI_SECOND_REVIEW_BOTTLENECK

**작업**: task-2565 / dev2-team (오딘) 단일 한정승인 / 4 Phase 일괄

---

## 1. 배경 — 왜 이 작업이 필요한가

ANU v2 자동화 green path에서 현재 가장 큰 병목은 **Gemini second-review 대기 구간**.

특히 follow-up commit 이후:
1. PR current head는 변경되는데
2. Gemini evidence는 old head에만 존재
3. CI gate가 SHA mismatch로 실패
4. 봇은 fresh review를 기다리지만 회장 수동 `/gemini review` 없이는 진행 불가
5. 봇 대기 시간이 누적되어 자동화 throughput 저하

이 문제를 **채팅 지시나 봇의 임의 대기로 해결하지 않고 코드/파일 자동화로 고정**한다.

## 2. 관찰 사례 (Fixture 박제용)

### 2.1. dev6 사례 — old Gemini SHA + follow-up push
- old Gemini review commit_id: `d251399c`
- follow-up commit: `cee55afe`
- CI gate failure: SHA mismatch (gemini-review-gate)
- 봇은 회장 수동 `/gemini review` 대기 상태로 stuck
- 기대 동작: `owner_trigger_only` runner 자동 호출 (180s grace 후)

### 2.2. dev5 사례 — merge-ready 후 wording-only commit
- CI 11/11 SUCCESS
- Gemini fresh on current head
- unresolved 0
- `mergeStateStatus` CLEAN
- 그러나 봇이 보고서/QC Verdict 문구 보강만을 위해 새 commit push
- 결과: 새 head 생성 → Gemini second-review 병목 재발 → merge 지연
- 기대 동작: `BLOCK_PRE_MERGE_REPORT_ONLY_COMMIT` + post-merge evidence redirect

### 2.3. race 사례 — fresh after CI fail
- CI gate가 stale evidence reason으로 실패
- 직후 Gemini fresh evidence가 current head에 도착
- current head는 unchanged
- 기대 동작: failed CI jobs 자동 rerun → 11/11 SUCCESS → merge_queue_decision 갱신

## 3. 기존 의존 모듈 — 절대 깨면 안 됨

### 3.1. `anu_v2/owner_trigger_only.py`
- task-2553+1 박제: OWNER_TRIGGER_ONLY_CAPABILITY 13 구현 원칙
- OWNER token은 `/gemini review` 1회 작성 외 모든 action 코드 차단
- 전용 token / hardcoded body / single endpoint / forbidden API hard-block / 9 tests
- **본 task에서 새 기능 추가 최소화** — 자동 호출 진입점만 연결

### 3.2. `anu_v2/executor_scheduler.py`
- bot의 PR/CI 상태 polling 핵심
- follow-up commit detection hook을 여기에 추가

### 3.3. `anu_v2/merge_queue_executor.py`
- task-2549 박제: same-PR post-Gemini push doctrine
- task-2537 박제: POST_MERGE 처리 (`anu_v2/post_merge_*`)
- CI rerun + pre-merge guard을 여기에 추가

### 3.4. `anu_v2/polling_policy.py`
- 기존 polling 상수 모듈
- `SECOND_REVIEW_GRACE_SECONDS` / `MAX_SECOND_REVIEW_RECHECKS` / `LONG_POLLING_FORBIDDEN` 추가

### 3.5. `silent_corruption_guard` / `taskctl`
- 본 task에서 직접 건드리지 않음
- 단 dev2는 checksum mismatch 감지 시 `merge_queue_decision=BLOCKED` 처리 의무

## 4. 적용 Doctrine (메모리 박제 → task md prompt에 강제 포함)

### 4.1. ★★★ OWNER_TRIGGER_ONLY_CAPABILITY (2026-05-11)
- 전용 모듈 / hardcoded body / single endpoint / forbidden API hard-block / 전용 token / redaction / dedupe atomic / audit jsonl / 9 tests
- 본 task의 자동 호출은 기존 capability 진입점 재사용

### 4.2. ★★ same-PR post-Gemini push doctrine (2026-05-11)
- Gemini review 도착 후 same-PR push/commit/amend 금지
- 위반 시 자동 Option A replacement 또는 `OWNER_DECISION_REQUIRED` 분기
- MERGE_READY pre-merge guard와 일관성 유지

### 4.3. ★★★ 14단계 finalize + 8항목 봇 직접 행동 (2026-05-10)
- task md prompt에 명시 강제
- "wrapper가 처리한다" 표현 금지
- BOT squash merge / post-merge smoke / reconcile evidence 단계 명시

### 4.4. ★★ GEMINI_EXTERNAL_TRIGGER_GAP (2026-05-09)
- bot의 `/gemini review` 코멘트는 무효
- 인간 계정 1회 코멘트만 trigger
- 본 task의 owner_trigger_only는 OWNER token으로 작성 (capability)

### 4.5. ★★ External gate stale recovery 5 모듈 분담 (2026-05-10)
- 본 task는 그 중 stale 감지 + 1회 nudge + self-resume 통합
- 5min cooldown / 같은 PR 24h 1회 / max_wait 24h hard limit과 정합

### 4.6. ★★★ Critical Escalation 7종만 회장 보고 (2026-05-08)
- 본 병목 자체는 Critical 7 아님
- 자동 처리 (DOGFOODING_PENDING/MERGE_PENDING_DEPENDENCY 류와 별개)

## 5. 회장 결정 사항 (확정)

| 항목 | 결정 |
|------|------|
| 담당 봇 | dev2-team (오딘) 단일팀 한정승인 |
| Phase 진행 | 4 Phase 일괄 위임 + phase-gate marker |
| `SECOND_REVIEW_GRACE_SECONDS` | 180초 |
| `MAX_SECOND_REVIEW_RECHECKS` | 1 |
| pre-merge 차단 primary | push 직전 executor guard / merge_queue_executor guard |
| pre-merge 차단 secondary | pre-commit hook은 optional 또는 dry-run evidence 수준 |
| 구현 형태 | 코드/파일 자동화 (정책 문서 X) |
| task ID | task-2565 |
| expected_files | strict 7파일 (plan.md §11 참조) |
| forbidden path | 기본 forbidden set |
| 상태 enum | 9개 (`MERGE_READY_AFTER_SECOND_REVIEW` 포함) |

## 6. 본 task 범위 밖 — 별도 처리

- **STATE_INTEGRITY_BLOCKED** (49건 stash / checksum mismatch / silent_corruption_guard vs taskctl 불일치 / taskctl 우회 후 direct merge 언급)
- dev2는 본 task 작업 중 위 상태 감지 시 `merge_queue_decision=BLOCKED`로 처리하고 회장 보고 (Critical 7 #4 candidate)
- 별도 audit decision은 다른 task에서 처리

## 7. Codex 사전 게이트 결과 + 설계 적응 (2026-05-13)

Codex 1차 게이트는 5건 risk (1 critical, 2 high, 1 medium, 1 low) 식별. 적응 결정:

### 7.1. Critical: 파일 미존재 (예정)
- 본 task가 새로 생성할 파일이 부재한 것은 기대 상태. 구현 후 자동 해소.

### 7.2. High: stale 판정 4조건 입력 데이터 부재
- **결정**: `second_review_recovery` 진입점은 **caller-provided dataclass** (`SecondReviewInput`)
  를 입력으로 받는다. 4조건 계산은 본 모듈이 직접 데이터 소스를 폴링하지 않고
  주입된 입력을 평가한다. 실제 caller(executor_scheduler / merge_queue_executor)는
  추후 follow-up timestamp, CI failure reason, failed job id, unresolved/outdated
  thread 정보를 입력 모델에 채워 호출한다.
- **테스트**: 본 task의 테스트는 fixture JSON으로 입력을 구성하여 4조건 판정 로직만 검증.
  데이터 수집 자체는 caller의 책임 (별도 후속 task가 채울 수 있음).

### 7.3. High: CI rerun 자동화 — owner_trigger의 endpoint hard-block과 충돌
- **결정**: CI rerun은 **owner_trigger_only**가 아닌 별도 경로로 수행.
  - `second_review_recovery.build_ci_rerun_decision()`는 decision JSON 만 생성
  - 실제 rerun 호출은 **dependency-injected callable** (caller 책임)
  - 본 task의 테스트는 decision JSON과 cap 제약(반복/long polling 금지)만 검증.
- **owner_trigger_only.py 변경 최소화**: 신규 함수 `trigger_for_second_review(pr_number, head_sha)`
  하나만 추가. 내부적으로 기존 `trigger_gemini_review` 호출. dedupe key는 그대로 (pr+head_sha).

### 7.4. Medium: 기존 GEMINI_STALE_ON_HEAD 상태 머신과 이중화 위험
- **결정**: 본 task의 9-state는 기존 marker와 **상충하지 않는 superset**.
  - 기존 `*.gemini-stale-on-head`는 generic 단일 stale signal
  - 본 task의 `*.gemini-stale-on-head-after-followup`은 follow-up 특화 분기
  - `second_review_decision.v1` JSON에 `legacy_marker_path` 필드로 기존 marker 경로 참조 가능
- executor_scheduler hook은 follow-up commit detection을 **추가**로 호출하며,
  기존 stale handling 경로를 제거하지 않는다 (회귀 방지).

### 7.5. Low: 설계 문서 내부 불일치 — merge_ready 5 vs 6 항목
- task.md §3.2 "merge_ready 5조건" + 추가 forbidden_path 0 = 실제 6 항목.
- **결정**: 구현에서는 6 항목 전수 (5 + forbidden_path 0)로 처리.
  코드 docstring과 test 이름에 명확히 6 항목 명시.
- expected_files strict는 본 task의 affected_files 7개 (plan.md §11 참조).

## 8. 3-Step Why 자기검증

1. **왜 이 설계가 필요한가?**
   follow-up commit 후 Gemini stale on head 패턴이 dev6/dev5에서 반복 발생.
   회장 수동 trigger 없이 봇 자체 정지 시간이 누적되어 throughput 저하 → 자동화 필요.
2. **왜 별도 모듈(second_review_recovery)이 최선인가?**
   기존 executor_scheduler/merge_queue_executor는 generic loop 중심.
   second-review 특화 상태머신을 한 모듈로 격리하면 (a) 단일 책임 (b) 테스트 격리
   (c) capability boundary 명확 (d) 회귀 영향 최소.
3. **왜 분리 모듈이 hook 호출 방식보다 나은가?**
   - executor_scheduler 내장 시: 850+ 라인 추가로 단일 책임 위반 + 기존 polling
     loop와 결합 위험 + 테스트 격리 곤란.
   - 분리 모듈 + hook: 호출 1줄로 결합 최소 + 모듈 단위 unit test + Phase별 점진
     확장 가능 + 추후 caller 교체에도 무방.
