# Phase 4: 씬 연출

> 물리적 존재 판정, 씬 단위 카메라 플로우 설계, 촬영 연출, 아웃룩(의상) 매핑, 씬 내 고정 요소 추출, 씬 상세 분석(T2I 프롬프트 생성), T2I 검수까지 수행하는 파이프라인의 핵심 Phase.
> **현재 기준**: v0.6.0 (2026-04-21). 비전문가용 요약은 `docs/architecture-easy/04-directing.md` 참조.
>
> **shot-more 브랜치 핵심 변경 (v0.5.0 / v0.5.1):** `scene_camera_flow`(신규)가 `shot_staging`의 기반이 되고, `scene_consistency`(신규)가 교차 샷 고정 요소를 추출하여 `scene_detail`에 주입된다.
>
> **이미지 품질 시리즈 (v0.5.7 ~ v0.5.14):**
> - `scene_detail` v9 (2026-04-20): 인물 복장 간단 언급 필수 + `zoom_in_detail` 처리로 상반신 탈의 버그 해결.
> - `scene_consistency` v4: 같은 인물 전신/확대 프레이밍 중복 `keep` 금지 (팔/문 중복 해결).
> - `shot_dependency_t2i` v5: `zoom_in_detail` ref_usage 신설 (`SAME FRAME ZOOMED`) + ignore 조건부 표현 금지.
> - `location_consistency` 신설 → v0.5.10 롤백 (manifest에서 제거, 코드는 보존).

## Active vs Legacy 구분표

| 구분 | Step ID | 실행 여부 |
|------|---------|-----------|
| **Active** | `scene_director`, `shot_director`, `scene_camera_flow` (신규), `shot_dependency`, `outlook_phase1/2/3`, `shot_staging`, `scene_consistency` (신규), `scene_detail`, `shot_dependency_t2i`, `t2i_review` | 12단계 |
| **Legacy / Disabled** | `scene_cinematography`, `shot_cinematography`, `scene_dependency`, `scene_verify`, `outlook_extraction`, `outlook_dedup` | 별도 섹션 참조 |
| **Removed** | `set_design` | 2026-04-27 제거. background_chain step(별도 PR)로 대체 예정 |

## 단계 목록

| 순서 | Step ID | 이름 | 모델 | 병렬 | 의존 |
|------|---------|------|------|------|------|
| 16 | `scene_director` | 씬 감독 (물리적 존재) | Gemini Pro | - | scene_save, entity_t2i |
| 16.5 | `shot_director` | Shot별 VE 판정 | GPT | - | scene_director, shot_selection, entity_relation, shot_validator |
| **17.05** | **`scene_camera_flow`** | **씬 카메라 플로우 (신규)** | **Gemini Pro** | **병렬** | **shot_validator, shot_selection, scene_director, entity_merge, scene_save** |
| 18.1 | `shot_dependency` | Shot 연관 분석 (1차) | 코드 (LLM 없음) | - | shot_selection, scene_director |
| 19 | `outlook_phase1` | 아웃룩 목록 추출 | Gemini Pro | - | scene_director |
| 19.1 | `outlook_phase2` | 아웃룩 씬별 매핑 | Gemini Pro | - | outlook_phase1 |
| 19.2 | `outlook_phase3` | 아웃룩 병합 정리 | Gemini Pro | - | outlook_phase2 |
| 19.5 | `shot_staging` | 촬영 연출 (DP) | GPT | - | shot_validator, shot_selection, visual_world_rules, entity_merge, **scene_camera_flow** |
| **19.9** | **`scene_consistency`** | **씬 시각적 일관성 (신규)** | **Gemini Pro** | **-** | **shot_staging, shot_director, entity_merge, shot_validator, shot_selection, beat_extract, scene_save, visual_world_rules** |
| 20 | `scene_detail` | 씬 상세 분석 | GPT | 병렬 | shot_dependency, shot_director, outlook_phase3, entity_t2i, shot_staging, **scene_consistency** |
| 20.5 | `shot_dependency_t2i` | Shot 연관 재계산 (LLM) | GPT Mini | - | scene_detail, scene_consistency |
| 20.7 | `t2i_review` | T2I 프롬프트 검수 | Gemini Flash | - | entity_t2i, scene_detail, shot_validator, visual_world_rules, entity_merge, entity_detail |

