# task-2509+2 — automation_contracts.py 공통 계약 freeze (5 모듈 본체 진입 전)

- 작업 유형: **공통 계약 코드 freeze + 회귀 테스트** (정책 문서 X)
- 작업 레벨: **Lv.2~3**
- 우선순위: **★★**
- Track: **automation_contracts / merge_lifecycle / 5 모듈 진입 전 freeze**
- parallel_policy: **serial_only**
- 일시: 2026-05-08
- 회장 결정: 2026-05-08 직접 명령 (task-2510-pre 명칭 폐기 → task-2509+2로 정정)

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

> task-2510-pre 명칭 폐기 (시스템 v2 task_id pattern이 `-pre` suffix 미지원).
> 정정 ID = task-2509+2.
> task-2509 → task-2509+1 → **task-2509+2** 시리즈로 일관.
> 후속 자동화 모듈 4개가 **서로 다른 enum/result/audit 형식**을 만들지 않도록, 공통 계약을 Python 코드로 **고정**한다.
> 산출물 = 코드 + 회귀 테스트. 문서는 부속.
> 본 task는 **계약 freeze**만 — 5 모듈 #2~#5 본체 wiring/구현 절대 금지.

## task 시리즈 의미

```
task-2509      merge_queue_executor
  → task-2509+1  review_gate_fallback hardening
    → task-2509+2  automation_contracts.py 공통 계약 freeze  ← 본 task
```

## dependency (충족)

