# real merge executor wiring spec (read-only design · 코드 구현 금지) — 260523

회장 결정(2026-05-23 PR #139 88a1f2b0 ACCEPT 직후): callback governance stack production verified 후속. **본 spec = 설계 작성만**. 코드 구현 / real merge 실행 / auto-merge 활성화 회장 별도 승인까지 금지.

기반: `system_merge_ready_executor_spec_260522.md`(classifier) + `system_merge_ready_executor_dryrun_spec_260522.md`(dry-run) + `system_dryrun_executor_shadow_validation_spec_260523.md` + `system_normal_callback_registration_implementation_spec_260523.md` + `system_callback_collector_canonical_root_spec_260523.md` + `feedback_critical_escalation_only_260508.md` + `feedback_executor_completion_callback_mandatory_260518.md`.

확정 stack (origin/main `88a1f2b0` · CALLBACK_GOVERNANCE_STACK_PRODUCTION_VERIFIED):
1. callback runtime enforcement (24fadbf8)
2. callback lifecycle 2축 분류 + `callback_lifecycle.json` artifact
3. merge-ready classifier (c80e875d)
4. dry-run executor (b2932ce0)
5. shadow validation (5ffa87ae)
6. normal callback registration enforcement 5축 (1e24d79e)
7. callback collector canonical-root hardening 6축 직교 (88a1f2b0)
8. fallback cancel-on-success

★ real auto-merge executor 미승인. 본 spec 은 **wiring 설계** 까지만.

---

## 1. 실제 merge executor 입력 evidence

`real_merge_execute(merge_ready_result, pr_identity, gate_snapshot) -> merge_execution_result` (순수함수 권장):

```
INPUT:
  merge_ready_result   (merge_ready_classifier output)
  pr_identity          {pr, head_sha, task_id, branch, base_sha}
  gate_snapshot        (pre_merge_gate_snapshot.json — §4 필수 gates 11종 결과)
  dryrun_artifact      (utils/merge_ready_dryrun_executor.dryrun_route output · WOULD_MERGE artifact)
  callback_envelope    (PR 발사 envelope · canonical_root 명시 · 5축 status 정합)
  chair_authorization  (회장 명시 승인 JSON · per-PR 또는 batch · §6 activation flag)
```

**evidence 결핍 → fail-closed**:
- `merge_ready_result.verdict != PASS` → `NO_OP_NOT_PASS`
- `dryrun_artifact.executor_action != WOULD_MERGE` → `NO_OP_DRYRUN_MISMATCH`
- `gate_snapshot` 11종 중 하나라도 미통과 → `NO_OP_GATE_FAIL`
- `chair_authorization` 부재 → `NO_OP_NO_AUTHORIZATION`

---

## 2. merge-ready classifier PASS 조건 (입력 견고성)

real merge executor 는 classifier output 만 입력. classifier 가 PASS 라야 진입:
- `verdict = PASS`
- `evidence_completeness = COMPLETE` (CORE_EVIDENCE_SOURCES 전부)
- `gemini_gate = pass` (high_severity_hits = 0 · unresolved medium/high = 0)
- `threads_resolved = True` (unresolved = 0)
- `ci_checks = 11/11 PASS`
- `phase3_merge_gate = pass`
- `mergeStateStatus ∈ {CLEAN, MERGEABLE}`
- `Critical7_hits = 0` (forbidden path / scope expansion / replacement_pr fail / smoke fail / dep_cycle / serial / admin override)
- `credential_scan: NET_NEW_IDENTIFIER_EXPOSURE = 0` (BLOCKING_SECRET = 0)
- `out_of_expected_files = False` (exact match)

classifier 출력에 위 모두 반영 (precedence: UNKNOWN > CHAIR_REQUIRED > HOLD > PASS).

---

## 3. dry-run WOULD_MERGE 와 real merge executor 의 관계

```
classifier (PASS) → dryrun_route (WOULD_MERGE auto_merge_candidate.v1) → real_merge_execute (REAL_MERGE_DO)
```

- dry-run = **read-only proof of decision correctness** (`actually_executed=false` · `WOULD_*` 강제)
- real = **side-effect executor** (`actually_executed=true` · `REAL_*` 접두사 사용 권장)
- 두 executor 의 routing 매핑이 **byte-equal** (verdict→action) 이어야 진입 — shadow validation fixture 가 reference contract
- real executor 는 dryrun artifact 의 `pr_identity` + `executor_action=WOULD_MERGE` 를 **필수 입력**으로 받음 (no input → fail-closed)

**불변식**: dryrun 이 다른 verdict 를 내면 real 은 NO_OP. dryrun WOULD_MERGE 일 때만 real merge 진입 가능. 두 단계 모두 동일 evidence 로 호출 (deterministic).

---

## 4. 실제 merge 전 required gates 11종

| # | gate | source | 조건 |
|---|---|---|---|
| 1 | CI 11/11 PASS | gh pr checks | 모든 check `conclusion=success` |
| 2 | gemini-review-gate PASS | gh pr checks | fresh head evidence (high_severity_hits=0) |
| 3 | phase3-merge-gate PASS | gh pr checks | 별도 PASS |
| 4 | unresolved thread 0 | gh api reviewThreads | `isResolved=true` 100% |
| 5 | mergeStateStatus CLEAN/MERGEABLE | gh pr view | CLEAN 또는 MERGEABLE |
| 6 | Critical7 0 | classifier + scope guard | 7 카테고리 hits=0 |
| 7 | BLOCKING_SECRET 0 | credential 3-tier scan | ghp_/ghs_/PEM/AKIA/NET_NEW_IDENTIFIER_EXPOSURE = 0 |
| 8 | expected_files exact | classifier scope | 변경 파일 100% expected_files 내부 |
| 9 | forbidden path 0 | static guard | replacement_pr_runner / finish-task.sh / merge_ready_classifier / merge_ready_dryrun_executor 무수정 |
| 10 | admin override 불필요 | non-admin merge path | `gh pr merge --merge` 가능 (admin force 0) |
| 11 | callback lifecycle artifact 정상 | callback_lifecycle.json | delivery_outcome / miss_cause / root_cause_tags 정합 |

**gate_snapshot.json** = 위 11종 결과 + 측정 ts + source URL 직접 기록. classifier 출력과 별개 **즉시 측정 결과** (real merge 직전 t=0 시점).

snapshot 시점 ≠ classifier 결과 시점이면 **STALE_SNAPSHOT** fail-closed. snapshot TTL ≤ 5분 권장.

---

## 5. real_merge_execute switch 설계

```python
def real_merge_execute(
    merge_ready_result,
    pr_identity,
    gate_snapshot,
    dryrun_artifact,
    callback_envelope,
    chair_authorization,
    *,
    activation_flag: bool = False,           # default OFF (§6)
    subprocess_runner=None,                  # gh pr merge wrapper · injection 가능
) -> RealMergeExecutionResult:
    """
    회장 명시 승인 (chair_authorization) + activation_flag=True 필수.
    둘 중 하나라도 부재 → NO_OP_NO_AUTHORIZATION.

    실제 호출: gh pr merge <pr> --merge --delete-branch=false (non-admin only).
    admin override CLI 옵션 hardcoded 차단 (정적 가드).
    """
```

### 5.1 switch 분기
- `activation_flag = False` (기본값) → 모든 입력 무시 · NO_OP_FLAG_DISABLED
- `activation_flag = True` 이지만 `chair_authorization = None` → NO_OP_NO_AUTHORIZATION
- 둘 다 True 인 경우에만 **§4 gate_snapshot 11/11 재검증** → 통과 시 merge 진행
- merge 도중 예외 발생 → `REAL_MERGE_FAILED` + rollback no-op (idempotent)
- merge 성공 → `REAL_MERGE_DONE` + post-merge smoke trigger (§9)

### 5.2 결과 enum
```
NO_OP_FLAG_DISABLED          — activation_flag=False
NO_OP_NO_AUTHORIZATION       — chair_authorization 부재
NO_OP_NOT_PASS               — classifier 결과 PASS 아님
NO_OP_DRYRUN_MISMATCH        — dryrun WOULD_MERGE 아님
NO_OP_GATE_FAIL              — gate_snapshot 11종 중 하나 이상 실패
NO_OP_STALE_SNAPSHOT         — snapshot TTL 초과
NO_OP_DUPLICATE              — 동일 PR head_sha 이미 처리 (§12 dedupe)
REAL_MERGE_DONE              — 성공
REAL_MERGE_FAILED            — 실패 (subprocess 예외 등)
```

---

## 6. activation flag 설계

```python
ACTIVATION_FLAG_DEFAULT = False                          # hardcoded default
ACTIVATION_FLAG_ENV_VAR = "ANU_REAL_MERGE_EXECUTOR_ACTIVE"  # env override (회장 외 사용 금지)
```

### 6.1 chair_authorization JSON schema
```
{
  "schema": "anu.chair_merge_authorization.v1",
  "scope": "per_pr" | "batch",
  "pr_numbers": [...],                       # explicit 명시 (per_pr 시 1건, batch 시 N건)
  "head_shas": [...],                        # 본 SHA 외 머지 시도 금지
  "expires_at_kst": "YYYY-MM-DD HH:MM",      # TTL · 미래 cap (예: ≤24h)
  "chair_signature": "...",                  # 회장 verbatim 메시지 (verbatim chair token)
  "issued_at_kst": "...",
  "task_id": "...",
  "expected_files_snapshot": [...],
  "notes": "...optional..."
}
```

### 6.2 검증 규칙
- `expires_at_kst` 경과 → NO_OP_NO_AUTHORIZATION
- `pr_identity.pr` ∉ `pr_numbers` → NO_OP_NO_AUTHORIZATION
- `pr_identity.head_sha` ∉ `head_shas` → NO_OP_NO_AUTHORIZATION (head 변경 시 재승인 강제)
- 회장 verbatim signature 정합 확인 (텍스트 매칭 doctrine — `feedback_chair_directive_exactness_260515` 적용)

### 6.3 default OFF doctrine
- ACTIVATION_FLAG_DEFAULT = False · hardcoded 상수 (런타임 변경 0)
- env_var 활성화는 회장 명시 setup 외 사용 금지 (audit log 강제)
- import time 정적 assertion: `assert ACTIVATION_FLAG_DEFAULT is False`

---

## 7. artifact writer 4종

`memory/events/real_merge/<pr_<num>>/<head_sha>/` 디렉터리 (canonical_root 기준):

### 7.1 `merge_decision.json` (real_merge_execute 진입 시 작성)
```
{
  "schema": "real_merge.decision.v1",
  "actually_executed": <bool>,                    # gate 통과 후 merge 호출 직전 결정
  "ts_kst": "...",
  "pr_identity": {...},
  "verdict_input": "PASS",
  "dryrun_action_input": "WOULD_MERGE",
  "activation_flag": <bool>,
  "chair_authorization_present": <bool>,
  "result_enum": "NO_OP_*" | "REAL_MERGE_DONE" | "REAL_MERGE_FAILED",
  "reasons": [...]                                # NO_OP 시 fail-closed 근거
}
```

### 7.2 `pre_merge_gate_snapshot.json` (§4 11종 즉시 측정)
```
{
  "schema": "real_merge.pre_gate_snapshot.v1",
  "ts_kst": "...",
  "snapshot_ttl_seconds": 300,
  "gates": [
    {"name":"ci_checks","conclusion":"success","count":"11/11","source":"gh pr checks","ts":"..."},
    {"name":"gemini_review_gate","conclusion":"PASS","source":"...","ts":"..."},
    {"name":"phase3_merge_gate","conclusion":"PASS","source":"...","ts":"..."},
    {"name":"unresolved_threads","value":0,"total":N,"source":"...","ts":"..."},
    {"name":"mergeStateStatus","value":"CLEAN","mergeable":"MERGEABLE","source":"...","ts":"..."},
    {"name":"critical7_hits","value":0,"breakdown":{...},"source":"...","ts":"..."},
    {"name":"blocking_secret","value":0,"net_new_identifier_exposure":0,"source":"...","ts":"..."},
    {"name":"expected_files_exact","value":true,"source":"...","ts":"..."},
    {"name":"forbidden_path","value":0,"checked_files":[...],"source":"...","ts":"..."},
    {"name":"admin_override_required","value":false,"source":"...","ts":"..."},
    {"name":"callback_lifecycle_artifact","value":"normal","delivery_outcome":"...","ts":"..."}
  ],
  "all_pass": <bool>
}
```

### 7.3 `post_merge_smoke_result.json` (REAL_MERGE_DONE 직후 §9)
```
{
  "schema": "real_merge.post_smoke.v1",
  "ts_kst": "...",
  "merge_commit_sha": "...",
  "origin_main_before": "...",
  "origin_main_after": "...",
  "smoke_checks": [
    {"name":"main_fetch_ff","ok":true},
    {"name":"forbidden_files_untouched","ok":true,"checked":[...]},
    {"name":"baseline_regression","ok":true,"summary":"X/X PASS"},
    {"name":"callback_canonical_root_lookup","ok":true,"resolved_path":"/home/jay/workspace/..."},
    {"name":"real_merge_executor_active_post_check","value":<bool>,"expected":<bool>}
  ],
  "all_ok": <bool>
}
```

### 7.4 `merge_execution_result.json` (최종)
```
{
  "schema": "real_merge.execution_result.v1",
  "ts_kst": "...",
  "pr_identity": {...},
  "result_enum": "REAL_MERGE_DONE" | "REAL_MERGE_FAILED",
  "merge_commit_sha": "...",
  "mergedAt": "...",
  "mergedBy": "...",
  "non_admin": true,
  "admin_override_used": false,
  "subprocess_invocations": [
    {"cmd":"gh pr merge <pr> --merge --delete-branch=false","exit":0,"ts":"..."}
  ],
  "post_smoke_ok": <bool>,
  "callback_handoff_event_id": "..."
}
```

### 7.5 write 조건
- canonical_root resolver (task-2636) 통과 후 절대 경로 사용
- atomic write (`*.tmp` → `rename`) — partial write 금지
- 동일 head_sha 재호출 → §12 dedupe (no overwrite)

---

## 8. branch protection 정책

- **non-admin merge 만 허용**: `gh pr merge --merge --delete-branch=false` (admin force 옵션 hardcoded 차단)
- branch protection bypass 0 · admin override 0 (static guard: `gh pr merge --admin` 호출 코드 경로 부재)
- **self-approval 방지**:
  - PR 작성자 (ANU OWNER 또는 dev bot) ≠ merger 권장
  - merger = JonghyukJeon (OWNER) 만 인정 (chair_authorization signature 일치)
  - self-approval 감지 시 NO_OP_SELF_APPROVAL (별도 enum)
- review_decision REQUIRED 정책 별도 (branch protection 설정과 sync)

**self-approval 발생 시 auto-merge 금지**: real_merge_execute 가 mergedBy 후보를 사전 추정 못함 → chair_authorization 의 `chair_signature` 가 곧 merger identity 와 일치 단언. 불일치 시 NO_OP.

---

## 9. post-merge smoke 연결

real_merge_execute 가 `REAL_MERGE_DONE` 직후 자동 trigger:

```
1. git fetch origin main (ff 확인)
2. forbidden file 4종 blob sha 비교 (base vs new main)
3. baseline regression (전체 callback governance + dryrun stack)
4. canonical_root resolver lookup 검증 (canonical 산출물 실 파일 존재)
5. real_merge_executor active state 재검증 (auto-merge 비활성 확인)
6. post_merge_smoke_result.json 작성 (§7.3)
```

smoke 실패 시 → `POST_MERGE_SMOKE_FAILED` (Critical7 분류 · 회장 보고)

---

## 10. failure 시 회장 보고 조건 (Critical7 트리거)

| trigger | 분류 |
|---|---|
| BLOCKING_SECRET 감지 (NET_NEW_IDENTIFIER_EXPOSURE) | CHAIR_REPORT |
| forbidden file 4종 수정 감지 | CHAIR_REPORT |
| admin override 시도 (코드 정적 가드 우회) | CHAIR_REPORT |
| chair_authorization signature 위변조 | CHAIR_REPORT |
| post-merge smoke 실패 | CHAIR_REPORT (POST_MERGE_SMOKE_FAILED) |
| replacement_pr fail | CHAIR_REPORT |
| scope expansion (expected_files 밖) | CHAIR_REPORT |
| dep_cycle / serial collision | CHAIR_REPORT |
| activation_flag 비정상 ON (env var 통한 우회 시도) | CHAIR_REPORT |
| Gemini medium/style + expected_files 내부 | auto-converge (보고 0) |

**NO_OP** 결과는 보고 0 (정상 fail-closed). **REAL_MERGE_DONE** 도 보고 0 (smoke OK 시).

---

## 11. low-risk PR production smoke pilot 조건

real merge executor 첫 활성화 = 1회성 low-risk PR pilot:

- **PR 조건**:
  - test/fixture only (프로덕션 코드 변경 0)
  - 변경 파일 ≤ 10
  - expected_files exact + Critical7 0
  - 기존 PR 패턴 (#136 dry-run / #137 shadow / #138 callback / #139 canonical-root) 와 동일 형식
  - branch base = origin/main HEAD (fresh)
- **chair_authorization** = per_pr scope · TTL ≤ 1h
- **rollback 준비**: 실패 시 OWNER 가 `git revert` 즉시 가능 (test-only 라 revert risk minimal)
- **observability**: 4개 artifact 전부 생성 + smoke result chair 보고
- **pilot success criteria**: REAL_MERGE_DONE + post-merge smoke all_ok + 회장 ACCEPT
- pilot 후 batch scope 확대 별도 결정

---

## 12. rollback / no-op / dedupe 정책

### 12.1 dedupe
- 동일 `(pr, head_sha)` 이미 `real_merge_execute` 호출 흔적 (`merge_execution_result.json` 존재) → NO_OP_DUPLICATE
- idempotent (재호출 0 부작용)

### 12.2 no-op
- 모든 NO_OP_* enum 은 부작용 0 (artifact writer 만 작성 · subprocess 호출 0)
- no-op 도 `merge_decision.json` 작성 (감사 trail)

### 12.3 rollback
- real merge 후 rollback **자동 안 함** (회장 결정 영역)
- post-merge smoke 실패 시 → CHAIR_REPORT + rollback 권장 사항 명시 (artifact 에 `rollback_recommended=true`)
- 실제 rollback (revert PR) = 별도 회장 결정 task

---

## 13. callback collector ↔ merge result handoff 연결

real merge 결과를 callback governance stack 과 연결:

```
real_merge_execute → callback envelope 생성:
  task_id = "task-2632 merge-ready" 또는 후속 wiring task
  registration_intent = True
  registration_attempted = True
  registration_result_status = REGISTERED                # cron 등록 후
  cron_schedule_id = <ANU collector cron id>
  delivery_method = "anu_cron_callback"
  callback_delivery_status = DELIVERED                  # cron 발사 성공 시
  collector_receipt_status = UNCONFIRMED → RECEIVED     # ANU 회수 후 update
  canonical_root = "/home/jay/workspace"                # 6번째 직교
  merge_execution_result_path = "memory/events/real_merge/.../merge_execution_result.json"
  post_merge_smoke_result_path = "memory/events/real_merge/.../post_merge_smoke_result.json"
```

ANU collector 가 envelope 수신 → canonical_root resolver 로 result_path 정확 resolve → handoff 완결.

5축 + 6번째 canonical_root + handoff path 2개 = 8 필드 정합. `is_callback_action_trigger=True` 단언.

---

## 14. real executor 가 절대 건드리면 안 되는 forbidden paths

정적 가드 (import time assertion + runtime guard):
- `utils/replacement_pr_runner.py`
- `scripts/finish-task.sh`
- `utils/merge_ready_classifier.py`
- `utils/merge_ready_dryrun_executor.py`
- `utils/callback_envelope_schema.py`
- `utils/anu_callback_registrar.py`
- `utils/canonical_root_resolver.py`
- `utils/anu_collector_action_trigger.py`
- `dispatch/finalize_hooks.py`
- `.tasks/` 디렉터리 일체
- 모든 fixture 디렉터리 (`tests/fixtures/`)
- 모든 regression 디렉터리 (`tests/regression/`)

real_merge_execute 가 PR 변경 파일 목록을 사전 점검 → 위 path 포함 시 NO_OP_FORBIDDEN_PATH + CHAIR_REPORT.

---

## 15. 금지 (회장 verbatim)

- 코드 구현 금지 (본 spec = 설계 작성만)
- real merge 실행 금지
- auto-merge 활성화 금지
- GitHub write 금지
- branch protection 우회 금지
- admin override 자동화 금지
- NL intake 코드 구현 금지
- foreign dirty 정리 금지
- replacement_pr_runner 수정 금지
- finish-task.sh 수정 금지
- production service task 와 혼합 금지

---

## 16. 코드화 전 남은 결정 사항 (회장 결정 대기)

1. **activation_flag 활성화 절차** verbatim 회장 결정 (env var 명령 + chair_authorization JSON 작성 권한 + signature 형식)
2. **chair_authorization signature 정합 방식** (verbatim token vs HMAC vs 단순 문자열 매칭)
3. **gate_snapshot TTL** 권장 5분 (회장 결정 필요)
4. **post-merge smoke 실패 시 자동 rollback 여부** (본 spec = 보고 only · 자동 rollback 미권장)
5. **low-risk PR pilot 1차 target** (어떤 PR 로 시범 — test-only PR 후보 식별)
6. **artifact 디렉터리 위치** 최종 (`memory/events/real_merge/` 후보 / 회장 결정)
7. **batch scope 확대 기준** (pilot 후 단계적 확대 vs 명시 PR 별 승인)
8. **dryrun→real switch dispatch 위치** (`dispatch/finalize_hooks.py` 확장 vs 신규 `dispatch/real_merge_hooks.py`)
9. **forbidden paths 갱신 정책** (stack 추가 시 자동 갱신 vs 회장 결정)
10. **본 spec 코드화 task 발행 시점** (real merge executor wiring 별도 dev6 또는 다른 봇 dispatch)

---

## 17. frozen anchor (D-SPEC-EXACTNESS)

- ANCHOR-1: "본 spec = 설계 작성만 · 코드 구현 금지 · real merge 실행 금지 · 회장 별도 승인까지 OFF 유지"
- ANCHOR-2: "activation_flag default=False (hardcoded) · chair_authorization 없으면 NO_OP_NO_AUTHORIZATION · 둘 다 True 일 때만 11종 gate 재검증 후 merge"
- ANCHOR-3: "non-admin merge 만 허용 · admin override 코드 경로 0 · self-approval 시 NO_OP"
- ANCHOR-4: "dryrun WOULD_MERGE → real REAL_MERGE_DONE 매핑 byte-equal (verdict→action 동일)"
- ANCHOR-5: "artifact 4종 + canonical_root 기준 atomic write · dedupe (pr+head_sha) · rollback 자동 안 함"
- ANCHOR-6: "low-risk PR pilot 1회 후 회장 ACCEPT 받고 batch scope 확대 결정"
- ANCHOR-7: "forbidden paths 11+ 종 정적 가드 · stack 추가 시 명시 갱신"
- ANCHOR-8: "callback governance stack production verified (88a1f2b0) 와 별개 — 본 wiring 은 다음 layer"
