# MoAI-ADK Phase 1 코드화 지시서

**작성자**: 아폴론 (콘텐츠 프로듀서)
**작성일**: 2026-03-31
**수신**: 헤르메스 (Lead Dev), 오딘 (Docs/Infra)
**분류**: 구현 지시서 (IMPLEMENTATION ORDER)

---

## 파일 경로 맵 (WORKSPACE_ROOT = /home/jay/workspace)

| 지시서 표기 | 절대 경로 | 비고 |
|---|---|---|
| `team_prompts.py` | `/home/jay/workspace/prompts/team_prompts.py` | 기존 파일 수정 |
| `dispatch.py` | `/home/jay/workspace/dispatch.py` | 기존 파일 수정 |
| `worktree_manager.py` | `/home/jay/workspace/scripts/worktree_manager.py` | 기존 파일 수정 |
| `qc_verify.py` | `/home/jay/workspace/teams/shared/qc/qc_verify.py` | 공용 QC 스크립트 수정 (dev1 복사본: teams/dev1/qc/) |
| `.claude/settings.json` | `/home/jay/workspace/.claude/settings.json` | [NEW] 디렉토리 없으면 생성 |
| `.claude/feature_flags.json` | `/home/jay/workspace/.claude/feature_flags.json` | [NEW] Phase 0b에서 생성 |
| `utils/feature_flags.py` | `/home/jay/workspace/utils/feature_flags.py` | [NEW] Phase 0b에서 생성 |
| `hooks/post_tool_use.sh` | `/home/jay/workspace/hooks/post_tool_use.sh` | [NEW] P1-7에서 생성 |
| `hooks/circuit_breaker.py` | `/home/jay/workspace/hooks/circuit_breaker.py` | [NEW] P1-7에서 생성 |
| `QC-RULES.md` | `/home/jay/workspace/teams/shared/QC-RULES.md` | 기존 파일 수정 |
| `DIRECT-WORKFLOW.md` | `/home/jay/workspace/prompts/DIRECT-WORKFLOW.md` | 기존 파일 수정 |
| `.metrics/baseline/` | `/home/jay/workspace/.metrics/baseline/` | [NEW] Phase 0a에서 생성 |

---

## 개요

### 목적
MoAI-ADK(Multi-Agent AI Development Kit) 시스템의 Phase 1 기능 3건을 코드로 구현한다.
기준선 측정(Phase 0a) → 인프라 구축(Phase 0b) → 기능 구현(P1-1, P1-2, P1-7) 순서로 진행한다.

### 범위
| Task | 코드명 | 기간 | DRI |
|------|--------|------|-----|
| Phase 0a | 기준선 측정 | 4/1~4/3 | 마아트 |
| Phase 0b | 인프라 (feature_flags) | 4/4~4/6 | 헤르메스 |
| P1-1 | Progressive Disclosure | 4/7~4/14 | 헤르메스 |
| P1-2 | 읽기/쓰기 격리 | 4/7~4/14 | 헤르메스 + 오딘 |
| P1-7 | hooks 자동 강제 | 4/15~4/24 | 헤르메스 |

### 기간
2026-04-01 ~ 2026-04-24 (총 24일, 스프린트 3회)

---

## 선행 조건

Phase 0a, 0b가 완료되어야 P1-1/P1-2/P1-7 구현을 시작할 수 있다.

- [ ] Phase 0a: `.metrics/baseline/baseline_2026-04-03.json` 파일 존재
- [ ] Phase 0a: `git tag baseline-v1` 태그 존재
- [ ] Phase 0b: `.claude/feature_flags.json` 파일 존재 (6개 플래그 모두 `false`)
- [ ] Phase 0b: `utils/feature_flags.py` 로더 구현 완료 + 단위 테스트 통과

---

## Task 1: Phase 0a — 기준선 측정

**DRI**: 마아트
**기간**: 4/1(수) ~ 4/3(금)
**상태**: CODE FREEZE (기존 코드 변경 없음, 측정만 수행)

### 1-1. 디렉토리 생성

```bash
mkdir -p .metrics/baseline
```

### 1-2. 수집 항목

기존 운영 데이터(로그, 실행 기록)에서 아래 지표를 추출한다.
측정 기간: 직전 30건의 실제 실행 데이터 기준.