## 전체 흐름도

```mermaid
flowchart TD
    SD[scene_director] --> SDr[shot_director]
    SD --> OP1[outlook_phase1]
    SD --> SCF["scene_camera_flow (신규)"]
    SD --> SDep[shot_dependency]

    OP1 --> OP2[outlook_phase2]
    OP2 --> OP3[outlook_phase3]

    SCF --> SS[shot_staging]
    SS --> SC["scene_consistency (신규)"]

    SDr --> Detail[scene_detail]
    OP3 --> Detail
    SS --> Detail
    SC --> Detail
    SDep --> Detail

    Detail --> SDT[shot_dependency_t2i]
    Detail --> TR[t2i_review]

    style SD fill:#fce4ec
    style Detail fill:#f48fb1,color:#000
    style SDr fill:#fce4ec
    style SS fill:#fce4ec
    style SCF fill:#fff59d,stroke:#f57f17
    style SC fill:#fff59d,stroke:#f57f17
```

---

## scene_director

**목적**: 각 씬에서 카메라에 물리적으로 보이는 엔티티를 V(Visible), A(Audible), H(Hidden)로 분류.

- **입력**: scene_save segments + entity_t2i (엔티티 목록 + short_id) + visual_world_rules (director_notes) + shot_selection (selected shots 컨텍스트)
- **출력**: `scenes[].present_entity_ids` (V 판정 엔티티 ID 목록), `scenes[].primary_location`
- **모델**: Gemini Pro (필수 — GPT는 빙의/소울라이드 물리적 존재 판별이 부정확)
- **핵심 로직**:
  - DB에서 short_id를 inject (`build_short_id_info`)
  - director_notes로 판타지 세계관의 물리적 존재 기준 전달
  - selected shot 컨텍스트를 시스템 프롬프트에 주입
  - 기획서의 줄거리/톤 정보 보강
  - `direct_scenes()` 호출
- **코드**: `backend/app/core/steps/director_steps.py:66` (`SceneDirectorStep`)

---

## shot_director

**목적**: scene_director의 씬 레벨 VE를 shot 단위로 세분화하고, 변형 캐릭터 전환 시점을 확정.

- **입력**: scene_director + shot_extract + shot_selection + entity_relation + segments
- **출력**: `scenes[].shots[].visible_entity_ids` (shot별 VE), `scenes[].shots[].variant_resolved` (변형 확정)
- **모델**: GPT
- **핵심 로직**:
  - scene_director는 "씬 전체"에 등장하는 엔티티를 판정하지만, 특정 shot에는 일부만 보일 수 있음
  - shot_director가 각 shot별로 정확한 VE 목록을 확정
  - 변형 캐릭터(빙의 전/후 등)의 전환 시점도 shot 단위로 확정
  - `direct_shots()` 호출
- **코드**: `backend/app/core/steps/shot_director_step.py:11` (`ShotDirectorStep`)

```mermaid
flowchart LR
    SceneVE["씬 VE: C01, C02, C03, L01"] --> |shot 1| ShotVE1["Shot VE: C01, C02, L01"]
    SceneVE --> |shot 3| ShotVE3["Shot VE: C02, C03, L01"]
    SceneVE --> |shot 5| ShotVE5["Shot VE: C01, L01"]
```

---

## scene_camera_flow (신규 — order 17.05)

**목적**: 씬 단위로 카메라 연속 이동 경로(플로우)를 설계하고, 선택된 각 샷을 플로우 위에 배정. 이후 `shot_staging`이 이 플로우에서 개별 샷 카메라를 파생시킨다.

- **입력**: scene_save + shot_extract + shot_selection + scene_director + entity_merge
- **출력**:
  - `scenes[].flow_summary`: 씬 전체 카메라 흐름 요약
  - `scenes[].flow_stages[]`: 단계별 카메라 상태(앵글, 높이, 거리, 이동 축 등)
  - `scenes[].shot_assignments[]`: 선택된 shot_index가 어느 flow_stage에 배정되는지