- ✅ task-2509.merged (PR #58 → main `38334b09`)
- ✅ task-2509+1.merged (PR #59 → main `e0396365`)

## 대상 후속 모듈 (계약 import 사용처)

- replacement_pr_runner (task-2510)
- auto_gemini_triage (task-2511)
- post_merge_smoke_runner (task-2512)
- critical_escalation_reporter (task-2513)
- merge_queue_executor 연동부 (별도 wiring task)

## Merge Topology Gate metadata

```yaml
expected_files:
  - "utils/automation_contracts.py"
  - "tests/regression/test_automation_contracts_2509_plus_2.py"

risk_area: "merge_lifecycle / automation_contract / queue_executor / replacement_pr / gemini_triage / post_merge_smoke / critical_escalation"

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

parallel_policy: "serial_only"

merge_queue_position: 6

stale_recheck_required: true

cherry_pick_allowed: false
```

## 필수 계약 타입 12종 (회장 명시 — 정확 매칭)

### 1. `CriticalEscalationType` (Enum, 정확히 7종)
```python
FORBIDDEN_PATH_INTRUSION
REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF
GEMINI_REAL_BUG_REQUIRES_SCOPE_EXPANSION
BLOCK_OVERRIDE_REQUIRED_OR_REASON_INSUFFICIENT
DEPENDENCY_CYCLE_OR_SERIAL_ONLY_COLLISION
REPLACEMENT_PR_FAILED
POST_MERGE_SMOKE_FAILED
```

### 2. `RiskLevel` (Enum)
```python
LOW
MEDIUM
HIGH
HIGH_CORE
```

### 3. `GeminiStatus` (Enum, 7종)
```python
GEMINI_COMPLETED
GEMINI_UNRESOLVED
GEMINI_UNAVAILABLE_QUOTA
GEMINI_TIMEOUT
GEMINI_STALE
GEMINI_REAL_BUG
GEMINI_SCOPE_EXPANSION
```

### 4. `AutomationDecision` (dataclass)
- `decision`
- `reason_codes`
- `critical_escalation_type` (Optional[CriticalEscalationType])
- `auto_handled` (bool)
- `requires_chair` (bool)
- `audit` (dict)

### 5. `ReviewGateStatus` (dataclass)
- `gemini_status` (GeminiStatus)
- `unresolved_threads` (int)
- `fallback_review_used` (bool)
- `fallback_review_passed` (bool)
- `review_gate_passed` (bool)
- `reason` (str)

### 6. `FallbackReviewResult` (dataclass)
- `used` (bool)
- `passed` (bool)
- `risk_level` (RiskLevel)
- `checks` (dict)
- `reason` (str)

### 7. `ReplacementResult` (dataclass)
- `source_pr` (int)
- `replacement_pr` (Optional[int])
- `original_pr_preserved` (bool)
- `expected_files` (list[str])
- `effective_diff_files` (list[str])
- `forbidden_paths` (list[str])
- `success` (bool)
- `failure_reason` (Optional[str]) — `success=False` 시 필수

### 8. `GeminiTriageResult` (dataclass)
- `status` (GeminiStatus)
- `false_positive_count` (int)
- `style_only_count` (int)
- `real_bug_small_count` (int)
- `scope_expansion_count` (int)
- `unresolved_count` (int)
- `actions_taken` (list[str])

### 9. `SmokeResult` (dataclass)
- `command` (str)
- `passed` (bool)
- `exit_code` (int)
- `stdout_tail` (str)
- `stderr_tail` (str)
- `failure_reason` (Optional[str])

### 10. `QueueAuditRecord` (dataclass)
- `task_id` (str)
- `pr_number` (int)
- `queue_position` (int)
- `head_sha` (str)
- `base_sha` (str)
- `decision` (AutomationDecision)
- `checks` (dict)
- `review_gate` (ReviewGateStatus)
- `smoke` (Optional[SmokeResult])
- `critical_escalation` (Optional[CriticalEscalationType])
- `timestamp` (str)

### 11. `AutoMergeResult` (dataclass)
- `merged` (bool)
- `merge_commit` (Optional[str]) — `merged=True` 시 필수
- `smoke_result` (Optional[SmokeResult])
- `following_prs_rechecked` (list[int])
- `critical_escalation` (Optional[CriticalEscalationType])

### 12. `EscalationPacket` (dataclass)
- `task_id` (str)
- `pr_number` (int)
- `escalation_type` (CriticalEscalationType) — Critical 7종 중 하나만
- `reason` (str)
- `why_auto_cannot_continue` (str)
- `safe_options` (list[str])
- `recommended_option` (str)
- `evidence` (dict)

## 기존 task-2509+1 enum 이름 정렬 정책 (회장 명시)

> "기존 task-2509+1에서 ReviewGateStatus 또는 유사 enum이 이미 있더라도, task-2509+2에서는 공통 계약을 utils/automation_contracts.py에 freeze한다.
> 기존 구현은 후속 wiring에서 이 공통 계약을 import하도록 정리한다.
> 이번 task에서는 대규모 wiring은 하지 않는다."

→ task-2509+1의 기존 enum이름(`FORBIDDEN_PATH_INVASION` 등)은 **후속 wiring task에서 정렬**. 본 task는 freeze만.

## 공통 원칙 (회장 명시)

- Critical 7종 외 회장 보고 금지
- non-critical은 automation result/audit으로만 남김
- enum/string 불일치 방지
- JSON 직렬화 가능 (dataclass + to_dict 또는 dataclasses.asdict 호환)
- dataclass 또는 Enum 기반
- 후속 모듈에서 import 가능한 안정적 public API

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

`tests/regression/test_automation_contracts_2509_plus_2.py`:

1. `CriticalEscalationType`이 정확히 7종 (snapshot test)
2. Critical 7종 외 값 생성 안 됨 (Enum 멤버 7개 정확)
3. 모든 result/dataclass가 JSON 직렬화 가능
4. `AutomationDecision`에 `critical_escalation_type`이 있으면 `requires_chair=True`
5. `AutomationDecision`에 `critical_escalation_type`이 없으면 `requires_chair=False` 가능
6. `ReplacementResult.success=False` 시 `failure_reason` 필수
7. `ReviewGateStatus`에서 `GEMINI_UNAVAILABLE_QUOTA + fallback_review_passed=True` → `review_gate_passed=True`
8. `ReviewGateStatus`에서 `GEMINI_SCOPE_EXPANSION` → Critical #3 (`GEMINI_REAL_BUG_REQUIRES_SCOPE_EXPANSION`)으로 연결 가능
9. `SmokeResult.passed=False` → `POST_MERGE_SMOKE_FAILED` EscalationPacket 생성 가능
10. `QueueAuditRecord` 필수 필드(task_id, pr_number, head_sha, timestamp) 보존
11. `AutoMergeResult.merged=True` 시 `merge_commit` 필수
12. `EscalationPacket.escalation_type`이 Critical 7종 enum만 허용 (다른 string 거부)
13. 기존 `utils/merge_queue_executor.py`에서 `from utils.automation_contracts import ...` 가능 (import smoke)
14. pyright / import smoke PASS

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

- **dispatch.py wiring 금지**
- **merge_queue_executor 대규모 수정 금지** (import 가능성만 확인, breaking change 0)
- **replacement_pr_runner 구현 금지**
- **auto_gemini_triage 구현 금지**
- **post_merge_smoke_runner 구현 금지**
- **critical_escalation_reporter 구현 금지**
- 정책 md 작성만 하고 종료 금지
- PR #52/#49/#50/#51 수정 금지
- force push 금지
- rebase 금지
- admin override 금지
- manual .done 금지
- contaminated branch 재활용 금지
- **task-2510 / task-2510-pre 명칭 사용 금지** (모두 폐기 — 회장 §2 명시)

## allowed_resources

```yaml
allowed_resources:
  read_only_paths:
    - "memory/tasks/task-2509*"
    - "memory/events/task-2509*"
    - "memory/reports/task-2509*"
    - "memory/feedback/feedback_critical_escalation_only_260508.md"
    - "memory/feedback/feedback_merge_topology_gate_260508.md"
    - "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-2509+2*"
    - "memory/reports/task-2509+2*"
    - "memory/events/task-2509+2*"
    - "utils/automation_contracts.py"
    - "tests/regression/test_automation_contracts_2509_plus_2.py"
  forbidden_actions:
    - "dispatch.py wiring"
    - "merge_queue_executor 대규모 수정"
    - "replacement_pr_runner 구현"
    - "auto_gemini_triage 구현"
    - "post_merge_smoke_runner 구현"
    - "critical_escalation_reporter 구현"
    - "force push"
    - "rebase"
    - "admin override (gh pr merge --admin)"
    - "manual .done 생성"
    - "required CI bypass"
    - "PR #52/#49/#50/#51 수정"
    - "contaminated branch 재활용"
    - "정책 문서만 작성하고 종료"
    - "expected_files 외 수정"
    - "Critical 7종 외 회장 보고"
    - "amendment 무시 / mid-dispatch correction 무시"
    - "task-2510 / task-2510-pre 명칭 사용"
```

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

1. ✅ `utils/automation_contracts.py` 생성 (12 타입)
2. ✅ `tests/regression/test_automation_contracts_2509_plus_2.py` 생성
3. ✅ 회귀 테스트 14건 PASS
4. ✅ pyright / import smoke PASS
5. ✅ Critical 7종 enum snapshot PASS (정확 7종)
6. ✅ JSON 직렬화 테스트 PASS
7. ✅ `utils/merge_queue_executor.py`에서 `from utils.automation_contracts import ...` import 가능성 확인 (실제 wiring은 후속 task)
8. ✅ effective diff == expected_files (정확히 2파일)
9. ✅ forbidden path 0
10. ✅ Critical 7종 외 회장 보고 0건

## 시스템 3문서 참조

- 정책 본체 (Critical 7종 + 자동 머지 10조건): `memory/feedback/feedback_critical_escalation_only_260508.md`
- task-2509 머지 evidence: `memory/events/task-2509.lifecycle-backfill`
- task-2509+1 fixture: AUTO_MERGE_SUCCESS_WITH_REVIEW_FALLBACK_ASSUMPTION (main `e0396365`)

## 후행 task (5 모듈 본체 — 본 task 머지 후)

본 task가 freeze한 12 계약을 import해서 다음 4 모듈을 **독립 모듈 단위로 병렬 가능** (회장 명시):
1. task-2510 — replacement_pr_runner
2. task-2511 — auto_gemini_triage
3. task-2512 — post_merge_smoke_runner
4. task-2513 — critical_escalation_reporter

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

본 task는 **freeze만**. 후속 wiring 절대 금지.