
    -ǚja                   R   d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m	Z	m
Z
 ddlmZ ddlmZmZmZmZmZmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZm Z  ddl!m"Z" ddl#m$Z$ ddl%m&Z& ddl'm(Z( ddl)m*Z* ddl+m,Z, ddl-m.Z.m/Z/ ddl0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z8m9Z: ddl;m<Z<m=Z=m>Z> ddl?m@Z@mAZAmBZCmDZD ddlEmFZF ddlGmHZH ddlImJZJ ddlKmLZLmMZMmNZOmPZQ ddlRmSZS ddlTmUZUmVZW d gZX ej                  eZ      Z[d1d!Z\d1d"Z]d2d#Z^d2d$Z_	 	 	 	 	 	 	 	 	 	 d3d%Z`	 	 	 	 	 	 	 	 d4d&Za	 	 	 	 	 	 	 	 d5d'Zbd6d(Zc	 	 	 	 	 	 	 	 d7d)Zd	 	 	 	 d8d*Ze	 	 	 	 	 	 d9d+Zf	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d:d,Zgddddd-d.	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d;d/Zh G d0 d       Ziy)<u  Scene generation coordinator — scene_image_service의 bound method 집합을 분리.

W5 F22 Phase B.25 (2026-04-23): SceneImageService의 9개 bound method(~720 LOC)
+ `_get_style_context` utility를 SceneGenerationCoordinator로 이관. facade는
self._coord를 주입받아 해당 메서드들에 위임. 모든 메서드는 literal lift (동작 보존).

## 내부 helper 메서드
- `_init_scene_gen_clients` — 4-client 초기화 (gemini/openai/sanitizer/tracker)
- `_generate_scene_in_loop` — 씬 단위 N variation 생성 (thread pool)
- `_run_fal_angle_pipeline` — fal.ai 앵글 N+1 이미지 생성
- `_finalize_and_track_primary` — primary 마킹 + 4-map tracking + checkpoint
- `_finalize_scene_with_variations` — A/B variation 완료 후 recommended + commit + log
- `_maybe_generate_variation_slot` — A/B variation 슬롯 기반 i2i
- `_build_single_scene_prompt_and_refs` — non-custom_prompt 경로 prompt+refs
- `_finalize_single_scene` — single scene 생성 후처리 (lineage + save + log + dict)
- `_generate_variation_in_loop` — variation 1개 생성 (retry + sanitize)
- `_get_style_context` — 프로젝트 세계관 → T2I prefix string

모든 메서드는 facade와 동일한 (db, project_id, actor_id, logger, 5 svc)를 주입받음.
    )annotationsN)ThreadPoolExecutoras_completed)datetimetimezonePath)AnyCallableDictListLiteralOptionalTuple)Session)settings)AppError)to_relative_image_path)FRAMING_CLOSEget_framing_scale_or_raise)bind_current_budgetRefContractError)ActivityLogger)ProjectSettings)GenerationTracker)ImageValidator)GeminiImageClientModerationError)OpenAIClient)PromptSanitizer)build_semantic_contract)apply_fal_angleselect_and_recommend_angle)build_lineage_fieldsimage_to_dictload_project_llm_config)LabeledRefPayloadRefRoleErrorbuild_final_scene_promptmake_labeled_ref_payload)load_shot_t2i_variations)ScenePersistenceService)SceneProvenanceService)SceneReferenceService apply_back_to_camera_constraintsbuild_image_indexrewrite_t2i_with_image_refs)SceneValidationService)SceneVariationServiceset_variant_primarySceneGenerationCoordinatorc                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     _/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/scene_generation_coordinator.py_nowr?   Q   s    <<%//11r=   c                 <    t        t        j                               S r8   )struuiduuid4r<   r=   r>   _new_idrD   U   s    tzz|r=   c                   t        t        | dd      xs g       }t        t        | dd      xs g       }t        t        | dd      xs g       }g }g }g }t        |      D ]  \  }}t        |t              s|j                  d      }	|j                  d      xs |t        |      k  r||   nd}
|t        |      k  r||   r||   d   nd}|	r+|	|vr|j                  |	       |j                  |	|
|d	       |
|d
}dD ](  }|j                  |      |j                  |      ||<   * |j                  |        |||dS )uN  P0 (2026-07-01): final scene payload 에서 **실제 Gemini 에 첨부된** image
    asset UUID + 상세를 수집(라벨 파싱 금지, 구조 metadata SOT).

    ref_role_metadata[i] 의 ``asset_id`` 만 실입력 lineage(UUID SOT) 로 본다.
    ★ ``bg_id`` 는 ImageAsset UUID 가 아니라 구조키(``L04B01`` variant_type) 이므로
    UUID fallback 으로 쓰지 않는다(Codex BLOCKING) — bg 는 attach 시점에 chain_bg
    ImageAsset UUID 를 resolve 해 ``asset_id`` 로 stamp 한 경우만 lineage 에 포함.
    labeled_refs[i][0] 은 표시/감사용 라벨(edge SOT 아님). asset_id 없는
    ref(prev_shot / bg UUID 미해결 등)는 unresolved 로 분리(input_image_ids 미포함).

    Returns dict: {image_ids: [uuid...], refs: [{asset_id, role, label}...],
                   unresolved: [{role, label}...]}
    labeled_refsNref_role_metadata	ref_rolesasset_idpipeline_role r   )rI   rolelabel)rL   rM   )rJ   group_idbg_key
guide_hashvisible_focussubject_statesource_still_idanchor_source)	image_idsrefs
unresolved)listgetattr	enumerate
isinstancedictgetlenappend)payloadlabeledmetasrolesrU   rV   rW   imetaaidrL   rM   _u_ks                 r>   collect_actual_attached_refsri   Y   sQ    77ND9?R@G"5t<BCE+t4:;EI!#D')JU#4$%hhz"xx(PSZU1XR!"S\!1gaj
1b)#  %KKS$GH +/!?B( 88B<+!XXb\BrF	(
 b!+ $, #D