| 지표 키 | 설명 | 단위 |
|---------|------|------|
| `build_prompt_tokens_p50` | build_prompt 토큰 수 중앙값 | tokens |
| `build_prompt_tokens_p95` | build_prompt 토큰 수 95 퍼센타일 | tokens |
| `qc_pass_rate` | QC 패스율 | 0.0~1.0 |
| `qc_fnr` | QC False Negative Rate (30건 샘플) | 0.0~1.0 |
| `execution_time_p50` | 에이전트 실행 시간 중앙값 | seconds |
| `write_edit_count_per_task` | 태스크당 Write/Edit 호출 횟수 평균 | count |
| `token_cost_per_task_usd` | 태스크당 토큰 비용 평균 | USD |
| `worktree_success_rate` | worktree 생성 성공률 | 0.0~1.0 |
| `sample_size` | 측정에 사용된 실행 건수 | count |
| `measured_at` | 측정 완료 시각 | ISO 8601 |

### 1-3. 저장 형식

**경로**: `.metrics/baseline/baseline_2026-04-03.json`

```json
{
  "schema_version": "1.0",
  "measured_at": "2026-04-03T18:00:00+09:00",
  "sample_size": 30,
  "metrics": {
    "build_prompt_tokens_p50": 0,
    "build_prompt_tokens_p95": 0,
    "qc_pass_rate": 0.0,
    "qc_fnr": 0.0,
    "execution_time_p50": 0.0,
    "write_edit_count_per_task": 0.0,
    "token_cost_per_task_usd": 0.0,
    "worktree_success_rate": 0.0
  },
  "notes": ""
}
```

### 1-4. git 태그

```bash
git add .metrics/baseline/baseline_2026-04-03.json
git commit -m "chore: add Phase 0a baseline metrics (2026-04-03)"
git tag baseline-v1
```

### DoD (완료 기준)
- [ ] `.metrics/baseline/baseline_2026-04-03.json` 파일 커밋됨
- [ ] `git tag baseline-v1` 존재
- [ ] 8개 지표 모두 0이 아닌 실측값으로 채워짐
- [ ] `sample_size >= 20`

---

## Task 2: Phase 0b — 인프라 구축

**DRI**: 헤르메스
**기간**: 4/4(토) ~ 4/6(월)
**선행**: Task 1 DoD 완료

### 2-1. feature_flags.json 구현

**경로**: `.claude/feature_flags.json`

**스키마 (6개 플래그)**:
```json
{
  "schema_version": "1.0",
  "updated_at": "2026-04-04T00:00:00+09:00",
  "flags": {
    "progressive_disclosure_enabled": false,
    "rw_isolation_enabled": false,
    "hooks_enforcement_enabled": false,
    "trust5_tagging_enabled": false,
    "model_map_enabled": false,
    "haiku_ab_enabled": false
  }
}
```

**규칙**:
- 모든 플래그 기본값: `false` (안전한 비활성화)
- 플래그 추가 시 반드시 `false`로 시작
- 수동 편집 허용 (`updated_at` 갱신 필수)

### 2-2. Python 로더 구현

**경로**: `utils/feature_flags.py`

**구현 요구사항**:

```python
# 필수 기능
class FeatureFlagLoader:
    def __init__(self, path: str = ".claude/feature_flags.json"):
        ...

    def is_enabled(self, flag_name: str) -> bool:
        """플래그 활성화 여부 반환. 존재하지 않는 플래그는 False 반환."""
        ...

    def reload(self) -> None:
        """파일 mtime 확인 후 변경된 경우에만 재로드 (mtime 캐시)."""
        ...

    def set_flag(self, flag_name: str, value: bool) -> None:
        """atomic write로 플래그 값 변경. updated_at 자동 갱신."""
        ...
```

**구현 세부 사항**:

1. **mtime 캐시**: `os.path.getmtime()` 비교로 파일 변경 시에만 재파싱
2. **atomic write**: `tempfile` → `os.replace()` 패턴 사용 (write-then-rename)
3. **JSONDecodeError 복구**: 파일 파싱 실패 시 이전 캐시 유지, WARNING 로그 출력
4. **존재하지 않는 플래그**: `KeyError` 대신 `False` 반환 + WARNING 로그

