# 실내 일반 샷 shared-model pose 가이드 (Wave5) — 설계

- **날짜**: 2026-06-30
- **브랜치**: feat/w19-w20-bg-planning-cleanup
- **상태**: 설계 (Codex 수평 의논 합의 완료, 사용자 검토 대기)
- **flag**: `indoor_shared_pose_guide_enabled` (default **False** — OFF=byte-identical)

## 1. 배경 / 문제

이미지 생성 파이프라인에서 시각 pose/구도 가이드가 붙는 경로는 둘뿐이다:

- **immobilized 샷** → `registered_pose_guide_service` (bg plate 위 단일 마네킹 등록, gate=`shared_state_contract`+env plate, flag `immobilized_registered_pose_guide_enabled`)
- **outdoor 연속성 그룹** → `outdoor_site_layout` shared-model (site_layout 좌표 SOT → `compute_camera_brief` → 카메라뷰 가이드, default-deny VLM judge)

**실내 일반 샷(immobilized 아님)은 시각 가이드가 전혀 없다 — 텍스트 t2i + 캐릭터/배경 ref만.** PoC(`twod5_poc` / `registered_guide_exp`)가 입증한 봉합 문제(지지면 모호로 인한 hand-float/물리오류, 같은 공간 cross-shot drift, per-shot 가이드의 구조물 발명·leakage)가 이 경로에서 그대로 발생한다.

PoC 핵심 결론: 봉합 감소 이득은 **어려운 샷(같은 공간 멀티샷, 지지면 모호)에서만 크고, 쉬운 단발 insert는 중립**. 따라서 "전샷 강행"이 아니라 **default-deny + guide QC** 가 설계의 중심이다.

## 2. 목표 / 비목표

### 목표 (v1)
- 같은 씬·같은 실제 배경 plate를 공유하는 **멀티샷 연속성 그룹**에 일관된 bg-grounded 마네킹 pose 가이드를 파생·부착해 cross-shot 봉합을 줄인다.
- 모든 단계가 default-off / default-deny. 가이드가 해를 끼치면 **no-guide baseline으로 degrade**(white-bg fallback 없음).

### 비목표 (후속 wave로 분리)
- **단발 복잡샷**(single-shot establishing, 지지면 모호하지만 그룹 없음) → `single_shot_complexity` 후속 wave. v1은 **diagnostic only, attach 안 함**.
- **씬을 넘는 location 단위 그룹핑** → 같은 location이라도 시간/상태/소품 배치가 달라질 수 있어 v1은 scene 경계 안에서만.
- **다인물 contact-heavy 샷**(손잡기/물건 주고받기 등 접촉이 핵심) → FSC만으로 contact 확정 불가 → v1 skip 또는 "contact not locked" diagnostic.
- **최종 still harm 자동 검출** → v1 과함. canary A/B 육안으로 대체.
- **outdoor식 좌표 모델/blockout** → 실내는 좌표 SOT가 없음. 새 좌표 추출은 hallucination 위험이라 v1 비대상.

## 3. 아키텍처 (신규 2모듈 + 1 role + attach 배선)

```
shot_staging(FSC)·scene_director(present_entity)·background_chain(bg_id) ──┐
                                                                          ▼
[유닛1] indoor_shared_pose_plan.py  (결정론, 의미판정 0)
  - candidate_groups: same scene_index + same actual bg_id + selected 2+
  - complexity_signals: FSC constraint 수 / depth_plane 분포 / multi-char / framing variation
  - pose_brief: FSC→character_angles→camera_direction→underlay 계층 (추가 LLM 0)
                                   │
                                   ▼
[유닛2] default-deny VLM judge  (outdoor _evaluate_shared_model_guide_judge 패턴)
  - route judge: needs + decision∈{cross_shot_continuity,both} + conf>=medium + grounded evidence
  - deny/single-shot/저신뢰/근거없음 = diagnostic only
                                   │  (attach 허용 시만)
                                   ▼
[유닛3] indoor_shared_pose_guide_service.py
  - bg plate=master underlay → gpt-image-2 edit 마네킹 등록
  - group base 캐싱 (set 일관성)
  - [유닛4] guide QC (생성 후 2번째 게이트) → fail=no-guide+diagnostic
                                   │  (QC pass 시만)
                                   ▼
[유닛5] attach_indoor_pose_guide_ref  (coordinator/anchor step)
  - 새 ref role `indoor_pose_guide` (registered/outdoor와 분리)
  - flag ON AND candidate admitted AND bg bytes AND QC pass 일 때만
  - 실패 시 no-guide degrade (white-bg fallback 없음)
```