- **모델**: Gemini Pro
- **병렬**: ThreadPool 씬별 병렬 (MAX_WORKERS=4)
- **핵심 로직**:
  1. shot_extract의 **모든 샷**(선택 + 비선택)을 입력하여 전체 씬 흐름을 파악 — 비선택 샷은 "왜 이 흐름이 필요한가"의 맥락.
  2. **선택된 샷에만** 카메라 위치(stage)를 배정 — 스키마 enum으로 `shot_index`를 선택된 것만 허용.
  3. resume 시 기존에 `flow_stages`가 채워진 씬은 재호출하지 않음.
  4. 단일 샷 씬(선택 샷이 0개)은 빈 flow로 스킵.
- **코드**: `backend/app/core/steps/scene_camera_flow_step.py:21` (`SceneCameraFlowStep`)

```mermaid
flowchart LR
    AllShots["모든 샷 (선택+비선택)"] --> LLM["Gemini Pro"]
    SelectedOnly["선택 샷 enum"] --> LLM
    Entities["엔티티 이름"] --> LLM
    SceneText["씬 원문"] --> LLM
    LLM --> Flow["flow_stages 1~N"]
    LLM --> Assign["shot_assignments (선택 샷만)"]
```

---

## shot_dependency (1차, 코드 기반)

**목적**: 같은 배경의 이전 shot 중 엔티티 오버랩이 최대인 것을 참조로 연결. LLM 호출 없음.