KKr=   c                6    t        |       }|d   |d   |d   dS )u>   collect_actual_attached_refs → var_result 키로 변환(P0).rU   rV   rW   )actual_attached_image_idsactual_attached_refsunresolved_attached_refs)ri   )r`   _cs     r>   _attached_lineage_fieldsro      s,    	%g	.B%'_ "6
$&|$4 r=   c                   ddl m} t        t        j                        | z  dz  dz  |z  dz  dz  }d}|j                         s |d| d	| d
|z         	 t        j                  |j                  d            }t        |t              s( |d| d	| dt        |      j                   d|z         |j                  d      }	|	8t        |	t              s( |d| d	| dt        |	      j                   d|z         |	xs i j                  d      }
|
g }
t        |
t               s( |d| d	| dt        |
      j                   d|z         t#        |
      D ]  \  }}t        |t              s+ |d| d	| d| dt        |      j                   d	|z         |j                  d      |k7  rV|j                  d      |k7  rk|j                  d      }t        |t              s |d| d	| d|z         |c S   |d| d	| d|z         # t        t        j                  f$ r} |d| d	| d| |z         d}~ww xY w)u:  scene_detail/manifest.json 에서 (scene_index, _shot_index) 매칭 RPC 반환.

    Manifest shape: data.scenes[*] (실제로 shot 단위 list).
    매칭 키: scene_index + _shot_index (legacy None == None 허용).

    실패 시 RefContractError (HTTP 422) — 운영자-readable msg. silent skip 금지.
    r   r   checkpointsepisodesscene_detailmanifest.jsonuH    — rerun scene_detail step or use an explicit custom_prompt to bypass.z2scene_detail render_prompt_card missing for scene=z shot=z (manifest file absent)utf-8encodingz6scene_detail render_prompt_card load failed for scene=: Nz4scene_detail render_prompt_card malformed for scene=z" (manifest root must be dict, got )dataz$ (manifest 'data' must be dict, got scenesz+ (manifest 'data.scenes' must be list, got z (manifest 'data.scenes[z]' must be dict, got scene_index_shot_indexrender_prompt_cardzC (shot row found but render_prompt_card field absent or not a dict)z# (no matching shot row in manifest))app.core.ref_contract_validatorr   r	   r   projects_direxistsjsonloads	read_textOSErrorJSONDecodeErrorr[   r\   type__name__r]   rX   rZ   )
project_id
episode_idr|   
shot_indexr   cp_path
msg_suffixcpexcrz   r{   idxscrpcs                 r>   lookup_render_prompt_cardr      s.    A 	X""#j0
	$	%'1	2
	*	+  	S  >> M
|3JLNXY
 	


ZZ))7);< b$ M
| 4b**+1.0:;
 	

 66&>D
4 6 M
| 4d,,-Q02<=
 	

 jbh'F~fd# M
| 4!!%f!6!6 7q:<FG
 	
 V$R"d#"$VJ< 8  #u$98$$%Q( +55  66- K/66- J.ff)*#t$"$VJ< 8ABDNO 
 
' %* VJ</R	T
	 i T))* 
 M
|2cU<>HI
 	

s   %H I)H==Ic                   | s| S |j                  dd       d|j                  dd       }|xs i j                  |      xs i }t        |j                  d            }|j                  d      s|s| S | j                  d      }t        |t              s| S |j                  d      }dd	l}|j                  |       }	|	d   }
g }t        |t              rg }|
j                  d      xs g D ]a  }t        |t              r>|j                  d
      dk(  r*|j                  t        |j                  d                   Q|j                  |       c ||
d<   |}nt        |t              r|
j                  di       j                  d      xs g D cg c]  }t        |       }}|
j                  d      xs i j                         D ci c]  \  }}|dk7  r|| c}}|
d<   |
d   j                         D cg c]  }|xs g D ]  }|  }}}n| S |s| S |s|
j                  d      dk(  rd	|
d<   |j                  d      r|j                  d      nd}t        j                  d|||       |	S c c}w c c}}w c c}}w )uB  space_set_bg no-plate policy ↔ render_prompt_card required background 의
    runtime reconciliation — validator 약화가 아니다 (Codex 합의 문구 그대로).

    W1-A (2026-06-11 fresh full E2E S10 실측): space_set_bg 가 해당 shot 을
    unassigned/connector_no_plate 로 진단하면 loader 가 legacy chain bg 를
    sentinel(`suppress_background_required`) 로 대체한다. 그 shot 의
    render_prompt_card 가 여전히 legacy bg(L##B##) exact attach 를 요구하면
    validator 가 차단하므로, 검증용 RPC '사본'에서 background required 만
    제거한다 — "이 shot 은 background plate 를 쓰지 않는 것이 더 안전" 이라는
    space policy 결론의 반영. persisted RPC 는 mutate 하지 않는다.
    silent waiver 금지 — 제거 내역을 warning 으로 기록.
    r|   r   _r   _dep_continuity_bg_waiversuppress_background_requiredasset_requirementsrequired_refsNkind
backgroundidreadiness_policyblock_if_missingreasondep_continuity_priorityu   space_set_bg_no_plate background required waived (shot=%s reason=%s removed=%s) — runtime reconciliation, persisted RPC unchanged)r]   boolr[   r\   copydeepcopyrX   r_   rA   itemsvaluesloggerwarning)r   background_chain_bg_map
still_datakeyentry_dep_waiver	asset_reqrr_copyrpc2ar2removedkepte	remainingxkvvs_reasons                       r>   %waive_required_background_if_no_plater      s   " 
^^M1-.a
|Q0O/P
QC$*//4:E
 z~~&ABCK9934[
,-Ii&
		'B>>#D
#
$CG"d)/R/A!T"quuV}'Ds155;/0A	 0
  $O		B	$'GGOR$@$D$D\$R$XVX$XZ$Xa3q6$XZ!ggo6<"CCE 
ETQL  qDE 
O "%_!5!<!<!>S!>2r1QQ!>	S

 !348JJ"& 9934 			(& 
 NN	JWg
 K1 [ 
 Ts   I.I37I9c                   ||y| xs g D ]  }t        |t              r4|j                  d      |j                  d      |j                  d      }}}n't        |dd      }t        |dd      }t        |dd      }||k(  st||k(  sz|s}|c S  y)uv  dep_detail(shot_dependency_t2i)이 명시한 (scene,shot) 타깃의 still id resolve.

    W3 (2026-06-11 fresh full E2E S29 실측): bytes 공급원(scene_still.
    dependent_scene_id = shot_dependency 산출)과 지시문 공급원(shot_dependency_t2i)
    이 서로 다른 샷을 가리킬 수 있다 — S29 sh11 은 dependent_scene_id=sh4(사진
    인서트), dep_detail=sh7(zoom) 로 'SAME FRAME' 지시가 sh4 프레임에 적용돼
    프레임 복제가 발생했다. 지시문의 타깃 still 을 bytes 로 써야 정합.
    still 항목은 dict(서비스 계약) 또는 ORM 양쪽 지원. 미발견 시 None.
    Nr|   r   r   )r[   r\   r]   rY   )stillst2i_scene_indext2i_shot_indexssishsids          r>   resolve_dep_target_still_idr   C  s     ."8\r\a%%.l0CQUU4[CBM40BL$/B!T4(C R>%9cJ  r=   c                    | j                         D ci c]-  }|j                  d      r|j                  d      r	|d   |d   / c}fdS c c}w )u'  T1 entry 확장 (`bg_id`, `location_id`) 활용 — 추가 IO 없이 bg_id→loc lambda 반환.

    Entry 가 T1 확장 전 (bg_id 또는 location_id 부재) 이면 lookup 에서 자연스럽게
    제외 — 사용자 binding G3 와 정합 (lineage 확인 불가 시 prev_shot 통과 X).
    bg_idlocation_idc                &    j                  |       S r8   r]   )r   	bg_to_locs    r>   <lambda>z'build_chain_bg_lookup.<locals>.<lambda>h  s    u-r=   )r   r]   )r   r   r   s     @r>   build_chain_bg_lookupr   ]  s]     -3355E99W%))M": 	gm,,5I
 .-s   2Ac                F   d| vrt        |       S |D ch c]  \  }}|	 }}}d|v sd|v rt        |       S |xs i j                  d      xs i j                  d      xs g }t        d |D              }|rt        |       S | D cg c]
  }|dk7  s	| c}S c c}}w c c}w )uS  FINDING C W2 (Category A) — background over-declaration consumer normalization.

    scene_detail LLM 이 [L##: ...] free-form location 묘사 블록을 보고
    per-variation sidecar `reference_phrase_kinds` 에 'background' 를
    over-declare 할 수 있다. background ref attach 여부는 coordinator runtime
    SOT (chain_bg map lookup + build_prev_shot_background_ref) 이므로 producer
    는 예측 불가 — attached_meta 가 확정된 consumer boundary 에서 normalize.

    'background' strip 3-조건 (모두 만족 시):
      1. reference_phrase_kinds 에 'background' 있음.
      2. attached_meta 에 ('background', *) / ('background_prev_shot', *) 없음.
      3. rpc.asset_requirements.required_refs 에 kind='background' 없음.
    조건 3 = genuine missing REQUIRED background 는 mask 안 함 — required
    background 이 있는데 미attach 면 validator step 4 가 step 6 전에 fail-fast.

    validator step 6 (sidecar phantom guard) 자체는 무변경 — over-declaration
    만 producer/runtime 불일치로 보고 consumer 가 정합화한다. 약화 0.
    r   background_prev_shotr   r   c              3  h   K   | ]*  }t        |t              xr |j                  d       dk(   , yw)r   r   N)r[   r\   r]   ).0rs     r>   	<genexpr>z4_normalize_background_phrase_kind.<locals>.<genexpr>  s4      A 	1d=f ==s   02)rX   r]   any)reference_phrase_kindsattached_metar   r   r   _attached_kinds_required_refs_has_bg_requireds           r>   !_normalize_background_phrase_kindr   p  s    . 11*++%23]TQq]O3&*@O*S*++	,-3	c/!      *++-C-!l1BA-CC 4 Ds   B
BBc                   i | xs g D ]c  }	 |j                  d      |j                  d      }}t        |t              s7t        |t              sH|j                  d      sZ|d   ||f<   e dfd}|S # t        $ r Y zw xY w)u  W21B-W8 option C — composition continuity_anchor 의 anchor_source (si,shi) →
    그 still 의 **현재-run primary bytes** 해석 클로저.

    `scene_paths_by_index_by_id` 는 메인 스레드가 batch barrier 에서 갱신하는 still_id
    → primary path 맵 (dep edge 가 source 선생성 보장). source still 의 현재-run
    프레임이 없으면 None 반환 → consumer 가 stale fallback 없이 no-op (option C 계약).
    list/path read 만 — db 접근 0 (worker thread 안전).r|   r   r   c                    j                  | d   | d   f      }|sy j                  |      }||j                         sy 	 |j                         S # t        $ r Y y w xY w)Nr      )r]   r   
read_bytes	Exception)src_keyr   path_key_to_sidscene_paths_by_index_by_ids      r>   _resolvez1_make_osl_source_bytes_resolver.<locals>._resolve  sh    oowqz71:67)--c2<t{{}	??$$ 		s   A 	AA)r   zTuple[int, int]returnzOptional[bytes])r]   AttributeErrorr[   int)r   r   _s_si_shir   r   s    `    @r>   _make_osl_source_bytes_resolverr     s     /1Kll	}-rvvl/CC c3JtS$9bffTl')$xKd$ 
 O#  		s   "A::	BBc                <    | r| j                  d      r| d   g|S |S )u  W22 직행 — resolver 입력 = [9절] + scene_detail 원 프롬프트 union.

    W4a NARROW_1 은 [9절] 단독이었으나 fresh E2E 에서 직행 샷 전건
    REF_CONTRACT_VIOLATION 실측(2026-07-10, 19 shots): resolver 의 캐릭터
    매칭은 복합 short_id 정규식 (C\d{2,3})(O\d{2,3}) 로 프롬프트를 스캔하는데
    9절은 설계상 ID-free 라 캐릭터/아웃룩 ref 가 구조적으로 0건 첨부됐다.
    ref 계약(reference_phrase_kinds)의 원천이 scene_detail 이므로 캐릭터/prop
    해석도 ID 를 보유한 scene_detail 프롬프트가 담당하고, 9절은 생성
    프롬프트(var_t2i)로만 쓴다 — s33 계약(ref=[실사+맵]+passport)과 정합.
    promptr   )odfallback_promptss     r>   _direct_resolver_promptsr     s+     
bffX80/00r=   c               N   ddl m}m}	 d|d    }
| j                  d|	|d   f       |j                  dd|
f       |j                  dd       d|d   d	}|j	                  d
      r|d
   |d<   |j                  d|       | j                  d||d   f       |j                  dd|
f       |j                  dd       d|d   d	}|j	                  d      r|d   |d<   |j	                  d      r|d   |d<   |j                  d|       t
        j                  d||||d          y)u  W22 직행 — 캐논 2장(index0=실사 마스터[룩 SOT], index1=맵[배치 전용]) 주입.

    단건/배치 공용 helper (Codex W4a MINOR — drift 방지). 4-list 평행 insert
    (length-parity invariant). attached kind="background"
    (value `outdoor_canon:<place>`) — declared/required background 충족
    (ref_contract_validator waiver 참조). asset_id metadata stamp → 최종 scene
    asset input_image_ids lineage.
    r   )LOCATION_PHOTO_LABELSITE_PLAN_LABELzoutdoor_canon:place_id	map_bytesr   outdoor_canon_map_refoutdoor_canon_map)rJ   r   map_asset_idrI   master_bytesoutdoor_canon_photo_refoutdoor_canon_photoprompt_versiondirect_prompt_versionmaster_asset_idzCScene %s Shot %s: outdoor direct canon refs injected (%s, place=%s)N)+app.modules.pipeline.outdoor_direct_composer   r   insertr]   r   info)rF   r   rH   rG   r   r|   r   wherer   r   	_bg_value	_map_meta_photo_metas                r>   _insert_outdoor_canon_refsr    sP   &
 !J 01IOR_=>\956Q/0,"Z.!I 
vvn "> 2	*Q	*0"^2DEF\956Q12.BzN#K 
vv/12B/C+,	vv "$%6"7JQ,
KKMZ:r=   F)r   var_t2i_overrideindoor_pose_ctxoutdoor_direct_ctxreturn_payloadc                V  tuv t        t        j                  ||| j                  | j                  | j
                  | j                        }|j                  d      }|(t        ||| j                  | j                        }||d<   ddl	m
}  |||       |r|d   j                  d|d         n|d   }||}|xs i j                  | j                   d	| j                         }|r|j                  d
      r||d
   }|rg|d   }d|vrt        d      |d   }|t        d      t        |t              s%t        dt        |      j                   d|d      t        |      }ng }t        j                   }|xs g D cg c]  }|j                  d      s| c}| d }|r |D cg c]  }|j                  dd       } }n|j                  dd      }!|!r|!gng } t#        ||       } |j                  d      }"|"t%        | dd      xs d}"|"|d<   |j                  d      }#|#t%        | dd      xs d}#|#|d<   d|vrt%        | dd      |d<   i }$|j'                  |||$      }%|j)                  |||%||j+                  |            }&|&rOt,        j/                  d|j                  dd      |j                  dd      t        |&j1                                      |j                  d      xs i j                  d      xs i j                  d      xs g }'ddlm}(  |(||      })|)xs i j                  |j                  d      |j                  d      f      xs i }*t7        |*j                  d      xs g       }+|j9                  | ||%||&|'|+r|nd|+xs d|$	      },t        |,j:                        }-t        |,j<                        }.t        |,j>                        }/t        |,j@                        }0tC        |.|/|0|||       |D 1cg c]  }1|1j                  d      dk(  s|1d     }2}1dd!lm"}3 dd"l#m$}4m%}5m&}6  |3||      }7|7r8 |5|7j                  d#      xs i |7j                  d$      xs
 t7               |      ni }8d}9d}:d%};|j                  d      }<|j                  dd       d	|j                  dd       }=|