### 신규 파일
- `backend/app/modules/pipeline/indoor_shared_pose_plan.py` — 결정론 plan (candidate groups + complexity signals + deterministic pose brief). 의미판정·글자패턴 0.
- `backend/app/services/indoor_shared_pose_guide_service.py` — bg underlay edit + cache + guide QC + no-guide fallback.
- 프롬프트는 신규 버전 디렉토리(기존 프롬프트 덮어쓰기 금지).

### 재사용/차용 (수정 아님)
- `registered_pose_guide_service.make_registration_underlay` (underlay PIL 가공) — 단일 subject 전제이므로 **서비스는 분리**하되 underlay 헬퍼는 차용 가능.
- `outdoor_site_layout_step._evaluate_shared_model_guide_judge` / `_has_grounded_evidence` — judge 평가 패턴 차용.
- `visual_continuity_anchor_step.attach_registered_pose_guide_ref` — attach 배선 패턴 차용.

## 4. 유닛1 — 결정론 plan (`indoor_shared_pose_plan.py`)

### 4.1 candidate_groups
- 입력: selected shots, shot_staging(FSC), `background_chain` bg_map(샷별 실제 부착 bg_id).
- 그룹 키 = **(scene_index, bg_id)**. bg_id는 **실제 consumer가 붙이는 background id** (label/primary_location 금지 — false group 방지).
- 조건: 같은 키에 selected shot **2개 이상**. 1개면 그룹 아님.
- `zoom_continuity_member` 진단 필드: 멤버가 이미 다른 continuity(zoom anchor 등)를 가지면 표시하되 **후보에서 무조건 제외하지 않음**(zoom류 hand-float 전례). judge가 pixel-crop으로 해결된다고 판단하면 no-guide deny.
- 반환: `[{scene_index, bg_id, member_keys:[(si,shi)..], anchor_key, signals_by_shot, zoom_members:[...]}]`.

### 4.2 complexity_signals (순수, 좌표/enum만)
shot당:
- `fsc_constraint_count`, `depth_plane_bucket`(고유 depth_plane 수), `entity_count_bucket`(none/single/multiple, FSC character target 수), `gesture_present`(gesture_action != none 존재).
- `framing`(camera_direction/shot_type/framing scale 텍스트 enum — wide/medium/close/insert).
그룹당:
- `camera_framing_variation`(멤버 간 framing이 다른가: close/insert vs wide/medium), `repeated_targets`(같은 C/P target이 멤버 간 다른 screen_zone/depth_plane 또는 whole vs part로 등장), `multi_depth`(depth_plane>=2).

### 4.3 deterministic pose_brief (추가 LLM 0) — 계층화
Codex 정렬: FSC만으로 support/contact 확정 금지. 계층:
1. **FSC**: 각 target의 screen_zone(화면 위치) + depth_plane(깊이) + gesture relation(gesture_action + gesture_target_label).
2. **character_angles**: body_pose/gaze/subject_state가 있으면 pose/facing 보강 (없으면 생략).
3. **camera_direction/framing**: wide/medium/close/insert + camera height/angle 보강.
4. **bg plate underlay**: support surface pixel registration은 **모델이 underlay에서 직접 보도록** 위임.
5. **support/contact = generic physical clause만**: "body parts must be supported by visible surfaces beneath them; nothing floats unsupported." held-object/contact는 `gesture_action != none` **AND** target이 prop/character로 구조상 존재할 때만 약하게 명시, 그 외 **발명 금지**.

브리프는 spatial descriptor만 사용(foreground/background figure). **이름/엔티티 ID/텍스트 라벨 누출 0.**

