# 프롬프트 버전 관리 정책

> **현재 기준**: v0.6.0 (2026-04-21).

## 배경

TheRoad 파이프라인은 각 LLM 호출 단계(step)마다 `prompts/_base/<step>/<version>/` 형식으로 **새 버전 디렉토리**를 만들어 프롬프트를 진화시킨다. 덮어쓰기 없이 버전을 누적하는 원칙이 rollback·비교·감사 목적에 유용하지만, 시간이 지나며 **단일 step에 10개 이상의 버전 디렉토리**가 쌓여 로더 스캔·저장소 크기·리뷰 난이도가 모두 악화된다.

이 문서는 프롬프트 버전의 **활성/아카이브 분리 정책**을 정의한다.

## 디렉토리 구조

```
prompts/
  _base/                    # 로더(PROMPTS_BASE)가 스캔
    <step>/
      <version_dir>/        # active (최신 ~3개만)
        system.md
        schema.json
  _archive/                 # _base와 같은 레벨, 로더가 스캔하지 않음
    <step>/
      <old_version_dir>/    # 과거 버전 보존
        system.md
        schema.json
```

- `prompts/_base/<step>/<version>/`: 로더가 스캔하는 "현역" 버전. 최신 3개 이내.
- `prompts/_archive/<step>/<version>/`: `_base`와 같은 `prompts/` 루트 하위이지만 `_base` 바깥이라 `PROMPTS_BASE`가 가리키지 않는다 → 로더가 자동으로 무시.

## 정책 규칙

1. **버전 네이밍**: 기존 `<major>.<YYYYMMDDHHmm>` 형식 유지 (`_version_sort_key`로 정렬). 예: `9.202604201700`.
2. **활성 개수**: 각 step 디렉토리에 **최신 3개 버전만 유지**. 4번째 버전이 생기면 가장 오래된 것을 아카이브로 이동.
3. **아카이브 시점**:
   - 해당 step의 4번째 새 버전 커밋 시점.
   - 또는 특정 버전이 6개월 이상 production에 미사용인 경우.
4. **아카이브 이동 방식**:
   - `git mv prompts/_base/<step>/<old_version> prompts/_archive/<step>/<old_version>`
   - 파일 내용 변경 없음 — 히스토리 보존.
5. **로더 동작**:
   - 활성 디렉토리만 `iterdir`.
   - `_archive` 디렉토리는 스캔 대상 외 (모듈 경계에서 명시적 필터).
6. **복원 절차**: 사고 발생 시 `git mv prompts/_archive/<step>/<ver> prompts/_base/<step>/<ver>` 역방향 이동.

## 예외

- **현재 production에서 실행 중인 마이그레이션**이 구버전을 참조하는 경우, 그 버전은 마이그레이션 완료까지 활성에 유지.
- **A/B 테스트**가 구버전을 대조군으로 사용하는 경우, 실험 종료까지 활성에 유지.
- **reference_image/outlook/scene_image 등 prototype_prompts**처럼 여러 step에서 공용 참조되는 디렉토리는 3개 원칙 적용이 부적절할 수 있음 — 별도 판단.

## 로더 구현 수정 (향후)

`backend/app/modules/prompt_loader.py`의 버전 스캔:

```python
# 현재
versions = sorted(
    [d.name for d in module_dir.iterdir() if d.is_dir()],
    key=_version_sort_key,
    reverse=True,
)

# 개선 (루트 '_archive' 제외는 PROMPTS_BASE 계층에서, step 내부는 그대로)
# PROMPTS_BASE / module 만 스캔하므로 '_archive'는 이미 경로 자체가 다름 → 추가 필터 불필요
# 단, 혹시 module_dir 내부에 임시 '_deprecated' 같은 서브폴더가 생기면 아래 식으로 방어:
versions = sorted(
    [d.name for d in module_dir.iterdir()
     if d.is_dir() and not d.name.startswith("_")],
    key=_version_sort_key,
    reverse=True,
)
```

현재 구조에서는 `_archive` 디렉토리가 `PROMPTS_BASE` 외부에 있으므로 추가 필터는 방어적 기능일 뿐.

## 아카이브 실행 체크리스트 (수동)

1. 대상 step의 버전 디렉토리 수 확인 (`ls prompts/_base/<step>/ | wc -l`).
2. 4개 이상이면 가장 오래된 버전 선별.
3. 해당 버전이 현재 MODULE_VERSIONS의 `prompt_dependency`로 참조되는지 확인 (`git grep <step>/v<N>` in `version_registry.py`).
4. 참조 없으면 `mkdir -p prompts/_archive/<step>/`
5. `git mv prompts/_base/<step>/<version>/ prompts/_archive/<step>/<version>/`
6. 단위 테스트 실행 → 프롬프트 로더 정상 동작 확인.
7. 커밋: `chore: archive <step>/<version> (inactive, superseded by <new_version>)`.

## 현재 상태 스냅샷 (2026-04-20)

| step | active 개수 (`prompts/_base/`) | 최신 버전 | 아카이브 후보 |
|------|-------------|-----------|---------------|
| scene_detail | 3 | `9.202604201700` | v1~v6 (이미 `_archive/`로 이동됨 — v0.5.20) |
| scene_consistency | 3 | `4.202604201700` | v1, v2 (이미 `_archive/`) |
| shot_dependency_t2i | 2 | v5 (2026-04-20) | v1, v2, v3 (이미 `_archive/`) |
| shot_extract | 3 | `11.202604201230` | v1~v8 (이미 `_archive/`) |
| shot_staging | 3 | `8.202604201230` | v1~v5 (이미 `_archive/`) |
| shot_selection | 3 | `4.202604191600` | v1 (이미 `_archive/`) |

※ v0.5.20 (2026-04-20)에서 프롬프트 아카이브 실행 완료. 각 step 디렉토리는 최신 3개만 유지.

### version_registry 확정 버전 (2026-04-20 기준)

주요 모듈 버전 (`backend/app/core/version_registry.py`):

- `shot_extractor: 2.1.0` (v11)
- `shot_validator: 1.2.0` (v3, Phase 9.2 — motion direction 보존)
- `shot_selector: 4.0.0` (v4, 2중 캡)
- `scene_camera_flow: 1.0.0`
- `shot_staging: 2.2.0` (v8)
- `scene_detail_composer: 1.11.0` (v11, Phase 9.2 — Rule E/F/G/H + gemini-pro)
- `scene_consistency: 2.2.0` (v4)
- `shot_dependency_t2i: 1.2.0` (v5)
- `character_state_variant: 1.0.0`
- `image_service: 1.9.0` (zoom_in_detail 라벨)
- `world_guide_generator: 1.1.0` (국가/시대 하드코딩 제거)
- `reference_image_generator: 1.3.0`
- `scene_image_generator: 1.4.0`