xs i j                  |=      xs i }>tO        ||>j                  d&      |>j                  d'            }?|?rW|?|v rS||?   jQ                         r@|<r<|<|?k7  r7t,        j/                  d(|j                  d      |j                  d      |<|?       |?}<|<r+|<|v r'||<   }@|@jQ                         r@jS                         }:d)};|j                  dd       d	|j                  dd       }A|
xs i j                  Ai       xs i j                  d*d      }B|:sBd+k7  r|2D ]  }C|C|v s|C   \  }:}9d,}; n |:s|8r |6|j                  d      |8|-      }D|Drr|D   \  }:}9d,};|D|2vr|2jU                  D       t,        j/                  d.|j                  d      |j                  d             n|:sBd+k(  rt,        jW                  d/A       tY        |d01      }E|EtZ        k(  }F|
xs i j                  |" d	|# i       xs i j                  d*d      }G|;d)k(  xr Gd2v }H|" d	|# }I|	j                  |I      }J|sJrJj                  d3      rFsHsJj                  d4      }K|Kst]        d5      |-j_                  dJd6   |Jd3   f       |.j_                  dd7Kf       |/j_                  dd8       |Kd9d:}L|ja                  ||K      }M|MrMLd;<   |0j_                  dL       t,        j/                  d<|j                  dd      |j                  dd      K       n JrRJj                  d3      rAHr?Fs=t,        j/                  d=|j                  dd      |j                  dd             d>|d?<   nMJrKJj                  d3      r:Fr8t,        j/                  d@|j                  dd      |j                  dd      E       	 |jc                  |:|;|||2|<|xs g ||
||&|A      }N|;d)k(  r|<n|;d,k(  r|9xs i j                  d       nd}P|)xs i j                  |j                  d      |j                  d      f      xs i j                  dC      dDk(  }QNrvN\  }R}S}T}U}V|7rPrQsi VdEti        P      dF}V|ri VdGd>i}V|-j_                  dRSf       |.j_                  ddHTf       |/j_                  dU       |0j_                  dV       |7rTQsQ|2D Wch c]:  }Wt        |j                  |W      xs i j                  dI      x}Xth              rXrX< }Y}W|j                  dd       d	|j                  dd       }Z|j                  d      |j                  d      cut |4ut|7j                  d$      xs
 t7               |7j                  d#      xs i YdJGtk        |<xs |
xs i j                  Z            tm        tuvfdK|xs g D              tk        N      |;Prti        P      ndFL      }[|[r/[|dM<   |[dN   s%t,        jW                  dOut[dP   dQ   |[dP   dR          |r3to        |-|.|/|0||j                  dd      |j                  dd      dST       tq        |-||/|0|.U      \  }-}\}]}/}0}.ddVl9m:}^m;}_  |_||      }`|`r, ^|-|/|0|j                  d      |j                  d      `W      }-|)r?ddXlm<}a  |a|-|/|0|.|j                  d      |j                  d      |)t{        ||      EY	       ddZl9m>}bm?}c dd[l@mA}d  |c||      }e|erdd\lBmC}f  |f||d]|j                  d       |j                  d      |j                  d      ^      5   b|-|/|0|.|j                  d      |j                  d      e|	t        t        j                        |z  d_z  |z  d`z  dz  a	       ddd       ddblEmF}g  |g|-|/|0|.|j                  d      |j                  d      |Ec       erdddl9mG}h i }i|xs g D ]j  }j|jj                  d      |jj                  d      }l}k|jj                  d       }mt        |kt              sGt        lt              sXms[ti        m      iklf<   l i }n h|-|/|0|.|j                  d      |j                  d      et{        ||      i|ne
       |nrn|dM<   t        |\]      }ddflJmK}o dpdg}p |o|-|.|/|0||pdh| j                   di| j                   j      }q|qr,t,        j/                  dk| j                  | j                  q       t        |-|/|0|.l      },ddmlJmM}r  |r||.       	 t        ||,|||n      }s|rs|-|.||,fS s|-|.|fS c c}w c c}w c c}1w # t        $ r  td        $ r  tf        $ rE}Ot,        jW                  dB|j                  dd      |j                  dd      O       d}NY d}O~Od}O~Oww xY wc c}Ww # 1 sw Y   !xY w# tf        $ r"}Ot,        jW                  doO       |}sY d}O~Od}O~Oww xY w)quL  spec §4.1 — 배치 generate_images 의 ref 빌드 블록(coordinator.py:178-326)
    동등 보존. chain_bg / prev_shot_ref / state_variant 통합. 5a~5d 분기.

    v2 (audit IMPORTANT 1):
    - state_variant_sids 는 helper 내부 detect / best_prev_bytes + bytes_source_kind
      는 coordinator 가 resolve (provenance annotator, spec
      docs/superpowers/specs/2026-05-15-zoom-in-detail-source-provenance-design.md §3).
    - cached_style_context + cached_entity_text_map 은 caller 가 episode 단위
      build 후 전달 (배치 N stills 시 N×rebuild 회귀 방지).

    D5 §4.3 (2026-05-09): return 을 (full_prompt, labeled_refs, attached_meta)
    3-tuple. attached_meta = list[(kind, id)] — chain_bg / prev_shot / character /
    character_outlook / character_state / prop 모두 source-of-truth 에서 record.
    invariant 1: len(labeled_refs) == len(attached_meta).

    Area #5 W3 §4.3 (2026-05-18): return 을 4-tuple 로 확장.
    `reference_phrase_kinds` 는 scene_detail v26 producer (W1) 가 each
    t2i_variations entry 에 emit 한 sidecar enum list — phantom guard 가 sidecar
    exact compare 로 step 6 검사 (ref_contract_validator.py:295-334). single
    path 는 helper 의 t2i_variations[0] sidecar 를 그대로 unpack 받아
    validate_attached_refs 에 전달. variation path 는 별도 (caller 가 var dict
    에서 direct read 후 _generate_variation_in_loop 에 전달).

    Returns (default, return_payload=False): 4-tuple
        (full_prompt, labeled_refs, attached_meta, reference_phrase_kinds).
    P0 (2026-07-01, Codex NARROW_1): return_payload=True 면 5-tuple
        (..., _payload[LabeledRefPayload, ref_role_metadata 에 asset_id stamp 포함]).
        단건 경로(_build_single_scene_prompt_and_refs)만 True — legacy caller/test 는
        기본 4-tuple 유지(byte-identical).
    )camera_jsonr|   still_indexr   r~   Nr   r   r|   r   r   )_strip_overdeclared_character_phrase_kind
t2i_promptstill_frame_promptr   r   r   u   scene_detail t2i_variations[0] missing 'reference_phrase_kinds' sidecar — producer (scene_detail v26 W1) emit required. stale v25 checkpoint? Run alembic upgrade + scene_detail re-run to bump producer. No Silent Fallback (Area #5 W3 §4.3)u   scene_detail t2i_variations[0].reference_phrase_kinds is None — producer emit malformed. No Silent Fallback (Area #5 W3 §4.3)zSscene_detail t2i_variations[0].reference_phrase_kinds malformed: must be list, got =u'   . No Silent Fallback (Area #5 W3 §4.3)rK   r|   r   dependent_scene_id)out_asset_id_map)identity_family_by_sid"Scene %d Shot %d: state_variant=%sr   r   load_composition_guide_contextforced_character_names	t2i_promptsvisible_entitiesscene_ref_image_mapentity_lookupstate_variant_sidsr   stagingforce_character_namesscene_ref_asset_id_mapr   r  r  entity_typelocationr   load_outdoor_prev_frame_contextbuild_outdoor_prev_frame_diagprimary_location_uuid_by_scene%resolve_outdoor_history_fallback_uuidprimary_location_by_sceneoutdoor_loc_sidsnonedep_scene_indexdep_shot_index^   Scene %s Shot %s: dep bytes target realigned to dep_detail (%s → %s) — W3 source coherence	dep_scene	ref_usagezoom_in_detaillocation_historyprimary_loc_uuid_by_scenelocation_scene_historyu|   outdoor_prev_frame: S%ssh%s history fallback — primary_location UUID 로 prev 프레임 확보 (VE location 부재 보정)   scene_generation_coordinator: %s zoom_in_detail dep_scene bytes missing — location_history fallback skipped (W20F10 O policy)z6scene_generation_coordinator.build_scene_attached_refsr  r4  exact_backgroundimage_bytesr   uN   chain_bg missing bg_id (single path) — No Silent Fallback gate (Area #11 v1)rM   r   background_chain_refbackground_renderr   rJ   rI   z:Scene %d Shot %d: background_chain ref injected (bg_id=%s)K   Scene %d Shot %d: bg map ref SKIPPED — dep_scene continuity priority (W2)Tr   uK   Scene %d Shot %d: chain_bg ref SKIPPED — close framing (framing_scale=%r)best_prev_bytesbytes_source_kindr   r  current_location_idsdep_scene_idr   r8  dep_detail_mapr   r  r  uJ   Scene %d Shot %d: prev_shot_ref build failed (%s) — entity-only fallbackmodecontinuity_anchorscene_prev_framerJ   rS   outdoor_direct_place_continuityr   short_idFc              3     K   | ]T  }|j                  d       k(  xr: t        |j                  d      xt              xr t        t              xr k   V ywr|   r   Nr]   r[   r   r   s__b_shi_b_si_p_shis     r>   r   z,build_scene_attached_refs.<locals>.<genexpr>  sl      )  , FF=)U2 D"bff\.B$BFSID"63/D4:VOD  ,   AAr|   r   r-  r,  ve_location_sidschain_bg_attachedr3  has_dephas_same_scene_priorresolvedrD  rS   close_framing_prev_frame_chain_diagr\  u   outdoor_prev_frame: S%ssh%s previous_frame_required_missing — outdoor+plate無+연속성 신호 샷인데 prev 프레임 미해결 (dep=%s, same_scene_prior=%s)required_signalsdepsame_scene_priorsingler|   r   r  rH   rG   r   $apply_keep_real_scale_to_prop_labels load_printed_prop_anchor_contextr|   r   
anchor_ctxattach_composition_guide_refr|   r   	guide_ctxsource_bytes_resolverframing attach_registered_pose_guide_ref'load_immobilized_subject_anchor_contextGUIDE_SUBDIRgeneration_contextregistered_pose_guidestagestill_idr|   r   rr   imagesr|   r   ri  r   	cache_dirattach_indoor_pose_guide_refr|   r   r  ro  !attach_immobilized_prev_frame_refr|   r   ri  rn  source_still_id_by_keydiag_out)attach_from_rpcc                @   ddl m}m} t        | xs i j	                  d      xs d      }||k(  r\ddlm}  |t        | j	                  d      xs d            }|j                         sdi fS |j                         |t        |      dfS ||k(  rt        d	      di fS )
u  좌표로 사진을 읽는다. ★**갈래를 갈라** 읽고 **읽은 자리**를 돌려준다.

        ★★★`asset_id or path` 로 모호하게 두면 **읽지도 않은 자산 UUID 가
        계보로 남는다** (Codex 2026-08-31). 그리고 `ref_image_map` 의 열쇠는
        **참조 map key** 지 ImageAsset UUID 가 **아니다** — 그것을 asset_id
        라고 부르면 안 된다.

        Returns:
            `(bytes, resolved)`. `resolved` 는 **실제로 읽은 자리**다.
        r   )SOURCE_ASSETSOURCE_FILEsourcerK   r   r   r=   )r  r   u   `source=asset` loader 가 아직 안 이어졌다 — D cutover 에서 project-scoped ImageAsset loader 를 잇는다. 그때까지 sidecar 는 `source=file` 좌표만 쓴다)
/app.modules.pipeline.grounding_reference_bundler  r  rA   r]   pathlibr	   r   r   NotImplementedError)coordr  r  src_Pps         r>   _grounding_load_bytesz8build_scene_attached_refs.<locals>._grounding_load_bytes  s    	
 5;B##H-34+*3uyy(.B/0A88:Bw<<>k3q6#JJJ, &=> > Bwr=   scene_shot)
