# `.done` 시맨틱 재설계 — state.json 스키마 (옵션 D)

**작성일**: 2026-05-02
**근거**: `memory/meetings/2026-05-02-done-semantics-redesign.md` (옵션 D 채택)
**관련 task**: task-2374

---

## 1. 마커 모델 (3-marker + 호환 shim)

### 1.1 마커 분리

| 마커 | 의미 (Argo CD 패턴) | 생성자 | 생성 조건 |
|---|---|---|---|
| `.work-done` | **declared** — 봇 작업 종료 선언 | `scripts/finish-task.sh` | QC PASS + worktree finish 성공 |
| `.merged` | **observed** — GitHub `mergedAt` 사실 관측 | `scripts/done-watcher.py` 또는 `scripts/auto_merge.py` | mergedAt != null 검증 (atomic) |
| `.merge-failed` | **escalation** — 머지 5회 retry 초과 | `scripts/auto_merge.py` 또는 `scripts/done-watcher.py` | retry_count > 5 또는 30분 timeout |

### 1.2 호환 shim (`.done` symlink, 6개월 유지)

**`.work-done` 생성 시 동일 디렉토리에 `.done` 호환 symlink 동시 생성**:

```
memory/events/
├── task-2374.work-done  # 신규 마커 (실제 파일)
├── task-2374.done       # 호환 symlink → task-2374.work-done
└── task-2374.merged     # 머지 관측 후 atomic 생성 (실제 파일)
```

기존 소비자(`auto_merge.py`, `done-watcher.py:scan_done_files`, `dispatch.py:--resume`, `dashboard/data_loader.py`, `qc_verify.py`, `g3_independent_verifier.py`)는 `.done` symlink로 회귀 0 동작.

**6개월 후 마이그레이션 완료 시 symlink 제거** (별도 task).

---

## 2. state.json 스키마 (SoT)

### 2.1 위치

`memory/state/{task_id}.json` — task별 단일 진실. 마커는 캐시.

### 2.2 필수 필드

```json
{
  "task_id": "task-2374",
  "phase": "work-done | merged | merge-failed | system-done",
  "kind": "code | meta",
  "merge_required": true,
  "timestamp_work_done": "2026-05-02T19:36:46+09:00",
  "timestamp_merged": null,
  "timestamp_merge_failed": null,
  "team": "dev7",
  "project_path": null,
  "pr_url": null,
  "merge_commit_sha": null,
  "retry_count": 0,
  "last_error": null
}
```

### 2.3 phase 상태 머신

```
[dispatched] → [work-done] → [merged]          (정상 code task)
                          ↓
                   [merge-failed]                (5회 retry 초과)

[dispatched] → [work-done] = [system-done]      (meta task, .work-done = terminal)
```

phase 전이는 atomic write (rename pattern) + flock으로 race 차단.

### 2.4 필드 의미

- `phase`: 현재 상태. 마커 파일과 동기화 (마커 = 캐시, state.json = SoT)
- `kind`: `code` (기본값) — PR 머지 필요 / `meta` — 시스템·문서 task, PR 부재
- `merge_required`: `kind == "code"`이면 기본 true. `kind == "meta"`이면 자동 false
- `timestamp_*`: phase 진입 시각 (ISO 8601 KST)
- `retry_count`: 머지 재시도 횟수 (5회 초과 시 `.merge-failed`)
- `last_error`: 마지막 머지 실패 사유 (디버깅용)

---

## 3. task.yaml frontmatter 확장

`memory/tasks/task-XXXX.md` YAML frontmatter (선택 필드):

```yaml
---
task_id: task-XXXX
team: dev7
level: 4
priority: P1
kind: code          # 신규 (기본값: code)
merge_required: true # 신규 (기본값: kind == "code"이면 true)
depends_on: []
created_at: 2026-05-02
deadline: null
---
```

**legacy 호환**: `kind` 미명시 → 자동 `code` + `merge_required: true`.

**meta task 예시**: 미팅, 회고, 문서 task.
```yaml
kind: meta
merge_required: false
```

---

## 4. 분기 흐름

### 4.1 정상 code task (`kind: code`, `merge_required: true`)

1. `dispatch.py`: state.json 사전 생성 (`phase: dispatched`)
2. `finish-task.sh`: QC PASS → `.work-done` 생성 + state.json `phase: work-done` + `.done` symlink
3. `done-watcher.py`: `.work-done` 발견 → `gh pr view --json mergedAt` 60초 폴링 (최대 30분)
4. mergedAt != null → `.merged` atomic 생성 + state.json `phase: merged` + `merge_commit_sha` 갱신
5. `auto_merge.py` 또는 watcher가 5회 실패 시 `.merge-failed` + state.json `phase: merge-failed`

### 4.2 meta task (`kind: meta` 또는 `merge_required: false`)

1. `dispatch.py`: state.json 사전 생성 (`kind: meta`, `merge_required: false`)
2. `finish-task.sh`: QC PASS → `.work-done` 생성 + state.json `phase: work-done` + `.done` symlink
3. `done-watcher.py`: state.json `merge_required: false` 확인 → 즉시 `.merged` atomic 생성 + state.json `phase: system-done`
4. mergedAt 폴링 skip

---

## 5. 회귀 0 보장

### 5.1 변경 금지 산출물

- `scripts/task-scope-guard.sh` (P0)
- `scripts/post_merge_probe.py` (P1)
- `scripts/auto_revert.py` (P1)
- `scripts/anu_confirm_bot/**` (P2)
- `scripts/daily_digest.py` (P2)
- `memory/audit/auto-merge.log` (read-only)
- `memory/capabilities/**`
- `CLAUDE.md` (5대 규칙)

### 5.2 `.done` 호환 shim 검증

- 12-cell 테스트 매트릭스 (3 phase × 4 시나리오)
- 4 소비자 모두 `.done` symlink 인식 검증

---

## 6. 운영 안정화 (후속 task)

- **task-237Y (P2 안정화)**: 1주일 shadow mode + 임시 운영 가드 격하
- **task-237Z (Dashboard 시각화)**: 3-state badge (work-done / merged / merge-failed)
- **6개월 후**: `.done` symlink 제거 + 기존 소비자 일괄 마이그레이션 (단일 atomic PR + 12-cell CI)

---

## 7. Atomicity 보장

### 7.1 마커 atomic 생성

```python
# .work-done 생성
import os, tempfile, json
def atomic_write(path: Path, content: str) -> None:
    """rename pattern으로 atomic 생성"""
    tmp = path.with_suffix(path.suffix + ".tmp")
    tmp.write_text(content)
    os.rename(tmp, path)  # POSIX atomic
```

### 7.2 state.json + 마커 동시 갱신 (best-effort)

state.json 업데이트 → 마커 파일 atomic rename 순서. 둘 사이 race 발생 시 state.json 우선.

### 7.3 race 차단

- `flock(LOCK_EX)` on state.json 디렉토리 (`memory/state/`)
- 동일 task 동시 reconciler 진입 시 두 번째는 no-op
