# merge_group Evidence Auditor — C안 상세설계 (문서만, 코드/설정 변경 0)

상태명: `MERGE_GROUP_EVIDENCE_AUDITOR_DESIGN_READY_ACTIVE_FALSE`
작성: ANU 직접 (2026-07-01 KST). **설계 문서 전용 — 코드 수정·CI 변경·ruleset 변경 0.** 근거: 회장 질의(8팀 병렬 merge 안전) → GPT 단계판정 3회(false-pass 정정 수용 95 → 함정2 수용 97 → C안 상세설계 진행 승인). 선행: [[parallel_merge_safety_design_260701]].

## 0. 목적 (한 줄)
Merge Queue 활성 시 `gemini-review-gate` job이 merge_group 이벤트에서 **skipped=success**로 보고되어 Gemini 검증축이 조용히 우회되는 **false-pass**를 차단한다. PR 단계 Gemini gate는 그대로 두고, merge_group 단계에 **evidence freshness auditor**를 별도 required check로 둔다. Merge Queue를 못 켜도 **ANU 자체 merge train**에서 그대로 재사용 가능한 구조로 설계한다.

## 1. ★ SHA 3종 분리 (설계 최상위 전제 — GPT 확정)
merge queue 환경에서 SHA를 3종으로 명확히 분리한다. 하나로 뭉치면 auditor가 항상 fail 또는 false-pass 난다.
- **`pr_head_sha`**: 각 PR의 **원본 head commit**. → **Gemini evidence 검증 기준**. `evaluate_gate(pr, pr_head_sha)`에 넘길 SHA는 반드시 이것.
- **`merge_group_sha`**: merge queue가 만든 **통합 검증 commit**(target 최신본 + 큐 앞쪽 PR 포함). → **통합 회귀·semantic conflict 검증 기준**. (임시 브랜치 `gh-readonly-queue/main/pr-<N>-<base_sha>`의 head)
- **`base_sha`**: merge_group 생성 당시 **target branch 기준 SHA**. → 감사 로그·정합성 확인용.
- **함정 재확인**: `merge_group_sha`를 `pr_head_sha`처럼 취급 금지. merge queue는 PR을 rebase해 새 SHA를 만들므로, `merge_group_sha`에는 Gemini review가 안 달려 있다(evidence 0 → 무조건 block). 반드시 원본 `pr_head_sha`를 API로 조회해 evidence를 읽는다.

## 2. auditor가 한 게이트 안에서 하는 2가지 (분리 처리)
1. **Gemini evidence 감사** (기준 = 각 PR의 `pr_head_sha`): 큐에 포함된 **모든 PR**에 대해 evidence 존재·fresh·HIGH/CRITICAL 0·OWNER resolve 잔존 0 검증.
2. **통합 검증** (기준 = `merge_group_sha`): 통합 회귀/semantic conflict(1차 = 전체 회귀). ※ 이 축은 always() 회귀 job들이 merge_group에서 이미 돎(§ parallel_merge §3). auditor는 **evidence 감사에 집중**, 통합 회귀는 기존 CI job 재사용.

## 3. PR 역추적 (C안 최난점 — GPT 확정 3계층)
merge_group에 포함된 **PR 번호 + 원본 head SHA** 목록을 신뢰 가능하게 확보한다.
- **1차 (신뢰원)**: **GitHub API/GraphQL** — merge queue entry 또는 merge_group에 associated된 PR 조회로 `(pr_number, pr_head_sha)` 확정.
- **2차 (보조)**: webhook/Actions context `github.event.merge_group`(head_sha·base_sha·head_ref) — 단일 PR 확인 보조.
- **3차 (fallback only)**: `gh-readonly-queue/main/pr-<N>-<sha>` **ref명 파싱** + `base_sha..merge_group_sha` **commit trailer(`(#N)`) 파싱**. batch에서 top PR만 ref에 담기므로 보조·fallback으로만.
- **★ fail-closed**: 위 3계층으로도 **포함 PR 목록/head SHA를 확정하지 못하면 통과 금지 → `MERGE_GROUP_PRS_UNRESOLVED`로 fail**. "모르면 통과"는 절대 금지.

