# task-2519 — repository_policy_adapter (P1 — repository capability layer 표준화)

- 작업 유형: **runtime infrastructure hardening + 회귀 테스트** (정책 문서 X)
- 작업 레벨: **Lv.3+** (repository capability runtime layer)
- 우선순위: **★★ P1**
- Track: **repository_policy_adapter / blocked_reason_classification / merge_capability / phase2_p1**
- parallel_policy: **limited_parallel** (회장 명시 parallel_safe — gate enum 호환은 limited_parallel)
- 일시: 2026-05-09
- 회장 결정: 2026-05-09 직접 — Phase 2 P1 진입

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

> GitHub repository ruleset / branch protection / merge capability를
> **runtime capability layer**로 표준화한다.
>
> **"회장 직접 머지 요청" fallback을 제거**하고, admin override 없이도
> deterministic하게 정상 merge 경로를 선택할 수 있어야 한다.

## 시스템 상태

- ✅ Phase 1 완료 + Phase 2 P0 (task-2517 canonical_workspace_resolver) MERGED
- ✅ task-2516+1 (W1 default 활성화) MERGED
- 🟡 **Phase 2 P1 시작** — task-2518 (lifecycle) + task-2519 (본 task) 병렬

## 회장 명시 7 해결 대상

1. **auto-merge disabled** (PR #61/#67 BEHIND/BLOCKED 사례)
2. **required approving review**
3. **required_review_thread_resolution** (PR #61: 5 unresolved → BLOCKED, PR #67: 3 unresolved)
4. **mergeStateStatus BLOCKED 원인 불명확**
5. **bot token merge capability** (어떤 토큰으로 어디까지 가능한지)
6. **admin override 금지 상태에서 가능한 정상 merge 경로 탐색**
7. **"회장 직접 머지 요청" fallback 제거**

## dependency

- ✅ task-2509.merged
- ✅ task-2509+2.merged (automation_contracts.py)
- ✅ task-2511.merged (auto_gemini_triage — thread resolve 연동 대상)
- ✅ task-2517.merged (canonical_workspace_resolver)

## Merge Topology Gate metadata

```yaml
expected_files:
  - "utils/repository_policy_adapter.py"  # NEW
  - "tests/regression/test_repository_policy_adapter_2519.py"  # NEW

risk_area: "repository_capability / blocked_reason / merge_path / governance"

dependency:
  - "task-2509.merged"
  - "task-2509+2.merged"
  - "task-2511.merged"
  - "task-2517.merged"

parallel_policy: "limited_parallel"

merge_queue_position: 17

stale_recheck_required: true

cherry_pick_allowed: false
```

## 필수 구현

### 1. RepositoryCapability dataclass (6 probe)

```python
@dataclass(frozen=True)
class RepositoryCapability:
    can_squash_merge: bool
    requires_approval: bool                # required_approving_review_count > 0
    requires_thread_resolution: bool       # required_review_thread_resolution
    auto_merge_enabled: bool                # repo settings.allowAutoMerge
    bot_can_merge: bool                     # bot token이 머지 가능한지
    admin_override_required: bool           # 머지에 admin이 필요한지
```

probe 방식:
- `gh api repos/{owner}/{repo}/rules/branches/main` (Repository ruleset)
- `gh api repos/{owner}/{repo}/branches/main/protection` (classic protection)
- `gh api repos/{owner}/{repo}` (settings.allowAutoMerge)
- `gh api repos/{owner}/{repo}/collaborators/{bot}/permission`

### 2. BLOCKED 원인 분류 7종 (deterministic)

```python
class BlockedReason(str, Enum):
    UNRESOLVED_REVIEW_THREAD     # required_review_thread_resolution + thread > 0
    REQUIRED_APPROVAL            # required_approving_review_count > 0 + 미충족
    STALE_BASE                    # mergeStateStatus=BEHIND
    MISSING_CI_CHECK              # required check 누락
    BRANCH_PROTECTION             # 그 외 ruleset/branch protection
    PERMISSION_ISSUE              # bot_can_merge=false
    AUTO_MERGE_UNSUPPORTED        # auto_merge_enabled=false
```

`classify_blocked_reason(pr, capability)` → `BlockedReason` 정확 매칭.

### 3. 정상 merge 경로 선택 (회장 §명시)

```python
def select_merge_path(pr, capability, blocked_reason) -> MergePathPlan:
    ...
```

회장 명시 우선순위:
- `UNRESOLVED_REVIEW_THREAD` + outdated/style-only → **auto_gemini_triage 연동** (resolveReviewThread 자동)
- `STALE_BASE` → **base sync** (origin/main merge, force 금지)
- `REQUIRED_APPROVAL` → **`AUTOMATION_CAPABILITY_GAP` 분류** (회장 직접 머지 요청 X)
- `BRANCH_PROTECTION` / `PERMISSION_ISSUE` → `AUTOMATION_CAPABILITY_GAP`
- **admin override는 항상 금지** (`gh pr merge --admin` 호출 0)
- **회장 직접 머지를 기본 해법으로 삼지 않음** (모든 경로가 막혔을 때만 보고)

### 4. auto_gemini_triage 연동 인터페이스

- `repository_policy_adapter`가 `UNRESOLVED_REVIEW_THREAD` 분류 시
  `auto_gemini_triage.triage_pr(pr_number)` 호출 가능한 hook 제공
- 본 task에서는 호출 인터페이스만 정의, 실제 wiring은 후속 task

### 5. CanonicalWorkspace + automation_contracts 연동

- import: `from utils.canonical_workspace_resolver import resolve_canonical_workspace`
- import: `from utils.automation_contracts import EscalationPacket, CriticalEscalationType`
- contract schema 변경 0

### 6. CLI entrypoint

```
python3 utils/repository_policy_adapter.py --pr <N> --json
python3 utils/repository_policy_adapter.py --probe-capability --json
python3 utils/repository_policy_adapter.py --pr <N> --classify-blocked
```
- 출력: `RepositoryCapability` JSON 또는 `BlockedReason` + `MergePathPlan`

## 필수 회귀 테스트 14건

`tests/regression/test_repository_policy_adapter_2519.py`:

### capability probe (4건)
1. `RepositoryCapability` 6 field probe
2. ruleset의 `required_review_thread_resolution=true` 정확 인식
3. `required_approving_review_count=0` → `requires_approval=false`
4. bot permission probe (`bot_can_merge` 정확 매칭)

### BLOCKED 원인 분류 7종 (7건)
5. `UNRESOLVED_REVIEW_THREAD` (PR #61 fixture: 5 unresolved + ruleset)
6. `REQUIRED_APPROVAL` 합성 fixture
7. `STALE_BASE` (PR #67 fixture: BEHIND)
8. `MISSING_CI_CHECK` 합성
9. `BRANCH_PROTECTION` 합성
10. `PERMISSION_ISSUE` 합성
11. `AUTO_MERGE_UNSUPPORTED` 합성

### merge path + replay (3건)
12. **PR #61 replay**: 5 unresolved → `UNRESOLVED_REVIEW_THREAD` → auto_gemini_triage 연동 path
13. **PR #67 replay**: BEHIND → `STALE_BASE` → base sync path
14. **PR #68 replay** (task-2517 정상 머지): capability OK → squash merge path
- `REQUIRED_APPROVAL` → `AUTOMATION_CAPABILITY_GAP` 분류 (회장 직접 머지 요청 X 검증)
- `admin override 호출 0` 검증 (subprocess args에 `--admin` 포함 시 RuntimeError)

## replay fixture (회장 명시 — 반드시 replay)

- **PR #61** (task-2510, required_review_thread_resolution BLOCKED 5건)
- **PR #67** (task-2516+1, BEHIND + 3 unresolved)
- **PR #68** (task-2517, 정상 머지 capability)
- **required_review_thread_resolution BLOCKED 사례** (회장 §명시)

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

- **admin override 사용 금지** (`gh pr merge --admin` 호출 정적 차단)
- **branch protection 우회 금지**
- **merge_queue_executor 대규모 rewrite 금지**
- **정책 문서만 작성 금지**
- **dispatch.py 수정 금지**
- **5 모듈 본체 수정 금지**
- **canonical_workspace_resolver 수정 금지** (READ ONLY)
- **automation_contracts 변경 금지**
- **task-2518 영역 침범 금지** (lifecycle은 task-2518)
- **회장 직접 머지를 기본 해법으로 삼지 않음**
- **force push / rebase 금지**
- **required CI bypass 금지**
- **PR #52/#49/#50/#51 수정 금지**
- **expected_files 외 수정 금지** (정확히 2 파일)
- **자동 cherry-pick 구현 금지**
- **live pilot 직접 시도 금지** (task-2520 영역)
- **Critical 7종 외 회장 보고 금지**
- **amendment 무시 / mid-dispatch correction 무시 금지**

## allowed_resources

```yaml
allowed_resources:
  read_only_paths:
    - "memory/tasks/task-2509*"
    - "memory/tasks/task-2510*"
    - "memory/tasks/task-2511*"
    - "memory/tasks/task-2512*"
    - "memory/tasks/task-2513*"
    - "memory/tasks/task-2514*"
    - "memory/tasks/task-2515*"
    - "memory/tasks/task-2516*"
    - "memory/tasks/task-2517*"
    - "memory/tasks/task-2518*"
    - "memory/tasks/task-2519*"
    - "memory/feedback/feedback_critical_escalation_only_260508.md"
    - "utils/automation_contracts.py"
    - "utils/canonical_workspace_resolver.py"
    - "utils/merge_queue_executor.py"
    - "utils/replacement_pr_runner.py"
    - "utils/auto_gemini_triage.py"
    - "utils/post_merge_smoke_runner.py"
    - "utils/critical_escalation_reporter.py"
    - "utils/merge_topology_gate.py"
    - "memory/orchestration-audit/merge-queue.jsonl"
    - "memory/task-timers.json"
    - ".env.keys"
  paths:
    - "memory/tasks/task-2519*"
    - "memory/reports/task-2519*"
    - "memory/events/task-2519*"
    - "utils/repository_policy_adapter.py"  # NEW
    - "tests/regression/test_repository_policy_adapter_2519.py"  # NEW
  forbidden_actions:
    - "admin override 사용"
    - "branch protection 우회"
    - "merge_queue_executor 대규모 rewrite"
    - "정책 문서만 작성"
    - "dispatch.py 수정"
    - "5 모듈 본체 수정"
    - "canonical_workspace_resolver 수정"
    - "automation_contracts 변경"
    - "task-2518 영역 침범 (lifecycle_reconciliation_manager)"
    - "회장 직접 머지를 기본 해법으로 사용"
    - "force push"
    - "rebase"
    - "required CI bypass"
    - "PR #52/#49/#50/#51 수정"
    - "expected_files 외 수정"
    - "자동 cherry-pick 구현"
    - "live pilot 직접 시도"
    - "Critical 7종 외 회장 보고"
    - "amendment 무시 / mid-dispatch correction 무시"
```

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

1. ✅ 실행 가능한 Python 코드 (`utils/repository_policy_adapter.py`)
2. ✅ 회귀 14/14 PASS
3. ✅ BLOCKED 원인 분류 7종 PASS
4. ✅ approval-required 판별 PASS
5. ✅ thread-resolution-required 판별 PASS
6. ✅ bot merge capability 판별 PASS
7. ✅ Critical 7종 외 회장 보고 0건
8. ✅ Merge Topology Gate 자기참조 PASS (effective diff = 정확히 2 파일)
9. ✅ CI 11/11 SUCCESS
10. ✅ admin override 호출 0건 (정적 차단)
11. ✅ amendment 보호 의무 명시 + 적용 evidence

## 후행 (회장 명시)

task-2518 + task-2519 모두 main 머지 완료 후:
- **task-2520** — low-risk live pilot
- **task-2521** — automation observability/dashboard

## affected_files
- utils/repository_policy_adapter.py (NEW)
- tests/regression/test_repository_policy_adapter_2519.py (NEW)