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

# 맥락 노트: task-2513

**task**: task-2513

---

## 결정 근거

### 결정 1 — 입력 event 스키마: 평탄한 dict + 명시적 source/event_type

회장 명시: "Critical 7종만 회장 보고", "그 외는 자동 처리". 따라서 input 스키마는:

```json
{
  "source": "replacement_pr_runner|auto_gemini_triage|merge_queue_executor|post_merge_smoke_runner|dispatch",
  "event_type": "<자유 문자열 — Critical 7 enum 값과 동일하면 reportable>",
  "task_id": "task-NNNN",
  "pr_number": 12,
  "evidence": { ... },
  "merge_commit": "<sha or null>",
  "occurred_at": "<ISO8601 or null>"
}
```

- **대안 기각**: nested envelope (예: `{"event": {...}}`) 은 audit log JSONL 라인 압축이 불리하고, automation_contracts.py가 이미 평탄 dataclass라 정합성 깨짐.
- **`event_type` → `CriticalEscalationType` 매칭**: 정확 문자열 일치만 critical. 그 외 → auto-handled로 분류. 이로써 "Critical 7종만 보고" 정책을 enum 정확 매칭으로 강제.

### 결정 2 — auto-handled 분류는 enum 외부 (별도 상수 라벨)

`automation_contracts.py`는 freeze 상태이므로 새 enum 추가 금지. auto-handled 라벨은 reporter 내부 상수 set:

```python
AUTO_HANDLED_LABELS = {
    "FALSE_POSITIVE_GEMINI",
    "STYLE_ONLY_GEMINI",
    "MINOR_FIX_ALLOWED_PATH",
    "CLEAN_REPLACEMENT_PR",
    "DEPENDENCY_SATISFIED",
    "QUEUE_AUTO_MERGE",
    "POST_MERGE_SMOKE_PASS",
    "OUTDATED_THREAD",
}
```

- **이유**: contracts freeze 위반 방지 + 회장 §10 자동 처리 7항목 + outdated thread 추가(§9 fixture)
- **대안 기각**: enum을 contracts에 추가 → freeze 위반. dispatch.py에 옮김 → forbidden_actions 위반.

### 결정 3 — dedup hash = SHA256(escalation_type|task_id|sorted(evidence_keys))

evidence 전체 해시는 noisy(타임스탬프 변동). evidence의 **정렬된 key 리스트**만 해시 → 같은 사건 반복 시 동일 해시.

- **대안**: evidence 전체 JSON sort_keys=True → 평가 결과 false-negative dedup 발생 가능 (timestamp 등이 evidence에 들어가면). key 리스트만 해시가 더 robust.
- **window**: 기본 3600초. fixture에서 override 가능.

### 결정 4 — severity 매핑

| escalation_type | severity |
|---|---|
| FORBIDDEN_PATH_INTRUSION | HIGH_CORE |
| REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF | HIGH_CORE |
| GEMINI_REAL_BUG_REQUIRES_SCOPE_EXPANSION | HIGH |
| BLOCK_OVERRIDE_REQUIRED_OR_REASON_INSUFFICIENT | HIGH_CORE |
| DEPENDENCY_CYCLE_OR_SERIAL_ONLY_COLLISION | HIGH_CORE |
| REPLACEMENT_PR_FAILED | HIGH |
| POST_MERGE_SMOKE_FAILED | HIGH |

- **HIGH_CORE 기준**: dispatch / merge_queue_executor / merge_topology_gate 영향 사건
  - FORBIDDEN_PATH_INTRUSION → topology gate 자체 침범
  - REPLACEMENT_PR_AUTO_CREATION_FAILED → merge_queue 진행 차단
  - BLOCK_OVERRIDE_REQUIRED → block 조건 위반
  - DEPENDENCY_CYCLE → dispatch graph 손상
- 그 외 HIGH (조치 필요하나 코어 차단 X)

### 결정 5 — legacy enum 이름 호환 매핑 (Codex G1 피드백 반영)