## 4. freshness 재정의 (300초 wall-clock 금지 — GPT 확정)
- PR 단계 gemini gate는 head push 후 **300초 wall-clock**(`TIMEOUT_SECONDS`) 기준이 의미 있다(실시간).
- **merge_group auditor에서는 300초 wall-clock 사용 금지.** PR이 큐에서 오래 대기하므로 시간 기준은 부적합.
- **SHA 일치 기반 freshness로 재정의**:
  - evidence의 `commit_sha == pr_head_sha` (기존 `stale = commit_id != head_sha` 로직이 정확한 `pr_head_sha`를 받으면 그대로 성립).
  - **queue admission 이후 PR에 새 push 없음**: 현재 PR head.sha == evidence 만들 때 `pr_head_sha`. 끼어든 commit 있으면 `HEAD_ADVANCED_AFTER_ADMISSION`로 fail.
  - evidence 부재 시 auditor는 **hold(대기) 하지 말고 즉시 fail-closed**(merge_group은 이미 큐 통과 후 시점이므로 "아직 없음"을 기다릴 이유 없음).

## 5. skipped=success 뒤집기 (GPT 확정)
- GitHub 기본: job-level `if`로 skip된 job은 conclusion=skipped → required check에서 success 취급 가능.
- **auditor는 `gemini-review-gate`의 check conclusion을 신뢰하지 않는다.** Gemini **evidence 자체를 직접 읽어 판정**한다.
- 판정 규칙: check가 success여도 **evidence 없음 / SHA 불일치 / skipped 상태**면 **fail**. → `GEMINI_EVIDENCE_SKIPPED_OR_MISSING` 계열 enum.

## 6. 구현 파일 후보 (신규/수정)
- **신규** `scripts/merge_group_evidence_auditor.py` — auditor 본체. 역추적 → PR별 `evaluate_gate(pr, pr_head_sha)` 호출 → 집계 → check run publish + exit code. (얇은 오케스트레이션)
- **신규** `scripts/merge_group_pr_resolver.py` — §3 3계층 역추적 전용(테스트 격리 목적 분리). 또는 auditor 내부 모듈로 흡수 가능.
- **신규 테스트** `tests/regression/test_merge_group_evidence_auditor_<id>.py`.
- **수정(최소)** `.github/workflows/ci.yml` — merge_group 전용 job 추가(`if: github.event_name == 'merge_group'`). ※ **본 설계 단계 미실행**, 활성화 시 회장 승인.
- **재사용(수정 0)** `scripts/gemini_evidence_verify.py::evaluate_gate` — §8 참조.
- **참고** `scripts/gemini_review_gate.py::publish_check_run` — check run publish 패턴 재사용.

## 7. required check 이름 (대칭 구조)
- PR 이벤트 전용: **`gemini-review-gate`** (기존 유지, `if: pull_request`).
- merge_group 이벤트 전용: **`merge-group-evidence-audit`** (신규, `if: merge_group`). ← merge_group required check 후보.
- **대칭 원칙**: 둘 다 required로 걸되 이벤트별 상호배타 실행. PR 이벤트=gemini 실행+auditor skip / merge_group=gemini skip+auditor 실행. 각자 자기 이벤트에서 실질 검증, 상대 이벤트에선 skip-pass(정합적). (phase3-merge-gate는 현재 required 아님 — 별도.)

## 8. 기존 evaluate_gate 재사용 범위 vs 수정 필요 범위
- **재사용(수정 0)**:
  - `evaluate_gate(pr_number, head_sha, repo)` — pass/hold/block + evidence(primary/secondary) + `stale = commit_id != head_sha` + HIGH severity 판정 전부 그대로.
  - auditor는 **호출 시 `head_sha = 원본 pr_head_sha`** 만 정확히 넘기면 SHA staleness가 그대로 성립.
- **auditor 레이어에서 재해석(evaluate_gate 미수정)**:
  - `state == "hold"`(elapsed<300s, evidence 없음)를 auditor는 **fail 취급**(merge_group은 대기 불필요). → wall-clock 미사용 효과를 auditor 판정에서 달성(evaluate_gate 내부 300초는 건드리지 않음).
  - `state == "block"`/HIGH>0/all_stale → fail. `state == "pass"` → 해당 PR OK.
  - queue admission 이후 새 push 검사(현 PR head.sha vs pr_head_sha)는 **auditor 신규 로직**(evaluate_gate 밖).
- **수정 최소화 결론**: evaluate_gate **본체 수정 불필요**. 필요한 신규 로직은 전부 auditor 레이어(역추적·집계·hold→fail 재해석·admission drift 검사)에 둔다. → 회귀 위험 최소.

