# task-2510 — replacement_pr_runner: contaminated PR 자동 replacement 생성

- 작업 유형: **자동화 코드 구현 + 회귀 테스트** (정책 문서 X)
- 작업 레벨: **Lv.3**
- 우선순위: **★★**
- Track: **replacement_pr / contaminated_pr_recovery / 5 모듈 #2**
- parallel_policy: **serial_only**
- 일시: 2026-05-08
- 회장 결정: 2026-05-08 직접 발행

## ⚠️ 본 task의 본질 — 회장 명시

> effective diff가 expected_files를 초과하거나 base 누적 commit이 섞인 **오염 PR을 자동 감지**하고,
> origin/main 기준 **clean branch에서 expected_files만 이식**하여 replacement PR을 자동 생성한다.
> **원 PR은 close/delete하지 않고 보존**하며, replacement 실패 시에만 회장에게 보고한다.
> 산출물 = 코드 + 회귀 테스트. 문서는 부속.

## dependency

- ✅ task-2509.merged (PR #58 → main `38334b09`)
- ✅ task-2509+1.merged (PR #59 → main `e0396365`)
- ⏳ task-2509+2.merged (automation_contracts.py freeze, dispatch 진행 중 cron `E1889F0E`)

## Merge Topology Gate metadata

```yaml
expected_files:
  - "utils/replacement_pr_runner.py"
  - "tests/regression/test_replacement_pr_runner_2510.py"

risk_area: "replacement_pr / contaminated_pr_recovery / pr_lifecycle / branch_safety"

dependency:
  - "task-2509.merged"
  - "task-2509+1.merged"
  - "task-2509+2.merged"

parallel_policy: "serial_only"

merge_queue_position: 7

stale_recheck_required: true

cherry_pick_allowed: false
```

## 실전 회귀 fixture (회장 명시)

- **PR #54 효과적 diff 78건 오염 사례** (task-2507 first attempt, base 누적 task-2487+1/2503/2485+1/2488/2489/2493 + POC + scripts)
- **task-2506 contaminated branch 사례** (task-2479-dev1 base 누적 117건)
- **task-2507 contaminated branch 사례** (PR #54)

본 fixture를 회귀 테스트 입력으로 사용 — fix 후 dry-run에서 동일 결과 재현 필수.

## 필수 구현 6건 (회장 §1~6)

### 1. contaminated PR 감지
- `effective diff != expected_files` 검출
- forbidden path 포함 검출
- base 누적 commit 포함 검출
- 다른 task 파일 포함 검출
- PR #54 / task-2506 / task-2507 fixture 재현

### 2. clean replacement branch 생성
- **origin/main 기준** (★ contaminated branch 재사용 금지)
- rebase/force push 금지
- **자동 cherry-pick 금지** (회장 §6 명시 amendment)
- **expected_files만 이식** (file copy from contaminated branch HEAD or git show)

### 3. 원 PR 보존
- close/delete 금지
- `[REPLACED]` 코멘트만 허용
- `original_pr_preserved=True` evidence 기록

### 4. replacement PR 생성
- effective diff == expected_files 사전 확인
- forbidden path 0 사전 확인
- replacement branch / PR metadata 기록

### 5. automation_contracts.py 사용 (task-2509+2 freeze 결과)
- `ReplacementResult` (source_pr / replacement_pr / original_pr_preserved / expected_files / effective_diff_files / forbidden_paths / success / failure_reason)
- `AutomationDecision`
- `CriticalEscalationType`
- `QueueAuditRecord`

### 6. critical escalation 조건 (회장 §6 5건)
- replacement PR 자동 생성 실패 → `REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF` (Critical #2)
- replacement PR도 effective diff 오염 → `REPLACEMENT_PR_FAILED` (Critical #6)
- expected_files 이식 불가 → `REPLACEMENT_PR_FAILED`
- forbidden path 침범 → `FORBIDDEN_PATH_INTRUSION` (Critical #1)
- 원 PR 보존 실패 → `REPLACEMENT_PR_FAILED`

## 필수 회귀 테스트 12건 (회장 §1~12)

`tests/regression/test_replacement_pr_runner_2510.py`:

1. clean PR이면 replacement 불필요 (no-op decision)
2. effective diff가 expected_files 초과하면 contaminated 판정
3. forbidden path 포함 시 Critical #1 (`FORBIDDEN_PATH_INTRUSION`)
4. contaminated PR에서 expected_files만 clean branch로 이식
5. original PR close/delete 없이 `[REPLACED]` 코멘트 생성
6. replacement diff가 expected_files와 정확히 일치
7. replacement 실패 시 Critical #2 또는 #6
8. **PR #54 effective diff 78건 오염 사례 재현** (★ fixture)
9. **task-2506 / task-2507 contaminated branch 사례 재현** (★ fixture)
10. 자동 cherry-pick 금지 검증 (subprocess args에 `cherry-pick` 포함 시 fail)
11. force/rebase/admin flag 정적 차단
12. `ReplacementResult` JSON 직렬화

## CLI entrypoint

`python3 utils/replacement_pr_runner.py --pr <N> --dry-run`
- 출력: `ReplacementResult` JSON
- contaminated 미검출 → `success=True, replacement_pr=None` (no-op)
- contaminated 검출 + replacement 생성 → `success=True, replacement_pr=<NEW>`
- replacement 실패 → `success=False, failure_reason=<enum>` + Critical EscalationPacket

## 금지 행위 (회장 명시 — 절대 준수)

- **dispatch.py wiring 금지**
- **merge_queue_executor 대규모 수정 금지** (import 가능성만 확인)
- **auto_gemini_triage 구현 금지** (task-2511 영역)
- **post_merge_smoke_runner 구현 금지** (task-2512 영역)
- **critical_escalation_reporter 구현 금지** (task-2513 영역)
- **기존 PR close/delete 금지** (★ original_pr_preserved=True 보장)
- force push 금지
- rebase 금지
- admin override 금지
- manual .done 금지
- **contaminated branch 재활용 금지** (★ origin/main 기준 신규 branch만)
- **자동 cherry-pick 구현 금지** (★ 회장 amendment)
- 정책 md만 작성하고 종료 금지

## allowed_resources

```yaml
allowed_resources:
  read_only_paths:
    - "memory/tasks/task-2509*"
    - "memory/tasks/task-2510*"
    - "memory/feedback/feedback_critical_escalation_only_260508.md"
    - "memory/feedback/feedback_merge_topology_gate_260508.md"
    - "utils/automation_contracts.py"  # task-2509+2 freeze
    - "utils/merge_queue_executor.py"
    - "utils/merge_topology_gate.py"
    - "memory/orchestration-audit/merge-queue.jsonl"
    - "memory/task-timers.json"
    - ".env.keys"
  paths:
    - "memory/tasks/task-2510*"
    - "memory/reports/task-2510*"
    - "memory/events/task-2510*"
    - "utils/replacement_pr_runner.py"
    - "tests/regression/test_replacement_pr_runner_2510.py"
  forbidden_actions:
    - "dispatch.py wiring"
    - "merge_queue_executor 대규모 수정"
    - "auto_gemini_triage 구현"
    - "post_merge_smoke_runner 구현"
    - "critical_escalation_reporter 구현"
    - "기존 PR close/delete"
    - "force push"
    - "rebase"
    - "admin override (gh pr merge --admin)"
    - "manual .done 생성"
    - "required CI bypass"
    - "PR #52/#49/#50/#51 수정"
    - "contaminated branch 재활용"
    - "자동 cherry-pick 구현"
    - "정책 문서만 작성하고 종료"
    - "expected_files 외 수정"
    - "Critical 7종 외 회장 보고"
    - "amendment 무시 / mid-dispatch correction 무시"
```

## 완료 조건 (회장 명시)

1. ✅ 실행 가능한 Python 코드 (`utils/replacement_pr_runner.py`)
2. ✅ 회귀 테스트 12건 PASS
3. ✅ dry-run으로 PR #54 오염 → replacement decision 재현
4. ✅ `automation_contracts.py` 기반 `ReplacementResult` 생성
5. ✅ Critical 7종 외 회장 보고 0건
6. ✅ Merge Topology Gate 자기참조 PASS
7. ✅ CI 11/11 SUCCESS
8. ✅ effective diff == expected_files (정확히 2파일)
9. ✅ forbidden path 0
10. ✅ amendment 보호 의무 명시 + 적용 evidence

## 시스템 3문서 참조

- 정책 본체: `memory/feedback/feedback_critical_escalation_only_260508.md`
- 공통 계약: `utils/automation_contracts.py` (task-2509+2 freeze)
- merge_queue_executor: `utils/merge_queue_executor.py` (task-2509 + task-2509+1)
- 실전 fixture: PR #54 (78건) / task-2506 (117건) / task-2507 contaminated 사례

## 후행 task

본 task가 5 모듈 #2. 머지 후 다음 3 모듈 병렬 가능 (회장 명시):
- task-2511 — auto_gemini_triage
- task-2512 — post_merge_smoke_runner
- task-2513 — critical_escalation_reporter

5 모듈 wiring(merge_queue_executor에 실제 연결)은 마지막 단일 serial task.

## affected_files (auto-detected)
- .worktrees/task-2509+2-dev3/tests/regression/test_automation_contracts_2509_plus_2.py
- .worktrees/task-2509+2-dev3/utils/automation_contracts.py

## goal_assertions (auto-generated)
- `python3 utils/replacement_pr_runner.py --pr <N> --dry-run`