## 5. 유닛2 — default-deny VLM judge

- 패턴: outdoor `_evaluate_shared_model_guide_judge` 차용. judge 입력 = name-free 구조 신호(4.2) + per-shot pose_brief 요약.
- judge 입력 신호: same bg_id group size>=2 / camera-framing variation / repeated visible targets / multi-depth(depth_plane>=2, gesture non-none) / entity count.
- **attach 조건 (strict, AND)**: `candidate` AND `judge.needs_indoor_pose_guide` AND `decision_type∈{cross_shot_continuity, single_shot_complexity, both}` AND `confidence∈{medium,high}` AND `_has_grounded_evidence`(shot_key/source_field/quote 전부 non-empty 1개+).
- `no_guide` / 저신뢰 / 근거없음·빈근거 / 판정실패 = **no attach, diagnostic only**.
- **★개정 (2026-06-30, 사용자 결정 + Codex B') — single_shot_pose_grounding lane 추가**: 실데이터(금월도 ep1)에서 실내 멀티샷 그룹이 모두 "figure 한 샷 + 0-figure establishing/insert"라 cross-shot 연속성 그룹 0(dormant). 사용자가 칭찬한 canary 14_5(1인 무릎꿇고 손-가방 grounding)가 바로 단일샷 지지면/접촉 케이스. → `single_shot_complexity` 도 admit하되 **shot-scoped**: judge가 `target_shot_keys` 명시(figure 있는 위험 샷만), context 루프가 `target ∩ member ∩ live ∩ figure_count>=1` 로 좁혀 **그 샷만** guide 생성. 같은 그룹의 0-figure establishing/insert 는 절대 target 불가. group-level 게이트(`evaluate_indoor_pose_guide_judge`)는 single_shot 통과, shot-scoping 은 context 루프 책임. lane 이름 `single_shot_pose_grounding`(continuity 아님). 전샷 강행 방어 = default-deny judge + grounded evidence + figure>=1 + bg plate mandatory + QC fail-closed.
- judge config flag(예: `indoor_shared_pose_guide_judge_enabled`) — ON일 때만 attach 평가, OFF면 candidate diagnostic만.

## 6. 유닛3·4 — 가이드 생성 + guide QC (`indoor_shared_pose_guide_service.py`)

### 6.1 생성
- bg plate bytes(그룹 env plate) → `make_registration_underlay`(faint low-contrast) → gpt-image-2 edit로 마네킹 등록.
- **establishing/wide** = master 전체. **insert/close** = 같은 master의 ROI crop(camera crop). 같은 그룹 base 1회 생성·캐싱(set 일관성).
- **multi-character 제한**:
  - max **2** primary figures = 상세 마네킹.
  - 3+ = no-guide **또는** simplified placement silhouettes only (judge high + evidence strong 아니면 no-guide).
  - 프롬프트 spatial descriptor만(foreground/background figure). 이름/ID/텍스트 라벨 금지.
  - identity/clothing/face는 final char refs/prompt 담당. guide=pose/placement only(label에서 강하게 분리).
  - contact-heavy(다인물 접촉이 핵심): v1 skip 또는 "contact not locked" diagnostic.
- 실패 = no-guide degrade (white-bg fallback 없음).

### 6.2 guide QC (생성 후 2번째 게이트, judge와 독립)
lightweight VLM/image inspection으로 다음만 gate:
- underlay layout 보존(환경 redraw 안 함), no photoreal person, no clothing/face, no text/labels/marker leakage, mannequin count 대략 일치(요청 figure 수 근사), no obvious environment redraw.
- **fail → no-guide + diagnostic** (white-bg fallback 없음).
- cost: 그룹당 base 1회 QC (멤버 공유). $10 미만 자율 범위.

### 6.3 cache key
`scene_index` + `bg_id` + member shot keys + FSC/pose_brief hash + prompt version + bg asset id/hash. (silent reuse 방지 — 입력 바뀌면 mismatch 재생성.)

## 7. 유닛5 — attach 통합

- `attach_registered_pose_guide_ref` 패턴 → 신규 `attach_indoor_pose_guide_ref`.
- 새 ref role **`indoor_pose_guide`** (registered/outdoor와 분리).
- label: "pose/placement/support registration only; bg plate is environment SOT; identity/clothing from refs/text; do not copy mannequin or any marks."
- coordinator에서 **flag ON AND candidate admitted AND bg bytes exists AND guide QC pass** 일 때만 부착.
- 4-list parallel mutate(labeled_refs/ref_roles/ref_role_metadata/attached_meta). worker thread 안전(db 접근 0).
- flag OFF = no-op, **byte-identical**.

## 8. persist-all 통합 (Wave1~4 연속성)

- guide/underlay PNG는 **intermediate role**로 capture sink에 자연히 노출 → 캔버스에서 실패/계보 inspect 가능.
  - role 예: `indoor_pose_guide`(가이드), `indoor_pose_underlay`(underlay), disposition=`accepted`/`rejected`(QC fail)/`diagnostic`(judge deny).
- 부착된 가이드는 attach 시 `input_image_ids`=[bg plate asset]로 lineage 연결.

## 9. 검증 전략

> **★ TDD 한계 규칙(절대)**: LLM/VLM/T2I 출력은 deterministic 보장 X. PASS 카운트로 완성도 주장 금지. 가이드 품질 = canary 육안 + reroll variance.

### 9.1 결정론 TDD (수학적 보장 영역만)
- `indoor_shared_pose_plan`: candidate_groups 그룹핑(scene/bg_id 경계, 2+ 조건, false group 차단), complexity_signals(enum/카운트), pose_brief 빌드(이름/ID 누출 0, generic clause 삽입, held-object 조건부), zoom_member 진단 필드.
- judge 평가 함수 `_evaluate_indoor_pose_guide_judge`: attach 조건 AND 게이트(needs/decision/confidence/grounded evidence), diagnostic-only 분기.
- guide QC 판정 함수(VLM 응답 dict → pass/fail), cache key 무결성.
- flag OFF byte-identical(config_hash + step diff 0).

### 9.2 canary 육안 (TDD 불가 영역)
- 실 데이터(금월도 등)에서 same-scene same-bg 멀티샷 그룹 발굴 → guide ON vs no-guide A/B.
- 판정: cross-shot 일관(같은 가구/지지면), hand-float 감소, 마네킹 leakage 0(가이드에만), 환경 발명 0, multi-char placement 합리.
- reroll로 variance 확인(단일 draw 과대주장 금지).
- 갤러리 8898 외부 웹서버.

## 10. 제약 준수 증명

- **default-deny 게이트**: flag OFF default + judge default-deny + guide QC 2중 게이트. ✓
- **no-guide baseline**: 모든 실패 경로(judge deny/QC fail/plate 부재/생성 실패) → 시각 가이드 없이 text+ref degrade, white-bg fallback 없음. ✓
- **guide harm 디텍터**: guide QC(생성 후 inspection) — underlay 보존/leakage/redraw gate. ✓
- **shot grouping / camera selection / support-contact 추출**: candidate_groups / framing 기반 master·crop 선택 / FSC+generic clause+underlay. ✓
- **하드코딩·시나리오 의존·글자패턴 의미판정 0**: 그룹핑=구조키(scene/bg_id), 신호=enum/카운트, pose=구조화 FSC/staging 필드. 특정 작품 고유명사·방·소품·문구 0. 프롬프트 generic. ✓
- **기존 모듈 삭제 금지 / 프롬프트 덮어쓰기 금지**: 신규 모듈·신규 프롬프트 버전. ✓

## 11. 후속 wave (v1 비대상)
- `single_shot_complexity`: 단발 복잡샷(그룹 없는 establishing) 가이드.
- contact-heavy 다인물 contact lock(FSC 초과 정보 필요).
- 최종 still harm 자동 검출(v1은 canary 육안).

## 12. 개발 사이클
설계(본 문서) → Codex 의논(합의 완료) → 사용자 검토 → GO 시 구현(결정론 TDD) → canary 육안 → Codex 리뷰 → 커밋(사용자 자율 위임).