- **입력**: shot_extract + shot_selection + scene_director + shot_director (VE)
- **출력**: `dependencies[].location_refs` (배경 참조 shot), `dependencies[].character_refs`
- **모델**: 없음 (순수 코드 스코어링)
- **핵심 로직**:
  - 같은 `primary_location`의 이전 shot만 후보
  - 교집합(공통 엔티티) 최대 + 여집합(현재에 없는 엔티티) 최소
  - 캐릭터(C##) 여집합은 3배 가중 감점 (이미지에서 인물이 가장 눈에 띔)
  - `score = |intersection| - |char_complement| * 3 - |non_char_complement|`
- **코드**: `backend/app/core/steps/shot_dependency_step.py:17` (`ShotDependencyStep`)

> 이 1차 결과는 scene_detail 이후 `shot_dependency_t2i`(LLM 기반)가 덮어쓴다. 두 체크포인트가 병존하지만 downstream은 항상 T2I 버전을 우선 참조.

---

## 아웃룩 3단계 (outlook_phase1/2/3)

**목적**: 캐릭터별 의상(아웃룩) 목록을 추출하고, 씬별로 어떤 의상을 입는지 매핑.

### Phase1: 아웃룩 목록 추출

- **입력**: segments + scene_director characters + scene_character_map + visual_rules
- **출력**: `outlooks[]` (short_id: O01~, name, description, character_id) + `null_outlook_chars`
- **모델**: Gemini Pro
- **핵심 로직**:
  - LLM에서 캐릭터별 아웃룩 추출
  - Short ID 부여 (O01, O02, ...)
  - **Orphan retry**: 아웃룩이 없는 캐릭터가 있으면 최대 2회 재시도
  - **O00 (Null Outlook)**: 비인간형 캐릭터 + visual_similarity=false 변형 캐릭터에 자동 할당
- **코드**: `backend/app/core/steps/outlook_steps.py:100` (`OutlookPhase1Step`)

### Phase2: 씬별 매핑

- **입력**: segments + phase1 outlooks + characters + scene_character_map
- **출력**: `scene_assignments[]` (scene_index, assignments: [{character_id, outlook_id}])
- **모델**: Gemini Pro
- **핵심 로직**:
  - O00 캐릭터는 LLM 입력에서 제외 (오인 방지)
  - **누락 retry**: 최대 3회, 누락된 씬/캐릭터만 재요청
  - O00 자동 주입: null_outlook_chars가 등장하는 모든 씬에 O00 강제 할당
- **코드**: `backend/app/core/steps/outlook_steps.py:243` (`OutlookPhase2Step`)

### Phase3: 병합 정리

- **입력**: phase2 결과 (outlooks + scene_assignments) + scene_summaries + character_routes
- **출력**: `cleaned_outlooks` + `cleaned_assignments` + `removed` (병합/제거된 아웃룩)
- **모델**: Gemini Pro
- **핵심 로직**:
  - O00은 LLM에서 제외하고 보호
  - 유사한 아웃룩 병합 (merge_map 적용)
  - 결과에 O00 복원
- **코드**: `backend/app/core/steps/outlook_steps.py:377` (`OutlookPhase3Step`)

```mermaid
flowchart LR
    P1[Phase1: 목록 추출] --> |outlooks| P2[Phase2: 씬별 매핑]
    P2 --> |assignments| P3[Phase3: 병합 정리]
    P3 --> |cleaned| Detail[scene_detail]

    P1 --> |orphan retry x2| P1
    P2 --> |누락 retry x3| P2
```

---

## shot_staging — scene_camera_flow 파생 규칙 적용 (v6)

**목적**: 샷별 창의적 촬영 연출(카메라 방향, 조명, 인물 배치, 배경 요소) 설계. scene_camera_flow의 플로우 위에서 **개별 샷 카메라를 파생**한다.

- **입력**: shot_extract + shot_selection + visual_world_rules + entity_merge + **scene_camera_flow**
- **출력**: `shots[]` (camera_direction, lighting_mood, key_bg_elements, character_angles, perspective, pov_character, perception_mode, gaze_direction_kind, gaze_target_id, subject_state)
- **모델**: GPT
- **프롬프트 버전**: `shot_staging` v2.0.0 (v6 프롬프트, 2026-04-15)
- **핵심 로직**:
  - `run_shot_staging()` 호출.
  - **독립 카메라 결정 금지** — 각 샷은 scene_camera_flow의 배정된 flow_stage를 출발점으로 삼음.
  - **"앞 샷과 달라진 축 하나만 강조" 규칙**: 매 샷마다 앵글/높이/거리를 전부 바꾸지 말고, 플로우에서 바뀐 하나의 축(delta axis)만 강조.
  - `subject_state` 필드로 `dead`, `unconscious`, `severely_injured` 등 특수 상태를 표기 → `character_state_variant` 단계의 입력이 됨. (Area #2 (2026-05-17): legacy mixed gaze field 폐기 후 `gaze_direction_kind` + `gaze_target_id` + `subject_state` 3 field SOT 분리.)
- **코드**: `backend/app/core/steps/shot_staging_step.py:11` (`ShotStagingStep`)

---

## scene_consistency (신규 — order 19.9)

**목적**: 씬 내 2개 이상의 샷에 걸쳐 동일해야 할 시각적 요소(고정 요소)를 추출. scene_detail이 이를 각 shot의 프롬프트에 통합한다.

- **입력**: shot_staging + shot_director + entity_merge + shot_extract + shot_selection + beat_extract + scene_save + visual_world_rules
- **출력**: `scenes[].fixed_elements[]` (element_type, element_id, description, applies_to_shots, character_name)
- **모델**: Gemini Pro (실패 시 순화+재시도, 최종 GPT fallback)
- **핵심 로직**:
  - **2+ 샷 씬만 분석**. 단일 샷 씬은 스킵 (교차 참조 필요 없음).
  - resume 시 성공한 씬(`analysis_summary`가 "분석 실패"로 시작하지 않는 경우) 보존.
  - **안전 필터 대응**: 민감한 표현(혈흔/시신/살해 등)을 `_sanitize_for_safety`로 영화 촬영 세트 용어로 순화(예: `"핏자국" → "붉은 자국"`, `"corpse" → "motionless figure"`).
  - 3단계 fallback: Gemini Pro → 순화 텍스트 + Gemini Pro → GPT fallback.
  - `element_type`:
    - `character_state`: 사망/부상 상태의 인물 자세와 위치 (예: "쓰러진 인물의 자세와 왼쪽 바닥 위치")
    - `environment_state`: 깨진 창문, 엎어진 테이블 등 환경 변경 상태
    - `persistent_prop`: 씬 내내 같은 위치에 있는 고정 소품
  - `applies_to_shots`에는 해당 요소가 공유되어야 할 shot_index 목록이 들어감.
- **코드**: `backend/app/core/steps/scene_consistency_step.py:65` (`SceneConsistencyStep`)

> 고정 요소 예시:
> - "쓰러진 인물의 자세와 위치 (character_name=인물A)"
> - "깨진 창문 파편의 배치"
> - "붉은 자국의 위치" (안전 순화된 혈흔)

---

## scene_detail (핵심 단계) — v9 프롬프트 규칙

**목적**: 선택된 각 shot에 대해 T2I 프롬프트를 포함한 상세 분석을 생성. 파이프라인의 최종 핵심 출력.

- **입력**: shot_dependency + shot_director + outlook_phase3 + entity_t2i + shot_staging + **scene_consistency** + beat_extract + shot_validator + shot_selection + scene_summary + visual_world_rules
- **출력**: `scenes[].t2i_variations[]` (t2i_prompt, outfit_assignments 등)
- **모델**: Gemini Pro (Phase 9.2 — gpt-5.5에서 swap, 시각 추론 강화)
- **병렬**: ThreadPool 병렬 (shot별)
- **프롬프트 버전**: `scene_detail_composer` v1.11.0 (v11 system.md, 2026-04-30, `prompts/_base/scene_detail/11.202604301730/`) — Phase 9.2: Rule E (close framing 시 reference 가정 표현 금지) + Rule F (카메라/frame edge 물리 일관성) + Rule G (fg/bg 인물 공유 공간) + Rule H (entity ID demographic descriptor) + 모델 swap
- **2-pass 의존성**: `consumes_downstream: ["shot_dependency_t2i"]` — `scene_detail`이 `scene_context_loader`를 통해 하류 step인 `shot_dependency_t2i`의 refined `ref_usage`(`zoom_in_detail` 등)를 역참조로 소비한다 (v0.5.14 `9956c27` 이후). force 시 cascade가 `shot_dependency_t2i` 파일을 지우면 역참조가 dead code가 되므로, 이 step은 파일 보존(DB는 stale)으로 처리.

### v9 프롬프트 핵심 규칙 (v2 → v9 누적)

#### 1) 단일 순간 (한 샷 = 한 찰나)

- 시간 연결어 금지: `"and then"`, `"while ~ing"`, `"after ~ing"`, `"as ~"`, `"~하자"`, `"~하며"`
- 두 개 이상 동작 금지
- 진행형(-ing) 남용 금지: `"pulling the curtain"` → `"hand gripping the curtain"`
- 앞뒤 샷 내용 끌어오지 않음

#### 2) C## 사용 규칙 (v0.5.1 핵심)

`t2i_prompt`에서 C##(인물 ID)을 사용하면 이미지 생성 시 해당 인물의 **얼굴 참조 이미지**가 자동 연결된다. 따라서 C## 사용 여부는 **얼굴이 식별 가능한지**로 판단한다.

| 상황 | 처리 |
|------|------|
| 얼굴 보임 (정면 / 프로필 / 3/4 / 눈 감음) | **C## 사용** |
| 완전히 뒤돌아선 인물 (back to camera) | C## 금지 → 보통명사 |
| 실루엣/흐릿한 윤곽 | C## 금지 → 보통명사 |
| 어깨 너머(OTS)에서 뒷통수/어깨만 보임 | C## 금지 → 보통명사 |
| OTS에서 얼굴 일부라도 보임 | C## 사용 가능 |
| 클로즈업 구도 (손, 손목, 소품) | **C## 금지** — 얼굴 참조가 강제 합성되어 부자연스러움. `"a hand"`, `"fingers"` 등 보통명사. |

#### 3) 단일 시점 (1인칭/3인칭 혼합 금지)

하나의 t2i_prompt는 **하나의 카메라 위치**에서 촬영한 것만 묘사.

- 3인칭 전신 + 같은 인물의 손 클로즈업 = 2개 시점 → **금지**
- 소품을 잡고 있을 때 방향은 **한 방향만** 명시 (양방향 모순 금지)
- `"Focus on"`은 초점 영역 지정이지 시점 전환이 아님 — 카메라 위치는 그대로

```
✗ "C01 stands at the left. Focus on C01's fingers gripping the photo" (전신+손 클로즈업 = 2 시점)
✓ "C01 stands at the left, one hand holding a photo at waist level" (3인칭에서 손은 몸의 일부)
✓ "A hand enters from the left foreground, fingers gripping the photo" (클로즈업만, 전신 없음)
```

#### 4) 고정 요소 통합 규칙 (scene_consistency 연동)

`scene_consistency.fixed_elements` 중 `character_state`에 `(인물이름 = C##)` 매핑이 명시된 경우:

1. 고정 요소 description의 보통명사 인물 묘사(`"A Korean man"`, `"a woman"` 등)를 해당 **C##으로 대체**.
2. 같은 인물을 C##과 보통명사로 **이중 묘사 절대 금지** — 3명이 아닌 2명인 씬에서 3명이 그려지는 bug.
3. 고정 요소의 자세/위치/상태는 그대로 유지, 인물 참조만 C##으로 교체.
4. 이미 C##으로 자세를 묘사했으면, 같은 인물의 고정 요소를 별도 문장으로 반복하지 않음.

```
고정 요소: "A Korean man stands rigidly, arms at sides" (인물이름 = C03)
✓ "C03 stands rigidly, arms at sides, in left profile"
✗ "C03 stands on the right. A Korean man stands rigidly inside the room." (이중 묘사)
```

#### 5) 복잡 구도 단순화

한 프롬프트에 인물 2명 + 소품 클로즈업 + 배경 묘사를 전부 담으면 이미지 모델이 혼란을 일으킨다. **가장 중요한 요소만 선택**하고 나머지는 생략.

- 인물 2명이 소품을 함께 보는 장면 → 한 인물 반응 + 소품만, 또는 두 인물 배치만
- 인물 전신 + 손 소품 클로즈업 → 3인칭 전신(소품은 몸의 일부)만 또는 클로즈업만
- 원칙: **단순하고 명확한 한 장면 > 복잡하게 모든 걸 담은 한 장면**

#### 6) 인물 복장 간단 언급 필수 (v9)

클로즈업/부분 앵글에서도 t2i_prompt에 **인물 복장을 한 줄로 간단 언급**해야 한다. 예: `"C01 in a white dress shirt, hand gripping the doorknob"`. 이 지시는 Gemini Image가 composite 참조 이미지를 받을 때 "상반신 탈의" 현상을 방지한다 (composite ref에는 옷이 있지만 프롬프트에 복장 묘사가 없으면 모델이 참조를 무시하는 현상).

#### 7) zoom_in_detail 처리 (v0.5.13, v0.5.14)

`shot_dependency_t2i`가 `ref_usage: "zoom_in_detail"`을 반환한 샷은 **앞 샷을 클로즈업으로 줌인**한 것. 이 경우:
- 배경/조명을 그대로 유지하되, 프롬프트에서 해당 영역만 묘사.
- `scene_consistency.fixed_elements`의 `keep` 지시에서 같은 인물의 전신과 확대 프레이밍을 **중복 keep 금지** (팔/문 등 중복 묘사 방지).
- image label: `"BACKGROUND from previous shot (SAME FRAME ZOOMED) — use as-is"`.

### 구조화 입력 우선순위

shot_staging의 `character_angles` / `camera_direction` / `lighting_mood`가 주어지면 그것을 **최우선**으로 반영. 임의 해석 금지.
`scene_consistency`의 `fixed_elements`가 주어지면 자세/위치/상태를 유지하되, 위 C## 통합 규칙 적용.
`shot_dependency`가 주어지면 이전 샷과의 연속성 유지.

### VE 위반 검사

T2I 프롬프트에 VE(visible_entities) 밖 엔티티 사용 시 retry + 강제 제거.

### outfit_assignments → C##O## 변환

LLM이 반환한 `outfit_assignments`를 코드에서 `C##O##` 복합 ID로 조합.

```mermaid
sequenceDiagram
    participant LLM
    participant Code
    participant Image as 이미지 생성

    LLM->>Code: t2i_prompt: "C01 sits across from C02..."
    LLM->>Code: outfit_assignments: {C01: O03, C02: O01}

    Code->>Code: C01 + O03 → C01O03
    Code->>Code: C02 + O01 → C02O01
    Code->>Code: t2i_prompt에서 C01 → C01O03, C02 → C02O01 치환

    Code->>Image: "C01O03 sits across from C02O01..."
```

### 고정 요소 블록 주입

scene_consistency의 `fixed_elements`가 shot별로 필터링되어 다음 블록으로 주입됨:

```
[교차 샷 고정 요소]
  [인물 상태] E01 (인물명 = C03):
    쓰러진 채 움직이지 않는 상태, 왼쪽 바닥에 위치
  [환경] E02: 깨진 창문 파편, 우측 하단
```

- **코드**: `backend/app/core/steps/detail_steps.py:42` (`SceneDetailStep._execute`)

---

## shot_dependency_t2i (2차, LLM 기반 — v5)

**목적**: scene_detail의 T2I 프롬프트 + shot description을 LLM에 전달하여 스토리 연관성 기반 의존성 판단. 1차(코드 기반)를 덮어씀.

- **입력**: scene_detail (T2I 프롬프트) + shot_validator + shot_selection + scene_director + entity_merge + scene_consistency
- **출력**: `dependencies[].location_refs[]` (참조 shot + keep_elements + ignore_elements + `ref_usage`)
- **모델**: GPT Mini
- **프롬프트 버전**: `shot_dependency_t2i` v1.2.0 (v5 프롬프트, 2026-04-20)
- **핵심 로직**:
  - 같은 location의 shot들을 그루핑
  - 그룹별 LLM 호출 (shot description + T2I 프롬프트 전달)
  - forward reference 검증 (앞쪽 shot만 참조 허용)
  - **고정 인물 상태**: scene_consistency의 character_state를 `[고정 인물 상태]` 블록으로 주입
  - LLM 결과에서 `keep_elements`/`ignore_elements`/`ref_usage` 지시 포함
  - `ref_usage` 분기 (v5 기준):
    - `"exact_background"`: 같은 방 → 배경 그대로 사용
    - `"atmosphere_reference"`: 다른 방/층/앵글 → 분위기/조명/색감만 참고
    - `"zoom_in_detail"` (v0.5.14 신규): 같은 앵글/구도를 줌인한 클로즈업 → `SAME FRAME ZOOMED` 라벨
  - `ignore_elements`는 **조건부 표현 금지** ("if visible" 같은 애매한 표현 제거 — v0.5.13).
- **코드**: `backend/app/core/steps/shot_dependency_t2i_step.py:19` (`ShotDependencyT2iStep`)

---

## t2i_review

**목적**: entity_t2i와 scene_detail의 T2I 프롬프트를 최종 검수하고 수정 사항 적용.

- **입력**: entity_t2i + scene_detail + entity_merge + entity_detail + shot_extract + visual_world_rules
- **출력**: 수정된 entity_t2i / scene_detail 체크포인트 (직접 덮어쓰기)
- **모델**: Gemini Flash
- **핵심 로직**:
  - `run_t2i_review()` 호출
  - entity_t2i, scene_detail 체크포인트를 아카이브 후 원자적 쓰기로 수정 적용
  - 기존 체크포인트는 `manifest_YYYYMMDD_HHMMSS_pre_review.json`으로 백업
- **코드**: `backend/app/core/steps/t2i_review_step.py:11` (`T2iReviewStep`)

---

## 참고: 레거시 / Disabled

아래 step들은 `step_manifest.py`에 등록되어 있지만 Active 경로에서 호출되지 않는다.

| Step | 상태 | 대체 | 비고 |
|------|------|------|------|
| `scene_cinematography` (order 17) | `on_demand` | `shot_staging` | 씬 단위 촬영 감독은 shot_staging으로 파생 |
| `shot_cinematography` (order 17.1) | `disabled` | `shot_staging` | v0.5.0에서 의존성 제거. 체크포인트/코드는 롤백 가능성을 위해 보존. |
| `scene_dependency` (order 18) | `disabled` | `shot_dependency` | 씬 단위 의존성은 샷 단위로 세분화됨 |
| `scene_verify` (order 21) | `disabled` | 없음 | STEP_CLASSES 등록됐지만 applicability=disabled로 run-all 경로 제외 |
| `outlook_extraction` (order 19.3) | `on_demand` | `outlook_phase1/2/3` | 하위 호환 래퍼 |
| `outlook_dedup` (order 100) | `on_demand` | — | 사용 안 함 |

파일 전체 dead code:

- `backend/app/core/steps/analysis_steps_legacy.py` — v3 잔재, STEP_CLASSES 미등록
- `backend/app/core/steps/shot_cinematography_step.py` — `disabled`, 코드만 잔존