load_bytesr  z5Scene %s Shot %s: grounding bundle refs attached (%d)rF   rH   rG   r   )assert_from_rpc)project_configentity_text_mapzPrompt build failed: %s)r  Dict[str, Any])Or,   r   r   r  r|   r  r   r]   r   app.core.steps.detail_stepsr  r   r[   rX   r   r   scene_variation_countr   rY   build_scene_ref_image_mapdetect_state_variant_sidsload_identity_family_by_sidr   r   keys'app.core.steps.outdoor_site_layout_stepr  setresolve_refs_for_prompt_setrF   r   rH   rG   r0   r'  -app.modules.pipeline.outdoor_site_layout_planr)  r*  r+  r   r   r   r_   r   r   r   r)   r   resolve_chain_bg_asset_idbuild_prev_shot_background_ref	TypeErrorr   rA   r   r   r  _build_image_index_helper,app.core.steps.visual_continuity_anchor_steprf  rg  rk  r   rq  rr  *app.services.registered_pose_guide_servicert  "app.services.image_capture.contextrv  r	   /app.core.steps.indoor_shared_pose_guide_contextr  r  r   _rewrite_t2i_helperr  r  r+   r  _build_final_scene_prompt)wstillr   r   r  ref_image_mapcached_style_contextcached_entity_text_mapr   r8  r   rG  r   r  r   r  reference_svcr   r  r  r  r	  t2i_variations_rpc_f11r  var_t2i_od
_first_var_phrase_kinds_rawr   _max_t2ir   _target_raw_ref_prompts	_fallback
_scene_idx	_shot_idxr"  r  r  _patch_a_required_refsr  _cg_ctx	_cg_entry_force_char_names_payloadrF   r   rH   rG   r   rE  r'  r)  r*  r+  _opf_ctx_opf_uuid_by_scene_opf_hist_stillrC  rD  rF  _w3_dep_key_w3_dep_info_w3_target_iddep_path_dep_key_for_policy_ref_usage_for_policyloc_id_opf_fb_framing_scale_is_close_framing_w2_ref_usage_has_dep_continuity_bc_key_bc_bg_bg_id_bg_meta_bg_asset_uuid_prev_shot_refr   
_b_src_sid_b_cg_anchor_label_bytes_loc_id_role	_metadata_lid_sid_b_ve_loc_sids
_b_dep_key_b_diag_sid_to_img	_sid_inforf  rg  _vca_ctxrk  rq  rr  rt  _imm_ctxrv  r  r  _ipf_key_to_sid_ipf_s_ipf_si_ipf_shi_ipf_id	_ipf_diag_grounding_attachr  	_gb_added_grounding_assert_full_promptrS  rT  rU  sw                                                                                                                       @@@r>   build_scene_attached_refsr    s   p .z:%%%%%%##N ~~23H,!j))e6F6F
 ,4
'( .nhG  	qlJ7K,LM)*>?  #" #
(
(
Qu//01C swwx %5%=h- #A&
#:5"J  ''?@$"Q  +T2"0)*334A6G5J K89  (,,='> "$
 --H-33L3l8K13LhYZXK9DEAlB/E NN#7<	&/	{R ,C>L
 .JUM15:
$.
=!|,IE<38q	#,
< :-+25:NPT+U
'( .0'AA}/ B  '@@-)<g,HHT A  0NN=!,NN<+#((*+		
 ..-
.
4"	9	9:N	O	USU	_		$!#  -ZDGB##		&
|(DEG MJL IMM*BCIrJ88 )/#-,
 -$/745 9 H --.L//0MX''(IX778 %y"3*:# *)AQUU=-AZ-O$)  
 
 /z:FH
 
	 8017R'(1CE 	 
 15OOJP>>"67L >>-+
,Ajnn\1.M-NO  #(b--k:@bL/  !23\5E5EFV5WM *DD*=9@@BLM9KK6}-z~~l/Km	 %(BB-l;??&113O + >>-+
,Ajnn\1.M-NO  
	2""#6;Ar	c+r  48HH*F//3I&3Q0$6!	 + #5;}-*<'=G
 3I'3R0$6!"66(//8ZNN=1:>>,3OQ !6:J!JN	
 0FN '-7 
	2""j\9+#>CIr	c+r  	[( 	DCC  Ai[)G$((1F
 6::m,!#
 G$`  	Aw1FGHQv 6723 $6IJ&@@VT#1HZ   H-HNN=!,NN<+		
 fjj/4GPaKK }a0|Q/	 7;J23

=16GKK%}a0|Q/	"*II /"3%!1%9)|'=-#5+ J NB .<L&*<< #(b--d3BF 	 m  .
|0LMO CCK#67 	
 8F5FFGUI J|Ay A;M03JA	Fy F>F	FF#34  %;W$EFQ&$$Q	2 L!5!5+//5;@@LLTsT !5   >>-34Ajnn\ST6U5VW  'NN=9:>>,;WME63!f!).@!A!JSULL!<=C!/"''\Sn.B-G-G
-ST%( )  &||	) & n-"33=J4/%G( 7>
34z*NN8 v 23E: 234FGI  	#-4Es"}a8!~~lA6		
 Yrm+#	YUL+y)5F 0
JGH;)%6"}5!~~l3	
 	
 	%)%6"}5!~~l3"A2#4"	
 H6z:NH 	J
*A^^D)"}5!~~l3	
 -i):M&NN=9%>>,7#(?x445
BZO'(*67
& !i!2MNN=1>>,/' 	
 79||F &

= 96::l;SXGjj&G'3'Jx,E'7:7|( 34	 $
 %'	))%6"}5!~~l3"A2#4#2		
 3<J/0!';	BGB "mY0A8(e''(e.>.>-?@I
 Cu//	
 (!+#	H h.0X3)2
 \=:PRZZZ}6LLL MEF@   	 	 	"NN'}a0|Q/ "N	"`^
 
Z  0#6sa   w9w9,w>x%x7!x ?y+;Ay0y= y(#:y##y(0y:=	z(z##z(c                  6   e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdddZ	 	 ddZdddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z	dd
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZdd	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 d dZdd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d!dZy)"r6   u?  Scene 이미지 생성 전용 코디네이터 (W5 F22 Phase B.25).

    SceneImageService의 bound method들을 분리해 facade의 독립성을 확보.
    상태는 facade와 동일한 의존성을 주입받음 (db/project/actor/logger + 5 svc).
    모든 메서드는 facade에서 literal lift (동작 보존).
    c
                    || _         || _        || _        || _        || _        || _        || _        || _        |	| _        y r8   )	_db_project_id	_actor_id_logger_persistence_svc_reference_svc_validation_svc_variation_svc_provenance_svc)
selfdbr   actor_idactivity_loggerpersistence_svcr  validation_svcvariation_svcprovenance_svcs
             r>   __init__z#SceneGenerationCoordinator.__init__  sI     %!& /+-+-r=   Nc           	        | j                   j                  t              j                  t        j                  | j
                  k(        j                         }|rg|j                  r[t        j                  |j                        }d|j                  dd       d|j                  dd       d|j                  dd       dS |rt        t        j                        | j
                  z  d	z  d
z  |z  dz  dz  }|j                         rft        j                  |j                  d            j                  di       }|j                  dd      }|j                  dd      }|s|r	d| d| dS y)uk   프로젝트 세계관을 T2I 프롬프트 앞에 붙일 컨텍스트로 변환 (HEAD 시그니처 보존).z)Photorealistic cinematic still. Setting: erarK   z, regionz	. Avoid: 
must_avoid.rq   rr   visual_world_rulesrt   ru   rv   rz   zPhotorealistic cinematic still.)r  queryr   filterr   r  firststyle_rules_jsonr   r   r]   r	   r   r   r   r   )r   r   pssrr   vwrr
  r  s           r>   _get_style_contextz-SceneGenerationCoordinator._get_style_context  sa   XX^^O,33&&$*:*::<<AEG 	"%%B//0BFF5,-Rx0C/D E&&b12!5 X**+d.>.>> ",-/9:&')89 
 yy{jjw!?@DDVRPggeR(2.&Fse2fXUVWW0r=   c                    t        t        j                        }t               }t	        |      }t        | j                  | j                        }||||fS )u  Scene 생성에 공통으로 필요한 4개 클라이언트 초기화.

        W5 F22 Phase B.23.2 (2026-04-23): generate_images + generate_single_scene_image
        양쪽에서 동일하게 쓰이던 4-client 초기화 블록을 공통화.

        Returns:
          (gemini_client, openai_client, sanitizer, tracker)

        PromptSanitizer(openai_client)는 API 호환성을 위해 인자를 받지만 현재
        내부적으로 사용하지 않음 (prompt_sanitizer.py 확인). 순서만 보존.
        )model)r   r   gemini_image_modelr    r!   r   r  r  )r   gemini_clientopenai_client	sanitizertrackers        r>   _init_scene_gen_clientsz2SceneGenerationCoordinator._init_scene_gen_clients0  sJ     *0K0KL$#M2	#DHHd.>.>?mY??r=   )"scene_primary_asset_id_by_still_idr"  zoom_ctxr  r  c               )   ||   }	 t        j                  |d         }g }|D ]~  }t        |t              rB|j                  d      xs |j                  dd      }|s;||v s@|j                  ||          Ut        |t              sf||v sk|j                  ||           d}d}|D cg c]  }|j                  d      dk(  s|d    }}d	d
lm	}  d	dl
m}!m}"m}#  | | j                  |      }$|$r8 |"|$j                  d      xs i |$j                  d      xs
 t               |      ni }%d}&|j                  d      |j                  d      f}'|xs i j                  d      xs i j                  |'      }(|(r?d	dlm}) d	dlm}*m}+  |)| j                  ||||j                  d      |j                  d            \  },}-t+        |(j                  d      xs d      t-        fd|D        d      }.d}/|.r|j                  |.      nd}0|02t/        |0      j1                         rt/        |0      j3                         }/|.r|xs i j                  |.      nd}1	 |/ |*d d       |+| j                  ||j                  d      |j                  d      |(||,|-|/d|j                  d      i|j                  d      |1      \  }2}3}4|t5                dz  }5|5j6                  j9                  dd       |5j;                  |2       t5               dd|j                  d      |t=        t        |5            |3t>        j@                  dddt        jB                  d |4id!"      d#d$tE               d%}6tF        jI                  d&|j                  d      |j                  d      |(j                  d'             ||6g||fS |j                  d)      }8|j                  dd	       d*|j                  dd	       }9|xs i j                  |9      xs i }:tM        ||:j                  d+      |:j                  d,            };|;rW|;|v rS||;   j1                         r@|8r<|8|;k7  r7tF        jI                  d-|j                  d      |j                  d      |8|;       |;}8|8r+|8|v r'||8   }<|<j1                         r|<j3                         }d.}|j                  dd	       d*|j                  dd	       }=|xs i j                  |=i       xs i j                  d/d      }>|s|>d0k7  r|D ]  }?|?|v s||?   \  }}&d1} n |s|%r |#|j                  d      |%|2      }@|@rr|@   \  }}&d1}|@|vr|j                  @       tF        jI                  d3|j                  d      |j                  d             n|s|>d0k(  rtF        jK                  d4|=       |j                  d5g       }A|As,|j                  d6      xs |j                  d7d      }Bd8d8|Bd9g}A|xs i j                  d:      xs i j                  |'      }C|CrtO        A      D DEcg c]  \  }D}E|DCd;   v ri Ed<Cd;   D   inE }A}D}EtF        jI                  d=|j                  d      |j                  d      Cj                  d>d?      |Cj                  d'             t>        jP                  }FAD cg c]  }|j                  d<      s| c}F d }G|GD cg c]  }|j                  d<d       }H}|j                  dd	       d*|j                  dd	       }I|xs i j                  I      }J|Jxs i j                  d@      xs d}KtS        JH      }H|j                  dd	       d*|j                  dd	       }L|r|j                  L      nd}M| jT                  jW                  ||||M      }N|NrOtF        jI                  dA|j                  dd	      |j                  dd	      tY        Nj[                                      |j                  d      }O|j                  d      xs |j                  dB      }P|j                  dC      }Q|Qt]        | j                  |OPD      }Q|Q|dC<   t_        Q|
