---
task_id: task-2512
type: context
scope: task
created: 2026-05-09
updated: 2026-05-09
status: completed
---

# 맥락 노트: task-2512

**task**: task-2512 — post_merge_smoke_runner

---

## 결정 근거

### 1. SmokeResult 필드 vs 회장 §6 신호 확장
- **회장 §6 명시**: `SmokeResult(merge_commit / status / stdout / stderr / duration_ms / smoke_command / task_id)`
- **자동 contracts freeze (task-2509+2 / PR #60)**: `SmokeResult(command, passed, exit_code, stdout_tail, stderr_tail, failure_reason)` — **freeze 상태**
- **기각된 대안**: contracts.py에 필드 추가
  - 사유: "automation_contracts.py freeze (task-2509+2)" 명시. 본 task의 expected_files에서 contracts.py 제외. forbidden_actions 위반.
- **채택**: frozen `SmokeResult` 그대로 사용 + 추가 메타데이터(`merge_commit`, `task_id`, `duration_ms`, `allow_continuation`, `escalation`, `stale`)는 별도 envelope `PostMergeSmokeRun` dataclass에 담아 반환. status는 별도 `SmokeStatus` enum으로 표현.
- **결과**: contract freeze 준수 + 회장 §6 정보량 보존. wiring 단계(task-2514)에서 envelope를 풀어 호출처에 전달.

### 2. smoke_command 출처 (registry vs task spec)
- **task §2 명시**: "registry: task_id → smoke_command 매핑". `load_task_spec`은 yaml block에서 `smoke_command`를 추출하지 않음 (None 고정).
- **채택**: 단계적 fallback — (1) task md에 `smoke_command:` yaml scalar/list가 있으면 그것 사용 (2) 없으면 모듈 내부 registry (task-2506/2507/2509/2511 fixture만 명시) (3) 미정의 → 정책 #10 적용.

### 3. timeout 처리 + zombie 방지
- **채택**: `subprocess.run(..., timeout=600)` 우선. `TimeoutExpired` catch 시 SmokeResult.failure_reason="TIMEOUT" + status=TIMEOUT + Critical #7 packet. zombie는 subprocess.run 내부에서 자동 kill 처리. 추가로 runner inject 가능 (테스트 격리).

### 4. stdout/stderr cap (64KB head/tail)
- **채택**: 64KB cap + head/tail 보존 — 전반 32KB head + 후반 32KB tail. 둘 사이 truncation marker `\n...[TRUNCATED N bytes]...\n`.
- **이유**: smoke 실패 진단에 처음/끝 모두 필요 (pytest는 끝, 환경변수/import 에러는 처음).

### 5. allow_continuation 신호 (PASS 시)
- **task §8 명시**: `AutomationDecision(allow_continuation=True)` 또는 `merge_queue_continuation_ready=True`. 
- **automation_contracts AutomationDecision 분석**: `decision/reason_codes/critical_escalation_type/auto_handled/requires_chair/audit` — `allow_continuation` 필드 없음. freeze 위반 회피.
- **채택**: envelope 자체 필드로 `allow_continuation: bool`. wiring 단계에서 `AutomationDecision(decision="ALLOW_CONTINUATION", reason_codes=["POST_MERGE_SMOKE_PASS"], ...)` 변환. 본 task는 packet 생성 X.

### 6. stale 판정 (merge_commit ≠ origin/main HEAD)
- **task §1 명시**: "merge_commit이 origin/main HEAD와 일치하지 않으면 stale 판정"
- **채택**: `git fetch origin main` → `git rev-parse origin/main`로 HEAD 비교. 불일치 시 envelope에 `stale=True` 표시. dry_run에선 skip 옵션 가능 (test injection runner).

## 3 Step Why 자문

**1st Why — 왜 이 설계가 필요한가?**
A: 자동 머지 직후 main 기준 smoke가 실패하면 다음 PR을 머지하기 전에 큐를 멈춰야 한다. 사람이 매번 main을 받아 smoke를 돌리는 비용이 누적되어 회장 보고/큐 정체가 발생함.

**2nd Why — 왜 A가 최선의 접근인가?**
B: smoke runner를 merge_queue_executor에 직접 inline하지 않고 별도 모듈로 분리해야 (1) 단위 테스트가 쉽고 (2) task-2514 wiring 시점에서 호출 지점만 노출되며 (3) task-2513 critical_escalation_reporter와 disjoint하게 packet만 만들 수 있다. **대안**: merge_queue_executor 내부 `run_post_merge_smoke` 확장 → 회장 명시 wiring 금지 + automation_contracts freeze 우회 위험. **기각**.

**3rd Why — 왜 B가 다른 대안보다 나은가?**
C: 분리 모듈 + envelope 패턴이 (1) contract freeze를 위반하지 않으면서 (회장 §1~10 정보량 전부 보존) (2) wiring/리포팅 책임을 task-2514/2513에 위임할 수 있고 (3) replay fixture 회귀 테스트로 동일 결과 재현이 가능하다. inline 확장은 회장 명시 금지인 dispatch/merge_queue_executor 수정이 불가피.

A→B→C 일관성: ✅ "main 기준 smoke 자동화 → 분리 모듈 + envelope → wiring 보류 가능".

## 참조 자료

- 정책: `memory/feedback/feedback_critical_escalation_only_260508.md`
- 공통 계약(freeze): `utils/automation_contracts.py` (PR #60 / task-2509+2)
- 큐 컨텍스트: `utils/merge_queue_executor.py` (PR #58, #59)
- 패턴 레퍼런스: `utils/replacement_pr_runner.py` (PR #61), `utils/auto_gemini_triage.py` (PR #62)
- replay fixture: task-2506(PR #56) / task-2507(PR #55) / task-2509(PR #58) / task-2511(PR #62) main commit
- 작업 지시: `memory/tasks/task-2512.md`

## 주의사항

- ★ `merge_queue_executor` import 가능성만 확인 (load_task_spec 재사용은 가능하나 wiring/모디파이 금지)
- ★ `dispatch.py` 수정 절대 금지
- ★ `automation_contracts.py` 수정 절대 금지 (task-2509+2 freeze)
- ★ `critical_escalation_reporter`는 task-2513 영역. 본 task는 EscalationPacket 생성만, 보고/전송 X.
- ★ admin override / force / rebase / cherry-pick / required CI bypass 전부 금지
- ★ Critical 7종 외 회장 보고 0건. POST_MERGE_SMOKE_FAILED만 사용.
- ★ amendment 보호 — 머지 후 main HEAD 변경 감지 시 stale 표시 + retry 권고만.
- ★ Sanitize 게이트 — 외부 AI 호출 전 PII 마스킹 (본 task는 코드/테스트만이라 주민번호/API키 등 민감 PII 없음 예상이지만 sanitize_text 통과 후 전달).