## 9. 실패 상태 enum (auditor 결과)
- `PASS` — 큐 포함 전 PR evidence fresh·HIGH/CRITICAL 0·admission 이후 push 없음.
- `MERGE_GROUP_PRS_UNRESOLVED` — 포함 PR 목록/head SHA 확정 실패(§3 fail-closed).
- `GEMINI_EVIDENCE_SKIPPED_OR_MISSING` — 어떤 PR의 evidence 부재/ skipped-success 위장.
- `GEMINI_EVIDENCE_STALE` — evidence `commit_sha != pr_head_sha`.
- `HEAD_ADVANCED_AFTER_ADMISSION` — queue admission 이후 PR head 전진(끼어든 commit).
- `UNRESOLVED_HIGH_OR_CRITICAL` — unresolved HIGH/CRITICAL > 0.
- `OWNER_RESOLVE_PENDING` — OWNER resolve 필요 thread 잔존.
- `AUDITOR_INTERNAL_ERROR` — 조회/실행 오류 → **fail-closed**(통과 금지).
- 매핑: `PASS`만 exit 0(check success). 그 외 전부 exit≠0(check failure). **어떤 미결정도 통과로 보지 않는다.**

## 10. 테스트 케이스 (tests/regression)
1. 단일 PR·evidence fresh·HIGH0 → `PASS`.
2. 단일 PR·evidence 없음 → `GEMINI_EVIDENCE_SKIPPED_OR_MISSING`(fail).
3. gemini-review-gate check가 skipped=success인데 evidence 없음 → **check 무시하고 fail**(false-pass 차단 핵심 케이스).
4. evidence `commit_sha != pr_head_sha` → `GEMINI_EVIDENCE_STALE`.
5. `merge_group_sha`를 실수로 넘김 → evidence 0 → fail (함정1 회귀 방지: auditor가 pr_head_sha를 쓰는지 검증).
6. admission 이후 PR head 전진 → `HEAD_ADVANCED_AFTER_ADMISSION`.
7. unresolved HIGH 1건 → `UNRESOLVED_HIGH_OR_CRITICAL`.
8. batch 3 PR 중 1개 stale → 전체 fail(부분 통과 금지).
9. 역추적 API 실패 + ref/trailer fallback도 실패 → `MERGE_GROUP_PRS_UNRESOLVED`(fail-closed).
10. 역추적 API 성공, ref 파싱 불일치 → API 우선(1차 신뢰원) 확인.
11. queue 대기 500초 경과·evidence fresh → **PASS**(300초 wall-clock 미적용 회귀 방지: 함정2).
12. 내부 예외 발생 → `AUDITOR_INTERNAL_ERROR` fail-closed.

## 11. Merge Queue 미가용 시 재사용 (GHE Cloud 무관)
- GHE Cloud 자격 미달로 Merge Queue를 못 켜도, auditor 로직(역추적 제외한 evidence 감사 코어)은 **ANU 자체 merge train**(순차 merge 전 최신 main rebase 후 검증)에서 그대로 호출 가능. 역추적만 "다음 merge 대상 PR 목록"으로 치환.
- 따라서 본 설계는 Merge Queue 활성화와 **독립적으로 가치**가 있음(매몰비용 0).

## 12. 실행 순서 (설계 이후, 각 단계 회장 승인)
1. ✅ false-pass 판별 + A/B/C 비교(선행 문서).
2. ✅ 본 C안 상세설계(이 문서).
3. ▶ (승인 시) auditor 코드 구현 = **dev팀 위임**(ANU 코딩 0) — 신규 2스크립트+테스트, evaluate_gate 무수정.
4. ▶ ci.yml merge_group job 추가 + `merge-group-evidence-audit` required 등록.
5. ▶ GHE Cloud 자격 확인 → Merge Queue 활성화(또는 자체 merge train 채택).

## 금지 (본 설계 단계)
코드 수정 · ci.yml 변경 · ruleset/required check 변경 · evaluate_gate 수정 · Merge Queue 활성화 · 실제 8팀 병렬 실행 — 전부 **미실행**(문서만). 각 실행 회장 승인.

## 상태
설계 완료·대기. 다음 = 회장 승인 시 §12-3(dev팀 위임 구현)로 전진.
