# Rooftop Spatial BG Experiment — plan v1

> standalone 실험 (production pipeline 0 수정). L05 옥탑방 내부만. API-only T2I/I2I + LVM realized-card pass.

## 0. 출처 / 절대 규칙

- 대상: project `6cb862d9-590c-4dce-86e6-d10c2977db19` / episode `08ad2cd3-3e96-4d84-808f-869ee628473c` / location `L05 옥탑방 내부` (canon id `3afbc7a8-b919-4431-a691-0a99057a26ca`).
- DB write **0**. checkpoint write **0**. backend/app/ 수정 **0**. import 만 허용.
- API-only. 로컬 ControlNet/ComfyUI/Blender/Diffusers 금지.
- LLM 호출 데이터 자르기 **금지** (CLAUDE.md 절대 규칙) — base/shot prompt 전문 전달.
- LVM 는 geometry oracle 아님 — visible-only spatial observer/validator.

## 1. 실험 대상 (Phase A 에서 확정)

14 L05 selected shots (scene_index, shot_index):
S5_S2, S5_S6, S12_S4, S12_S6, S12_S14, S14_S4, S14_S5, S14_S9,
S18_S5, S18_S9, S25_S3, S25_S7, S27_S1, S27_S4.

시간/상태 axis:
- time: morning / day / dusk / night
- state: normal / corpse_and_blood / cleaned / empty / vandalized
- sub_space (관찰 기반 후보): main_room (거실+식탁) / bedroom / bathroom (욕실 거울) / entry_corner

각 shot 별 분류는 Phase C 자동 분류 (1차 = scene_summary + shot_description 텍스트 키워드 매칭, 신뢰 낮은 건 `manual_review_needed`).

## 2. 디렉토리 구조

```
scripts_output/rooftop_spatial_bg_experiment/<run_id>/
  space_bible.json
  shot_background_plan.tsv
  prompts/
    base/<space_key>__<camera_slot>.txt
    shot/<scene>_<shot>.txt
  base_plates/<space_key>__<camera_slot>.png
  shot_plates/<scene>_<shot>.png
  realized_spatial_cards/<base|shot>__<key>.json
  final_scene_context_cards/<scene>_<shot>.json
  planned_vs_realized_report.tsv
  cost_log.jsonl
  llm_call_meta.jsonl
  index.html
  run_meta.json
```

`<run_id>` = `<YYYYMMDD_HHMM>_<short_uuid>`.

## 3. CLI / 안전장치

```
backend/scripts/experiment_rooftop_spatial_bg.py
  --run-id <id>            # 명시 안 하면 자동
  --dry-run                # default — 계획/프롬프트/디렉토리만, API 호출 0
  --generate               # 실제 image API 호출 허용
  --skip-lvm               # LVM pass 건너뛰기
  --max-shots N            # default 14, 안전장치
  --max-plates N           # base+shot plate 총량 cap
  --space-keys main,bedroom,bathroom   # 부분 실행
  --shots S5_S2,S12_S4     # 부분 실행
  --resume                 # 이미 생성된 plate 재사용 (기존 run_id 지정)
  --estimate-only          # 호출 건수+예상 비용만 출력 후 exit
```

Hard guards:
- `--generate` 없으면 image / LVM API 호출 0.
- `--max-plates` default 30. 초과 시 abort + 안내.
- 중간 실패 시 `run_meta.json` 에 partial state 기록.

## 4. 7 단계 흐름

### Stage 1 — collect (Phase C 일부)
- DB read-only: scene_still WHERE episode_id AND is_selected AND visible_entities_json LIKE '%L05%'.
- t2i_variations_json / dependent_scene_id / shot_description / scene_summary / visible_entities_json 수집.
- shot 14건 → shot_background_plan.tsv 초안.

### Stage 2 — space bible (Phase C)
- L05 의 sub_space 4 후보 → 자동 분류. 룰:
  - "욕실|거울|화장실" → bathroom
  - "침실|침대|방 안.*시신|방 안.*깨끗" → bedroom
  - "현관|진입|문 안쪽" → entry_corner
  - "거실|식탁|싱크대|식사 공간" → main_room
  - 다중 매칭 → 우선순위 bathroom > bedroom > entry_corner > main_room (좁은 곳 우선)
  - 매칭 0 → `manual_review_needed=true`.
- 각 sub_space 의 fixed geometry (벽/문/창/가구) — entity_canon L05 description + 기존 chain_bg prompt 8개에서 텍스트 키워드 추출 (지명, 가구명, 창문 등). 자동 추출 신뢰 낮으면 sub_space 별 `confidence=low` 표시 + manual_review.
- camera_slot 정의: (space_key, slot_label, slot_description) 튜플. 예: (main_room, eye_level_wide, "거실 중앙 1.6m 눈높이 35mm 광각, 식탁 정면").

### Stage 3 — camera slot 배정 (Phase C)
- 각 shot → (space_key, camera_slot, state, time) 배정.
- shot_description 의 framing (wide/medium/close) 키워드 + visible 요소 (식탁/거울/벽/시신) 로 slot 매칭.
- 자유 구도 금지 — slot pool 미스 시 `slot=fallback_unknown` + manual_review.

### Stage 4 — base plate (Phase D)
- (space_key, camera_slot) 의 unique 조합만 base plate 1장씩 생성 (T2I).
- prompt: 사람/캐릭터/동물/이름 0. 공간만. 가구는 fixed (Stage 2 bible). state=normal/empty/daytime 으로 통일.
- 모델: gemini-3.1-flash-image-preview (CLAUDE.md 기재) 또는 openai images.generate. 양쪽 다 코드 path 존재 — Stage 4 진입 시 Codex 에 모델 선택 한 번 더 확인.