**atomic write 패턴**:
```python
import tempfile, os, json

def _atomic_write(self, data: dict) -> None:
    dir_path = os.path.dirname(self._path)
    with tempfile.NamedTemporaryFile(
        mode='w', dir=dir_path, delete=False, suffix='.tmp'
    ) as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
        tmp_path = f.name
    os.replace(tmp_path, self._path)
```

### 2-3. 구현 스펙 문서 6건 작성

**DRI**: 헤르메스 (초안) → 오딘 (검토)
**경로**: `docs/specs/`

| 파일명 | 내용 |
|--------|------|
| `spec-p1-1-progressive-disclosure.md` | P1-1 상세 스펙 |
| `spec-p1-2-rw-isolation.md` | P1-2 상세 스펙 |
| `spec-p1-7-hooks-enforcement.md` | P1-7 상세 스펙 |
| `spec-feature-flags.md` | feature_flags 시스템 스펙 |
| `spec-metrics-baseline.md` | 기준선 측정 스펙 |
| `spec-rollback-procedure.md` | 롤백 절차 스펙 |

### DoD
- [ ] `.claude/feature_flags.json` 커밋됨
- [ ] `utils/feature_flags.py` 구현 완료
- [ ] `utils/feature_flags.py` 단위 테스트 통과 (mtime 캐시, atomic write, JSONDecodeError 복구)
- [ ] 스펙 문서 6건 `docs/specs/` 에 존재
- [ ] `python -c "from utils.feature_flags import FeatureFlagLoader; f = FeatureFlagLoader(); print(f.is_enabled('progressive_disclosure_enabled'))"` → `False` 출력

---

## Task 3: P1-1 — Progressive Disclosure

**DRI**: 헤르메스
**기간**: 4/7(화) ~ 4/14(월)
**선행**: Task 2 DoD 완료
**feature_flag**: `progressive_disclosure_enabled`
**데드라인**: 2026-04-14 23:59

### 3-1. 수정 파일

**`team_prompts.py`** — `build_prompt` 함수에 `disclosure_phase` 파라미터 추가

### 3-2. 구현 스펙

**3단계 disclosure_phase 정의**:

| phase | 토큰 한도 | 전체 대비 비율 |
|-------|----------|--------------|
| `"summary"` | ≤ 600 tokens | 15~25% |
| `"standard"` | ≤ 1800 tokens | 40~60% |
| `"full"` | 제한 없음 | 100% |

**CRITICAL 셋 (항상 포함, 하드코딩)**:
- 금지행위 목록
- QC 의무 사항
- 보고 형식

CRITICAL 셋은 phase에 관계없이 반드시 포함된다. 예상 토큰: ~80 tokens.
`summary` phase에서도 CRITICAL 셋은 600 토큰 한도 내에 포함되어야 한다.

**함수 시그니처 변경**:
```python
# 변경 전
def build_prompt(agent_type: str, task: dict, ...) -> str:

# 변경 후
def build_prompt(
    agent_type: str,
    task: dict,
    ...,
    disclosure_phase: str = "full"  # "summary" | "standard" | "full"
) -> str:
```

**구현 로직**:
```python
CRITICAL_SET = """
[CRITICAL - 항상 준수]
- 금지행위: (기존 금지행위 목록)
- QC 의무: (기존 QC 필수 항목)
- 보고 형식: (기존 보고 형식 규칙)
"""  # ~80 tokens, 변경 금지

TOKEN_LIMITS = {
    "summary": 600,
    "standard": 1800,
    "full": None,  # 무제한
}

def build_prompt(agent_type, task, ..., disclosure_phase="full"):
    from utils.feature_flags import FeatureFlagLoader
    flags = FeatureFlagLoader()

    if not flags.is_enabled("progressive_disclosure_enabled"):
        # 플래그 비활성화 시 기존 동작 유지 (full과 동일)
        return _build_prompt_legacy(agent_type, task, ...)

    limit = TOKEN_LIMITS[disclosure_phase]
    prompt = CRITICAL_SET + _build_body(agent_type, task, ..., token_limit=limit)
    return prompt
```

**토큰 카운트**: `tiktoken` 또는 `len(text.split()) * 1.3` 추정치 사용 허용.
단, 실제 배포 전 `tiktoken` 기반으로 교체 권장.

