---
task_id: task-2518
type: context
scope: task
created: 2026-05-09
updated: 2026-05-09
status: in-progress
---

# 맥락 노트: task-2518

**task**: task-2518

---

## 결정 근거

### 핵심 결정 1: source-of-truth는 GitHub/CI/smoke evidence (file marker는 derived)

- **결정 이유**: 회장 §2 task-2517 dev2 사례 — Telegram reply cut-off 후 task running처럼 남았지만 PR은 이미 merged였다. file marker (.done / .merge-done) 기준으로 판단하면 lifecycle이 "stuck"이라 결론이 나오지만 GitHub evidence (PR merged + CI PASS + smoke PASS) 기준으로는 "FINALIZED 가능"이다. evidence 충돌 시 GitHub 우선 규칙이 회장 명시 사항.
- **대안과 기각 이유**:
  - (대안 1) file marker 우선 → 회장 §2 사례 해결 불가 (위장 강제 생성으로 수렴), 기각.
  - (대안 2) timer 우선 → 봇 세션 종료 시 timer가 stale될 수 있음, evidence 신뢰도 낮음, 기각.

### 핵심 결정 2: dry-run / apply 분리 (--apply 없이는 read-only)

- **결정 이유**: lifecycle reconcile은 file marker / timer를 backfill하므로 side effect가 발생한다. 잘못된 reconcile은 회장 §1 (PR merged인데 .done 누락)을 더 악화시킬 수 있다. dry-run으로 plan을 출력하고, --apply가 명시되어야만 실제 backfill을 수행해야 안전하다.
- **대안과 기각 이유**:
  - (대안 1) 항상 apply → 자동 reconcile이 의도치 않은 상태 변경을 유발. cron으로 scan-stuck 호출 시 잠재적 위험. 기각.
  - (대안 2) 명령어 분리 (--reconcile vs --apply-reconcile) → CLI 복잡도만 증가. 기각.

### 핵심 결정 3: backfill 시 evidence 메타데이터 명시 (위장 차단)

- **결정 이유**: 회장 §5 "manual .done 위장 금지". backfill로 생성되는 .done.acked / .merge-done 파일에는 반드시 (a) evidence source (b) timestamp (c) mergeCommit (d) reconcile run id를 포함해야 한다. evidence 없는 .done 강제 생성은 RuntimeError.
- **대안과 기각 이유**:
  - (대안 1) 단순 빈 파일 .done → 회장 §5 위장 금지 위반, 기각.

### 핵심 결정 4: stuck 8 케이스는 회장 §1~7 + Telegram cut-off로 정확 매칭

- **결정 이유**: 회장 명시 7 사례에 cron history 기반 Telegram cut-off (task-2517 dev2 cron `2BAB8982`)을 추가하여 8 케이스를 구성한다. 각 케이스는 회귀 fixture로 replay 가능해야 한다.
- **대안과 기각 이유**:
  - (대안 1) 케이스 통합 (예: 4 → 3 케이스) → 회장 §1~7 정확 매칭 불가, 기각.

### 핵심 결정 5: evidence 우선순위 — PR state > mergeCommit > origin/main 포함 > CI > smoke > timer > file marker

- **결정 이유**: GitHub upstream이 가장 신뢰도 높음. file marker는 봇이 만들 수 있어 마지막 derived. timer는 봇 세션 종료 시 stale 가능.
- **2nd Why 검증 (Codex 사전 리뷰 질문)**: "이 우선순위의 대안은 무엇이며, 왜 이 접근이 최선인가?" → 대안: smoke를 mergeCommit보다 위로 → mergeCommit이 없으면 smoke가 의미없음, 기각.

## 참조 자료

- 회장 명시 7 사례: `memory/tasks/task-2518.md` 라인 28-36
- 8 stuck 케이스: `memory/tasks/task-2518.md` 라인 99-108
- automation_contracts 12 타입: `utils/automation_contracts.py` (READ ONLY)
- canonical_workspace_resolver 8 함수: `utils/canonical_workspace_resolver.py` (READ ONLY)
- task-2517 dev2 evidence: `memory/events/task-2517.done` + `memory/events/task-2517.merge-done`
- task-timers 스키마: `memory/task-timers.json` (tasks dict, status field: running/completed)
- post_merge_smoke_runner envelope: `utils/post_merge_smoke_runner.py` PostMergeSmokeRun
- merge_queue_executor audit: `memory/orchestration-audit/merge-queue.jsonl` (스키마 참조)

## 주의사항

- **PII 마스킹 필수**: Codex/Gemini 호출 전 sanitize_text 적용 (utils/sanitize_gate)
- **expected_files 정확히 2 파일** — 외 파일 수정 시 Merge Topology Gate 위반
- **automation_contracts schema 변경 0건** (import만)
- **canonical_workspace_resolver 변경 0건** (import만)
- **dispatch.py / finish-task.sh / 5 모듈 본체 변경 0건** (회장 금지)
- **manual .done 위장 차단**: evidence 없으면 RuntimeError, 위장 강제 생성 차단 회귀 필수
- **--apply 없이는 read-only**: 모든 backfill은 dry-run 기본
- **gh CLI / git 호출은 runner injection으로 테스트 가능**해야 함 (canonical_workspace_resolver 패턴 따름)
- **Telegram cut-off 감지는 cron history JSONL 마지막 라인 truncation 검사**: `/home/jay/.cokacdir/schedule_history/<id>.log` (단, task-2518 본체에서는 path를 인자로 받아 inject 가능하게)

## 진행 기록

- 2026-05-09 10:33: 다그다 — task-2518 분석 시작
- 2026-05-09 10:35: 다그다 — worktree 생성 + origin/main 동기화 완료
- 2026-05-09 10:38: 다그다 — 3문서 업데이트 + 3 Step Why 검증 완료