|      }Q|Q|dC<   d	dEl0m1}R  |RA|Q       |j                  dC      xs i j                  dF      xs i j                  dG      xs g }Sd	dHlm2}T  |T| j                  |      }U|Uxs i j                  |j                  d      |j                  d      f      xs i }Vt        |Vj                  dI      xs g       }W| jT                  jg                  H|||NS|WrMndWxs d|	J	      }XtY        |Xjh                        }YtY        |Xjj                        }ZtY        |Xjl                        }[tY        |Xjn                        }\tq        |Z|[|\M||K       |j                  dd	       d*|j                  dd	       }]|
j                  |]      }^ts        |MdLM      }_|_tt        k(  }`|xs i j                  ]i       xs i j                  d/d      }a|d.k(  xr adNv }bJs^r^j                  dO      r`sbs^j                  dP      }c|cstw        dQ      Yjy                  d	^dR   |^dO   f       Zjy                  d	dScf       [jy                  d	dT       |cdUdV}d| jT                  j{                  ||c      }e|ereddW<   \jy                  d	d       tF        jI                  dX|j                  dd	      |j                  dd	             nB^rR^j                  dO      rAbr?`s=tF        jI                  dY|j                  dd	      |j                  dd	             d|dZ<   nM^rK^j                  dO      r:`r8tF        jI                  d[|j                  dd	      |j                  dd	      _       	 | jT                  j}                  ||||||8|||MN|\      }f|d.k(  r|8n|d1k(  r|&xs i j                  d      nd}hUxs i j                  |j                  d      |j                  d      f      xs i j                  d^      d_k(  }ifrf\  }j}k}l}m}n|$rhrisi nd`t        h      da}nJri ndbdi}nYjy                  d	jkf       Zjy                  d	dclxs df       [jy                  d	m       \jy                  d	n       tF        jI                  dd|j                  dd	      |j                  dd	      |m       |$rTisQ|D och c]:  }ot        |j                  |o      xs i j                  de      x}pt              rprp< }q}o|j                  dd	       d*|j                  dd	       }r|j                  d      |j                  d      c |!|$j                  d      xs
 t               |$j                  d      xs i qd!at        |8xs |xs i j                  r            t        fdf|xs g D              t        f      |hrt        h      nd`g      }s|sr/s|dh<   |sdi   s%tF        jK                  djsdk   dl   |sdk   dm          Jr3t        YZ[\J|j                  dd	      |j                  dd	      dno       t        Y|[\Zp      \  }Y}t}u}[}\}Zd	dqlmF}vmG}w  |w| j                  |      }x|xr, vY[\|j                  d      |j                  d      xr      }YUr?d	dslmH}y  |yY[\Z|j                  d      |j                  d      Ut        ||      _t	       d	dulmJ}zmK}{ d	dvlLmM}|  |{| j                  |      }}|}rd	dwlNmO}~  |~| j                  |dx|j                  d      |j                  d      |j                  d      y      5   zY[\Z|j                  d      |j                  d      }|
t/        t>        j                        | j                  z  dzz  |z  d{z  |z  |	       ddd       d	d}lQmR}  |Y[\Z|j                  d      |j                  d      |_~       }rd	dlmS} i }|xs g D ]j  }|j                  d      |j                  d      }}|j                  d      }t        |t              sGt        t              sXs[t              f<   l i } Y[\Z|j                  d      |j                  d      }t        ||      |
       |r|dh<   t>        jP                  }g }tO        A      D ]  \  }D}E|Ej                  d|Ej                  dd|D             }|Ej                  d|Ej                  d|            }Krj                  DEKf       fEj                  d<d      }t        |tu      }|Ej                  dd      }|r(j                         j                         vrd d }sj                  DEf         d }tF        jI                  d|t        |      t        |             t        |
      }t        M|j                  dC      |      }|j                  }g }t        t        t        |      xs dd            5 }t        | j                        }i }D ]  \  }D}E}}}d|Evrj                  ddD ddd d       )Ed   }|j                  ddD ddd d       Lt        tX              s4j                  ddD dt              j                   d|dd d       tY              }t        Y[\Z      }Dj                  |||Q`|||||||||      <    | j                        }ddd       rj                         |||fS # t         j                  $ r g }Y w xY wc c}w # |*$ rA}7tF        jK                  d(|j                  d      |j                  d      |7       Y d}7~7ld}7~7ww xY wc c}E}Dw c c}w c c}w # t~        $ r  t        $ r  t        $ rE}gtF        jK                  d]|j                  dd	      |j                  dd	      g       d}fY d}g~gd}g~gww xY wc c}ow # 1 sw Y   xY w# 1 sw Y   xY w)u  Generate N variation images for one still (runs in thread pool).

        W5 F22 Phase B.22.4 (2026-04-22): generate_images nested function
        _generate_one_scene을 bound method로 lift. closure 16개 변수를
        explicit keyword-only param으로 승격.

        Returns (scene_index, list_of_results, visible_entities, current_location_ids).
        Each result dict (from _generate_variation_in_loop) or None.
        visible_entities_jsonr   	entity_idrK   Nr.  r$  r%  r   r&  r(  r,  r-  r|   r   zoom_targets)build_zoom_fill_prop_refs)ZoomContinuityRenderErrorgenerate_continuity_crop_png)r|   r   r  r<   c              3     K   | ]:  }|j                  d       |j                  d      fk(  r|j                  d       < yw)r|   r   r   Nr   )r   rR  _zc_src_keys     r>   r   zESceneGenerationCoordinator._generate_scene_in_loop.<locals>.<genexpr>  s?      R"VVM*BFF<,@A[P s   A Azsource_unavailable: Su    primary path not in scene_paths map (dep edge 가 source 를 선행 batch 로 보장하므로 정상 경로에선 발생하지 않아야 함)rz  )r   r   r|   r   zoom_targetr  	prop_refsprop_refs_have_anchor_contractsource_bytes
trace_metarz  source_asset_id.pngT)parentsexist_okr  	generatedzoom_continuityFensure_asciizoom_continuity_cropzzoom continuity crop)r   
asset_typer#  rz  r   	file_pathprompt_usedgeneration_modelwidthheightstatusreview_notesvariant_typetheme_label
created_atu?   zoom_continuity(batch): S%ssh%s crop/i2i-fill 성공 (group=%s)rN   uA   zoom_continuity(batch): S%ssh%s crop fallback → 기존 T2I (%s)r  r   r/  r0  r1  r2  r3  r4  r5  r6  ug   outdoor_prev_frame(batch): S%ssh%s history fallback — primary_location UUID 로 prev 프레임 확보r9  r  t2i_prompt_cinematicr  base)themerA  r  source_overridesrevisedr  uI   scene prompt override 적용(batch): S%ssh%s (prompt_source=%s, group=%s)prompt_sourcezoom_continuity_anchorr   r  r}   r~   r  r  r   r   r  r  r  r#  z3scene_generation_coordinator.generate_scene_in_loopr:  r;  r=  r   uM   chain_bg missing bg_id (batch path) — No Silent Fallback gate (Area #11 v1)rM   r   r>  r?  r@  rI   z/Scene %d Shot %d: background_chain ref injectedrA  r   uT   Scene %d Shot %d: chain_bg ref SKIPPED — close framing detected (framing_scale=%r)rB  uV   Scene %d Shot %d: prev_shot_ref build failed (%s) — falling back to entity-only refsrH  rI  rJ  rK  rL  r   zMScene %d Shot %d: prev_shot_ref injected (no chain bg for this shot, role=%s)rM  c              3     K   | ]T  }|j                  d       k(  xr: t        |j                  d      xt              xr t        t              xr k   V ywrO  rP  rQ  s     r>   r   zESceneGenerationCoordinator._generate_scene_in_loop.<locals>.<genexpr>d  sl      - $0B }-6 H&"&&2F(FMH&vs3H8>H $0rV  rW  r^  r\  u   outdoor_prev_frame(batch): S%ssh%s previous_frame_required_missing — outdoor+plate無+연속성 신호 샷인데 prev 프레임 미해결 (dep=%s, same_scene_prior=%s)r_  r`  ra  batchrc  rd  re  rh  rj  rl  rp  rs  ru  rw  rx  rr   r{  r|  r~  r  r  r  variant_labelrE  var_camera_effectrA  	shot_namez	[Camera: z] z<Scene %d: %d prompts total, generating last %d T2I + 1 angle)shot_stagingr~   r  r      )max_workersr   REF_CONTRACT_VIOLATIONzvariation vi=u    missing 'reference_phrase_kinds' sidecar — producer (scene_detail v26 W1) emit required; stale v25 cp? rerun scene_detail to bump  _failure_reason_failure_detailu;    reference_phrase_kinds is None — producer emit malformedz5 reference_phrase_kinds malformed: must be list, got r  r  )r   r  	var_themevar_theme_labelr  r`   r   is_close_framingchain_bg_lookupr   r  r  	validator	scene_dirr  r  world_guider   semantic_constraints)er   r   r   r[   r\   r]   r_   rA   r  r'  r  r)  r*  r+  r  r  r  r%  +app.services.zoom_continuity_render_servicer&  r'  tuplenextr	   r   r   rD   parentmkdirwrite_bytesr   r   r  dumpsr?   r   r   r   r   rZ   r  r   r  r  rX   r  r   r   r  r  r  r  rF   r   rH   rG   r0   r   r   r)   r   r  r  r   r  r   r   r   r  r  rf  rg  rk  r   rq  rr  r  rt  r  rv  r   r  r  r  r   r  lowerr^   r   r"   sanitizer_constraintsr   minr   _generate_variation_in_loopr   r   r+   submit_await_variation_resultsextend)r   r   r   r  r   r8  staging_mapr  r  r"  r   rG  r  r  r\  r]  r  r  r^  r   r   r  r  r   visible_idsr  r   eidrC  rD  r   rE  r'  r)  r*  r+  r  r  r  _zc_key
_zc_targetr%  r&  r'  _zc_prop_refs_zc_prop_anchor_applied
_zc_src_id_zc_src_bytes_zc_src_path_zc_src_asset_id_zc_png_zc_fill_prompt_zc_diag_zc_out
_zc_result_zc_excrF  r  r  r  r  r  r  r  r  
variationsbase_t2i_zc_overridevivarr  r  _target_raw_prompts_od_keyr  
_od_prompt_staging_key_stg_state_variant_sids_scene_indexr}   r   r  _patch_a_required_refs_batchr  r  r  r  _batch_payloadrF   r   rH   rG   r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r  r  r  r  r  r  rf  rg  r  rk  rq  rr  rt  r  rv  r  r  r  r  r  r  r  r  max_t2ivalid_variationsrX  rY  r  rO  target_variationsr[  _semantic_contract_semantic_constraints_pre_submit_failuresvar_executor_submit_variationvar_futures_var_phrase_kinds_raw_var_phrase_kinds_var_payloadvar_resultsrS  rT  rU  r)  s                                                                                                                                                            @@@@r>   _generate_scene_in_loopz2SceneGenerationCoordinator._generate_scene_in_loopD  s   F BZ
	**Z0G%HIK A!T"eeDk;QUU;%;3-/$++M#,>?As#%$++M!,<=  NT- 
-}1E1SAdG- 	  

	
	
 	

 343C3CZP
 	 <LL45;LL+,5
 	 	
 59 >>-0*..2NO~2**>:@bEEgN
 6O  * "5&NN=9%>>,7	62M2  
x 8 >B?KR RJ
 !MIS599*EY]L'D,>,E,E,G $\ 2 = = ?  49r>>zJ#' 5 (3/} =Z Z[ [ 6R#//) *} =)~~l; *"/+3J!. !+JNN4,@A'^^D1$462(" $4&88$$TD$A##G,!)")!% *t 4",!7G!E#2(0(C(C!T)$(JJ*H5E%K$:#9"&&
" UNN=1:>>,3ONN:.
 ZL*:<PQQ "~~&:;
 ~~mQ/0*..q2Q1RS 	 ',"11+>D"3./-.

 ].HH.}=DDF =:NN=1:>>,3O -	 )LL,FF1,?H "*"5"5"7$/! ~~mQ/0*..q2Q1RS 	 !r&&':B?E2
#k2
 	 #8<L#L.337Mf7U4O_(:%	 / #'9?NN=1.@+A
 7Mg7V4O_(:%&::,33G<KKJ"}5z~~l7SU !%:>N%NNNR#  ^^$4b9
!~~&<=iPdfhAiH$*6QYZ[J "R,,-?@FBKKGT  )4  5GB ,y11 DCC|I'>r'BC7:;4  
 KK/}-z~~l/K  2JK  , 11",D*Ql0Cq*DhYZP@KL1quu\26L  ^^M156a
|UV8W7XY!'R,,W5iR__X.4"
6s<OP %..:;1Z^^LZ[=\<]^0;{|,"11KKm-@$
 KK<"}a8*..WX:Y05578: "~~m4 nn\2Sjnn]6Snn12;+++
([C 03J+, 4(*
 ,/
'(
	
 	2*cB nn128b==>RSYWYS!(%' 	%	
 11A1A:N]''^^M*JNN<,HIK QNP 		.F G M2N,,HH+- 3'26 .D4"3";t#9 I 
 N778^99:112	 !A!AB(9&7+;'	
   ^^M156a
|UV8W7XY(,,W53G
 +m;
 !r&&w39r
#k2
 	 , H!GG 	 vzz-0%' ZZ(F"c  F7OVM5J#KL  \6$:;Q 67 "(:MNH!00JJ:W]^N'5$$$Q1KKA}a0*..q2Q
 &**]38KTe $NN=!4NN<3	 ;?
67FJJ}5:K 2NN=!4NN<3"&!%!4!4!S!S$3&7)%5)=!-f+A#14':- "T 
"0 !2[ @*.@@ ',"11$7FJ  -R$$^^M2JNN<4PQS F':; 
 <J9	 
<!E9 !E?Q47
O!EI!J9 !J!BD!JI##A'78$$Q)?B(OP  E*!((I6cNN=!4jnn\ST6UW\ %9"%9T!"/"3"3D"9"?R!D!DZ!PPSVX %9  " "~~mQ78*..WX:Y9Z[  !+} =z~~l?[v7 %&%-\\2D%E%N %@AGR%3&++ !W.2FB1K1KJ1WX), - $*<R<	- * ".1&77AC
Ot"3%( ;BJ78":.U "6#$67>#$678JKM  'mY8I3&NN=!<%>>,:	 ]v-/'	]
Yk9i9JM	
 4D4D4DjQ?i):&NN=9%>>,7#	L  )i):M&NN=9%>>,7!&E6'8&	
 	L:4;K;KZX
 N#  *4K#-&NN=9%>>,7	 1 )-> *} =)~~l;',C"8#8#89D<L<LLzY !#+,.:;&	
 	%)%6"}5!~~l3+"	
  ;=O!<R<$*JJ}$=vzz,?W **T*gs+
8S0Ig;>w<OWh$78	 (
 )+I-i):M&NN=9%>>,7#&E6'8'6"	 7@
34 00 ,GBD+1NOI!ggosww}i7XYO !''j)_E gglB/G)';	JGR0IY__.gmmoE%i[7)< ''S'9o(VW' -, -gXY7R,-s3D/E	G
 00GH
 5)~~.BC-

 !3 H H
 68C4E0F0K!Q,OPT` !4D4T4T UK@Q<C)_ ,36(//+C+B4 0Q R 3	, 1  (+,D(E%(0(//+C+B4 06 73, 1   ""7>(//+C+B4 01#$9:CCDA479 3, 1  $()>$?!  8!-'&7"/	 < + L//%)#'$3%5(%6$3+<"/''')=+A +))>) 0  k ARV 77DKc Qh  34K!13GHHm ## 	K	" 
d - WNN=1:>>,3OQX t( ELH $   &lNN=!4jnn\ST6UWZ "&&X"\ V QPs   AO
 0AO&
AO& EAO+ ;!AP4	AP: AP:/AP?('AQ ;?AR'A AR,J*C?AR9O
AO#O"AO#O+AP1O06AP,P,AP1QAR$Q:ARRAR$R,AR6R9ASc                   ddl }ddlm} g }t        |      D ]&  }	 |j	                         }|r|j                  |       ( |S # |$ r  t        $ r}ddlm}  ||      r+t        j                  dt        |dt        |                    |j                         }	t        j                  d||	       |j                  dt        |      j                    d	| dd
 |	dd d       Y d}~d}~ww xY w)u  variation future loop — completes all + propagates StaleUpstreamError.

        D6 T9-fix BLOCKING: 이전 ``except Exception`` 만으로는 StaleUpstreamError
        (validator fallback / preflight bypass path) 가 generic logging 으로 swallow
        되어 ``var_results`` 가 빈 list 로 반환 → 상위가 "all variations failed" 로
        scene_cp 기록 → STALE_UPSTREAM 의 운영 신호 손실. fix: StaleUpstreamError 는
        별도 catch 후 즉시 re-raise (scene-wide stale 의미 — single variation 실패
        가 아니라 catalog/render manifest stale, fail-fast 가 정합).

        Fix C (2026-05-10): generic Exception 도 silent swallow 하지 않고 typed
        failure dict (`_failure_reason`/`_failure_detail`/`_failure_traceback`)
        로 변환해 var_results 에 누적. scene_image_service 가 cp.failed 메시지
        직렬화 시 사용 — 24 shot deterministic 실패의 root cause 가시화.
        r   N)StaleUpstreamError)is_abortu   variation 중단 — %smessagez(Variation generation thread error: %r
%sGENERIC_EXCEPTIONrx   rT  i  )rV  rW  _failure_traceback)	tracebackapp.core.errorsr  r   resultr_   r   app.core.run_controlr  r   r   rY   rA   
format_excerrorr   r   )
r   r  
_tracebackr  r  futurer  r   r  tbs
             r>   rl  z3SceneGenerationCoordinator._await_variation_results  s     	'6,.";/F&&v.	 0@ 7 &   : C=NN173	3s83T **,?b ""':*.s)*<*<)=Ru'Eds'K*,Ud)$  )s   #AC.BC))C.c               <   d}t         j                  rt         j                  rt        |      dkD  s|S 	 g }	|D ]V  }