### 3-3. 테스트

**`tests/test_progressive_disclosure.py`**:

```python
# TC-PD-01: summary phase 토큰 비율 검증
def test_summary_phase_token_ratio():
    full_prompt = build_prompt(agent_type="write", task=SAMPLE_TASK, disclosure_phase="full")
    summary_prompt = build_prompt(agent_type="write", task=SAMPLE_TASK, disclosure_phase="summary")
    ratio = count_tokens(summary_prompt) / count_tokens(full_prompt)
    assert 0.15 <= ratio <= 0.25, f"summary ratio {ratio:.2f} out of range [0.15, 0.25]"

# TC-PD-02: summary phase 절대 토큰 한도
def test_summary_phase_token_limit():
    prompt = build_prompt(agent_type="write", task=SAMPLE_TASK, disclosure_phase="summary")
    assert count_tokens(prompt) <= 600

# TC-PD-03: CRITICAL 셋 포함 확인
def test_critical_set_always_included():
    for phase in ["summary", "standard", "full"]:
        prompt = build_prompt(agent_type="write", task=SAMPLE_TASK, disclosure_phase=phase)
        assert "금지행위" in prompt
        assert "QC 의무" in prompt
        assert "보고 형식" in prompt

# TC-PD-04: 플래그 비활성화 시 기존 동작 유지 (회귀 테스트)
def test_flag_disabled_fallback():
    # feature_flag = false 상태에서 build_prompt 결과가 기존과 동일해야 함
    ...
```

### DoD
- [ ] `team_prompts.py` `build_prompt`에 `disclosure_phase` 파라미터 추가됨
- [ ] 3단계 토큰 한도 준수 (TC-PD-01, TC-PD-02 통과)
- [ ] CRITICAL 셋 전 phase에서 포함 (TC-PD-03 통과)
- [ ] `progressive_disclosure_enabled=false` 시 기존 동작 유지 (TC-PD-04 통과)
- [ ] 회귀 테스트 전체 통과

---

## Task 4: P1-2 — 읽기/쓰기 격리

**DRI**: 헤르메스 (코드), 오딘 (문서)
**기간**: 4/7(화) ~ 4/14(월)
**선행**: Task 2 DoD 완료
**feature_flag**: `rw_isolation_enabled`
**데드라인**: 2026-04-14 23:59

### 4-1. 수정 파일

1. **`dispatch.py`** — `--agent-type` 인자 추가
2. **`worktree_manager.py`** — `--read-only` 옵션 추가
3. **`DIRECT-WORKFLOW.md`** — 섹션 5 추가 (DRI: 오딘)

### 4-2. dispatch.py 수정

**`--agent-type` 인자 추가**:

```python
# argparse에 추가
parser.add_argument(
    "--agent-type",
    choices=["read", "write"],
    default="write",  # 기본값: write (안전하게)
    help="에이전트 유형: read(읽기 전용, worktree 미생성) | write(쓰기, worktree 생성). 기본값: write"
)
```

**플래그 연동 로직**:
```python
from utils.feature_flags import FeatureFlagLoader

flags = FeatureFlagLoader()
if flags.is_enabled("rw_isolation_enabled"):
    agent_type = args.agent_type
else:
    agent_type = "write"  # 플래그 비활성화 시 항상 write

# worktree 생성 여부 결정
if agent_type == "read":
    worktree_path = None  # worktree 미생성
else:
    worktree_path = worktree_manager.create_worktree(task_id)
```

### 4-3. worktree_manager.py 수정

**`--read-only` 옵션 추가**:

```python
# create_worktree 함수에 read_only 파라미터 추가
def create_worktree(task_id: str, read_only: bool = False) -> Optional[str]:
    """
    read_only=True: worktree를 생성하지 않고 None 반환
    read_only=False: 기존대로 worktree 생성 후 경로 반환
    """
    if read_only:
        return None
    # 기존 로직 유지
    ...
```

**CLI 사용 예시**:
```bash
# 읽기 에이전트 (worktree 미생성)
python dispatch.py --agent-type read --task-id 1234

# 쓰기 에이전트 (worktree 생성, 기본)
python dispatch.py --agent-type write --task-id 1234
python dispatch.py --task-id 1234  # --agent-type 생략 시 write
```

