---
task_id: task-2374
type: context
scope: task
created: 2026-05-02
updated: 2026-05-02
status: completed
---

# 맥락 노트: task-2374 — 옵션 D 시맨틱 재설계

**task**: task-2374

---

## 결정 근거

### 핵심 결정 1 — 호환 shim (`.done` symlink 6개월) 채택

**결정**: `.work-done` 생성 시 동일 디렉토리에 `.done` symlink 동시 생성. 6개월 후 제거.

- **이유**: Codex G1 critical 1건 — `.done` 소비자 그래프 과소추정. 14+개 파일이 `.done`을 직접 stat/glob한다 (dispatch.py:1709 `--resume`, auto_merge.py 50+ 위치, done-watcher.py:scan_done_files, dashboard/data_loader.py, qc_verify.py, g3_independent_verifier.py, 테스트 다수).
- **대안**:
  - (A) 모든 소비자 동시 마이그레이션 → 단일 atomic PR이 되지만 회귀 위험 폭증, 12-cell 테스트로도 모든 케이스 커버 어려움
  - (B) `.done` 마커 그대로 유지 + 새 마커 추가만 → 의도 모호 (declared vs observed 분리 못함)
  - (C) **shim 채택** → 기존 소비자 0 변경 + 신규 소비자(reconciler/mergedAt 폴러)만 `.work-done` 직접 인식. 6개월 후 점진 마이그레이션
- **기각 사유**: A는 회귀 0 보장 불가능 (마아트 G3 FAIL 위험), B는 task-2364 사례 차단 못함

### 핵심 결정 2 — state.json은 마커와 동시 생성, dispatch.py 신규 소비자만 인식

**결정**: `memory/state/{task_id}.json`을 마커 생성 시점에 atomic 생성. 기존 소비자(dispatch.py:--resume, dashboard 등)는 변경하지 않음 (호환 shim과 동일 패턴).

- **이유**: Codex G1 medium 1건 — state.json SoT 선언했지만 기존 흐름은 마커 직접 읽음. 이중 권위 위험.
- **해결**: state.json은 신규 코드의 SoT. 기존 코드는 마커 그대로 읽음 → 마커가 캐시 역할로 자동 정의됨. 6개월 후 마커 제거 시 state.json만 남음.

### 핵심 결정 3 — meta task는 `.work-done` = terminal

**결정**: `kind: meta` 또는 `merge_required: false` task는 `.work-done` 생성 시 즉시 `.merged` 동시 atomic 생성. mergedAt 폴링 skip.

- **이유**: 시스템·문서 task는 PR 부재 → mergedAt 영원히 null → 옵션 D만으로는 영구 정체 (로키 DA 지적, 미팅 Cycle 1)
- **결정**: state.json에 `phase: "system_done"` 또는 `phase: "merged"` 즉시 기록

### 핵심 결정 4 — 5회 retry 후 `.merge-failed` 즉시 알림

**결정**: auto_merge.py 또는 done-watcher.py가 GitHub mergedAt 폴링 5회 실패 시 `.merge-failed` atomic 생성 + state.json `phase: "merge-failed"` + retry_count 기록.

- **폴링 주기**: 60초 간격, 최대 30분 (=30회). 미팅에서는 "5회 retry"라 했지만 task 파일에는 "30분"이라 명시. 실제 구현은 둘 다 만족하도록 설계.

## 3 Step Why 자문 (Lv.4 필수)

**1st Why**: 왜 이 설계가 필요한가?
→ A: task-2364 사례에서 `.done` 17:08 → 머지 18:11 (63분 갭) → 아누가 회장에게 "머지 누락" 잘못 보고 사고 발생. `.done`이 declared(작업자 의도)와 observed(GitHub 사실)를 한 파일에 욱여넣은 카테고리 오류 (Argo CD declared/observed 위반). 시맨틱을 두 개의 독립 마커로 분리해야 jiang 사례 차단 가능.

**2nd Why**: 왜 옵션 D(하이브리드 마커 분리 + 호환 shim)가 최선의 접근인가?
→ B: 미팅에서 4 외부 페르소나(로키/마아트/아틀라스/다빈치) 모두 옵션 D로 수렴. 옵션 A(3마커만)는 race window 3개로 폭증, 옵션 B(동기 폴링)는 GitHub 장애 시 DoS 벡터, 옵션 C(watcher 단독)는 SPOF + meta task 영구 정체. D는 A의 마커 분리 + C의 mergedAt 검증을 결합하여 race 차단 + meta task 분기. **호환 shim 추가**는 Codex G1 critical 대응 — 14+개 소비자 동시 마이그레이션 회귀 위험을 6개월 분산.

**3rd Why**: 왜 호환 shim이 다른 대안(전수 마이그레이션, 마커 폐기)보다 나은가?
→ C: 전수 마이그레이션은 14+개 소비자 변경 = 회귀 0 보장 불가능 (마아트 G3 FAIL 위험). 마커 폐기(아틀라스 Unconventional 1)는 GitHub API 장애 시 마비 + state.json만으로 외부 검증성 약화 (마아트 G4 FAIL). shim은 기존 소비자 무변경 + 신규 소비자만 새 시맨틱 → 회귀 0 + 점진 마이그레이션 + 운영 가시성 보존. 6개월 후 마커 제거하면 자연스럽게 state.json SoT 단일화 도달.

**A-B-C 일관성 검증**: PASS — 사고 차단 → 옵션 D 결합 → 호환 shim으로 점진 적용. 논리 흐름 일관.

## 참조 자료

- 미팅 파일: `memory/meetings/2026-05-02-done-semantics-redesign.md`
- task-2368 보고서: `memory/reports/task-2368.md`
- task-2371 P0 핫픽스: 커밋 `c0577015`
- task-2364 사례: `.done` 17:08 → 머지 18:11 (63분 갭)
- Codex G1 결과: `memory/events/task-2374.codex-gate` (critical 1, high 2 — 호환 shim 채택으로 대응)
- DRAFT: `memory/tasks/dispatch-done-semantics-impl-DRAFT.md`

## 주의사항

- ★ **변경 금지**: P0/P1/P2 산출물 (task-scope-guard.sh, post_merge_probe.py, auto_revert.py, anu_confirm_bot/**, daily_digest.py, audit log, capabilities/**, CLAUDE.md, plans/bot-capability-system/{plan,context-notes}.md, .github/**)
- ★ **finish-task.sh 자기 호출 회귀 주의**: 본 task가 finish-task.sh 자체 수정 → task-2371 P0 핫픽스 fail-closed 동작 검증됨. 본 task의 finish-task.sh 호출은 *수정 후 새 시맨틱*으로 실행됨
- ★ **PII 마스킹 게이트**: 외부 AI(Codex/Gemini) 호출 전 sanitize_text() 필수
- ★ **회장 승인 게이트**: Lv.4 → Tier 3 회장 명시 승인 필수 (auto-merge 차단)
- ★ **호환 shim 검증**: 모든 변경 후 기존 4 소비자(auto_merge, done-watcher, dispatch.py:--resume, dashboard/data_loader)가 `.done`만 보고도 정상 동작해야 함