|
j	                  d      rt        |
d         nd}|s'|j                         s8|	j                  |j                                X |	s|S d}d}|j	                  d      }|rA|D ]<  }|j	                  d      |k(  s|j	                  dd      }|j	                  d	d      } n t        |	|j	                  dd      |j	                  d
d      ||      }|s|S |d   }t        j                  d||dz   t        |      |d   |d   |d   |j	                  dd             |t        |      k  r||   n|d   }|j	                  d      rt        |d         nd}|t        |      k  r||   n|d   }|r|j                         s|S t        |j                         |d   |d   |d         \  }}|s|S t               }|| dz  }|j                  |       | j                  j!                  ||j"                  |d   |d   |d   t%                      | j&                  j)                  |t+        |      |d   |d|d    d|d    d|d    |t%                      |j                  |       |j                  |t+        |      d|d   |dd       d}t        j                  d|||       |S # t,        $ r"}t        j/                  d||       Y d}~|S d}~ww xY w)u  Run fal.ai angle pipeline for a scene (N+1 image).

        W5 F22 Phase B.22.6 (2026-04-22): scene_image_service.generate_images의
        fal.ai 앵글 블록(~95 LOC)을 bound method로 lift.

        처리 (literal lift):
          1. settings.fal_ai_enabled + settings.fal_key + var_results_list 비어있지 않으면 진입
          2. 각 variation file_path에서 bytes 수집
          3. dependent_scene_id 있으면 _prev_title/_prev_desc 로드
          4. _select_and_recommend_angle → angle_selection (None이면 skip)
          5. _apply_fal_angle → fal_bytes (실패 시 skip)
          6. fal_bytes 있으면 파일 저장 + PNG 메타데이터 embed + DB asset 저장
          7. var_results_list와 saved_asset_ids에 append (in-place)
          8. 전체 try/except — 실패 시 warning log + fal_generated=False

        Returns fal_generated bool. var_results_list / saved_asset_ids mutate 발생.
        Fr   r9  NrK   r  r   