### 4-4. DIRECT-WORKFLOW.md 섹션 5 추가

**DRI**: 오딘
**위치**: `DIRECT-WORKFLOW.md` 말미에 섹션 5 추가

```markdown
## 섹션 5: 읽기/쓰기 에이전트 격리 (P1-2)

### 에이전트 유형 선택 기준

| 작업 유형 | --agent-type | worktree | 설명 |
|----------|-------------|---------|------|
| 코드 읽기, 분석, 리뷰 | `read` | 미생성 | 파일 수정 없음 |
| 코드 작성, 수정, 리팩토링 | `write` | 생성 | 파일 수정 있음 |
| 불명확한 경우 | `write` (기본값) | 생성 | 안전한 쪽 선택 |

### 주의사항
- `read` 에이전트는 worktree를 생성하지 않으므로 파일 write 시도 시 에러 발생
- `rw_isolation_enabled` 플래그가 `false`이면 `--agent-type` 인자는 무시되고 항상 `write` 동작
- 기능 플래그 상태는 `.claude/feature_flags.json` 에서 확인
```

### 4-5. 테스트

**`tests/test_rw_isolation.py`**:

```python
# TC-RW-01: read 에이전트 worktree 미생성 확인
def test_read_agent_no_worktree():
    result = run_dispatch(agent_type="read", task_id="test-001")
    assert result.worktree_path is None

# TC-RW-02: write 에이전트 worktree 생성 확인
def test_write_agent_creates_worktree():
    result = run_dispatch(agent_type="write", task_id="test-002")
    assert result.worktree_path is not None
    assert os.path.exists(result.worktree_path)

# TC-RW-03: 기본값 write 확인
def test_default_agent_type_is_write():
    result = run_dispatch(task_id="test-003")  # --agent-type 생략
    assert result.agent_type == "write"

# TC-RW-04: 플래그 비활성화 시 항상 write
def test_flag_disabled_always_write():
    # rw_isolation_enabled=false 상태에서 --agent-type read 전달해도 write로 동작
    result = run_dispatch(agent_type="read", task_id="test-004", flag_override=False)
    assert result.worktree_path is not None  # write처럼 worktree 생성
```

### DoD
- [ ] `dispatch.py` `--agent-type read|write` 인자 추가됨 (기본값: `write`)
- [ ] `worktree_manager.py` `read_only=True` 시 worktree 미생성 (TC-RW-01 통과)
- [ ] write 에이전트 worktree 정상 생성 (TC-RW-02 통과)
- [ ] `rw_isolation_enabled=false` 시 항상 write 동작 (TC-RW-04 통과)
- [ ] `DIRECT-WORKFLOW.md` 섹션 5 추가됨 (오딘 확인)
- [ ] 회귀 테스트 전체 통과

---

## Task 5: P1-7 — hooks 자동 강제

**DRI**: 헤르메스
**기간**: 4/15(화) ~ 4/24(금)
**선행**: Task 3 + Task 4 DoD 완료
**feature_flag**: `hooks_enforcement_enabled`
**데드라인**: 2026-04-24 23:59

### 5-1. 수정/생성 파일

1. **`.claude/settings.json`** — `PostToolUse` 훅 설정 추가
2. **`hooks/post_tool_use.sh`** — 훅 실행 스크립트
3. **`hooks/circuit_breaker.py`** — circuit breaker 상태 관리
4. **`QC-RULES.md`** — "hooks≠QC면제" 규칙 추가

### 5-2. .claude/settings.json 훅 설정

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": {
          "tool_name": ["Write", "Edit"]
        },
        "hooks": [
          {
            "type": "command",
            "command": "bash hooks/post_tool_use.sh"
          }
        ]
      }
    ]
  }
}
```

`hooks_enforcement_enabled` 플래그가 `false`이면 `hooks/post_tool_use.sh` 내부에서 즉시 exit 0 처리.

### 5-3. hooks/post_tool_use.sh 구현

```bash
#!/bin/bash
set -euo pipefail

# 환경 변수로 전달되는 훅 컨텍스트
# TOOL_NAME: Write | Edit
# FILE_PATH: 수정된 파일 경로
# (Claude Code PostToolUse 표준 환경 변수 사용)