### Stage 5 — shot plate (Phase E)
- 각 shot 의 state/time 만 base plate 에 I2I/edit 로 적용.
- prompt: base = "이 공간을 유지하라" + delta = state/time/lighting overlay.
- 사람/캐릭터/동물 금지. **시신 같은 인체 elements 는 이번 실험에선 환경 마감재 (피 자국, 어지러진 가구) 까지만 표현, 시체 자체는 final scene 단계로 미룸.**
- I2I: gemini i2i (편집 / 1 reference image input) 또는 openai images.edit.

### Stage 6 — LVM pass (Phase F)
- 모델: GPT LVM (`gpt-5` vision — production 의 image_review_service 와 동일 backend).
- 입력: 각 plate (base/shot) → "이 이미지에 보이는 것만 기술하라" prompt.
- 출력 schema (`realized_spatial_card.json`):
  ```json
  {
    "plate_id": "...",
    "plate_kind": "base|shot",
    "space_key": "main_room",
    "camera_slot": "eye_level_wide",
    "fixed_objects": [
      {"label": "식탁", "bbox_xyxy_norm": [0.32,0.55,0.61,0.78], "confidence": 0.82},
      ...
    ],
    "zones": {
      "foreground": "...",
      "midground": "...",
      "background": "..."
    },
    "camera": {"direction_text": "북서쪽 벽 향함", "estimated_eye_height_m": 1.6, "lens_text": "약 35mm 광각"},
    "placement_zones": [
      {"label": "식탁 앞 0.8m 공간", "bbox_xyxy_norm": [...], "scale_anchor_m": 1.7}
    ],
    "unknown": ["창문 너머 풍경"],
    "overall_confidence": 0.7
  }
  ```
- evidence 강제: bbox / "보이지 않음" 표기 의무. LVM 이 채워 넣는 hallucination 차단 (prompt 에 명시 + 출력 검증 step).
- mismatch: planned (Stage 2-3) vs realized — fixed_objects 누락 / camera direction drift / sub_space 다르게 그려짐.
- retry 정책: major mismatch (e.g. wrong sub_space) → 같은 plate **1회만** 재생성. 두 번째도 실패 시 `unresolved` 표기 후 진행.

### Stage 7 — final scene context card (Phase G)
- 각 shot 의 `final_scene_context_cards/<scene>_<shot>.json`:
  - 어떤 base_plate / shot_plate 가 source 인지 path
  - realized_spatial_card 발췌 (placement_zones / camera / fixed_objects)
  - 미래 final scene 생성 시 reference 로 쓸 instruction text (예: "S12_S6 generation: use shot_plate `12_6.png` as exact_background reference. 인물 C04(민숙) 시신을 placement_zones[1] 에 배치. lighting=night.")
- index.html: base plate / shot plate / realized card 요약 / context card 한 페이지 contact sheet.

## 5. 모델 선택 (Codex 결정 요청 #1)

| 단계 | 후보 A | 후보 B | 권장 |
|---|---|---|---|
| base plate (T2I) | gemini-3.1-flash-image-preview | openai images.generate | A (CLAUDE.md 기재 + 기존 chain_bg 동일 backend) |
| shot plate (I2I) | gemini i2i | openai images.edit | A 일관성 위해 — 단, gemini i2i 의 ref 입력 제약은 확인 필요 |
| LVM verify | gpt-5 (vision) | gemini-3.1-pro vision | A (production image_review_service 와 동일 backend) |

## 6. 비용 estimate (대략)

- base plate: sub_space 4 × camera_slot 평균 2 = ~8장 (T2I)
- shot plate: 14장 (I2I)
- LVM: (8 + 14) × 1 call = 22 call
- retry: 5장 (보수적)

단가 가정 (gemini image preview ~$0.04/img, gpt-5 vision ~$0.02/call) → 약 $1.5 미만. 안전.

## 7. Open questions (Codex 결정 요청)

1. **모델 선택 표 (#5)** OK 인지, 아니면 i2i 백엔드를 OpenAI 로 가야 ref 제약이 낫다든지.
2. **시신/피 같은 인체 elements** 를 shot plate 단계에서 어디까지 그릴지 — 환경 마감(피 자국, 어지러진 가구)까지만? 시체 silhouette 까지? final scene 단계로 완전 분리?
3. **camera slot 분류 자유도** — Stage 3 에서 매칭 실패 시 `fallback_unknown` 후 manual_review 가 맞는지, 아니면 자유 구도 생성 1회 허용?
4. **sub_space 자동 분류 우선순위** (bathroom > bedroom > entry_corner > main_room) — 보수적 (좁은 공간 우선) 으로 잡았는데 OK?
5. **realized_spatial_card 의 bbox precision** — normalized xyxy float 4 자리로 충분한지, 아니면 categorical zone label (top-left/center/...) 가 더 안전한지.
6. **LVM unknown 표기** — "보이지 않음" 의 negative evidence 가 얼마나 신뢰 가능한가. confidence threshold 룰 필요?
7. **production 코드 import 범위** — `gemini_image_client.GeminiImageClient` + `llm_logger.log_llm_call` 만 import vs. `image_review_service` 등 helper 도?

## 8. Out of scope (이번 실험 안 함)

- 다른 location (L01~L29 중 L05 외) 실험.
- final scene 이미지 실제 생성.
- production pipeline 수정 PR.
- DB schema 변경.
- frontend UI.
- evaluation 자동 점수 (mismatch report 는 사람이 읽고 판단).