`utils/merge_queue_executor.py` (PR #58/#59 머지)가 freeze 이전 이름을 사용하고 있어, reporter가 exact match만 하면 wiring(task-2514) 시 모든 critical 사건이 suppression되는 사고 위험. 따라서 reporter 내부에 호환 매핑 테이블을 둔다:

```python
LEGACY_CRITICAL_MAP = {
    "FORBIDDEN_PATH_INVASION": CriticalEscalationType.FORBIDDEN_PATH_INTRUSION,
    "EFFECTIVE_DIFF_CONTAMINATION_REPLACEMENT_FAILED":
        CriticalEscalationType.REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF,
    "GEMINI_REAL_BUG_SCOPE_EXPANSION":
        CriticalEscalationType.GEMINI_REAL_BUG_REQUIRES_SCOPE_EXPANSION,
    "BLOCK_OVERRIDE_REQUIRED_OR_INSUFFICIENT_REASON":
        CriticalEscalationType.BLOCK_OVERRIDE_REQUIRED_OR_REASON_INSUFFICIENT,
    "DEPENDENCY_CYCLE_OR_SERIAL_ONLY_CONFLICT":
        CriticalEscalationType.DEPENDENCY_CYCLE_OR_SERIAL_ONLY_COLLISION,
    "REPLACEMENT_PR_ALSO_FAILED": CriticalEscalationType.REPLACEMENT_PR_FAILED,
    "POST_MERGE_SMOKE_FAILURE": CriticalEscalationType.POST_MERGE_SMOKE_FAILED,
}
```

routing 순서:
1. `event_type` → `CriticalEscalationType(event_type)` 시도 → 성공이면 critical
2. 실패 시 `LEGACY_CRITICAL_MAP[event_type]` 시도 → 성공이면 critical (canonical로 변환)
3. 둘 다 실패 시 auto-handled

- **wiring 위반 아님**: 이는 reporter 입장의 "입력 호환층"이며 merge_queue_executor를 수정/wiring하지 않음 (회장 forbidden_actions 준수).
- **회귀 테스트**: legacy 7개 이름 모두 입력 시 freeze enum으로 정확 변환되는지 케이스 추가.

### 결정 6 — packet 텍스트 포맷 (회장 보고용)

Telegram 4096자 limit. 다음 포맷:

```
🚨 [Critical-{N}] {ESCALATION_TYPE}
task: {task_id} / PR: #{pr_number} / merge_commit: {short_sha or "N/A"}
why: {reason}
auto cannot continue: {why_auto_cannot_continue}
options:
  - [recommended] {recommended_option}
  - {other safe_options}
evidence: {key fields summary, max ~600 chars}
```

- evidence는 raw json이 아니라 핵심 field 요약 (forbidden_paths, expected_files, smoke_command, etc.)
- 4096자 초과 시 evidence 요약을 잘라낸다 (assertion + 회귀 테스트 case 6)

## 참조 자료

- 정책: `memory/feedback/feedback_critical_escalation_only_260508.md` (회장 직접 발행)
- 공통 계약: `utils/automation_contracts.py` (PR #60 freeze)
- merge queue: `utils/merge_queue_executor.py` (read-only — wiring 금지)
- replacement: `utils/replacement_pr_runner.py` (read-only — Critical #2/#6 입력 출처)
- triage: `utils/auto_gemini_triage.py` (read-only — Critical #3 입력 출처)
- audit pattern: `memory/orchestration-audit/merge-queue.jsonl` (JSONL append 패턴)

## 주의사항

- ★ contracts freeze 위반 금지 — `automation_contracts.py` 절대 수정 X
- ★ Critical 7종 외 회장 보고 0건 — 회장 직접 명시
- ★ Telegram 실 발송 hook은 stub만 (예: `_telegram_send` 함수 정의는 하되 default `lambda *a, **k: None`)
- ★ duplicate detection은 same-process 메모리가 아닌 **audit log read 기반** (process restart에도 dedup 동작)
- ★ audit log append는 atomic (write tempfile → rename) 또는 line-buffered append + lock — JSONL 부분 기록 금지

## 3 Step Why 자문

**1st Why — 왜 이 설계가 필요한가?**
A: Critical 7종은 회장이 직접 보고받아야 할 시스템 무결성 침해 사건이고, 그 외 자동 처리 가능 항목에 회장을 노출하면 정책이 무너진다. 매핑/억제/audit 계층화가 필요.

**2nd Why — 왜 (입력 평탄 dict + enum 정확 매칭 + audit-driven dedup) 조합이 최선인가?**
B: (1) 평탄 dict는 JSONL 1라인=1사건 audit 패턴과 정합 (PR #60/#61/#62 패턴 동일). (2) enum 정확 매칭은 "Critical 7종만"을 코드 레벨로 강제. (3) audit-driven dedup은 process restart에도 살아남고, 동일 사건의 noisy timestamp가 영향 미치지 않음.

**3rd Why — 왜 audit-driven dedup이 in-memory dedup보다 나은가?**
C: in-memory dedup은 (a) 워커 재시작 시 memory loss → 중복 알림, (b) 멀티 프로세스 (queue executor 별도 프로세스) → 동기화 부재, (c) 테스트 격리 불가. audit-driven은 파일이 SSOT이며 cron/cli/import 모든 진입점에서 동일 동작 보장.

A → B → C 일관성: "회장 보고 신뢰성" 단일 목표를 향해 계층마다 더 좁은 답.