beat_titler  rC  )prev_scene_titleprev_scene_descriptionbest_for_angleuF   Scene %d: angle selection — image %d/%d, H=%.0f V=%.0f Z=%.0f — %sr   horizontal_anglevertical_anglezoomr   r0  )r9  source_image_namer  r  r  rB  z[fal.ai angle] H=z V=z Z=)rI   r9  rz  r   r:  source_image_idrB  r  	angle_fal)r   r9  r8  rz  r   r@  Tu1   Scene %d: fal.ai angle image saved (%d ms) → %sz-fal.ai angle pipeline failed for scene %d: %s)r   fal_ai_enabledfal_keyr^   r]   r	   r   r_   r   _select_and_recommend_angler   r   _apply_fal_anglerD   re  r  embed_fal_angle_metadatanamer?   r  save_fal_angle_assetrA   r   r   )r   var_results_listsaved_asset_idsr   r   r]  r   r   fal_generatedangle_image_bytesvrfp_prev_title
_prev_descdep_id_ssangle_selectionsel_idx
sel_resultsel_pathsel_asset_id	fal_bytesfal_elapsedfal_idfal_pathr   s                             r>   _run_fal_angle_pipelinez2SceneGenerationCoordinator._run_fal_angle_pipeline  s   8 ''H,<,<EUAVYZAZ  e	U "&.0ff[.AT"[/*t"))+%,,R]]_= '
 %$$ KJ^^$89F!Cwwt}.&)gglB&?%(WW-A2%F
	 " :!|R05r:!,'1O #$$%&67GKKXGaK%5!6 23 01'##Hb1 7>DT@U6U)'2[klm[nJ8B{8StJ{34Y]H7>_AU7U?73[jkl[mL!2$$%5##% 23 01'	&"I{ $$YF fXT?2H  +  99""*--!01C!D./?@$V,6 :  !!66h-#D)%'8J(K'L/*:;</&124 !-6 7  ""6*## ]%&t,( +%  !MKKCK   	UNNJBPSTT	UsD   *K0 K0 0$K0 0K0 AK0 !B*K0 +K0 8C6K0 0	L9LL)r  c       
           |t        |      k  r||   n|d   }| j                  j                  |       |t        |      k  r||   n|d   }|||<   t        |d         }|j	                         r1|||<   ||	|j                  dd      <   |
||
|j                  dd      <   |j                  |j                  dd      ||t        |      d       |S )ur  Primary asset 설정 + 3개 tracking map 갱신 + 체크포인트 기록.

        W5 F22 Phase B.22.9 (2026-04-23): scene_image_service.generate_images의
        primary 마킹/추적/체크포인트 블록(~22 LOC)을 bound method로 lift.
        2026-04-27: sd_shot_path_map 인자 제거 (downstream 미사용 dead variable).

        처리:
          1. selected_id = saved_asset_ids[best_idx] fallback [0]
          2. persistence_svc.set_primary_asset(selected_id)
          3. primary_result = var_results_list[best_idx] fallback [0]
          4. scene_results_by_index[si] = primary_result (무조건 기록)
          5. primary_path가 exists()면 path map 2개 업데이트
          6. scene_cp.mark_completed(still_id, {asset_ids, primary_id, primary_path})

        Returns:
          primary_path (caller가 location_scene_history 업데이트에 사용)
        r   r9  r   rK   )	asset_ids
primary_idprimary_path)r^   r  set_primary_assetr	   r   r]   mark_completedrA   )r   best_idxr  r  r   r   scene_cpscene_results_by_indexscene_paths_by_indexr   r  selected_idprimary_resultr  s                 r>   _finalize_and_track_primaryz6SceneGenerationCoordinator._finalize_and_track_primary@	  s   B #o.. H% # 	
 	//< #.// X&!!$ 	
 &4r"N;78 '3 $CO&z~~dB'?@ 2=OZ2:>>$3KL
tR 8(%-;
 	
 r=   c                  |j                   xs d}|dk(  r(|r&t        | j                  | j                  |d   |       n,|dk(  r'|r%t        | j                  | j                  |d   |       | j                  j	                          | j
                  j                  | j                  dd|| j                  ||r|d   nd|r|d   nd|d|	       ||||d
S )u  generate_scene_with_variations 최종화: recommended primary + commit + log + dict.

        W5 F22 Phase B.24.2 (2026-04-23): A/B variation 생성 후
        recommended 설정 + db.commit() + activity log + return 블록(~30 LOC)을
        bound method로 이관.

        처리 (literal 보존):
          1. `recommended = still.recommended_variant or 'original'` 기본값
          2. recommended == 'A' + variant_a_result 있으면 A primary
             elif 'B' + variant_b_result 있으면 B primary
             (그 외: 아무것도 안 함 — original이 이미 primary)
          3. `self._db.commit()`
          4. activity log: action='still.generate_with_variations',
             resource_type='still', resource_id=still_id,
             detail={original_id, variant_a_id, variant_b_id, recommended}
          5. 반환 dict: {original, variant_a, variant_b, recommended}

        _set_variant_primary_helper는 module-level (sib=0, target=1 update, commit은 caller).
        originalAr   Bzstill.generate_with_variationsr  N)original_idvariant_a_idvariant_b_idrecommendedr  actionresource_typeresource_idr   detail
ip_address)r  	variant_a	variant_br  )recommended_variant_set_variant_primary_helperr  r  commitr  logr  )	r   r  rz  original_resultr  variant_a_resultvariant_b_resultipr  s	            r>   _finalize_scene_with_variationsz:SceneGenerationCoordinator._finalize_scene_with_variations|	  s    < //=:#"2'$2B2BDTUYDZ\deC$4'$2B2BDTUYDZ\de^^3! ''*:J 0 6PT:J 0 6PT*	  	 	
  ())&	
 	
r=   c                   t        |d| dd      }	|	r|	dk(  ry| j                  j                  ||||d| |	t        |d| dd      t        |d| dd      |||      S )	u  generate_scene_with_variations의 variation_{slot}_* 슬롯 기반 i2i 생성.

        W5 F22 Phase B.24.1 (2026-04-23): A/B variation 호출 중복 블록(각 ~16 LOC)
        공통화. slot='a' 또는 'b'를 받아 getattr로 still의 동적 필드 접근.

        처리 (literal 보존):
          1. still.variation_{slot}_type이 truthy이고 'none'이 아니면 i2i 진입
          2. variation_svc._generate_variation_i2i 호출:
             - variant_label=f'variant_{slot}'
             - var_type, angle_json, color_prompt: getattr로 still의 variation_{slot}_{type,angle,color}
          3. type이 없거나 'none'이면 None 반환
        
variation__typeNr.  variant__angle_color)
i2i_editororiginal_bytesr  r  rL  var_type
angle_jsoncolor_promptr]  r   lineage)rY   r  _generate_variation_i2i)
r   slotr  r  r  r  r]  r   r  r  s
             r>   _maybe_generate_variation_slotz9SceneGenerationCoordinator._maybe_generate_variation_slot	  s    0 5JtfE":DA8v-""::!)#$TF+u
4&&?F *TF&(A4H! ; 
 	
r=   c               `   ddl m}m}m}m} | j
                  }| j                  j                  |      }| j                  j                  t        |j                                     }	 |t        j                  ||      }
 |t        j                  ||      } |t        j                  ||      } |t        j                  ||      }|j                   d|j                   }|r|j                  |      nd}| j!                  |      }| j                  j#                  t        |j                               |      }| j$                  j'                  |      \  }}|D ch c]%  }|j                  d      s|j                  d      ' }}ddlm} ddlm}  |||      }|r8 ||j                  d	      xs i |j                  d
      xs
 t1               |      ni }| j$                  j3                  ||||xs d      }||	|j                  di       |j                  di       |
||||||dS c c}w )u  Task 2 of single-vs-batch reference contract fix.

        단건 generate_single_scene_image 의 helper input prep — batch generate_images
        의 사전 build 코드와 **동일 source 호출** 로 9 fields build (DRY 강제).

        spec: docs/superpowers/specs/2026-05-08-single-batch-reference-contract-design.md §4.1
        plan v2 Task 2: build_single_still_context — batch 동일 source.

        Source mapping (vs batch generate_images):
          - entity_lookup           = ref_svc.load_episode_entity_lookup(episode_id)
          - ref_image_map           = ref_svc.load_entity_reference_images(...)
          - background_chain_bg_map = load_background_chain_bg_map(...)
          - dep_detail_map          = load_shot_dependency_map(...)
          - staging_map → staging   = load_shot_staging_map(...)[scene_shot_key]
          - cached_style_context    = self._get_style_context()
          - cached_entity_text_map  = ref_svc.build_entity_text_map(entities)
          - scene_paths_by_index_by_id, location_scene_history
                                    = persistence_svc.build_resume_state(...)
          - stills                  = persistence_svc.fetch_episode_stills(...)
        r   )load_background_chain_bg_mapload_outdoor_direct_contextload_shot_dependency_mapload_shot_staging_mapr   N)r   r   r&  )r*  r,  r-  )fallback_location_uuid_by_scener   r8  )r  r  r   r8  r   r  rG  r   r  r  r   )%app.services.scene_checkpoint_loadersr  r  r  r	  r  r  load_episode_entity_lookupload_entity_reference_imagesrX   r   r   r   r|   r   r]   r  build_entity_text_mapr  load_episode_still_dictsr  r'  r  r*  r  build_resume_state)r   r   r  r  r  r  r	  pidr  r  r   r  rG  rn  r  r   r  r  r   _stills_ormr   _all_still_idsr'  r*  
_opf_ctx_s_opf_uuid_by_scene_s_rss                              r>   build_single_still_contextz5SceneGenerationCoordinator.build_single_still_context	  sG   .	
 	
  ++FFzR ++HH%%'(

 #?!!3
#
 9!!3


 2!!3


 ,H,A,A3
S++,Ae.>.>-?@3>+//,/D  $66zB
 "&!4!4!J!J%%'(Z "K "
 #33LLZX/5Ev!t!%%+vE	
	
 5S*E

 	  >NN67=2NN-.7#% 
  	 	
 ##66NM,@,HD 7 
 +**-''2NPR*S&)gg.F&K'>"4,$8&<
 	
) Fs   H+4H+)r  c          	        | j                  ||      }|r|n|d   }t        di d|d|d|d|d|d|d|d   d	|d	   d
|d
   d|d   d|d   d|d   d|d   d|d   d| j                  dt        | j                  | j                        d| j
                  d|d   d|j                  d      dd\  }	}
}}}ddlm} |j                  d      }t        |d      }|t        k(  }|j                  d      }|2t        | j                  ||j                  |j                        }||d<   t        ||d   |      }||d<   t        |d         }t!        |||      } |||
||	|||       |	|
||fS )uX  generate_single_scene_image의 non-custom_prompt 경로 prompt+refs 구성.

        W5 F22 Phase B.23.5 (2026-04-23): _build_final_scene_prompt 경로 블록
        (~48 LOC)을 bound method로 lift.

        처리 (literal 보존):
          1. `load_shot_t2i_variations` — 체크포인트에서 shot 변형 프롬프트 로드
          2. var_t2i = variations[0].t2i_prompt or still_frame_prompt fallback
          3. `load_episode_entity_lookup(episode_id)` 로드
          4. `build_entity_text_map(entity_lookup.values())` 빌드
          5. `build_scene_ref_image_map(ref_image_map, entity_lookup)` 빌드
          6. `resolve_refs_for_prompt(var_t2i, visible, scene_ref, entity_lookup)`
          7. `_get_style_context(episode_id)` 스타일 맥락
          8. `_build_final_scene_prompt(...)` try/except — 예외 시 warning + var_t2i fallback

        D5 §4.3 (2026-05-09): return 을 (_full_prompt, labeled_refs, attached_meta)
        3-tuple. T2 의 plumbing — T3 에서 validator 가 attached_meta 활용 예정.

        Returns: (_full_prompt, labeled_refs, attached_meta)
        )r   r  r  r  r   r   r  r  r  r  r   r8  r   rG  r   r  r   r  r  r   r  r	  Tr   )validate_attached_refsz@scene_generation_coordinator.single_scene_attached_ref_validatorr:  r~   r  rZ  r[  r   r<   )r  r  r  r'   r  r  r]   r   r  r   r   r   r|   r   r   r   r   )r   r  r   r   r  r  r  ctx_ref_image_mapfull_promptrF   r   r   _attached_payloadr  _stagingr  r  _rpc_chain_bg_lookups                       r>   #_build_single_scene_prompt_and_refsz>SceneGenerationCoordinator._build_single_scene_prompt_and_refsR
  si   D --5-Q +8S=Q^w _
_
!_
 "_
 .	_

 )_
 ._
 "%%;!<_
 $''?#@_
 (++G'H_
 $''?#@_
 %((A$B_
 /0_
 	N_
 o._
 ''_
  3488T=M=MN!_
" --#_
$ x=%_
&  #ww';<'_
(  )_
[\=2HJ[@ 	K779%3T
 +m;~~23<,++%!-- ++	D 04J+, 5#/0*
 ,0
'( 15N1OP "C"M4"
 	,{.,#9		
 L-9JJJr=   c          	     <   |D cg c]  }|d   	 }}t        | j                  | j                  d|d      }| j                  j	                  ||      }| j
                  j                  | j                  dd|d   | j                  d|i|       t        |      S c c}w )	u  Single scene 생성 후처리: lineage + asset 저장 + activity log + image dict.

        W5 F22 Phase B.23.4 (2026-04-23): generate_single_scene_image의 후처리
        블록(~21 LOC)을 bound method로 lift.

        처리:
          1. `ref_entity_ids` = visible_entities의 id 목록
          2. `build_lineage_fields` 호출 (prompt_type='original')
          3. `save_single_scene_asset(scene_result, lineage)` 위임
          4. activity log: action='image.generate_single_scene', detail={'still_id'}
          5. `image_to_dict(asset)` 반환

        literal 보존: builder/save/log 순서 + 페이로드 필드 + prompt_type='original'.
        r   scene_image_generatorr  )ref_entity_idsprompt_typezimage.generate_single_sceneimagerz  r  )	r%   r  r  r  save_single_scene_assetr  r  r  r&   )	r   scene_resultr  rz  r  r   r%  r  assets	            r>   _finalize_single_scenez1SceneGenerationCoordinator._finalize_single_scene
  s    , ,<<+;a!D'+;<&HHd&&#)"	
 %%==lGT^^0!$T*'') 	 	
 U##) =s   B)r_  c               n
   ddl m} ddlm}m} 	 t        ||||      }	 t        |i |      }t        |
|j                  |      }
 |||j                  |j                  |||	|
       |}d}t!        d      D ]_  }	  ||||j                  dd      ||j                  r|j                  ndd| j"                  |d|j                  d
      |j                  d      xs |j                  d      |j                  d      d|      }d}d}d} t%        j&                  |j                  di       d      }!|r|D "cg c]  }"|"j                  dd       }#}"|j                  dd      |j                  dd      |j                  dd      |j                  dd      d}$| j(                  j+                  ||d    |$|#| |!      \  }}} }!i dt-               d!d"d#dd$|j                  d      d%|d |d    d&|d'|d'   d(dd)dd*| d+|!d,|d-|d.|d/|d0|r|j                  d1      nd|r|j                  d2      nd|j.                  D %cg c]G  }%t1        |%t2              r5|%j                  d3      d4v r"|%j                  d5      r|%j                  d5      I c}%d6t5        |      |j                  d7      rd8|j                  d7      ini d9t7               ic S  y# t        $ r-}t        j                  d||       |r| d| n|}Y d}~d}~ww xY w# |$ rf}t        j                  d	|j                  d
d      |j                  d      xs |j                  d      ||       dt        |      dd dcY d}~S d}~ww xY wc c}"w c c}%w # t8        $ r
}&|d:k\  r<t        j                  d;|j                  dd      dd< |||&j:                         Y d}&~& y	 |j=                  ||&j:                  |&j>                  |d=z   |>      }'|'j                  d?|      }|'}t        jA                  d@|j                  dd      dd< |d=z   |'j                  d1d             n=# t        $ r1 t        j                  dA|j                  dd      dd<        Y Y d}&~& yw xY wY d}&~&$d}&~&wtB        $ r  t        $ r}&|d:k  rJt        j                  dB|j                  dd      dd< ||d=z   |&       ddl"}(|(jG                  d       Y d}&~&t        j                  dC|j                  dd      dd< ||&       Y d}&~& yd}&~&ww xY w)Du^  Generate a single variation image for a scene (runs in thread pool).

        W5 F22 Phase B.22.3 (2026-04-22): generate_images nested function
        _generate_one_variation을 bound method로 lift. closure 변수 8개
        (gemini_client, sanitizer, validator, scene_dir, cached_*, world_guide,
        episode_id)를 explicit keyword param으로 승격.

        D5 T4 §4.6 (B2 architectural fix): variation 별 _full_prompt 가
        다르므로 phantom guard 도 variation-specific. validator 는
        _build_final_scene_prompt 직후 + generate_and_validate_scene 직전에
        호출. RefContractError 시 그 variation 만 skip (다른 variation 계속).

        Returns dict with result data or None on failure. Retry 3회 (원본 1회 +
        sanitized 2회), ModerationError 및 재시도 가능 일반 예외 처리 보존.
        r   )generate_and_validate_scene)r  r   )r  z(Prompt build failed for variation %s: %sz

Nr  uL   Scene %d Shot %s variation=%s: ref contract violation — skip variation: %sr|   r   r}   rS  rT  rU  rQ  r  rK   scene_image_genr   )r   r   operation_typer|   r   rz  )r  r  r  
output_dirreference_imagesprevious_scene_bytesr.  r_  r3  
validationFr5  r  screenplay_scene_headingr  world_setting_summary)scene_headingr  r  world_contextr9  r8  r  r#  rz  r   r:  r;  r<  r=  r>  r?  validation_scorevalidation_resultr@  rA  sanitization_strategystrategynoterJ   )indoor_pose_guidecomposition_guiderI   )sanitization_notepose_guide_asset_idsr^  prev_frame_chainrB     z:Shot %s variation %s blocked after %d sanitize retries: %s   r   )attemptr_  sanitized_promptz,Shot %s sanitized (attempt %d, strategy: %s)zSanitization failed for shot %sz8Shot %s variation %s failed (attempt %d/3, retrying): %sz0Shot %s variation %s failed after 3 attempts: %s)$)app.modules.pipeline.scene_image_pipeliner-  r   r  r   r  r   r   r   r   r   r   rF   r  r]   rA   ranger  r   rf  r  validate_scenerD   rG   r[   r\   ro   r?   r   block_reasonsanitizeblock_categoriesr   r   timesleep))r   r   r  rX  rY  r  r`   r   rZ  r[  r   r  r  r\  r]  r  r  r^  r   r_  r-  r  r   r  _prompt_exc_ref_exc_current_prompt_sanitization_info_attemptpipe_resultr8  validation_result_strv_statusv_review_notesr   entity_names
scene_infomr   _sanrL  s)                                            r>   rj  z6SceneGenerationCoordinator._generate_variation_in_loop
  s   L 	Z\	g4"6 6L 	
 8RLC &G&(=(=s&" #W))7+@+@,!1 /'=	. '!aHr9"/.)~~lB?(=D=Q=QW%9%9W[)- '+&6&6&0*;'1~~m'D&0nn\&B&cjnnUbFc$.NN4$8  *>!& $( (,%&!%KOOL",M\a!b?O#P?O!AEE&"$5?OL#P)38RTV)W&0nn\2&F.8nn=QSU.V)49PRT)U	"J ,,;;%{;'?&h V$&;X~'')' ''  ' 
t 4	'
 !*'  [!9' "?' '4F(G' T' $,T' h' #N' '(8' ()>' #I' "?'  ,Se-?-C-CJ-Oko!'" L^);)?)?)Gcg 4;3L3L-3La%a.EE/2 7F FEE*-	 j)3L-/'B /w7C'J %..)AB +JNN;S,TUHJK'L !$&M' 'S !h A  	gNNEyR]^EY234yA_fL	g2   	LL^}a0|,M
}0M8	 $<#&x=##6 	\ $QJ-" #  q=NN#_#->>$#;BQ#?HVYVfVfh $--'((,, (1-A . D '+hh/A?&SO)-&KK N *tR 8! <hqj$((S]_aJbd   NN#DjnnUY[]F^_a`aFbc d
  
  a<NN#]",..r":2A">	8VW<Y\^AOQ[Q_Q_`dfhQijlklQmoxz}~s   K AL ;B<N7NC7NAN
AN	L#"LLM>AM93M>9M>
N
T4;RA?QR2R	RRRT4/A	T/>+T//T4)r  
OrmSessionr   rA   r  rA   r  r   r  r-   r  r/   r  r3   r  r4   r  r.   r   Noner8   )r   rA   r   rA   )r   zL'tuple[GeminiImageClient, OpenAIClient, PromptSanitizer, GenerationTracker]').r   r   r   List[Dict[str, Any]]r  Dict[str, Dict[str, Any]]r   Dict[str, Path]r8  zDict[str, tuple]rn  r  r  Optional[Dict[str, str]]r  Dict[str, bytes]r"  r`  r   r  rG  r  r  r
   r  r
   r\  Optional[ImageValidator]r]  r	   r  rA   r  Dict[str, str]r^  r  r   rA   r   Optional[Dict[str, Any]]r  rd  r  rd  r   ra  )r  zDict[Any, int]r   r]  )r  r]  r  	List[str]r   r  r   r]  r]  r	   r   rA   r   r   r   r   )r  r   r  re  r  r]  r   r  r   r   r  r
   r  zDict[int, Dict[str, Any]]r  zDict[int, Path]r   r_  r  r`  r   r	   )r  r
   rz  rA   r  r  r  rA   r  rd  r  rd  r  Optional[str]r   r  )r  rA   r  r
   r  r
   r  bytesr  rA   r]  r	   r   rA   r  r  r   rd  )r   rA   r  r
   r   r  )r  r
   r   rA   r   r  r  r]  r  ra  r  rf  r   z 'tuple[str, list, list, object]')
r)  r  r  r]  rz  rA   r  rf  r   r  )(r   r  r  rA   rX  rA   rY  rA   r  rX   r`   r(   r   rd  rZ  r   r[  z(Optional[Callable[[str], Optional[str]]]r   re  r  r
   r  r
   r\  rb  r]  r	   r  rA   r  rc  r^  r  r   rA   r_  rd  r   rd  )r   
__module____qualname____doc__r  r  r  r  rl  r  r  r  r  r  r"  r+  rj  r<   r=   r>   r6   r6     s   .. . 	.
 (. 1. -. /. -. /. 
.,16@	U@: HL;? .2487;1}I }I %	}I
 1}I %4}I !1}I $}I -E}I .}I !9}I "0}I '}I }I }I  ,!}I" #}I$ "%}I& !/'}I( $)}I* +}I, +-}I. 2/}I0 51}I2 
3}I~3jF /F #	F
 #F %F F F F 
Fh HL: : #	:
 /: #: : : !:: .: %4: -E: 
:x:
 :
 	:

 (:
 :
 3:
 3:
 :
 
:
x(
 (
 	(

 (
 (
 (
 (
 (
  (
 
"(
Tn
 n
),n
	n
p +/oK oK 	oK
 #oK /oK (oK (oK 
*oKb*$ %*$ /	*$
 *$ *$ 
*$B :>+O #O 	O
 O O O #O &O O BO !*O O O ,O  !O" "#O$ !/%O& $'O( )O* 7+O, 
"-Or=   )r   rA   )r`   r
   r   r  )
r   rA   r   rA   r|   r   r   zOptional[int]r   r  )r   rd  r   r^  r   r  r   rd  )r   r
   r   r
   r   r
   r   rf  )r   r^  )r   re  r   zList[Tuple[str, str]]r   rd  r   re  )r   Optional[List[Any]]r   r_  )r   rd  r   re  r   re  )rF   r   r   r   rH   r   rG   r   r   r  r|   r
   r   r
   r  rA   r   r\  ),r  r
   r   rA   r   r  r  r]  r  ra  r  rA   r  rc  r   r_  r8  r  r   r  rG  r  r   rd  r  zDict[str, Dict]r   rA   r  r  r  r
   r   rk  r  rf  r  rd  r  rd  r	  r   r   z'tuple')jrj  
__future__r   r   loggingrB   concurrent.futuresr   r   r   r   r  r	   typingr
   r   r   r   r   r   r   sqlalchemy.ormr   r[  app.core.configr   r  r   app.core.file_pathsr   app.core.framing_scaler   r   app.core.image_call_budgetr   r   r   app.logging.activity_loggerr   app.models.projectr   app.modules.generation_trackerr   app.modules.image_validatorr   #app.modules.llm.gemini_image_clientr   r   app.modules.llm.openai_clientr    app.modules.prompt_sanitizerr!   $app.modules.semantic_contract_routerr"   app.services.fal_angle_helpersr#   r  r$   r  "app.services.image_service_helpersr%   r&   r'   app.services.prompt_servicer(   r)   r*   r  r+   r  r,   &app.services.scene_persistence_servicer-   %app.services.scene_provenance_servicer.   $app.services.scene_reference_servicer/   r0   r1   r  r2   r  %app.services.scene_validation_servicer3   $app.services.scene_variation_servicer4   r5   r  __all__	getLoggerr   r   r?   rD   ri   ro   r   r   r   r   r   r   r   r  r  r6   r<   r=   r>   <module>r     s?  ( #    ? '  F F F 0 $ $ 6 L : < 6 . < 6 R 6 8 H 
  K J H  I
 (
(			8	$2*LZ$WWW W 	W
 WDF	!F6F F 	FR"%7:4.&%D%%D(%D 
"%D 	%Db   / F 4=$222 2 	2
 	2 2 2 2 
2P #'&*0437 /iMiM iM 	iM
 +iM $iM iM +iM !0iM +iM ,iM #iM &iM #iM  !iM" ##iM$ %iM&  'iM( $)iM* .+iM, 1-iM. /iM0 1iMXG Gr=   