# 플래그 확인
ENABLED=$(python3 -c "
from utils.feature_flags import FeatureFlagLoader
f = FeatureFlagLoader()
print('true' if f.is_enabled('hooks_enforcement_enabled') else 'false')
")

if [ "$ENABLED" = "false" ]; then
  exit 0
fi

FILE="${FILE_PATH:-}"
if [ -z "$FILE" ] || [[ "$FILE" != *.py ]]; then
  exit 0  # Python 파일이 아니면 스킵
fi

# circuit breaker 확인
python3 hooks/circuit_breaker.py check "$FILE" || exit 0

# pyright 실행 (type check)
PYRIGHT_EXIT=0
PYRIGHT_OUTPUT=$(pyright "$FILE" 2>&1) || PYRIGHT_EXIT=$?

# ruff 실행 (style check)
RUFF_EXIT=0
RUFF_OUTPUT=$(ruff check "$FILE" 2>&1) || RUFF_EXIT=$?

# 심각도 분류 및 처리
if [ $PYRIGHT_EXIT -ne 0 ]; then
  # critical: type error → 중단 (exit 1)
  echo "[HOOK][CRITICAL] pyright type error in $FILE"
  echo "$PYRIGHT_OUTPUT"
  python3 hooks/circuit_breaker.py record critical "$FILE" "pyright_type_error"
  exit 1
fi

if [ $RUFF_EXIT -ne 0 ]; then
  # standard: style warning → 경고만 출력 (exit 0)
  echo "[HOOK][WARNING] ruff style warning in $FILE"
  echo "$RUFF_OUTPUT"
  python3 hooks/circuit_breaker.py record standard "$FILE" "ruff_style_warning"
  exit 0
fi

exit 0
```

### 5-4. hooks/circuit_breaker.py 구현

**상태 파일**: `.metrics/circuit_breaker_state.json`

**발동 조건**:

| 조건 | 임계값 | 동작 |
|------|--------|------|
| warning 누적 | 15회 | halt (exit 2) |
| critical 누적 | 30회 | halt (exit 2) |
| 동일 (tool, error_code, file_path) 3-튜플 연속 3회 | 3회 연속 | halt (exit 2) |

**상태 파일 스키마**:
```json
{
  "schema_version": "1.0",
  "updated_at": "ISO8601",
  "warning_count": 0,
  "critical_count": 0,
  "recent_events": [
    {
      "tool": "Write",
      "error_code": "ruff_style_warning",
      "file_path": "utils/foo.py",
      "severity": "standard",
      "timestamp": "ISO8601"
    }
  ],
  "halted": false,
  "halt_reason": null
}
```

**CLI 인터페이스**:
```bash
# 현재 상태에서 실행 허용 여부 확인 (halt이면 exit 2)
python3 hooks/circuit_breaker.py check <file_path>

# 이벤트 기록
python3 hooks/circuit_breaker.py record <severity: critical|standard> <file_path> <error_code>

# 수동 리셋 (운영자 승인 후)
python3 hooks/circuit_breaker.py reset
```

**3-튜플 연속 감지 로직**:
```python
def _check_consecutive_halt(recent_events: list, new_event: dict) -> bool:
    """동일 (tool, error_code, file_path) 3-튜플이 연속 3회이면 True"""
    key = (new_event["tool"], new_event["error_code"], new_event["file_path"])
    consecutive = 0
    for event in reversed(recent_events[-3:]):
        if (event["tool"], event["error_code"], event["file_path"]) == key:
            consecutive += 1
        else:
            break
    return consecutive >= 2  # 기존 2회 + 신규 1회 = 3회
```

### 5-5. QC-RULES.md 규칙 추가

**DRI**: 헤르메스 (오딘 검토)
**추가 위치**: `QC-RULES.md` 상단 CRITICAL 섹션

```markdown
## [CRITICAL] hooks와 QC는 별개 의무

hooks(pyright/ruff 자동 실행)가 통과했더라도 QC 의무는 면제되지 않는다.

- hooks: 정적 분석 자동화 도구 (type check, style check)
- QC: 기능 정확성, 비즈니스 로직, 요구사항 충족 여부 검증

hooks 통과 ≠ QC 면제. 두 프로세스는 독립적으로 수행되어야 한다.
```

### 5-6. 테스트

**`tests/test_hooks_enforcement.py`**:

```python
# TC-HK-01: Write 훅 동작 확인 (Python 파일)
def test_write_hook_triggered_for_py():
    result = simulate_write_tool(file_path="test_file.py", content="x: str = 1")
    assert result.hook_executed is True

# TC-HK-02: type error 시 중단 (exit 1)
def test_type_error_causes_halt():
    result = simulate_write_tool(file_path="bad_types.py", content="x: int = 'string'")
    assert result.hook_exit_code == 1

# TC-HK-03: style warning 시 경고만 (exit 0)
def test_style_warning_no_halt():
    result = simulate_write_tool(file_path="bad_style.py", content="x=1")  # ruff E225
    assert result.hook_exit_code == 0
    assert "[HOOK][WARNING]" in result.output

# TC-HK-04: circuit breaker warning 15회 누적 후 halt
def test_circuit_breaker_warning_threshold():
    cb = CircuitBreaker(state_path=":memory:")
    for _ in range(14):
        cb.record("standard", "foo.py", "ruff_style_warning")
    assert cb.check("foo.py") == "ok"
    cb.record("standard", "foo.py", "ruff_style_warning")  # 15번째
    assert cb.check("foo.py") == "halt"

# TC-HK-05: 3-튜플 연속 3회 halt
def test_circuit_breaker_consecutive_tuple():
    cb = CircuitBreaker(state_path=":memory:")
    for _ in range(3):
        cb.record("standard", "foo.py", "ruff_style_warning", tool="Write")
    assert cb.check("foo.py") == "halt"

# TC-HK-06: 플래그 비활성화 시 훅 스킵
def test_flag_disabled_hook_skipped():
    result = simulate_write_tool(
        file_path="bad_types.py",
        content="x: int = 'string'",
        flag_override=False
    )
    assert result.hook_exit_code == 0

# TC-HK-07: 회귀 테스트 (기존 Write/Edit 동작 변화 없음)
def test_regression_existing_write_behavior():
    ...
```

### DoD
- [ ] `.claude/settings.json` `PostToolUse` 훅 설정 추가됨
- [ ] `hooks/post_tool_use.sh` 구현 완료 (실행 권한 `chmod +x` 포함)
- [ ] `hooks/circuit_breaker.py` 구현 완료
- [ ] TC-HK-01 ~ TC-HK-07 모두 통과
- [ ] `QC-RULES.md` "hooks≠QC면제" 규칙 추가됨
- [ ] `hooks_enforcement_enabled=false` 시 훅 스킵 확인 (TC-HK-06)
- [ ] 회귀 테스트 전체 통과

---

## 킬 스위치

각 기능의 긴급 비활성화 방법. 이상 감지 시 즉시 실행.

### 즉시 비활성화 (30초 이내)

```bash
# P1-1 Progressive Disclosure 비활성화
python3 -c "
from utils.feature_flags import FeatureFlagLoader
f = FeatureFlagLoader()
f.set_flag('progressive_disclosure_enabled', False)
"

# P1-2 읽기/쓰기 격리 비활성화
python3 -c "
from utils.feature_flags import FeatureFlagLoader
f = FeatureFlagLoader()
f.set_flag('rw_isolation_enabled', False)
"

# P1-7 hooks 자동 강제 비활성화
python3 -c "
from utils.feature_flags import FeatureFlagLoader
f = FeatureFlagLoader()
f.set_flag('hooks_enforcement_enabled', False)
"

# 전체 비활성화 (모든 플래그 false)
python3 -c "
import json
with open('.claude/feature_flags.json') as f:
    data = json.load(f)
for k in data['flags']:
    data['flags'][k] = False
with open('.claude/feature_flags.json', 'w') as f:
    json.dump(data, f, indent=2)
print('All flags disabled.')
"
```

### circuit breaker 수동 리셋

```bash
# hooks circuit breaker 리셋 (운영자 승인 후)
python3 hooks/circuit_breaker.py reset
```

### 확인

```bash
# 현재 플래그 상태 확인
python3 -c "
import json
with open('.claude/feature_flags.json') as f:
    print(json.dumps(json.load(f), indent=2, ensure_ascii=False))
"
```

---

## 통합 테스트

Phase 0b 완료 후 전체 통합 테스트를 수행한다.
**파일**: `tests/integration/test_phase1_integration.py`

| 시나리오 | 검증 내용 | 기대 결과 |
|---------|----------|----------|
| INT-01 | 모든 플래그 false 상태에서 기존 태스크 실행 | 기존 동작과 100% 동일 |
| INT-02 | `progressive_disclosure_enabled=true` 후 summary phase 실행 | 토큰 15~25% 범위 |
| INT-03 | `rw_isolation_enabled=true` 후 read 에이전트 실행 | worktree 미생성 |
| INT-04 | `rw_isolation_enabled=true` 후 write 에이전트 실행 | worktree 정상 생성 |
| INT-05 | `hooks_enforcement_enabled=true` 후 type error 파일 Write | exit 1, 작업 중단 |
| INT-06 | `hooks_enforcement_enabled=true` 후 clean 파일 Write | exit 0, 정상 진행 |
| INT-07 | circuit breaker halt 상태에서 Write 시도 | exit 2, 운영자 알림 |

**실행**:
```bash
pytest tests/integration/test_phase1_integration.py -v
```

---

## 롤백 계획

3-Layer 롤백 구조. 이상 심각도에 따라 단계적으로 적용.

### Layer 1: Feature Flag (즉시, ~30초)

기능 이상 감지 시 플래그만 `false`로 변경. 코드 변경 없음.

```bash
# 해당 플래그만 비활성화
python3 -c "from utils.feature_flags import FeatureFlagLoader; FeatureFlagLoader().set_flag('FLAGS_NAME', False)"
```

**적용 기준**: 기능 오동작, 성능 저하, 예상치 못한 동작

### Layer 2: 설정 파일 복원 (~5분)

설정 파일(.claude/settings.json 등)을 이전 커밋 버전으로 복원.

```bash
# 이전 설정 복원
git show HEAD~1:.claude/settings.json > .claude/settings.json
git show HEAD~1:.claude/feature_flags.json > .claude/feature_flags.json
```

**적용 기준**: Layer 1으로 해결 안 되는 경우, 설정 충돌

### Layer 3: git revert (~15분)

해당 Task의 커밋을 revert. 코드 전체 롤백.

```bash
# Phase 1 전체 롤백 (baseline-v1 시점으로)
git revert --no-commit HEAD..baseline-v1
git commit -m "revert: rollback Phase 1 to baseline-v1"

# 특정 Task만 롤백 (커밋 해시 확인 후)
git log --oneline | grep "P1-"
git revert <commit-hash>
```

**적용 기준**: 데이터 손상, 시스템 불안정, Layer 1/2로 해결 불가

### 롤백 의사결정 트리

```
이상 감지
  ↓
기능 오동작? → Yes → Layer 1 (플래그 false)
  ↓ No
설정 충돌?  → Yes → Layer 2 (설정 복원)
  ↓ No
코드 버그?  → Yes → Layer 3 (git revert)
```

---

## 성공 기준

### KPI (Phase 1 완료 후 측정)

| 지표 | 목표 | 측정 방법 |
|------|------|----------|
| summary phase 토큰 절감 | 15~25% (full 대비) | TC-PD-01 |
| QC 패스율 유지 | baseline ±2% 이내 | 30건 샘플 |
| worktree 성공률 유지 | baseline ±1% 이내 | 실행 로그 |
| hooks false positive | < 5% | 정상 파일 대상 훅 실행 |
| circuit breaker 오발동 | 0회 | 14일 모니터링 |
| 회귀 테스트 통과율 | 100% | CI |

### DoD 요약

| Task | 핵심 DoD |
|------|---------|
| Phase 0a | `baseline_2026-04-03.json` + `git tag baseline-v1` |
| Phase 0b | `feature_flags.json` 6플래그 + `feature_flags.py` 로더 |
| P1-1 | `build_prompt` 3단계 disclosure + TC-PD-01~04 통과 |
| P1-2 | `--agent-type` 인자 + worktree 격리 + TC-RW-01~04 통과 |
| P1-7 | PostToolUse 훅 + circuit breaker + TC-HK-01~07 통과 |

### Phase 1 완료 조건

모든 Task DoD 체크리스트 100% 완료 + INT-01~07 통합 테스트 전체 통과.

---

*END OF DOCUMENT*