
    #%jI                       d Z ddl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	 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 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.m/Z/  ej`                  e1      Z2deeee3ef         dee4eee3ef      f   de4dedeee3ef      f
dZ5dZ6dZ7de4fdZ8	 	 d`dddde4de4deee4e4f   ee3   f   d eee4e4f   e3f   d!eeee4e4f   ee3e3f   f      d"eeee4e4f   ee3   f      d#e9d$e9d%e9d&e9de3fd'Z:de4dee4   dee3   fd(Z;de4dee3e<f   fd)Z= ej|                  d*      Z?d+eeee3ef         deee3e3f      fd,Z@d-e3d.ee3   de3fd/ZAd+eeee3ef         d0eee3ef      ddfd1ZB ej|                  d2      ZCd+eeee3ef         d0eee3ef      ddfd3ZDd4eee3ef      d5ee3   dee3   fd6ZEde4dee4   deee3e3f      fd7ZFde4dee4   deee3ef      fd8ZGde4dee4   deee3ef      fd9ZHd:eee3ef      d;eee3ef      dee3ef   fd<ZIdddddddddddddd=d:eee3ef      d;eee3ef      dee3ef   fd>ZJd?ed@ede4dee4   dAedBedee3   fdCZKdDeee3ef      dEee3   dFee3ef   dee3   fdGZLdHee3ef   dIee3   dJe3dKe9de9f
dLZMdMdNdOZN	 dadPe3dQe3dRe3dSe3dTe3de3fdUZOdVe<dWePdXePde3fdYZQ G dZ d[      ZR G d\ d]eRe#      ZS G d^ d_eRe#      ZTy)bu6   상세 Phase StepRunner -- scene_detail, scene_verify.    N)ThreadPoolExecutoras_completed)Path)AnyDictIterableListOptionalSetTuple)SQLAlchemyError)SceneAnalysisContext)AppError)get_perception_guide)phrase_diagnosticvalidate_echoes)FRAMING_CLOSEget_framing_scale_or_raise)
StepRunner)is_immobilized_state)_normalize_scene_detail_resultassert_fresh_llm_evidenceSceneContextLoader)call_structured)load_promptload_schemadependenciesshot_scenes_mapscene_index
shot_indexreturnc           
      J   g }| xs g D ]  }|j                  d      xs g dd D ]  }t        |t              s|j                  d      dk7  r)|j                  d      |k7  r>|j                  d      |k7  rS|j                  d      }|j                  d      }d}	|j                  |g       xs g D ]@  }
|
j                  d      |k(  s|
j                  d	      xs |
j                  d
      xs d}	 n |j                  |||	|j                  d      xs g d         |S )u  현재 shot이 후속 shot의 zoom_in_detail source인지 reverse-lookup.

    결함 A fix (forward-look continuity). 같은 씬 내 후속 shot이 location_refs[0]
    로 (scene_index, shot_index)를 가리키고 ref_usage='zoom_in_detail' 이면, 이 shot
    의 frame에 후속 close-up 대상 영역이 visible 위치로 잡혀야 한다 (특히 죽은/
    motion-frozen 인물 케이스).

    schema 키는 location_refs[].scene_index / shot_index (접두사 없음).
    shot_dependency_t2i 5.202604201700 / shot_dependency_t2i_step 양쪽 일관.

    일반화 메커니즘 — 시나리오 의존 0. dependencies/shot_scenes_map 만 입력.

    Args:
        dependencies: ctx.dependencies 리스트. None / [] 모두 안전.
        shot_scenes_map: scene_index → [shot_meta, ...].
        scene_index: 현재 shot의 scene_index.
        shot_index: 현재 shot의 shot_index (int 또는 str).

    Returns:
        후속 shot 메타 dict 리스트:
        [{"scene_index", "shot_index", "description" (전문, no truncation),
          "keep_elements": list[{label, kind}]}, ...]

        keep_elements entry shape: {label: str, kind: enum["environment",
        "static_prop"]}. Area D-next-min (2026-05-15) — shot_dependency_t2i v7
        producer SOT. character / person / body 묘사는 keep_elements 가 다루지
        않음 (별도 layer: scene_consistency / character_state_variant /
        semantic_contract_router).
    location_refsN   	ref_usagezoom_in_detailr    r!    shot_descriptiondescriptionkeep_elements)r    r!   r*   r+   )get
isinstancedictappend)r   r   r    r!   outdreffollowup_sifollowup_shifollowup_descsh_metas              Q/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/detail_steps.py_compute_forward_zoom_targetsr8   $   s=   F !#CREE/*0b"15C c4(ww{#'77ww}%4ww|$
2%%.K55.LM*..{B?E2E;;|,<$67 ";;}5 "
  F JJ**,!$!9!?R	 - 6  : J       z47.202608250422c           	      V  	 ddl m}m} ddlm} ddlm	  | |t        | dd      xs i             }|r#| j                  d      }	j                  |       n	 | j                  d      }|sy	j                         	j                  |||	fd	fd	
      S # t        $ r Y yw xY w)u  이 컷의 고증 참조 sidecar 를 카드에 적는다. ★없으면 **무동작**.

    ★좌표로 적고 bytes 는 안 담는다 — CP 는 JSON 이다. 읽는 것은
    조립(`attach_from_rpc`)이 하고, 그때 해시를 확인한다.
    ★중앙 조사 CP 가 없거나 이 컷에 해당 멤버가 없으면 `0` 을 내고 카드를
    **한 글자도 안 건드린다** — 그것이 비회귀의 문이다.
    r   resolve_grounding_modeuses_chunk_producergrounding_reference_bundlegrounding_sidecar_writerproject_configNreference_acquisitionc                 *    j                  |       S N)root)row_content_sha256r_rootsws    r7   <lambda>z*_write_grounding_sidecar.<locals>.<lambda>   s    !6!6qu!6!Er9   c                 *    j                  |       S rF   )row_file_coordinaterI   s    r7   rM   z*_write_grounding_sidecar.<locals>.<lambda>   s     6 6qu 6 Er9   )content_sha_ofcoordinate_of)app.core.grounding_moder=   r>   app.modules.pipeliner@   rB   getattr_load_prev_checkpointassert_central_checkpoint	Exceptiondefault_reference_rootwrite_for_shot)
runnercardvisible_entitiesr=   r>   rbrequiredcprK   rL   s
           @@r7   _write_grounding_sidecarr`   o   s    >EC
 #wv/?FL"MOH ))*AB
$$R(	--.EFB  %%'Eb"EE     		s   B 	B('B(F)chain_bg_camera_meta_enabledis_close_framingsishiessence_by_shotchain_bg_guide_by_shotchain_bg_camera_meta_by_shotchain_bg_owned_by_shotshot_essence_enabledchain_bg_guide_enabledra   rb   c                   g }
|rE|j                  | |f      xs g }|r,dj                  d |D              }|
j                  d|z          |r/|	s-|j                  | |f      xs d}|r|
j                  d|z          |r|	s|xs i j                  | |f      xs i rtt        fddD              r`g }dD ]4  \  }}j                  |      xs d}|s|j                  d	| d
|        6 |r#|
j                  ddj                  |      z          |	sI|xs i j                  | |f      xs g }|r,dj                  d |D              }|
j                  d|z          |
sydj                  |
      dz   S )u  Phase 1b essence + Phase 2 chain_bg_guide + Phase 9.1 camera_meta +
    G3.2 chain_bg owned objects prepend.

    각 토글이 off면 해당 블록 없음. 모두 off/data 없음 → 빈 문자열 (회귀 0).
    순서: essence → chain_bg_guide → chain_bg_camera_meta → chain_bg_owned.
    사이에 빈 줄.

    Phase 9.1: chain_bg_camera_meta_by_shot이 None이면 빈 dict처럼 동작
    (positional arg 호환 유지).

    Phase 9.2 (framing_scale enum SOT v1, 2026-05-15): is_close_framing bool
    인자 — production caller 가 helper (`get_framing_scale_or_raise`) 로 staging.
    framing_scale enum 을 read 후 `== FRAMING_CLOSE` 결과 전달. close framing
    이면 chain_bg.camera_meta block 을 prepend skip — Rule E (close framing shot
    은 합성 단계에서 chain_bg ref 가 자동 skip) 과 LLM 입력 일관 유지.

    G3.2 (round 4 Q3=A): chain_bg_owned_by_shot 인자 추가 — owned objects block
    은 close framing 시 skip + non-close 시 owned 1+ entries 면 inject. toggle
    없음 (correctness guard 라 항상 적용). 동시에 close framing 시 chain_bg_guide
    도 skip — chain_bg_guide / chain_bg_camera_meta / chain_bg_owned 3 종 모두
    deterministic skip 일관 (round 2 #3, Codex 일관성 review).
    
c              3   &   K   | ]	  }d |   yw- N ).0es     r7   	<genexpr>z/_build_phase2_prepend_blocks.<locals>.<genexpr>   s     811#h   u>   [샷 핵심 시각 요소 — 반드시 t2i_prompt에 포함]
r(   uC   [chain_bg reference에 이미 있음 — 다시 그리지 말 것]
c              3   @   K   | ]  }j                  |        y wNr,   )rq   kmetas     r7   rs   z/_build_phase2_prepend_blocks.<locals>.<genexpr>   s"       .
 .
s   )camera_positioncamera_height	lens_hintframing_notes))positionrz   )heightr{   )lensr|   )zframing notesr}     : ui   [chain_bg reference 카메라 정보 — t2i_prompt에서 일치시키거나 명시적 deviation 명시]
c              3   &   K   | ]	  }d |   ywrn   rp   )rq   os     r7   rs   z/_build_phase2_prepend_blocks.<locals>.<genexpr>   s     #<ebHert   uU   [chain_bg에 이미 그려진 객체 — 새로 그리지 말 것 (Rule C contract)]


)r,   joinr/   any)rc   rd   re   rf   rg   rh   ri   rj   ra   rb   blocksessencelinesguide
meta_linesk_labelk_fieldvownedowned_linesry   s                       @r7   _build_phase2_prepend_blocksr      s   F F!%%r3i06BII888EMMQTYY
 &6&**B95;MMVY^^ $,<,277S	BHbC  .
   J%  HHW%+%%7)2aS&9:%  Aii
+, '-222B9=C))#<e#<<KMMh
 ;;v''r9   c                     |.||f| j                   xs i v rt        | j                   ||f         S t        | j                  j                  |g       xs g       S )u)  ctx 에서 shot 단위 visible_entities 도출 — `_analyze_one()` 와 동일 우선순위.

    shot_director 결과가 있으면 shot 단위 변형 확정 ID 사용 (variant resolved),
    없으면 scene 전체 visible 사용. shot 부재 (legacy / scene-level path) 면
    scene 전체.
    )shot_director_velistscene_visibler,   )ctxrc   rd   s      r7   _derive_visible_for_shotr      s\     B9)=)=)CDC(("c344!!%%b"-344r9   c                    i }| j                   ro| j                   xs i j                  dg       D ]L  }|j                  d      xs |j                  dd      }|j                  dd      }||f}|r|||<   |sH|||<   N i }| j                   r| j                   xs i j                  dg       D ]  }|j                  d      |k7  r|j                  dg       D ]n  }	|	j                  d	d      }
|	j                  d      xs |	j                  d
d      }|
s=|s@||v sE||   }|
|vrg ||
<   |||
   vs[||
   j                  |       p  |S )u   `_analyze_one()` 의 scene_outlooks 빌딩 로직 mirror.

    cid → [(osid, oname), ...] 매핑 — 같은 씬의 outlook assignments 만
    포함 (cross-scene 배정 차단).
    outlooks
outlook_idshort_idr(   namescene_assignmentsr    assignmentscharacter_idoutlook_name)outlook_datar,   r/   )r   rc   outlook_id_to_infoolosidonameentryscene_outlookssaacidosid_keys               r7   _derive_scene_outlooks_for_shotr     sd    ,.
##)r..z2>B66,'A266*b+ADFF62&E5ME+0"4(,1"5) ? ')N
##)r../BBGBvvm$*VVM2.eeNB/55.K!%%2K84F(F.x8E.0.0s+N3$77&s+2259 / H r9   z(C\d{2,3})(O\d{2,3})t2i_variationsc                    t               }| xs g D ]  }t        |t              s|j                  d      xs g D ]V  }t        |t              s|j                  d      xs d}|j                  d      xs d}|sA|sD|j	                  ||f       X t
        j                  |j                  d      xs d      D ]3  }|j	                  |j                  d      |j                  d      f       5  |S )u  variation 들이 실제 사용한 (character_id, outlook_id) set.

    outfit_assignments + t2i_prompt regex (C##O##) union — variation 별로
    선택된 outlook 만 추출. RPC.required_refs 를 이 set 으로 narrow 하면
    attached_meta 와 정합 가능.
    outfit_assignmentsr   r(   r   
t2i_promptr%      )setr-   r.   r,   add_OUTLOOK_COMPOSITE_REfinditergroup)r   resultvaroar   oidms          r7   _compute_used_outlook_pairsr   .  s     $'5F$"$#t$77/06B6Bb$'&&(.BC&&&,"Cs

C:& 7 '//0E0KLAJJ
AGGAJ/0 M % Mr9   textinvalid_sidsc                    t        |D ch c]  }|s|	 c}t        d      }|r| s| S t        j                  ddj	                  d |D              z   dz         }ddd	t
        fd
}|j                  ||       \  }}|r|S | S c c}w )u  무효 closed-world SID 토큰을 text 에서 token-complete 하게 제거.

    invalid_sids = _check_prompts() pure_remove / bad_in_sfp 가 산출한 VE 위반
    SID 집합 (C##/L##/P##/C##O## — _ve_pattern 형식의 closed-world ID). 각 SID 와
    그 후행 소유격('s) 를 한 단위로 소거하되, 양쪽이 모두 공백이면 단일 공백으로,
    한쪽이라도 비공백이면 공백 없이 잇는다 (제거 위치 국소 정규화만). 무효 SID 가
    text 에 없으면 (subn substitution 0건) 원본 text 를 그대로 반환 — blind
    global trim/collapse 없음. 제거 자리에 semantic subject phrase 재삽입 없음
    (글자 삭제 + 국소 공백 정규화만).
    T)keyreversez(?P<left>\s*)\b(?:|c              3   F   K   | ]  }t        j                  |        y wrv   )reescaperq   ss     r7   rs   z&_strip_invalid_sids.<locals>.<genexpr>V  s     (Dt!1ts   !z)\b(?:'s)?(?P<right>\s*)r   zre.Match[str]r"   c                 N    | j                  d      r| j                  d      rdS dS )Nleftright r(   )r   )r   s    r7   _replz"_strip_invalid_sids.<locals>._replZ  s"    wwv1777+;sD"Dr9   )sortedlenr   compiler   strsubn)r   r   r   sidspatternr   cleanedns           r7   _strip_invalid_sidsr   G  s     l0la1l0c4HDtjj(Dt(D DD
%	&G
E ES E eT*JGQ7!T! 1s
   BBrender_prompt_cardc                 F   | sy|xs i j                  d      xs i j                  d      xs g }t        d |D              }|ry| D ]S  }t        |t              s|j                  d      }t        |t              s6d|v s;|D cg c]
  }|dk7  s	| c}|d<   U yc c}w )u6  FINDING 9 W4 (Cat4) — reference_phrase_kinds 의 'prop' over-declaration 제거.

    scene_detail LLM 이 per-variation sidecar `reference_phrase_kinds` 에 'prop'
    을 declare 했어도, 그 prop 이 numbered image mapping 에 매핑되지 않은
    descriptive P## 언급이면 `render_prompt_card.asset_requirements.required_refs`
    에 kind="prop" required_ref 가 생기지 않는다. required_refs 가 prop reference
    의 SOT 이므로, prop required_ref 부재 시 모든 variation 의
    `reference_phrase_kinds` 에서 'prop' 을 제거해 sidecar 를 SOT 와 정합시킨다.
    이렇게 안 하면 ref_contract_validator step 6 phantom guard 가 image-gen 직전
    fail-fast 한다 (e2e-bughunt-v1 FINDING 9).

    producer self-consistency normalization 일 뿐 validator step 6 약화가 아니다 —
    downstream phantom guard 의 fail-fast 는 그대로 보존된다. 'character' /
    'background' 는 건드리지 않는다 (Cat2 / id_policy / background contract 와
    얽혀 W4 scope 밖). prop required_ref 가 있으면 무변경.
    Nasset_requirementsrequired_refsc              3   h   K   | ]*  }t        |t              xr |j                  d       dk(   , yw)kindpropNr-   r.   r,   rq   rJ   s     r7   rs   z7_strip_overdeclared_prop_phrase_kind.<locals>.<genexpr>z  s4      A 	1d7f 77s   02reference_phrase_kindsr   )r,   r   r-   r.   r   )r   r   r   card_has_prop_refr   rpkrx   s          r7   $_strip_overdeclared_prop_phrase_kindr   a  s    ( 		!r&&';<B	c/!      #t$gg./c4 Vs]8;,K1qF{Q,KC() 
 -Ls   
BBz!\bC\d{2,3}(?:O\d{2,3})?\b|\bO00\bc                    | sy|xs i j                  d      xs i j                  d      xs g }t        d |D              }|ry| D ]{  }t        |t              s|j                  d      }t        |t              rd|v s:t
        j                  |j                  d      xs d      rc|D cg c]
  }|dk7  s	| c}|d<   } yc c}w )	u  FINDING 11 — reference_phrase_kinds 의 'character' over-declaration 제거.

    FINDING 9 W4 `_strip_overdeclared_prop_phrase_kind` 의 'character' 변종.

    인물이 창문 반사·실루엣·무-안면 등으로만 등장하면 id_policy 가 C## ID 대신
    generic descriptor 사용을 지시 → render_prompt_card.asset_requirements.
    required_refs 에 character/character_outlook ref 가 생기지 않는다 (정상).
    그런데 scene_detail LLM 이 per-variation sidecar `reference_phrase_kinds` 에
    'character' 를 over-declare 하면 ref_contract_validator step 6 phantom guard
    가 image-gen 직전 fail-fast 한다 (e2e-bughunt-v1 FINDING 11, still ec601991).

    strip 조건을 좁게 잡는다 — 'character' 는 Cat2 / id_policy 와 얽혀 있어
    무조건 strip 하면 W2 base_id_required / O00 base / state_variant / legacy
    같은 legit character meta 경로를 over-strip 할 수 있다. 다음 셋을 모두
    만족할 때만 variation 의 `reference_phrase_kinds` 에서 'character' 제거:
      1. variation 의 `reference_phrase_kinds` 에 'character' 가 있다.
      2. required_refs 에 kind in {character, character_outlook} 가 없다.
      3. 해당 variation 의 t2i_prompt 에 character ID-form signal (C##, C##O##,
         O00) 이 없다.
    required character ref 가 있거나 prompt 에 명시적 ID-form signal 이 있으면
    무변경 — 명시적 ID 는 silent-normalize 하지 않고 validator step 6 fail-fast
    에 맡긴다 (W2 base_id_required / O00 base / state_variant / legacy attach
    over-strip 방지).

    producer self-consistency normalization 일 뿐 validator step 6 약화가 아니다
    — downstream phantom guard 의 fail-fast 는 그대로 보존된다. 'prop' /
    'background' 는 건드리지 않는다 ('prop' 은 W4 helper 소관, 'background' 는
    background_chain / prev_shot fallback 등 별도 attach 경로와 얽혀 scope 밖).
    Nr   r   c              3   f   K   | ])  }t        |t              xr |j                  d       dv  + yw)r   )	charactercharacter_outlookNr   r   s     r7   rs   z<_strip_overdeclared_character_phrase_kind.<locals>.<genexpr>  s5      !A 	1dSf1S SSs   /1r   r   r   r(   )r,   r   r-   r.   r   _CHARACTER_ID_FORM_REsearch)r   r   r   card_has_character_refr   r   rx   s          r7   )_strip_overdeclared_character_phrase_kindr     s    B 		!r&&';<B	c/!   ! !!  #t$gg./3%+*< ''(=(CD47(LCq1;KC(L$%  )Ms   .
C9Cstagingvisible_charsc                    | s
t               S i }t        |d      r|j                  j                  dg       ng D ]7  }|j                  d      xs d}|j                  d      xs d}|s0|s3|||<   9 t               }| j                  d      xs g D ]a  }t	        |t
              s|d   }	t        |	      s%|j                  |j                  d      xs d      }|sL||v sQ|j                  |       c |S )	u~  staging.character_angles 의 subject_state 가 immobilized (dead/unconscious/
    severely_injured) 인 character 의 short_id (C##) set.

    scene_reference_service.detect_state_variant_sids 의 scene_detail-side mirror
    — scene_ref_image_map 없이 staging + visible 만으로 detect. resolver 가
    그 character 를 character_state ref 로 attach 하므로 character_outlook
    required 에서 제외해야 validator 정합 (S12_Shot6 dead-character 결함 fix).

    Area #2 W5 (2026-05-17): legacy mixed gaze field literal 판정 폐기 —
    ``is_immobilized_state(ca["subject_state"])`` 단일 SOT helper (Gate 1).
    entities
charactersr   r(   r   character_anglessubject_stater   )r   hasattrr   r,   r-   r.   r   r   )
r   r   r   name_to_sidrr   sidr   r   castates
             r7   _detect_state_variant_charsr     s      u"$K4;C4Lcll|R0RTTeeJ%2uuV}"4 #K	 U
 uF{{-.4"4"d#?##E*oobff[17R83-'JJsO 5 Mr9   c                 \   t        | ||      }|D cg c]  }|j                  d      s| }}t        |      }t        | |      }i }|D ]  }	|j	                  |	g       }
|
sd||	<   t        |
      dk(  r|
d   d   dk(  rd||	<   <t        |
      dk(  r|
d   d   ||	<   Vt        |
      dk\  se|
D cg c]  \  }}|dk7  s||f }}}t        |      dk(  r|d   d   ||	<   |rd||	<    g }t               }|j                         D ]E  \  }	}|	|vr|D ]6  \  }}|s	|	|f|vs|j                  |	|d       |j                  |	|f       8 G |j                         D ];  \  }	}|	|vr|s|	|f|vs|j                  |	|d       |j                  |	|f       = |S c c}w c c}}w )u{  `_analyze_one()` 의 _g41_outlook_pairs 빌딩 로직 mirror.

    scene_outlooks (LLM assignments) + fixed_outfits (1개 자동 배정) 통합.
    각 (cid, osid) 1회만. order: scene_outlooks 순서 + fixed_outfits 순서.

    Codex iter 1 B4 carry — shot-specific filter: 본 shot 의 visible_chars
    에 속한 character_id 만 통과 (validator Source 3b 와 lockstep). 같은
    scene 의 다른 shot 에만 등장하는 character 의 pair 가 본 shot 의 card
    에 흘러가면 정상 shot 이 false-fail. mirror path 가 _analyze_one
    producer 와 일치해야 verify/_user_edited recompute 가 hash-stable.
    CO00r%   r   r   r   r   )	r   
startswithr   r   r,   r   itemsr/   r   )r   rc   rd   visibler   r   visible_chars_setr   fixed_outfitsr   r   r   r   non_nullpairsseenolsr   _s                      r7   _derive_outlook_pairs_for_shotr    s    'sB4G$+CGSs~~c/BSGMCM*4S"=N$&M!%%c2.!&M#]aHQKNe$;!&M#]a!)!QM#]a+3B841aqEzA8HB8}!%-a[^c"%*c"  #%ED"((*S''GD!d4/cFG#t%  + #((*	T''S$Kt+LL#TBCHHc4[! + LG D Cs   F#F#4F(F(c                    |g S 	 t        |      }g }| j                  xs i j	                  |g       xs g D ]Z  }|j	                  dg       xs g }t               }|D ]  }	 |j                  t        |              ||v sJ|j                  |       \ |S # t        t        f$ r g cY S w xY w# t        t        f$ r Y bw xY w)u   `_analyze_one()` 의 fixed_for_shot 빌딩 로직 mirror.

    scene_consistency 의 fixed_elements 중 applies_to_shots 가 본 shi 포함하는
    것만. shi=None (scene-level) → 빈 list (scene-level 은 fixed_element 무관).
    applies_to_shots)int	TypeError
ValueErrorfixed_elements_by_scener,   r   r   r/   )	r   rc   rd   shi_intr0   ferawappliesr   s	            r7   _derive_fixed_elements_for_shotr    s     {	c( !#C**0b55b"=CCff',2uACF# 
 gJJrN D J z" 	 z* s#   B  B-B*)B*-B?>B?c           	      (   g S t        fd| j                  xs g D        i       }|j                  d      xs g dd }|j                  d      xs g }g }|D ]W  }t        |t              s|j                  |j                  d      |j                  d      |j                  dd	      d
d       Y |D ]W  }t        |t              s|j                  |j                  d      |j                  d      |j                  dd	      dd       Y |S )u   `_analyze_one()` 의 _g41_prev_refs 빌딩 로직 mirror.

    ctx.dependencies 에서 본 (si, shi) 의 location_refs[0] + character_refs 를
    추출. shi=None 이면 빈 list.
    Nc              3   r   K   | ].  }|j                  d       k(  r|j                  d      k(  r| 0 ywr    r!   Nrw   )rq   r1   rd   rc   s     r7   rs   z-_derive_previous_shot_refs.<locals>.<genexpr>F  s;      	G+qEE- B&155+>#+E 
+   47r$   r%   character_refsr    r!   r&   r(   locationr    r!   r&   r   r   )nextr   r,   r-   r.   r/   )r   rc   rd   dep_for_shotdep_loc_refsdep_char_refsr0   rJ   s    ``     r7   _derive_previous_shot_refsr  ;  s    {		GS%%++ 	G
L
 !$$_5;Ra@L $$%56<"M "C!T"

55/%%-{B/	
 		  !T"

55/%%-{B/	
 	  Jr9   seg	shot_infoc           
         ddl m} d}t        |t              r1t	        |j                  d      xs |j                  d      xs d      }d}t        |t              r|j                  d      }|	 t	        |      }d}t        |t              r|j                  d      }t        | ||      }ddl	m
}	 i }
| t        | d	d      r|	D ]  \  }}| j                  j                  |      xs g D ]  }t        |t              r|j                  d
      nd}|s)|j                  d      }t        |t              r	 t        j                  |      }nt        |t              r|}ni }||j                  dd      xs d||j                  dd      xs d|d|
|<     |xs g D cg c]  }||
v r|
|    }}t#        | ||      }d}|8| j$                  xs i j                  | d|       }t        |t              r|r|}d}g }d}d}|| j&                  xs i j                  ||f      }| j(                  xs i }|j                  ||f      }||t+        dd| d| d|d      |t-        |      ng }| j.                  xs i j                  ||f      }| j0                  xs i j                  ||f      }|j2                  dv }d}t        |t              rt5        |d| d|       t6        k(  }t9        | ||      }t;        | ||      }| *t=        | | j>                  nd| | j@                  ni ||      ng } |}!|ddi}!i d|d||ndd|d|!d |d!|d"|d|d#|d$|d%|d&|d'|d(|d)|d*|d+|| t        | d,i       xs i t        | d-d      | d.S # t        t        f$ r d}Y 0w xY w# t        j                   t        f$ r i }Y nw xY wc c}w )/u  G4.1 Wave 4 R4 B1+B2+B3: ctx 단일 source 에서 card 의 7 input 모두 도출.

    `_analyze_one()` / `verify_completion()` / `_user_edited_card_contract_
    violated()` (reuse path) 가 모두 본 helper 를 호출 → drift detection input
    이 한 source. 하드코드 empty 인풋 (B1/B2 결함) 차단.

    7 ctx-derived input source:
      - visible_entities: ctx.shot_director_ve / ctx.scene_visible
      - outlook_pairs:    ctx.outlook_data + scene_outlooks + fixed_outfits
      - staging:          ctx.staging_map[f"{si}_{shi}"]
      - bg_id:            ctx.chain_bg_id_by_shot[(si, shi)]
      - bg_owned:         ctx.chain_bg_owned_by_shot[(si, shi)]
      - bg_camera_meta:   ctx.chain_bg_camera_meta_by_shot[(si, shi)]
      - bg_guide:         ctx.chain_bg_guide_by_shot[(si, shi)]
      - fixed_elements:   ctx.fixed_elements_by_scene[si] filtered by applies_to_shots
      - previous_shot_refs: ctx.dependencies (loc_refs[:1] + char_refs)
      - forward_zoom_targets: _compute_forward_zoom_targets(ctx.dependencies, ...)

    derived flags:
      - is_close_framing: staging.framing_scale == FRAMING_CLOSE (helper read)
      - background_mode_on: settings.background_mode in {on, floor_plan_anchored}
      - perception_mode: shot_info.perception_mode (passthrough)

    Returns dict with keys matching `build_render_prompt_card(...)` signature.
    `ctx` 본 dict 에 포함 (caller 가 strip — splat call 전).

    R1-I1 / B4 fail-fast 분기:
    - shot_info 가 dict 이고 staging 부재 → staging_not_applicable marker 설정
      안 함 (builder 가 raise — shot path 의 staging 누락 = contract violation).
    - shot_info 가 None → legacy / scene-level path → marker dict synthesize
      해서 builder 가 not_applicable mode 로 처리.
    r   settingsr    indexNr!   perception_mode)ENTITY_KEY_TO_OWNERr   r   metadata_jsonr   r(   r   )r   r   entity_typer   r(  r  step.contract_violation0chain_bg_owned_by_shot missing entry for (scene=, shot=) but bg_id=~    present — upstream loader did not populate. NO silent fallback. Use explicit [] for intentionally empty (no owned objects).codemessage>   onfloor_plan_anchoredFz-detail_steps._build_render_prompt_card_dict S_Shotwherestaging_not_applicableTr   r!  r\   visible_entity_detailsoutlook_pairsr   bg_idbg_ownedbg_camera_metabg_guiderb   background_mode_onfixed_elementsprevious_shot_refsname_by_short_idepisode_reference_policyforward_zoom_targetsrA  rB  r   )!app.core.configr$  r-   r.   r  r,   r  r  r   $app.modules.pipeline.grounding_carryr'  rT   r   r   jsonloadsJSONDecodeErrorr  staging_mapchain_bg_id_by_shotrh   r   r   rg   rf   background_moder   r   r  r  r8   r   r   )"r   r   r!  	_settingsrc   rd   raw_shir&  r\   r'  _patch_a_entity_by_sid
_etype_key
_etype_val_e_sid_md_raw_md_dict_sr8  r9  r   candr:  r;  r<  r=  _bg_owned_map_bg_owned_rawr>  rb   r?  r@  rD  shot_info_for_cards"                                     r7   _derive_card_inputs_from_ctxr[  c  s   L 6	
B#t'@3777+;@qAC)T"---'l O)T"#--(9:/R= I8:
73
D9&9"J
||''
39r9-7D-Arvvj)t
 !ff_5G!'3/*'+zz'':H $GT2#*#% %) "vr 2 8b'1&(ff\2&>&D")14*40% : ':6 /?.D".D4.Dr'' 	r".D  4 33C@MG
%2**bT3%=9dD!dGEHNH
((.B33RI> 228b%))2s)4!6. T\% CRS  +8*C4&::@bEEr3iP..4"992s)D!!%BB  '4 &EbTseT 	 	 5S"cBN3CSA
 
	 9O"B
C !	  #6=rS_c! 	s 	'	
 	, 	!"8 	 	? 	7 	 	H 	. 	H 	, 	0  	.!" 	0#$ !5#C);R@FB$++T%3- k z* : !% 4 4i@ *')H*4s*   0N 4N/ON,+N,/OO)r\   r9  staging_for_shotbg_id_for_shotbg_owned_for_shotbg_camera_meta_for_shotbg_guide_for_shotrb   r>  fixed_elements_for_shotprevious_shot_refs_for_shotforward_zoom_targets_for_shotrA  c                    t        | t              }|rt        | ||      }nd}t        |t              r1t	        |j                  d      xs |j                  d      xs d      }d}t        |t              r	 t	        |j                  d      xs d      }d}t        |t              r|j                  d      }i d|d|d|d	|d
|dg d|d|d|d|d|d|d|	dt        |
      dt        |      d|d||i | t        | dd      nddd}|||||||	||||d}|j                         D ]  \  }}|	|||<    |
t        |
      |d<   |t        |      |d<   |S # t        t        f$ r d}Y w xY w)u  G4.1 Phase 5 Task 14 (R2-B3) + Wave 4 R4 (B1/B2/B3): RenderPromptCard
    builder 의 단일 input source.

    `_analyze_one()` (Task 14/15), `verify_completion()` (Task 18), 그리고
    `_user_edited` reuse path (Task 19) 모두 이 helper 를 호출 → drift detection
    의 input 이 일관됨.

    Wave 4 R4 redesign: ctx 가 제공되면 ``_derive_card_inputs_from_ctx`` 가
    7 input 모두 ctx 에서 도출 (B1/B2 hardcode empty 결함 차단). 명시적
    keyword 인자가 None 이 아닌 다른 값으로 전달되면 그 값으로 override
    (legacy callers / unit test 호환).

    Override semantics (N1 cleanup — explicit, ctx-derived path 기준):
      - kwarg = ``None``  → derived 값 유지 (override 안 함, sentinel)
      - kwarg = ``[]`` / ``{}`` / ``False`` / ``0`` → derived 위 override
        (빈/falsy 도 명시적 no-data 의도이므로 sentinel 아님)
      - kwarg = 비어있지 않은 값 → override
    즉 ``None`` 만 sentinel — falsy 값은 모두 override 적용.

    bool flag 두 개 (``is_close_framing`` / ``background_mode_on``):
      - ctx-derived path: ``None`` = sentinel, 아니면 ``bool(...)`` 캐스팅 후 override.
      - legacy fallback path (ctx 미제공): ``None`` 도 ``bool(None) == False`` 로
        즉시 default 셋팅 (sentinel 의미 X). 즉 fallback path 에서는 ``False`` 와
        ``None`` 입력이 동일 결과 — 옛 구현과의 호환을 위한 의도적 동작
        (review iter1 MINOR 명시화).

    Returns dict with keys matching `build_render_prompt_card(...)` signature
    + `ctx` (callers strip `ctx` before splat-call into builder).
    r   r   r!  r   r    r%  r!   Nr&  r   r!  r\   r8  r9  r   r:  r;  r<  r=  rb   r>  r?  r@  rB  rC  )r\   r9  r   r:  r;  r<  r=  r?  r@  rD  rA  )r-   r   r[  r.   r  r,   r  r  boolrT   r   )r   r   r!  r\   r9  r\  r]  r^  r_  r`  rb   r>  ra  rb  rc  rA  _ctx_is_analysis_ctxderivedrc   rd   r&  	overridesrx   r   s                           r7   _collect_card_inputsrj  "  sV   d &c+?@.	

 c4 SWW]+Dswww/?D1EBi&)--5:; i&'mm,=>O
2
#
 3
 	

  0
 %b
 ]
 
 '
 ^
 )
 5
 )
 %5 6
 !$'9":
  5!
" !"=#
$ %B " ? 7>(,1
: -&#%1%19 =,I !1=GAJ " #&*+;&<"#%(,-?(@$%Nq z* s   8E& &E:9E:rZ   base_ctxused_outlook_pairsstate_variant_charsc                    ddl }| j                  d      }|xs i j                  di       j                  d      }|syt        |dd      xs g |k(  ry	 |j                  |      }	||	_        |d|ind}
t        |	d|i|
      }|j                         D ci c]  \  }}|d	k7  s|| }}}||d
<   ||d<   ddlm}m	}  | |di |      S c c}}w # t        $ r"}t        j                  d|||       Y d}~yd}~ww xY w)u_  FINDING 8 (e2e-bughunt-v1) — render_prompt_card verify dual-source fallback.

    ``verify_completion()`` 의 card-hash recompute 는 ``SceneContextLoader.
    _load_dependencies()`` 결과를 dependency source 로 쓴다. 이 loader 는
    downstream ``shot_dependency_t2i`` checkpoint 가 존재하면 그것을 우선한다.
    ``shot_dependency_t2i`` (order 21.71) 는 ``scene_detail`` (order 21.7)
    *이후* 에 실행되므로, scene_detail 이 카드를 저장한 뒤 ``shot_dependency_t2i``
    가 갱신/생성되면 이후의 verify recompute 가 카드 build 당시와 다른
    dependency source 를 읽어 ``previous_shot_refs`` / ``forward_zoom_targets``
    가 달라지고 false ``card_drifted`` hash 가 발생한다.

    본 helper 는 1차 recompute 가 mismatch 일 때만 caller 가 호출 — upstream
    ``shot_dependency`` checkpoint 의 ``data.dependencies`` 를 source 로 한
    fallback recompute 를 정확히 1회 수행한다. fallback 은 false drift 를
    **구제만** 한다: upstream cp 부재 / 1차와 동일 source / recompute 실패
    시 ``None`` 을 반환해 caller 가 원래 drift 신호를 유지하게 한다. 구조 손상
    (stored card missing / shape invalid) 은 caller 의 1차 recompute 단계에서
    이미 fail-fast 처리되므로 본 helper 는 hash-only mismatch 회복으로 제한된다.
    r   Nshot_dependencydatar   r!   r    re  r   rl  rm  build_render_prompt_cardcompute_card_hashuv   verify_completion: upstream-dependency card recompute fallback failed (s%s_sh%s): %s — keeping original drift signalrp   )copyrU   r,   rT   r   rj  r   !app.core.steps.render_prompt_cardrr  rs  rW   loggerwarning)rZ   rk  rc   rd   rl  rm  _copyshot_dep_cpupstream_depsfallback_ctx
_shot_info_inputsrx   r   _builder_inputs_build_hashexcs                     r7   -_recompute_card_hash_with_upstream_dependencyr    s*   8 ../@AK &B++FB7;;NKM.$/52-Gzz(+$1!,/OlC(
&=""5
 -4MMOJODAqqEz1a4OJ0B,-1D-.	
 V.o.// K  FS	

 s1   AC C%C*"C C 	C>C99C>stored_cardstored_hashcard_inputsc                 f   ddl m}m}m} | |y	  || d       |j                         D ci c]  \  }}|dk7  s|| }	}}	  |di |	}
 ||
      }||k7  rd
| d| S y# t        $ r}d|j
                   cY d}~S d}~ww xY wc c}}w # t        $ r}d	|j
                   cY d}~S d}~ww xY w)u  G4.1 Phase 7 Task 19 (R2-I6 / R3-I2): `_user_edited` reuse path card
    drift guard — testable helper.

    Returns:
        None  → reuse OK (card present + shape OK + hash matches recompute).
        str   → violation reason (caller rejects reuse → fresh path).

    G3.1 `_evidence_violated()` / G3.2 `_owned_validation_violated()` 패턴
    mirror — caller (`_user_edited` reuse 분기) 가 본 helper 의 return 값으로
    reuse vs fresh 결정.
    r   )rr  rs  assert_card_shapeNcard_or_hash_missing_user_editedr5  zcard_shape_invalid: r   zrecompute_failed: zhash_drift: stored=z vs recomputed=rp   )ru  rr  rs  r  r   r1  r   )r  r  r  rr  rs  r  r  rx   r   builder_inputsrecomputed_cardrecomputed_hashs               r7   #_user_edited_card_contract_violatedr    s    "  k1%4+^< (3'8'8':I':tq!a5jad':NI22D^D+O< +%!+oo=NO	
   4%ckk]334 J  2#CKK=112sE   
A# BBB #	B,B :B B	B0B+%B0+B0	variationexpected_ownedexpected_camera_directionis_closec                 x   ddl m}m}m}m}m}m}	 | j                  d      }
|
y	  ||
d       |r|n|}|
j                  d      |k7  ry|
j                  d       |	| j                  d	d
            k7  ry|
j                  d       ||      k7  ry|
j                  d       ||      k7  ryy# t        $ r Y yw xY w)u(  G3.2 round 4 IMPORTANT 4 + round 5 BLOCKING 3: ``_user_edited`` reuse 의
    owned_validation drift 검사.

    True 반환 시 reuse 거부 + fresh 재생성. False 면 reuse 정상.

    검사 순서 — 어느 한 검증 실패 시 즉시 True:
    1. owned_validation field 존재 + shape OK.
    2. validator type 이 현재 close framing 상태와 일치.
    3. t2i_prompt_hash 일치 (round 5 BLOCKING 3 — user 가 cp 직접 편집해서
       prompt 수정 시 차단).
    4. owned_hash 일치.
    5. camera_direction_hash 일치.
    r   OWNED_VALIDATOR_CLOSE_SKIPOWNED_VALIDATOR_FULLassert_owned_sentinel_shapecompute_camera_direction_hashcompute_owned_hashcompute_t2i_prompt_hashowned_validationTr  r5  	validatort2i_prompt_hashr   r(   
owned_hashcamera_direction_hashF)	app.core.steps._owned_helpersr  r  r  r  r  r  r,   rW   )r  r  r  r  r  r  r  r  r  r  sentinelexpected_validators               r7   $_user_edited_owned_contract_violatedr    s    &  }}/0H#HNC '/"4H  ||K $66||%&*AlB'+  ||L!%7%GG||+,0M!1  #  s   
B- -	B98B9u   Orientation constraint: the content-bearing surface of the {element} is visible as follows — {orientation}. Render exactly this face; do not invent content on a face that is not visible to the camera.u   Reflection constraint: the reflecting surface of the {element} shows — {orientation}. Render this reflection accurately; do not invent other reflections or scenes.)content_surfacereflective_surfaceelementr   
camera_useorientationdirectionality_classc                     d|  d| d| d}|r|j                         r	|d| dz  }t        j                  |      }|r+|r)|j                         r|d|j                  | |      z   z  }|S )	u  user_prompt 의 'bg element' 한 줄 + 5-class directive 분기.

    Contract:
      - orientation NL non-empty → "[방향: {orientation}]" inline (모든
        class 공통, 기존 동작 보존).
      - directionality_class in _DIRECTIVE_TEMPLATES (content_surface /
        reflective_surface) AND orientation non-empty → 강한 영어
        directive 를 다음 줄에 추가.
      - 그 외 (transparent_surface / directional_3d / non_directional /
        legacy cp 의 class 누락 / 미지 enum value) → 영어 directive 0.
      - **code 가 element 문자열을 분류하지 않음** — directionality_class
        enum literal 만 본다. element 가 어떤 noun 이든 무영향.
    z  - r    ()u
    [방향: ]z
    )r  r  )strip_DIRECTIVE_TEMPLATESr,   format)r  r   r  r  r  basetemplates          r7   _build_bg_element_liner  M  s    ( '"UG2j\3D{((**[M++#''(<=HKK$5$5$78??7?TTTKr9   r\   rA  traits_by_short_idc           
      
   | D ch c]7  }t        |t              r%|j                  d      r|j                  d      d   9 }}|sy|t	        |j                               z
  }|rt        ddt        |       dd	      d
g}d}t        |      D ]W  }|j                  |      xs g }|sdj                  |      }	|j                  d| d|j                  |d       d|	        d}Y |sydj                  |      dz   S c c}w )u|  G4.6 RC-H — visible_entities 안 character base 들의 stable_traits block.

    Codex iter 1 B1 carry — db 인자 제거, ctx 의 prebuilt map 만 사용.
    ThreadPool worker 안 SQLAlchemy session race 회피 (Phase 3 prebuild
    pattern lockstep). prebuild 는 SceneContextLoader.
    `_load_entity_canon_character_maps` 가 main thread 에서 1회 query.

    visible_entities 안 character base ID (C##) 만 추출 → prebuilt map 조회 →
    stable_traits 가 있는 entity 만 block 에 포함. fail-fast: visible_entities
    안 character base ID 가 prebuild map 에 없으면 AppError (RO-15 silent
    bypass 금지).

    location/prop/outlook ID 는 무시 (block 은 character 한정).

    Args:
        visible_entities: shot 의 visible_entities list (C##/L##/P##/C##O##).
        name_by_short_id: ctx.name_by_short_id — short_id → entity_canon.name.
        traits_by_short_id: ctx.traits_by_short_id — short_id → parsed list.

    Returns:
        "[Entity stable_traits for this shot]\n- C##: ...\n\n" 형태 문자열.
        block 비어있으면 "" (visible_entities 에 character 없음 또는 모든
        entity 의 stable_traits 가 빈 list).

    Raises:
        AppError: visible_entities 의 character base ID 가 prebuild map 에
            없음 (entity_canon project_id + entity_type='character' scoped
            prebuild 에서 누락).
    r   Or   r(   z"step.scene_detail.unknown_short_idz8visible_entities references unknown character short_id: u    — prebuild EntityCanon map 에 없음 (entity_type='character' scoped). Fix: shot_validator character_ids must reference existing entities.i  )r0  r1  status_codez$[Entity stable_traits for this shot]F, ro   r  ): Trl   r   )r-   r   r   splitr   keysr   r   r,   r   r/   )
r\   rA  r  r   base_idsmissingr   has_anytraits
traits_strs
             r7   _build_entity_traits_blockr  j  s4   F &6%5cc3CNN3$7 			#q%5   -22455G5J'?# $BC
 	
 		
 44EGh#'',2YYv&
r#b!1!5!5c2!> ?s:,OP   99Uf$$;s   <D c                   .    e Zd Zdedee   fdZdefdZy)_DetailStepMixinstep_idr"   c                     ddl m} t        |j                        | j                  z  dz  dz  | j
                  z  |z  dz  }|j                         r%t        j                  |j                  d            S y )Nr   r#  checkpointsepisodeszmanifest.jsonutf-8)encoding)
rE  r$  r   projects_dir
project_id
episode_idexistsrG  rH  	read_text)selfr  r$  r_   s       r7   rU   z&_DetailStepMixin._load_prev_checkpoint  su    ,&&'$//9()+/??;'( 	
 99;::bllGl<==r9   c                    ddl m} ddlm} | j                  j                  |      j                   ||j                              j                  |j                  | j                  k(        j                         }|r|j                  S dS )uS   raw fulltext 로드 — scene_segmentation 오프셋과 동일한 텍스트 소스.r   )Episode)undeferr(   )app.models.projectr  sqlalchemy.ormr  dbqueryoptionsfulltextfilteridr  first)r  r  r  eps       r7   _load_fulltextz_DetailStepMixin._load_fulltext  sc    .*GGMM'"WWW--./VGJJ$//12UW	 	 !r{{(b(r9   N)__name__
__module____qualname__r   r
   r   rU   r  rp   r9   r7   r  r    s%    	S 	Xd^ 	
) 
)r9   r  c                      e Zd ZU dZdZeeeef      e	d<   dZ
eeeef      e	d<   deeef   fdZdeeef   fdZdefdZdeeef   fd	Zd
ddddeeeeef   eeeef      f      dedeeef   deeeef      dededededeeeeef      eeeeef      f   fdZdeeeeef   f   fdZddeeef   fdZd ZdddddZddZy) SceneDetailStepub   Step 16: 씬 상세 분석 (ThreadPool 병렬). visible_entities는 확정 데이터에서 구축.N_vca_prop_ctx_cache_vca_immobilized_ctx_cacher"   c                     | j                   (ddlm}  || j                  | j                        | _         | j                   S )Nr   ) load_printed_prop_anchor_context)r  ,app.core.steps.visual_continuity_anchor_stepr  r  r  )r  r  s     r7   _vca_printed_prop_contextz)SceneDetailStep._vca_printed_prop_context  s<    ##+ (H(2D$'''r9   c                     | j                   (ddlm}  || j                  | j                        | _         | j                   S )Nr   )'load_immobilized_subject_anchor_context)r  r  r  r  r  )r  r  s     r7    _vca_immobilized_subject_contextz0SceneDetailStep._vca_immobilized_subject_context  s<    **2 /V/2D+...r9   c           	         ddl }ddl}ddlm} t        t
        |j                  |j                  |j                  d}| j                         }|r|j                  di       |d<   | j                         }|r|j                  di       |d<   |j                  rd|d	<   dd
lm}m}  | |t!        | dd      xs i             rJddlm}	 ddlm}
 ddlm} ddlm} |j,                  |	j.                  |
j0                  |j2                  d|d<   |j5                  |j7                  |d      j9                  d            j;                         dd S )u   v12 (2026-05-02): Rule E/F/G/H + 신규 Rule J Primary Subject Framing
        (한 shot = 한 framing scale). 모델은 gemini-pro 유지.
        SCHEMA_VERSION 또는 PROMPT_VERSION bump 시 step_runner 가 stale cp 자동 감지.r   Nr#  )schema_versionprompt_versionrj   ra   ri   stampvisual_continuity_anchor_stamp immobilized_subject_anchor_stampTcard_spatial_rules_scopedr<   rC   )grounding_bundle_projection)grounding_central_acquisitionr?   rA   )writer
projectionacquisition_projectionbundlegrounding_sidecar_contract)	sort_keysr     )hashlibrG  rE  r$  SCENE_DETAIL_SCHEMA_VERSIONSCENE_DETAIL_PROMPT_VERSIONrj   ra   ri   r  r,   r  !card_spatial_rules_scoped_enabledrR   r=   r>   rT   rS   r  r  r@   rB   SIDECAR_WRITER_CONTRACT_VERSIONPROJECTION_CONTRACT_VERSIONACQUISITION_PROJECTION_VERSIONBUNDLE_CONTRACT_VERSIONsha256dumpsencode	hexdigest)r  r  _jsonr$  payload_vca_vca_immr=   r>   _bp_ca_rb_sws                r7   _config_hashzSceneDetailStep._config_hash  sE    	,99&.&E&E,4,Q,Q$,$A$A
 --/8<"8MG4588::B,,wPR:SG67 5537G/0	B
 5.5; = >ONL ==!==*-*L*L555G01 ~~KK4K077@

)+cr 	r9   c                     | j                  d      xs i }|j                  di       xs i }|j                  dd      xs d|j                  dd      xs ddS )u  D6 T5d: master_plan cp 의 sibling field (bg_catalog_hash + shot_binding_hash)
        를 dict 로 반환 — `_execute` return data 에 stamp.

        review iter6 minor note: scene_detail 은 binding hash 위주 (어느 shot 이 어느
        bg 에 매핑인지가 핵심). catalog hash 도 보조 stamp (drift 추적).

        legacy master_plan cp (D6 sibling 부재) / cp 자체 부재 → 둘 다 빈 문자열.
        preflight (T8) 가 빈 문자열을 mismatch 로 처리.
        background_master_planrp  bg_catalog_hashr(   shot_binding_hash)consumed_bg_catalog_hashconsumed_shot_binding_hash)rU   r,   )r  plans_cp
plans_datas      r7   _load_d6_consumed_hashesz(SceneDetailStep._load_d6_consumed_hashes%  sc     --.FGM2\\&"-3
(27H"(M(SQS*4..9Lb*Q*WUW
 	
r9         g       @)primary_max_workersretry_max_workersretry_sleep_secondstaskssystemschemaresultsfailedr  r  r  c       	           ( g (dt         f(fd}
ddlm} t        |      5 } || j                        }|D ci c]  \  }}|j                  ||||||      ||f  }}}t        |      D ]4  }||   \  }}	 |j                         }|r|j#                  |       0|dz  }6 	 ddd       dh}|dkD  rpt%               }|D ]7  }|j                  d
      }|j                  d      }|j'                  ||f       9 (D ch c]  }|j                  d      |v r
|d
   |d   f! }}|D cg c][  \  }}|j                  d
d      |r|j                  d      ndf|vr-|j                  d
d      |r|j                  d      ndf|vr||f] }}}|r(dd D cg c]$  }d|d
    d|d    d|j                  dd       d& }}t        j                  dt)        |      |       i }(D ]\  }t        |j                  d      xs d      j+                  d      s1|j-                  |d
   |d   fg       j#                  |d          ^ t/        j0                  |	       t        |      5 }  || j                        }!|D ci c]T  \  }}| j                  |!||||||j                  |j                  d
d      |r|j                  d      ndf            ||fV }"}}t        |"      D ]4  }|"|   \  }}	 |j                         }|s|j#                  |       |dz  }6 	 ddd       |D ch c]$  }|j                  d
      |j                  d      f& }#}i }$(D ].  }|j                  d
      |j                  d      f}%|%|#v r*||$|%<   0 t3        |$j5                               }&|dkD  rPd#j7                  d$ |&dd D              }'t        j!                  d%|t)        |&      t)        (      t)        |      |'       |||&fS c c}}w # t        $ r}ddl	m
}  ||      r!t        j                  d|j                           |
|||d       t        j                  d	|j                  d
      |r|j                  d      nd|       d}Y d}~d}~wt        $ rR} |
|||d       t        j!                  d|j                  d
      |r|j                  d      nd|       d}Y d}~Xd}~ww xY w# 1 sw Y   FxY wc c}w c c}}w c c}w c c}}w # t        $ r}ddl	m
}  ||      r!t        j                  d|j                           |
|||d       t        j!                  d |j                  d
      |r|j                  d      nd|       Y d}~d}~wt        $ r,} |
|||d!       t        j!                  d"|       Y d}~d}~ww xY w# 1 sw Y   xY wc c}w )&uT  1차 ThreadPool + retry path 의 shot-level typed failure handling.

        본 helper 는 `_execute` 에서 분리된 retry 코어. unit test 용이 + 운영
        retry policy 의 단일 진입점.

        흐름:
          1. 1차 ThreadPool (primary_max_workers): 각 shot 의 `_analyze_one`
             호출. AppError / 일반 Exception 모두 try/except 로 잡고 typed
             failure record 보존, retry 대상에 포함.
          2. failed > 0 시 retry path (retry_max_workers): 1차 실패 shot 만 재실행.
             retry_sleep_seconds 만큼 sleep 후. 같은 typed failure 처리.
          3. retry 후에도 failed > 0 면 `AppError(code=
             'step.scene_detail.contract_violations_after_retry')` raise.

        Returns:
            (results, failed) — results 는 성공한 _analyze_one 결과 append 된 리스트,
            failed 는 최종 실패 count (성공 시 0).

        Raises:
            AppError(code="step.scene_detail.contract_violations_after_retry") —
            retry 후에도 fail 잔존 시. typed summary message 포함.
        _stagec           	          t        |dd       xs d}j                  | j                  dd      |r|j                  d      nd ||t        |      d       y )Nr0  zstep.scene_detail.unexpectedr    r   r!   )r    r!   r0  stager1  )rT   r/   r,   r   )_seg_sh_excr$  _codefailed_shotss        r7   _record_failurez>SceneDetailStep._run_shots_with_retry.<locals>._record_failure[  sU    D&$/Q3QE#xxq97:cggl3t9! r9   r   )bind_current_budgetmax_workers)is_abortu   scene_detail 중단 — %sprimaryz1Scene %s Shot %s contract violation (primary): %sr    r!   Nprimary_unexpectedz)Scene %s Shot %s unexpected (primary): %sr%   1scene_detail.frame_spatial_contract_echo_mismatch_shot_indexr0  Sr4  (?r  z+Retrying %d failed tasks (typed last 5: %s)r(   z$step.scene_detail.contract_violationr1  )correctionsu$   scene_detail 재시도 중단 — %sretryz/Scene %s Shot %s contract violation (retry): %sretry_unexpectedzRetry unexpected exception: %s; c              3      K   | ]A  }d |d    d|d    d|j                  dd       d|j                  dd       d	|d
   dd  
 C yw)r6  r    r4  r!    [r&  r8  /r0  ]: r1  Nx   rw   )rq   fs     r7   rs   z8SceneDetailStep._run_shots_with_retry.<locals>.<genexpr>  so      ! )A Am$%U1\?*; <EE'#&'qvc):(;3q|DS?Q>RT(s   AA	u   scene_detail: %d shots failed after 1 retry (미해결 %d · 시도 이력 %d) — 성공 %d건은 partial 로 보존한다. Last 5: %s)r   app.core.image_call_budgetr-  r   _analyze_onesubmitr   r   r   app.core.run_controlr0  rv  rw  r1  r,   rW   errorr/   r   r   r   r   
setdefaulttimesleepr   valuesr   ))r  r  r   r  r   r!  r"  r  r  r  r,  r-  pool_oner   shfuturesrB  r'  r(  rJ   r  r0  _NON_RETRYABLE_CODES	done_keysrc   shot_idxnon_retryable_keysretry_tasks_last5_why_fpool2_one2futures2	_resolved_by_keyrx   
unresolved_summaryr+  s)                                           @r7   _run_shots_with_retryz%SceneDetailStep._run_shots_with_retry6  s<   F .0	S 	 	C,?@D&t'8'89D  %$GC D#r3?#rJ$   "'*#AJ	c
A8 NN1%aKFE + A\ !TTA:IUU=)55/r8n-  &"%A55=$88 =!1\?3%  " */).gc2GGM1-,.BFF<(D:AJKWW]A.-/RVVL)T;BTU b	    *"#.. -()q.?qvcAR@SSTU.   A$f
 02&B266&>/R0;;<bcM):B|<L(MrRYYZ\]fZgh ' 

./'4EF%/0A0ABE
 (3	  (3GC UCS&&15377=RS;TSU266,;O[_;a 2b % cehjldmn (3	    *(3$,QK	c% !
A, #NN1-"aKF7 4 Gn CJ
BIQQUU=!155#78' 	 
 02A}%quu\':;AI~GAJ	 
 '..*+
A:yy ! $BC! H
 LLVJ\):CL( 
**w    >  }'CS[[Q#D#sI>NNK/14-$	 A  #D#s4HILLC/14-$	 A9 A@j"
    ( %E  (} &$JCKK!Y %+D#sGD"LL Q $ 79< 5$ #	 %( %+D#s<NO"LL)I3O$%; GFl
s   S'#O;S'7PS'>$S4)A S9)S?%W=ATW0T
 WW()W;S'	S$
A6R S'S$ASS'S$$S''S1W
	WA4VWW!W :W WWWc           
         	 t        dd      }t        dd      }|j                  xs i }|rg }|j	                  d      r|j                  d|d           |j	                  d	      r|j                  d
|d	           |j	                  dg       D ]@  }|j	                  d      dv s|j                  d|d    d|j	                  dd              B |r|ddj                  |      z   z  }|j	                  dd      }|r|d| z  }|j                  }|*||j                  dd      z  }||j                  dd      z  }ddl	m
}	 | |	| j                        z  }||fS # t        $ r t        dd      }t        dd      }Y iw xY w)u~  system prompt + JSON schema 빌드.

        2026-05-11 redo-shot service 재사용용 helper 로 추출. 본 메서드는
        `_execute` 의 setup 단계와 1:1 동일 — system prompt 에 visual_world_rules
        / planning_context 를 동적으로 inject 한다.

        Returns:
            (system, schema) — system 은 inject 완료된 최종 system prompt.
        scene_detailr  detail_schemascene_extractor_v2turn_scene_detailscene_detail_schemaerau   시대: regionu   지역/국가: rules	rule_type)time_periodcostume
technology[] visual_guideliner(   uc  

## 시각적 세계관 (T2I 프롬프트 생성 시 반드시 반영)
아래 설정을 모든 T2I 프롬프트에 반영하세요.
특히 지역/국가 설정에 맞는 인종, 건축양식, 복장, 소품을 사용하세요.
참조 이미지가 없는 인물/배경/소품은 아래 세계관에 기반하여 최대한 구체적으로 묘사하세요.
rl   t2i_contextu  

## T2I 시각 스타일 참고 (복사 금지)
아래는 프로젝트 전체의 시각 톤입니다. t2i_prompt 에 이 문장을 그대로 복사하지 마세요.
색감/조명/분위기만 간접 반영하고, 지명이나 장소 열거는 t2i_prompt 에 넣지 마세요.
	tone_moodu   ## 기획서: 톤/분위기visual_conceptsu   ## 기획서: 비주얼 컨셉r   )project_corrections_block)r   r   rW   world_rulesr,   r/   r   planning_contextinject_if_availableapp.core.creator_correctionsrs  r  )
r  r   r  r   rdworld_linesrJ   _t2i_ctxpctxrs  s
             r7   _build_llm_inputsz!SceneDetailStep._build_llm_inputs  s   	N :F AF __"Kvve}""Xbi[#9:vvh""_R\N#CDVVGR(55%)QQ&&1[>*:"QUUCUWY=Z<['\] )   99[1	2 vvmR0H "
$ ##d..{<YZZFd../@BbccF 	K+DOO<<v~W  	N !57JKF !57LMF	Ns   E !FFc                    t        |       j                         }|j                  }|j                  }|j                  }| j                  |      \  }}g }d}	t               }
|j                         D ]I  \  }}|r*|D ]$  }|
j                  ||j                  d      f       & 2||vs7|
j                  |d f       K | j                  d      }g }t               }|rd|j                  di       j                  d      rB|d   d   D ]6  }|j                  d      s|j                  d      |j                  d      f}||
v rt        |d	
       |j                  j                  |j                  d      |j                  d      f      xs g }|j                  j                  |j                  d       d|j                  d             xs i }t        |t              r|j                  dd      nd}d}t        |t              r8t!        |d|j                  d       d|j                  d       
      t"        k(  }|j                  d      }t%        |d|j                  d      i|d|ind       }t'        |j                  dg       xs g       }t)        ||j                  d      |      }|xs g D ch c]7  }t        |t*              r%|j-                  d      r|j/                  d      d   9 }}t1        t        |t              r|nd ||      }||d<   ||d<   t3        |j                  d      |j                  d      |      }|*|j                  d      t4        j7                  d||       q|j                  dg       xs g D ][  }t        |t              s|j                  d      dk(  r)	 t9        |d	       tA        ||||      sEt4        j7                  d|        n% |j                  |       |jC                  |       	t4        jE                  d |       !t4        jE                  d!|       9 |rt4        jE                  d"tG        |             t               }!|d#k7  r|r|j                  d$      d%k(  r|j                  d&      }"|"rl|"| jI                         k(  rX|j                  d'      xs g D #ch c]$  }#|#j                  d      |#j                  d      f& }$}#|j                  di       xs i j                  dg       D ]  }|j                  d      |j                  d      f}||v s||!v r.||
vs||$v r7|j                  d      sI|j                  d$      d(k(  r6t4        jE                  d)|j                  d      |j                  d             t        |d*
       |jC                  |       |!j                  |        |!rAt4        jE                  d+tG        |!      tG        |$             n|"rt4        jE                  d,       g }%|D ]  }&|&j                  dd      }|j                  |g       }'|'r7|'D ]1  }||j                  d      f}||v s||!v r|%jC                  |&|f       3 `||vse|d f|vsl|d f|!vss|%jC                  |&d f        |jK                  |       t4        jE                  d-tG        |      tG        |%      tG        |             | jM                  |%|||||	.      \  }}	}(|jO                  d/ 0       tG        |      tG        |      z
  })| jQ                         }*tG        |      tG        |%      tG        |      z   tS        dtG        |%      |)z
        tT        | jI                         d|i|*d1}+|(rZ|(D #,-cg c]G  }#|#j                         D ,-ci c]  \  },}-|,d2k7  s|,|- c}-},d2|#j                  d2      xs dd d3 iz  I c}-},}#|+d'<   |+S c c}w # t:        $ r=} | j<                  dk7  r t4        j7                  d|| j>                         Y d } ~  -d } ~ ww xY wc c}#w c c}-},w c c}-},}#w )4Nr   r!   ra  rp  scenesr  r    r4  zscene_detail._user_editedr5  r  camera_directionr(   Fz!detail_steps._user_edited_reuse Sr4  re  r   r   r  rl  rm  r   render_prompt_card_hash)r  r  r  u\   scene_detail _user_edited %s: card contract violated (%s) — reuse 거부, fresh 재생성.
confidencelegacyr*  u7   scene_detail _user_edited %s: contract violation — %suL   scene_detail _user_edited %s: owned drift — reuse 거부, fresh 재생성.u@   scene_detail: user-edited shot %s contract violation — refreshuA   scene_detail: user-edited shot %s no longer selected — droppingz,scene_detail: %d user-edited shots preservedforcestatuspartialconfig_hashr+  contract_violationu]   scene_detail: S%s shot%s 는 규칙 위반 상태 — 재사용하지 않고 다시 만든다zscene_detail._partial_reuseus   scene_detail: partial resume — 직전 성공 %d shot 재사용 (config_hash 일치, 실패 %d shot 만 재호출)u`   scene_detail: 직전 cp config_hash 불일치 — 전량 재생성 (prompt/schema/anchor 변경)uF   scene_detail: %d segments → %d tasks (shot-based, %d edited skipped))r  r   r  r   r!  r"  c                 P    | j                  dd      | j                  d      xs dfS )Nr    r   r4  rw   rJ   s    r7   rM   z*SceneDetailStep._execute.<locals>.<lambda>^  s#    AEE-$;QUU==Q=VUV#Wr9   r   )completed_countapplicable_countfailed_countr  r  rp  r1  i,  )+r   load_allsegmentsselected_mapr   r|  r   r   r   r,   rU   r   rh   rJ  r-   r.   r   r   rj  r   r   r   r   r  r   r  rv  rw  r   r   r0  r1  r  r/   infor   r  extendr_  sortr  maxr  ).r  moder   r  r  r   r  r   r!  r"  current_shot_keysrc   shotsrN  existing_detail_cpedited_resultsedited_keysr   r   _owned_for_var_staging_cam_dir	_is_close_g41_shot_idx_ue_g41_card_inputs_ue_used_pairs_ue_visible_uer   _visible_chars_ue_state_var_chars_ue_g41_card_violation_varr  reused_keys
_prev_hashrB  _prev_failedr  r   shots_for_scener+  new_results_count	d6_hashesr0   rx   r   s.                                                 r7   _executezSceneDetailStep._executeC  s    !&//1 <<''-- //4 "%(..0IBB%))2rvvl/C*DE  
 \)%))2t*5 1 "77Ge"4"8"8"D"H"H"R'/955(55/}1EFC//
 7%@ *-)C)C)G)GUU=11553GH* *  ' $'??#6#6 uu]34AaeeM6J5KL$ $  !
  *(D9 %LL);R@?A ! %*	%h5 :$,+,,-EE-,@+A B//0uu]/C.D)F!" $1!1 & ,-55+?(.B #!.m0D E $4#? "./? @%)/+ *EEE"2B7=2* '?}!57G' ;F:K:K7:K#)#s3s8K  IIcN1-:K * 7 /J(28T(BH-s/+ ES+,@AEX+,AB.Q().B(C().G(H(;/+ 0; !&: ; G"NN!S #%8 %$%EE*:B$?$E2$ED#-dD#9 (#xx5A (& 9$@[ \  D $nh	  !'%E$'!"
 !&7 %F: (OOC0*11!4$^`c $gilmU :V KKFKHXY" 5GO""&&x0I=+//>JjD,=,=,?? 144^DJJ J UU=)155+>?J    -00<BGGRTUA55/}1EFCk)SK-? "33sl7J  55!12  uuX*>>FEE-0!%%2FH !2!> #))!,OOC(= V> KKMK(#l*;
 4 C*B-11"b9O)Brvvl34Ck)SK-? LL#r+	 *
 <':[0b$Z{5RLL#t-   	~&\M3u:s;/?	A )-(B(BSF )C )
%
 	WXL3~+>>113	"7| #E
S-@ @3u:0A#AB9,,.w4)4
  &# &A #$'')>)$!QqI~A)>quuY/52t<=>%#C
 
Q7L $, 	&#&88/H#H$) &$]$'!" !&	&r J ?#sB   8<_67_;)a a8a	a	!a;	a1`<<a	ac                   EFGHI ddl m} ddlm}m}m}m}m}m} t        | dd      }|&| j                  d      }	|	s |ddgd	ddg d
      S |	}|j                  di       j                  dg       xs g }
|
D ]  }t        |d        |
s |ddgd	ddg d
      S ddlm} ddlm}  ||       F|j"                  dv }g I|r	 Fj%                         }nFj%                         }|r	 Fj-                         }nFj-                         }dGdHFGHIfd}g }g }g }g }|j                  dd      }|t.        k(  }|s'|
r%t0        j3                  d|t.        t5        |
             ddlm}m}m} |
D ]  }|j                  dg       xs g }|j                  d      }|j                  d      }|s|j)                  ||f       R|j                  d      dk(  r|j)                  ||f       |sn|j                  d      }|j                  d       } || |j)                  ||d	f       n	  ||d!| d"| d#        ||      | k7  r|j)                  ||d$f       d}!|d%|i}! |       }"|"|j)                  ||d&f       n/	 t?        |"d|i|!'      }#|#jA                         D $%ci c]  \  }$}%|$d(k7  r|$|% }&}$}%tC        |      }'tE        |"||      }(|(xs g D )ch c]7  })tG        |)tH              r%|)jK                  d)      r|)jM                  d*      d   9 }*})tG        |tN              r|j                  | d+|       nd}+tQ        |+|*|"      },|'|&d,<   |,|&d-<   	  |dYi |&}- ||-      }.|.| k7  rCtS        | |"|||'|,.      }/|/| k7  r|j)                  ||d/f       nt0        jU                  d0||       	 |D ]  }1|1j                  d7      }2|2|j)                  ||d	f       ,	  ||2d8| d9|        |j                  ||fg       }3| d+| }4tG        |tN              r|j                  |4      nd}5d:}6tG        |5tN              r|5j                  d;d:      xs d:}6d}7tG        |5tN              rt]        |5d<| d=|       t^        k(  }7|7r|n|}8|2j                  d>      |8k7  r|j)                  ||d?f       |1j                  d@d:      }9|2j                  dA       ||9      k7  r|j)                  ||dAf       @|2j                  dB       ||3      k7  r|j)                  ||dBf       p|2j                  dC       ||6      k7  s|j)                  ||dCf         t5        |
      }:g };Ir|;ja                  I       |r%|;j)                  t5        |       dD|: dE|ddF         |r"|;j)                  t5        |       dG|ddF         |r"|;j)                  t5        |       dH|ddF         |D <cg c]  }<|<dI   d/k7  s|< }=}<|D <cg c]  }<|<dI   d/k(  s|< }>}<|>r#t0        j3                  dJt5        |>      |>ddK        |=r"|;j)                  t5        |=       dL|=ddF         |;rtc        |=      xr" te        dM |=D              xr t5        |=      |:k\  }?Ist5        |      |:k\  s|?rd	ndN}@h dOEtg        EfdP|D              }Atg        EfdQ|=D              }BIs|rdR}CnAsBrdR}Cn	|s|=rdS}CndR}C |d|;@|:t5        |      t5        |      z   t5        |      z   t5        |=      z   ||||IdTCU      S |:dg d
}D|>r|>DdV<    |dWg dXD      S # t&        $ r+}i }Ij)                  d|j*                          Y d}~d}~ww xY w# t&        $ r+}i }Ij)                  d|j*                          Y d}~d}~ww xY wc c}%}$w c c})w # t&        $ r |j)                  ||d&f       Y .tV        $ r2}0t'        d1d2| d"| d3tY        |0      jZ                   d4|0 5      |0d}0~0ww xY w# t&        $ r |j)                  ||d&f       Y w xY w# tV        $ r |j)                  ||d6f       Y w xY w# tV        $ r |j)                  ||d6f       Y w xY wc c}<w c c}<w )Zu  Group 1 #3 + G3.2: scene_detail 산출물 검증.

        검증 항목:
        - data.scenes 존재.
        - 각 result 에 ``t2i_variations`` 1+개 (빈 배열은 partial).
        - (G3.2 Task 19) 각 variation 에 owned_validation sentinel 존재 + shape OK.
        - (G3.2 Task 19 / round 4 BLOCKING 2 / round 5 BLOCKING 3, 4) 4 종 drift 검증:
            validator type / t2i_prompt_hash / owned_hash / camera_direction_hash.
            close-skip variation 도 hash 일치 강제.
        - (G3.2 Task 19) ``result["status"] == "contract_violation"`` 이면 partial 마킹.

        source 우선순위: step_runner 가 보존한 ``_last_execute_result`` → cp
        fallback. 둘 다 없으면 missing.

        framing_scale enum SOT v1 (2026-05-15): staging.framing_scale enum
        helper read — 생성 path 와 동일한 enum 소비 (regex 폐기).

        partial / blocked 시 manifest ``allow_partial_downstream=False`` 가
        downstream 차단.
        r   )CompletionReportr  _last_execute_resultNra  Fu&   scene_detail cp + result 모두 부재r  )total_resultsr  failed_indices)is_completer  severitymetadatarp  r~  zscene_detail.verify_completionr5  u&   scene_detail data.scenes 빈 리스트r#  r   >   r2  r3  z!owned loader contract violation: z#staging loader contract violation: c            
      <   rS d	 j                         S # t        $ r+} d j                  d| j                          Y d } ~ S d } ~ wt        t
        t        t        t        t        t        t        f$ r!} d j                  d|         Y d } ~ S d } ~ ww xY w)NTz)scene_context loader contract violation: z"scene_context loader infra error: )r  r   r/   r1  AttributeErrorr  KeyErrorr  RuntimeErrorOSErrorImportErrorr   )r  _loader_verify_ctx_verify_ctx_loadedloader_violationss    r7   _ensure_verify_ctxz=SceneDetailStep.verify_completion.<locals>._ensure_verify_ctx  s    !""!%%..06 5  "!((?}M 0 ) 	8Zg{O " #!((8>  +s     	B A

0B:BBr  u   verify_completion: card check bypass — result schema_version=%s != current=%s (step_runner force escalate path expected, %d scene results affected).)r  rr  rs  r   r    r4  r  r  r   r  zverify_completion(sr(  r  	self_hashr!   recompute_failedre  r   r   r  r  rl  rm  )rZ   rk  rc   rd   rl  rm  hashzverify_completion: s%s_sh%s card hash drift resolved by upstream shot_dependency recompute (downstream shot_dependency_t2i false-positive)zstep.verify_crashedz#build_render_prompt_card crashed (sr  r   r/  shaper  zscene_detail.verify s_shotr(   r  z detail_steps.verify_completion Sr4  r  validator_typer   r  r  r  r?  z1 scene/shot result(s) have empty t2i_variations: r  z owned contract_violation: z$ owned_validation sentinel_drifted: r   u   verify_completion: %d render_prompt_card hash-only drift — build 이후 하류 재산출로 인한 가짜 드리프트, 차단하지 않음 (예: %s)r  z# render_prompt_card drift/missing: c              3   .   K   | ]  \  }}}|d k(    yw)r  Nrp   )rq   _si_shitags       r7   rs   z4SceneDetailStep.verify_completion.<locals>.<genexpr>	  s       ?7G^S$y 7Gs   r  >   r  r  r  r  r  c              3   .   K   | ]  \  }}}|v   y wrv   rp   rq   r  r  r  _STRUCTURAL_TAGSs       r7   rs   z4SceneDetailStep.verify_completion.<locals>.<genexpr>/	  s"      +>NNCs''>N   c              3   .   K   | ]  \  }}}|v   y wrv   rp   r  s       r7   rs   z4SceneDetailStep.verify_completion.<locals>.<genexpr>2	  s"      '>NNCs''>Nr  contract_driftinvariant_drift)r  r  r  contract_violationssentinel_driftedcard_driftedr  )r  r  r  r  originignored_card_hash_driftTcleanrp   )4app.core.integrity_reportr  r  r  r  r  r  r  r  rT   rU   r,   r   rE  r$  #app.core.steps.scene_context_loaderr   rL  _load_chain_bg_owned_by_shotr   r/   r1  _load_staging_mapr  rv  rw  r   ru  r  rr  rs  rj  r   r   r   r-   r   r   r  r.   r   r  r  rW   typer  r   r   r  rf  allr   )Jr  r  r  r  r  r  r  r  r   r_   r~  rJ   r$  r   _bg_onowned_by_shotr  rJ  r  r  r  r  r  _result_schema_version_card_check_active_g41_assert_card_shape_g41_build_card_g41_compute_card_hashtvarsrc   rd   _stored_card_stored_card_hash_shot_info_for_v_ctx_for_recompute_card_inputs_vrx   r   _builder_inputs_v_used_pairs_v
_visible_vr   _visible_chars_v
_staging_v_state_var_chars_v_recomputed_card_recomputed_hash_fallback_hashr)  r   r  r  staging_keystgcam_dir_nowexpected_closer  	v_t2i_nowtotalmissing_msgst_card_structural_card_hash_only_all_card_missingr  _has_structural_sentinel_has_structural_cardr  _clean_metar  r  r  r  r  sJ                                                                        @@@@@r7   verify_completionz!SceneDetailStep.verify_completionu  s
   * 	?	
 	
 5t<>++N;B' %0X/Y&/0!WYZ 
 FFB'++Hb9?R
 A*14TU #!,T+U"+,aSUV  	-J$T*))-JJ') ' D D F $@@BM %779 "335K "!	F  "$&!#  !',<a!@48SS
 "fNN. '(CS[		
 	

 AEE*B/52E}%B%%&C%%r3i0uuX"66#**B95
 &  uu%9:$%EE*C$D!'+<+D ''S)(<=..($7t3se1"E 2,?CTT(//S+0FG
 FJ(?0<c/B, .@-A*-5 )//S:L0MN].1E(:)6(;.>2" 6D5I5I5K5"5KTQ'(Ez %&qD5K !2 5" 1LE0R-E$6C."
 CMBRPRBR>"BR#'1#s';s@S %(IIcN1$5BR !1 >" (2+t'D %0OOrd!C5M$BJN !+ 6Q$.0@BT6" 2 KX 12F GK] 12G H4!.7F 8&*;8&$4 8N(88&$4" (8;L'L -Z7;9K353CP4F-. )7 ,:=N+N,8,?,?13S&0A-. -3KK1B DFs-. 77#56#$++Ri,@A/ *?t5(N "/!2!2B9b!A!#AcUm6@d6Skook2Y] c4("%''*<b"A"GRK "'c4(2$DRDcU"S )	) # 3A.- # <<,0BB$++R6F,GHGGL"5	<< 126Mi6XX$++R6G,HI<<-1CN1SS$++Rl,CD<< 78<Y=  %++R6M,NOo q b F"$ 12~&'q 0##1"1#5"68 *+,,G&r*+-  '())M#BQ'(* (4F|!qtv~A|F&2Eladfn1lENN 1?2A3F
 '())L#BQ'(*  !%%5 6 !13 ?7G? < !1&'50 
 %~&%/$    A'* +>N+ ($ $' '>N' $  !$7))-A)!%5*)#!$!%*N+123./0 ./0
 '5+>(8$0): % * #A'
  6EK12b7 
 	
}   "!((7}E    !((9#++G j5">"8 (0 !&$0$7$7)+S2D(E%& (1 !0*2-B/113CuC/3Dz/B/B.C2dV-M+& ,0%0!0] $, S , 3 3R>P4Q RSG % @$++Rg,>?@H ! $++Rg,>?` GEs   ] )]: (`= `#^1<^7^<a!)b7bb
b
	]7 ]22]7:	^. ^))^.<``$-```:9`:=aa!bbF)r\   prop_term_mapretried_modelc                   ddl m}m}m} ddlm} ddlm} ddlm	} |D cg c](  }t        |t              r|j                  d      dk(  r|* }}|syd	|	 d
|
 }d}d}d}i | j                  xs i |d|ii}|r|fn| j                  |f}t        |      D ];  \  }}	  |||||t        || j!                  dg|rdgng z   |	|
|dz   d            } n |y|d   }|d   } 	  || |d|      }!	  ||||!|t        | j                  | j!                  ddg|	|
d      |j                  d      xs |j                  d      xs d !      }" ||"|!||xs g |#      }" ||"      rt&        j)                  d$|       y|d   }#||d<   d}$	  ||      }%|%rx|sLt&        j)                  d%|t/        |%      |       |#|d<   | j1                  |||	|
||||||||d&'      |$s|#|d<   S S t&        j)                  d(|t/        |%             	 |$s|#|d<   yy |||       |!|d<   d&}$t&        j3                  d)|t%        |             ||!|"f|$s|#|d<   S S c c}w # t"        $ rS}|dk(  r,t%        |      dkD  rt&        j)                  d|||       Y d}~t&        j+                  d||       Y d}~ yd}~ww xY w# t,        $ r!}t&        j)                  d||       Y d}~yd}~ww xY w# t"        $ r!}t&        j+                  d"||       Y d}~yd}~ww xY w# t,        $ r)}t&        j)                  d*||       Y d}~|$s|#|d<   yyd}~ww xY w# |$s|#|d<   w w xY w)+u  FINDING 7 (e2e-bughunt-v1): owned redraw violation 1-attempt atomic repair.

        owned judge 가 검출한 redraw_violation evidence 로 repair LLM 을 1회 호출 —
        위반 t2i_prompt 를 owned redraw → anchor/omit 으로 좁게 rewrite. 후보를
        owned merge + owned judge + ID contract + visible_entities contract 로
        전부 재검증하고, **모두 통과할 때만** variation 에 적용한다 (atomic). 어느
        하나라도 실패하면 원본 variation 을 보존하고 None 을 반환 → caller 가 기존
        대로 contract_violation 마킹.

        retry budget = 1 — repair LLM 은 1회만 호출, 무한 루프 없음. 본 메서드는
        non-close + owned 1+ 분기에서만 호출된다 (close framing 은 owned redraw
        불가, owned 부재는 violation 불가).

        Args:
            variation: ``result["t2i_variations"]`` 안의 대상 variation dict
                (성공 시 in-place 갱신).
            original_t2i: repair 전 t2i_prompt.
            owned: chain_bg owned 객체 목록.
            judge_violations: 1차 ``run_owned_judge`` 결과 violations (verdict 포함).
            cam_dir: shot 카메라 방향.
            result: scene_detail shot 결과 dict (재검증 validator 입력).
            name_by_short_id: ``ctx.name_by_short_id`` (visible validator 입력).
            check_prompts_fn: ``_analyze_one`` 의 ``_check_prompts`` closure.
            si / shot_idx: 로깅용.

        Returns:
            ``(repaired_t2i, repaired_merged_usage, repaired_violations)`` — 전부
            재검증 통과 시. ``variation["t2i_prompt"]`` / ``["owned_object_usage"]``
            도 함께 in-place 갱신된다.
            ``None`` — repair 실패/revert. 원본 variation 불변.
        r   )has_redraw_violationmerge_owned_object_usage&reconcile_owned_prop_namespace_overlaprun_owned_judge)run_owned_repair"validate_visible_entities_contractverdictredraw_violationNzscene_detail owned_repair Sr4  gptscene_detail_owned_repairmodelowned_repairowned_repair_retryr%   )r    r!   attempt
extra_tagsextra_metadata)r   r   redraw_violationsr  call_structured_fnrC   opik_metadatau=   %s: 1차 수리 실패 — 상위 모델(%s)로 재시도: %su6   %s: owned repair 재시도까지 실패 — revert: %sr   owned_object_usageFrb   r6  u;   %s: repaired owned_object_usage merge 실패 — revert: %sowned_judgeowned_repair_verifyr    r!   representative_momentstill_frame_promptr(   r   r   r  r  r  rC   r  shot_intentu=   %s: repair 후보 owned judge 재검증 실패 — revert: %s
violationsr  r   r\   r  u7   %s: repair 후에도 redraw_violation 잔존 — revertu=   %s: 수리본 ID 위반 %s — 상위 모델(%s)로 재수리T)r   r  rc   rR  r   judge_violationscam_diroriginal_t2icheck_prompts_fnrA  r\   r  r  u+   %s: 재수리본도 ID 위반 %s — revertu=   %s: owned redraw repair 성공 — %d redraw_violation 해소u>   %s: repaired prompt visible/id 재검증 실패 — revert: %s)r  r  r  r  app.core.steps._owned_judger
  app.core.steps._owned_repairr  #app.core.visible_entities_validatorr  r-   r.   r,   rC   	enumerater   build_opik_metadatarW   r   rv  rw  rG  r   r   _attempt_owned_redraw_repairr  )&r  r  r)  r   r'  r(  r   rA  r*  rc   rR  r\   r  r  r  r  r  r
  r  r  r   redraw_evidencer6  _RETRY_MODEL_TAG
repair_out_upper_planr  pcr  cand_t2icand_echo_rawcand_mergedcand_violations_orig_prompt	committedid_bads&                                         r7   r0  z,SceneDetailStep._attempt_owned_redraw_repair]	  s;   H	
 	

 	@A	

 (
'!!T"quuY'7;M'M ' 	 
 -bTxjA" *
OT((.BO7NO*	1D1Df0M$U+KGR-+5&5'6#%"&":":$2#36=122$G796>3:Q;(@ #; #
  ,2 l+"#78	2uuEK	-#5[!(_#22"66 -/DE35X#N 7  JJ67 >zz"67>;=O0 A&*-3'
  0NNTV[\
 !."*	,	)	7%f-F %NNWvf~|= /;Il+<<%r!))97%1)9)9)9&3&* = 
6 *6	,' ! A5&QW.Z *6	,'  /v7GH /:I*+IKKWs?35k?; *6	,' 
^  a<CJNNNW|S2 U"C)*  	NNX #'	0  	LLX%	|  	NN[ #'*6	,' 	
 *6	,' s   -I4I#J* 0AK AL  L 	4L 	J'+J"J""J'*	K3KK	L K<<L	L6L1$L9 1L66L9 9
Mc                 ^H   |j                  dd      |j                  dd      }|j                  j                  g       }|j                  j                  d      }	|r|j                  d      nd "f|j                  v r|j                  f   }
n|}
i }|j                  rk|j                  j                  dg       D ]L  }|j                  d      xs |j                  dd      }|j                  d	d      }||f}|r|||<   |sH|||<   N i }|j                  r|j                  j                  d
g       D ]  }|j                  d      k7  r|j                  dg       D ]n  }|j                  dd      }|j                  d      xs |j                  dd      }|s=|s@||v sE||   }||vrg ||<   |||   vs[||   j                  |       p  |
D cg c]  }|j                  d      s| }}g }|
D ]s  }d}dD ]K  }|j                  j                  |g       D ]*  }|j                  d      |k(  s|j                  d	d      } K M |j                  |r	d| d| dnd|        u i }|D ]N  }|j                  j                  dg       D ]-  }|j                  d      |k(  s|j                  d	d      ||<    N P d	t        dt        fd}i }|j                  j                  dg       D ],  }|j                  dd      }|s|j                  d	d      ||<   . i }|j                         D ]3  \  }} ||      }|s|j                  |g       j                  ||f       5 g } |j                         D ]l  \  }}!|!D cg c]  \  }}||v s||f }"}}t        |"      dk  r0|"j                  d        |"d   \  }#}$|"dd  D ]  \  }%}&| j                  |#|%|$|&f        n g }'i }(|D ]e  }|j                  |g       })|)sd|(|<   |'j                  d| d| d       6t        |)      dk(  r&|)d   d   dk(  rd|(|<   |'j                  d| d       jt        |)      dk(  r3|)d   d   |(|<   |'j                  d| d|)d   d    d|)d   d    d        t        |)      dk\  s|)D cg c]  \  }}|dk7  s||f }*}}t        |*      dk(  r4|*d   d   |(|<   |'j                  d| d|*d   d    d|*d   d    d        |*r2d!j                  d" |*D              }+|'j                  d| d#|+ d$       Ld|(|<   |'j                  d| d%       h dd&lm}, |r|,j                   }-|j                  dd      n7|j"                  j                  g       }.|.rt        |.      n|,j$                  }-d |d}/n|j                  d|j                  d'd            }0|j&                  j                   d(|0       xs i }1t)        |1t*              r|1j                  d)d      nd}2t-        |1d* d+|0 ,      t.        k(  }3t1        |0|j2                  |j4                  |j6                  |j8                  |,j:                  |,j<                  |,j>                  |3-
      }/|/d. d/|	 d0z   }4|r|4d1|d    d2|j                  d3d       d4z  }4|j                  d5      }5|5ry|j@                  j                  i       j                  |5      }6|6rC|4d6|5 d7|6j                  d8d       d9|6j                  d:d       d;|6j                  d<d       d4	z  }4n	|4d=|5 d4z  }4tC        fd>|jD                  D        i       }7|7j                  d?g       d d }8|7j                  d@g       }9|8s|9rn|4dAz  }4dBdCdDdE}:|8D ]B  };|;j                  dFd      }<|:j                  |<|<rd7|< dGnd      }=|4dH|;d    d+|;d    |= d4z  }4D |9d d D ]  };|4dI|;d    d+|;d    d4z  }4 |4dJz  }4tG        |jD                  |jH                        }>|>rdK}?t        |>      |?kD  r"tJ        jM                  dLt        |>      |?       |4dMz  }4dN}@|>d |? D ]  }A|Ad3   }B|4dO|Ad    d+|Ad    dP|B d4z  }4|Aj                  dQ      xs g }C|Cs5t        C      @kD  r"tJ        jM                  dRt        C      @       dSj                  dT Cd @ D              }D|4dU|D d4z  }4 |4d4z  }4|j                  d|j                  d'd            }E	 tO        E      }Fg }GFz|jT                  v rl|jT                     D ]Z  }H|Hj                  dVg       xs g }ItW               }J|ID ]  }K	 JjY                  tO        K              FJv sJGj                  H       \ GrdWdXdYdZ}Li }M|
D ]`  }N|Nj                  d      s|j                  j                  dg       D ]-  }O|Oj                  d      Nk(  sNMOj                  d	d      <    ` b |4d[z  }4GD ]  }H|Hj                  d\d      }PLj                  |P|P      }Qd]|Q d^|Hj                  d_d       }R|Hj                  d`d      }S|Sr*Mj                  Sd      }T|TrRdS daT dz  }Rn	RdS dz  }R|4R dbHj                  d3d       d0z  }4 |r|4dcd4j                  |      z   d0z   z  }4n|4ddz  }4|'r|4ded4j                  |'      z   d0z   z  }4|r d(|j                  d|j                  d'd             }U|j&                  j                  |U      }V|VrVj                  d)d      }W|Vj                  dfd      }X|Vj                  dgg       }Y|Vj                  dhd      }Z|Vj                  did      }[|4dj|Z d4z  }4|[r	|4dk[ dlz  }4Vj                  dmdn      }\|\r\dnk7  rt[        \      }]|4do|\ dp|] d4z  }4|4dqX d4z  }4Yrg }^YD ]q  }^j                  t]        |j                  drd      |j                  dsd      |j                  dtd      |j                  dud      |j                  dvd      w             s |4dxd4j                  ^      z   d4z   z  }4Vj                  dyg       }_|_r.tW               }`dD ]I  }|j                  j                  |g       D ](  }|j                  d	      s`jY                  |d	          * K g }a_D ]  }|j                  dzd      }b|j                  d{d      }c|j                  d|d      }d|d}   }e|j                  d~      xs d}f|d   }gde |frdf ndz   }hdg }ibr#b`vraj                  db dc d!d h i        aj                  db d2c d!d h i         |4dt_        d      j                  a       d4z  }4|4d4z  }4n!|j`                  r|4d|j`                  z   d0z   z  }4|4dz  }4n%|jH                  j                  g       }j|jr@g }kjD ]  }lkj                  d|ld    d2|ld3           ! |4dd4j                  k      z   d0z   z  }4|j"                  j                  g       }.|.rg }mtc        |.d      D ]~  \  }n}o|oj                  d'      rdoj                  d'd       dnd}pmj                  dn |p d2oj                  d	d       dp|oj                  dd       d|oj                  dd       d
        |4dd4j                  m      z   d0z   z  }4| rd uxr f|j                  v }q|qradg}r|rj                  d       | D ]%  \  }#}%}s}trj                  d|% d|t d|# d|s d	       ' rj                  d       |rj                  d       ndg}r|rj                  d       | D ]%  \  }#}%}s}trj                  d|% d|t d|# d|s d	       ' rj                  d       |rj                  d       |rj                  d       |rj                  d       |rj                  d       |rj                  d       |rUrj                  d       |rj                  d       |rj                  d       |rj                  d       |rj                  d       |4d4j                  r      d0z   z  }4|4d|- d|s|j"                  j                        rdndz   d| z   z  }4ddl2m3}um4}v dd&lm}w |
xs g D ch c]7  }t)        |t              r%|j                  d      r|jk                  d      d   9 }x}g }ytW               }z|j                         D ]E  \  }{}||{xvr|D ]6  \  }}}~|}s	{}fzvsyj                  {}d       zjY                  |{|}f       8 G |(j                         D ];  \  }{}}|{xvr}s{}fzvsyj                  {}d       zjY                  |{|}f       = tm               }to        |      }|rj                  d      nd }rt-        d d+ ,      t.        k(  nd}d }r|jp                  xs i j                  f      }|j8                  j                  f      }|ts        dd d dd      tu              ng }|j6                  j                  f      }|j4                  j                  f      }ng }d }d }wjv                  dv }rj                  d      nd xs g }g }rj                  d      xs g D ]F  }j                  |j                  d      |j                  d      |j                  dFd      dd       H j                  d      xs g D ]F  }j                  |j                  d      |j                  d      |j                  dFd      dzd       H rj                  d      nd xs g }|}|ddi}ty        |||
y      }|j                         D ci c]  \  }}|dk7  s }}} ud*i |} v|      }|j                         D ci c]  \  }}|dk7  r }}}dt{        j|                  |ddd      z   d0z   }t        |
|j                  |j                        }| j                         }|rj                  di       j                  fg       }g }|D ]s  }j                  di       j                  |      }|s'j                  d dj                  dd       d|j                  dd       d|j                  dd              u r|4dd4j                        z   d0z   z  }4| j                         }|rj                  di       j                  fg       }g }|D ]  }j                  di       j                  |      }|s't        j                  dʫ      xs d      j                         }|sVj                  dd      }|j                  d̫      xs i j                  t                    }d d7j                  dd       d9 }|rd z  }j                          r|4dd4j                        z   d0z   z  }4z   |4z   }4|r!ddj                  dЄ |D              z   d4z   |4z   }4	 tW        |
      t        j                  dѫ      t        j                  dҫ      tW               |j                         D ]%  \  }}|D ]  \  }}|s	jY                  f        ' |(j                         D ]  \  }}|s	jY                  f        dt        dt        ffdԄfdՄ}t        d||4|| j                  d׉ | j                  diث      ٫      } ||      }t        |      }|srg }r,|j                  dd!j                  t                            D ],  }|j                  d|d    d|d    d|d    d|d    d	       . tJ        jM                  ddj                  |             dd4j                  d |D              z   d0z   |4z   }t        d|||| j                  d׉ d| j                  dgdi      ٫      } |      }|r|j                  j                  fi       }i }tW               }D ]9  } |      }j                  |      }|rk7  r<   )jY                  |       ; j                  dg       D ]  j                  dd      }j                         D ]5  \  }}t        j                  dt        j                  |      z   dz   |      }7 rt              }d<   j                  dg       D ]!  }|j                  dd      }|v s|   d<   # sD ch c]
  } |       }}j                  dg       D cg c]  }|j                  dd      vr c}d<    rtJ        j                  d       rtJ        jM                  d       t              }|rts        dd d ddi      xs i j                  d      xs i }|j                  d      }|{j                  d      xs g }j                  dg       xs g D ]M  t        j                  dd            }|D ]*  }tJ        jM                  dj                  dܫ      |       , O j                  dd      xs j                  dd      }|rNj                        D ch c]  } |      vs| }}|rt              }d<   |j                  dd        d<   |rnd d'<   |
|d<   |d<   |d<   |j                  dg       }t        |«      |-kD  rd |- d<   |d   }i }|j                         D ]  \  }}|D ch c]  \  }}|	 c}}|<    D ]  j                  dg       }|r$D cg c]  }|j                  dd      v s| }}g }D ]  }|j                  dd      }|j                  dd      }j                  |tW                     }|stJ        jM                  d |       \vr*||   d   d   }tJ        jM                  d||ɫ       ||d<   j                  |        }|ŉd<   |D ch c]  }|j                  dd       }}tW        t        j                  dj                  dd                  }||z
  D ]  }||(v rj                  |(|   d       |v rE|   r@j                  ||   d   d   d       tJ        jM                  d|||   d   d          htJ        jM                  d̫        D cg c]  }|j                  dd      v s| }}|ŉd<   j                  dd      }t        |D cg c]0  }|j                  d      s|j                  d      s'|d   |d   f2 c}d       }|D ]8  \  }}t        j                  dt        j                  |       d| |Ǜ       }: d<    |j"                  j                  g       }|rbr`D cg c]  }|j                  d'      k(  sϑ }}tc        «      D ]-  \  }n|nt        Ы      k  sn   j                  d	d      d<   / n=r;tc        «      D ]-  \  }n|nt        Ϋ      k  sn   j                  d	d      d<   / t        «      }t        x|      } ud*i i ||Ґd}t        | ||
       |d<    v|ӫ      |d<   t        ||ӫ       dd	lXmY} |j                  d      xs i j                  d
      xs i j                  d      xs g }|D ch c]N  }t)        |t*              r<|j                  d      dk(  r&t)        |j                  d      t              r|d   P }}|rKxs g D ]B  }t)        |t*              st)        j                  d      t              s4 d   ֫      |d<   D D ]=  t        fddD              sj                  d      dk(  r1	 t        d֫       ? ddl^m_}m`}ma}mb}mc}md} ddlemf} |_xs g D ]T  } |אd   g dd d      }||אd<    g d|j                  dd      dg |      } |d d,       ||אd<   V n|j8                  j                  f      xs g }2xs d}t-        1d d+ ,      t.        k(  }d}xs g D ]U  }|j                  dd      } |אd   d d       }||אd<   |r" dg       } |d d ,       ns g dg       }n t        | j                  | j                  d gd!      j                  d      xs j                  d      xs d"      } ||
t        |d#d       $      } |      r<| j                  |j                  |
t        |d#d       %      }|\  }}}       rd} d      } d d ,       |אd<   X rd&d'<   dd(limj}  ||j                         |S c c}w c c}}w c c}}w # tP        tR        f$ r d }FY w xY w# tP        tR        f$ r Y w xY wc c}w c c}}w c c}}w c c}w c c}w c c}w c c}}w c c}w c c}w c c}w c c}w c c}w c c}w # tr        $ rN}j                  dk7  r tJ        jM                  dj                  dܫ      j                         Y d }~ y d }~ww xY w# tr        $ r  t        $ r"}tJ        j                  d)ث       Y d }~y d }~ww xY w(+  Nr    r   r   r(   r!   r   r   r   r   r   r   r   r   r   )r   	locationspropsr   r  r  r   r"   c                 P    t        j                  d|       d   j                         S )Nu
   \s*[\(（]r   )r   r  r  )r   s    r7   
_base_namez0SceneDetailStep._analyze_one.<locals>._base_namep
  s!    88M40399;;r9   r   c                     | d   S Nr   rp   xs    r7   rM   z.SceneDetailStep._analyze_one.<locals>.<lambda>
  s    qtr9   r  r%   r   u>    아웃룩: 없음 (이 씬에 배정된 아웃룩 없음 — zO00)uS    아웃룩: 없음 (비인간형/변형체 — 아웃룩 불필요, bare ID 사용)u    아웃룩: r7  u
   ) [확정]r  c              3   2   K   | ]  \  }}| d | d  yw)r7  r  Nrp   )rq   r   r   s      r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>
  s"     $T8KD%vQugQ%78s   u    아웃룩 선택지: u    [variation별로 선택]u4    아웃룩: 없음 (비인간형 — bare ID 사용)r#  r4  r  r  z$detail_steps.phase2_prepend_caller Sr4  r5  )
rc   rd   re   rf   rg   rh   ri   rj   ra   rb   u   씬 u	    요약: r   u   [분석 대상 Shot]
  Shot r   r*   rl   based_on_beatz  Beat #r>  change_typer@  before_stateu    → after_stateu     Beat 근거: #c              3   r   K   | ].  }|j                  d       k(  r|j                  d      k(  r| 0 ywr  rw   )rq   r1   rR  rc   s     r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>
  s;      T,qEE-(B.1553F(3R ,r  r$   r  u,     [앞쪽 연관 Shot — 연속성 유지]
uB    [zoom_in_detail — 같은 시공간, 카메라 확대/이동만]u1    [exact_background — 같은 방, 다른 순간]u)    [atmosphere_reference — 톤만 참고])r'   exact_backgroundatmosphere_referencer&   r  u       배경 참조: Su       인물 참조: Su   [중요 — 순간 고정]
t2i_prompt는 위 Shot description이 묘사하는 정확한 순간만 캡처하세요.
씬의 다른 시점이나 다른 장면은 포함하지 마세요.
이 shot에서 카메라에 찍히는 것만 영어로 묘사하세요.

   uA   scene_detail S%s_Shot%s: %d forward zoom targets — capped to %du  [중요 — 후속 zoom_in_detail 대상 영역 (forward-look continuity)]
이 shot 이후에 후속 shot이 이 frame의 특정 영역을 close-up할 예정입니다. 후속 close-up이 이 frame에서 자연스럽게 zoom-in 가능하도록, 그 대상 영역이 현재 shot의 frame에 visible 위치로 잡히고 가려지지 않게 배치하세요. 특히 죽은/의식불명/움직일 수 없는 대상이면 위치/자세를 frame 안에 명확히 노출시키세요.
r  u
   - 후속 Su	    묘사: r+   uJ   scene_detail S%s_Shot%s forward zoom keep_elements=%d > cap=%d — cappingr<  c              3   &   K   | ]	  }|d      yw)labelNrp   )rq   rx   s     r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>8  s      10A1AgJ0Art   u     보존 요소: r
  u   인물 상태u   환경 상태u   고정 소품)character_stateenvironment_statepersistent_propu   [교차 샷 고정 요소]

element_typez  [rn  
element_idcharacter_namez = z:
    u   [사용 가능한 엔티티]
u  [사용 가능한 엔티티]
  없음 — 이 씬에 할당된 엔티티가 없으므로 엔티티 ID(C##, L##, P## 등)를 사용하지 마세요. outfit_assignments도 비워두세요. 씬 텍스트에 묘사된 장면을 그대로 영어 프롬프트로 작성하세요.

ua   인물별 아웃룩 선택지 (각 variation의 outfit_assignments에 인물별 1개씩 선택):
lighting_moodkey_bg_elementsperspectivepov_characterue   [촬영 감독(DP) 연출 지시 — 반드시 t2i_prompt에 반영하세요]
카메라 관점(POV): u   POV 인물: u    — 이 인물의 시점이므로 t2i_prompt에서 이 인물을 제외하세요. 이 인물의 참조 이미지도 사용하지 마세요.
r&  directu   지각 모드: u    — u   조명/색감: r  r   r  r  r  )r  r   r  r  r  u   배경 핵심 요소:
r   r   angle	body_posegaze_direction_kindgaze_target_idr   z, gaze=u   →z, state=u=    (엔티티 미등록 — t2i에서 보통명사로 묘사): u  [인물-카메라 각도 + 시선 + 상태 — 절대 무시 금지, t2i_prompt에 반드시 반영]
아래 각 인물의 카메라 대비 방향, 자세, 시선 방향, 신체 상태를 t2i_prompt에 영어로 구체적으로 명시하세요:
- facing_camera → 'facing the camera' / back_to_camera → 'with back to camera'
- profile_left → 'in left profile' / three_quarter_right → 'at three-quarter angle from the right'
- gaze=camera → 'looking at the camera' / gaze=down → 'looking down' / gaze=up → 'looking up'
- gaze=distant → 'gazing into the distance' / gaze=closed_eyes → 'with eyes closed'
- gaze=off_screen → 'looking off-screen' / gaze=looks_at_character→C## → 'eyes fixed on [that character]'
- gaze=looks_at_object→P##/B## → 'eyes fixed on [that prop/background element]'
- state=alive → 평소 자세/표정 / state=unconscious → 'unconscious, slack and limp'
- state=dead → 'lying motionless, lifeless' / state=severely_injured → 'visibly wounded, pained expression'
인물이 2명 이상이면 각각의 방향, 시선, 상태를 따로 명시하세요.
- 엔티티 미등록 인물은 C## ID 대신 보통명사 + demographic descriptor (인종/연령대/성별)로 묘사하세요. 인종은 visual_world_rules.region 이 명시한 ethnicity 그대로 carry — production code / prompt 어디에도 hardcoded ethnicity 어휘 list 를 강요하지 마세요 (시나리오/region 마다 다름, A-prime binding).

   u}   [촬영 기법 선택] — 아래 기법 중 이 shot에 가장 적합한 것을 선택하여 t2i_prompt에 반영하세요:
u  [카메라 포커싱]
t2i_prompt에 카메라가 무엇에 초점을 맞추는지 반드시 명시하세요:
- 인물의 특정 신체 부위/표정 (영어 명사구로 구체적 묘사 — gender / emotional label 은 본 beat 의 staging.subject_state 와 character_angles 에서 가져오기)
- 본 shot 의 핵심 narrative 객체 (해당 beat 에서 등장하는 prop 또는 background element)
- 두 인물 간 공간/시선 관계 (subject 간 frame 안 spatial tension)
- 단순히 앵글만 쓰지 말고, 그 앵글로 무엇을 강조하는지 반드시 포함할 것

[조명/색감 판단]
beat의 상태 변화와 씬 분위기를 고려하여 t2i_prompt에 조명과 색감을 반영하세요:
- 시간대(낮/밤/새벽/석양), 자연광/인공광, 색온도(따뜻한/차가운)
- 색감은 beat의 정서와 씬 분위기에서 직접 derive하고, 특정 정서에 고정 색조를 대응시키도록 강요하지 마세요
- 핵심 인물/물체에 대한 조명 방향(역광/사광/정면광)

z  Shot u   [씬의 Shot 분석]
z (Shot z  variation llm_descriptionz	 (focus: focusu   [촬영 감독 추천 기법] — 각 variation의 t2i_prompt에 이 촬영 기법의 카메라 앵글/구도를 반영하세요:
u   [변형 관계 참고]u   아래 인물들은 같은 인물의 변형 관계입니다. 이 shot의 visible에 이미 올바른 형태만 포함되어 있습니다:u   ) ← u   )의 변형u4   - visible에 있는 형태의 ID만 사용하세요.u9   - visible에 없는 형태의 ID는 절대 사용 금지.u   [변형 관계 주의]u<   아래 인물들은 같은 인물의 변형 관계입니다:us   각 t2i_prompt에서 선택한 순간에, 해당 인물이 어떤 형태인지 씬 텍스트에서 판단하세요.ud   - 변형/변신 이후 시점이면 반드시 변형 ID를 사용하세요. 원본 ID 사용 금지.uP   - 변형 전 시점이면 원본 ID만 사용하세요. 변형 ID 사용 금지.u   - 같은 순간에 두 형태가 물리적으로 동시에 존재하는 경우에만(클론, 분신, 환각 등) 둘 다 사용 가능합니다.ud   - 시간 순서로 전환되는 변신/변형은 절대 한 프롬프트에 같이 넣지 마세요.u0   [아웃룩 선택 규칙 — beat 상태 확인]uI   - beat의 상태 변화(before_state → after_state)를 확인하세요.uj   - 변신/변형 이전 순간이면 → 원본 캐릭터(C##)의 아웃룩을 outfit_assignments에 사용uj   - 변신/변형 이후 순간이면 → 변형 캐릭터(C##)의 아웃룩을 outfit_assignments에 사용u  [규칙]
- 인물+아웃룩은 복합 ID(C01O02)를 사용 — 합성 단계가 'the character from Image N'으로 자동 치환.
- 신체 부위 클로즈업/사진·거울 속 인물 등 system prompt가 명시한 예외 구도에서는 보통명사로 대체 (system 룰 참조).
- outfit_assignments에 각 인물이 이 variation에서 입는 아웃룩 ID를 지정 — t2i_prompt의 복합 ID와 같은 정보를 명시.
- t2i_variations 정확히 u   개.
u\   - 각 variation의 t2i_prompt는 해당 촬영 기법의 카메라 구도를 반영할 것.
u   
씬 텍스트:
rq  r  r   r   zdetail_steps._g41 SFr*  r+  r,  r-  r.  r/  >   r2  r3  fixed_for_shotr  r  r  r  rD  r7  T)r   r   r!  r\   r9  r\  r]  r^  r_  r`  rb   r>  ra  rb  rc  r   _card_metadataz[RenderPromptCard v1]
),:)r  ensure_ascii
separatorsmembers_by_shotby_propz: printed/displayed content = printed_contentz | physical form = physical_formz | scale contract = scale_contractu  [고정 소품 시각 계약 — visual continuity anchor]
아래 소품의 인쇄/표시 내용과 실물 크기는 에피소드 전체에서 고정된 계약입니다. t2i_prompt 에서 이 소품을 묘사할 때 이 계약과 모순되는 내용(다른 인쇄 내용, 포스터/문서 크기로의 확대 등)을 쓰지 마세요. 크기는 환경 대비 실물 크기를 유지하세요. 인쇄된 글자의 자구는 고정하지 않습니다 — 구조(장소+인물 구성)만 지키세요.
anchors_by_groupshared_state_contractcharacter_short_idper_shot_visible_focusu*    | 이 샷 프레임에 보이는 부분: u  [부동 피사체 연속성 계약 — immobilized subject continuity]
아래 인물은 같은 씬의 여러 컷에 걸쳐 스스로 움직이지 못하는 상태입니다. 자세·신체 방향·위치, 그리고 쥐고 있거나 곁에 닿은 소품의 상태는 컷마다 동일해야 하는 고정 계약입니다. 카메라 앵글/프레이밍/크롭은 이 샷의 묘사대로 자유이나(가까이 잡아 일부만 보여도 됨), 이 물리적 상태/자세/소품 상태는 바꾸거나 새로 지어내지 마세요.
u3   [수정 요청] 직전 답이 계약을 어겼다:
c              3   (   K   | ]
  }d | d  yw)ro   rl   Nrp   rq   cs     r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>7  s     $EAr!BZs   z"[CLP]\d{2,3}(?:O\d{2,3})?(?![\dB])z[CLP]\d{2,3}r   c                 P    j                  |       }|r|j                  d      S | S )u   ID 에서 base 추출. composite (C01O02) → C01, bare 는 그대로.

                sid[:3] 단순 truncate 는 P123 같은 3-digit ID 에서 P12 로 misalign.
                정확한 prefix regex 매치 사용.
                r   )matchr   )r   r   _base_patterns     r7   _extract_basez3SceneDetailStep._analyze_one.<locals>._extract_baseV  s*     "'',%&qwwqz/C/r9   c           	         t               }| j                  dg       D ]  }t        j                  |j                  dd                  }|D ]o  } |      }|
vr|j                  |       !||k7  s'|j	                  d      s|j                  |       J|t        |      d }||f	vs_|j                  |       q  |S )u  위반 검사 — base ID invalid 또는 composite outlook invalid 면 to_remove.

                composite ID(C##O##) 의 outlook 부분 (O##) 검증 — (cid, oid) pair 가
                allowed_outlook_pairs 에 있어야 valid. fail-fast 원칙으로 invalid
                outlook 을 silent 통과 안 시키고 retry 강제. P##O## / L##O## 같은
                non-character composite 는 valid v13 출력 형식 아님 — 무조건 reject.
                r   r   r(   r   N)r   r,   findallr   r   r   )res	to_remover   foundr   base_sidoutlook_sidrz  _ve_patternallowed_outlook_pairsallowed_sets          r7   _check_promptsz4SceneDetailStep._analyze_one.<locals>._check_prompts_  s      E	77#3R8C 3 3CGGL"4M NOE$#0#5#;6%MM#.$(?#+#6#6s#; )c 2 (*-c(mn*=K (+6>SS )c 2  % 9  ! r9   ra  scene_detail_)r  stepsystem_promptuser_promptresponse_schemarC   schema_namer  u   제거할 ID: zvariation 'variant_labelu[   ': applied_frame_spatial_constraint_ids echo 가 contract id set 과 다릅니다. missing=r  z extra=extraz (injected=injected_idsu   Scene %d: %s — retryz / u   [수정 요청]
c              3   &   K   | ]	  }d |   ywrn   rp   ru  s     r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>  s     >+Q"QC+rt   _retryr:  r  r   r   z\bz(?=O\d{2,3}\b|\b)r   z%Scene %d Shot %s: variant replaced %su5   Scene %d: retry 후에도 위반 %s — 강제 제거r3  zscene_detail Sz shotui   : per-variation applied_frame_spatial_constraint_ids echo mismatch after 1 retry. graceful fix 불가능.r&  )r0  r1  detailsrender_strategyframe_spatial_contractconstraintszQscene_detail S%d shot%s variation %s frame_spatial_contract phrase diagnostic: %sr!  r"  r\   r   r  u-   씬 %d: %s 아웃룩 없음 — 할당 제거u<   씬 %d: %s 유효하지 않은 아웃룩 %s → %s로 교체z(?<![CO\d])C\d{2,3}u&   씬 %d: %s outfit 자동 할당 → %su?   씬 %d: %s 아웃룩 없음 — t2i_prompt에서 bare ID 유지c                      t        | d          S rE  )r   rF  s    r7   rM   z.SceneDetailStep._analyze_one.<locals>.<lambda>T  s    3qt9*r9   z	\b(?!O\d)	shot_name)rl  rm  )+canonicalize_base_id_required_outlook_forms	id_policysubject_reference_policypolicybase_id_required
subject_idc              3   &   K   | ]  }|v  
 y wrv   rp   )rq   rB  r   s     r7   rs   z/SceneDetailStep._analyze_one.<locals>.<genexpr>  s       .18 .s   )source_factsvisual_inferencescreative_decisionsr  r  r  uL   Scene %d Shot %s contract violation in %s: %s — None 반환 (retry 대상))build_owned_sentinelr   assert_close_framing_absent_echor  r  r  r	  r  zscene_detail._analyze_one sz (scene-level)r  )r   r  r   rb   r&  r  r  zdetail_steps.owned_judge Szscene_detail sr  zscene_detail close-framing sr  r   r#  r  r%  )r  r)  r   r'  r(  r   rA  r*  rc   rR  r\   r  r  r  r  zScene %d detail failed: %srp   )mr,   r   	summariesr   r   r/   r   r   r   r   rH  r   r  r   rE  r$  shot_variation_countscene_shots_mapscene_variation_countrJ  r-   r.   r   r   r   re   rf   rg   rh   ri   rj   ra   beats_by_scener  r   r8   r   rv  rw  r  r  r  r  r   r   r   r  chrshot_types_blockr.  ru  rr  rs  r  localsrf  rK  r   r   rL  rj  rG  r  r  rA  r  r  r  r  r   r   r   rC   r/  _fsc_validate_echoesr   shot_director_vrsubr   r   r  _fsc_phrase_diagnosticr|  popr   r   r`   r   !app.core.subject_reference_policyr  r   r   r0  r1  r  r  r  r  r  r  r  r+  r
  rT   r0  r-  r  rW   rG  )r  r   r!  r   r  r   r9  
scene_textall_visiblesummaryr   r   r   r   r   r   r   r   r   r   r   r   r   entity_block_linesr   etyperr   char_id_namerC  all_char_namesbase_groupsbnvariant_pairsmembersvisible_membersr  base_nmvar_sidvar_nmoutlook_linesr  r   r  optsr$  	var_countr  prepend_blocksrd   staging_for_blockcam_dir_for_blockis_close_for_blockr  beat_idx	beat_datar  r  r  
_ru_labelsr2   _ru	_ru_labelrD  _FZT_CAP	_KEEP_CAPfztdesckeepskeep_summary_shot_idx_for_fe_shot_idx_for_fe_intre  r  _raw_applies_v_type_labels_name_to_cid_eidrR  fe_typerR  header	char_namematched_cidr  r   r(  lightbg_elemsr[  pov_char
perception_pguidebg_lineschar_angles_registered_namesangle_lines_cname_angle_pose_kind
_target_id_state	_gaze_str
_state_strscene_shots_infoshot_desc_linesrN  
shot_linesvishot_tshot_idx_info_has_shot_directorvlines	base_namevar_name
_g41_build	_g41_hash_g41_settings_visible_chars_for_pairs_g41_outlook_pairs	_g41_seen_g41_cid_g41_ols	_g41_osid_g41__g41_locals_g41_has_shot_g41_staging_g41_is_close
_g41_bg_id_g41_bg_owned_raw_g41_bg_owned_g41_bg_camera_meta_g41_bg_guide_g41_background_mode_on_g41_fixed_elements_g41_prev_refs_r_g41_forward_zoom_g41_shot_info_for_card_g41_card_inputsrx   r   _g41_builder_inputs_g41_render_prompt_card_g41_render_prompt_card_hash_g41_card_for_inject_g41_card_block_g46_entity_traits_blockr	  	_vca_sids
_vca_linesrS  _ar
  	_imm_gids
_imm_lines_gid_sa	_contract_csid_focus_line_cid_ols_osidr  r  r   r~  fsc_echo_violationsretry_promptpost_remove_vrreplace_mappure_removebase_idresolvedpromptold_idnew_idr   r   	_pr_basespost_fsc_fsc_rs_fsc_obj_fsc_constraintsdiagr1   sfp
bad_in_sfpt2i_varsvalid_outfitsr  r   cleaned_assignmentsr   validcorrectassigned_charsprompt_charsmcreplacements
cine_shotsr  cine_for_shot_used_pairs_state_var_chars_narrow_cardr  _w4b_srp_w4b_base_requiredr  r  r  r  r  r  r  r  r
  merged_usager  r   r  r  r  v_t2ir&  repairedr  ry  rz  r  r  r  rR  rc   r   s                                                                                                                                                                                                                                             @@@@@@@@r7   rD  zSceneDetailStep._analyze_one.
  s+   WW]A&WWVR(
''++B3--##B+ 3<9==.RNc6J6J$J**B>:G!G 02&&**:r:vvl+Ervvj"/Evr*u/4&t,05&u- ; +-&&**+>C66-(B.r2A%%3C uu\2OaeeNB6OHxH8J,J 28 <n424N3/ s(;;*3/66u= 3 D  )0G3>>#3FG  CD=))%4AuuZ(C/ uuVR0 5 >
 %%T3%r$q&9C5zR  (* C\\%%lB755$+()fb(9L% 8 !	<S 	<S 	< *,!!,3A%%
B'C&'eeFB&7s# 4 (*'--/ICD!B&&r2.55sDkB 0 !&,,.KB<C[GysDslGZT{GO[?#a'  ^ 4 / 2Hg#212#6$$h&%IJ $7 / (* C%))#r2H &+c"$$r#.lmplqqu%vwX!#A%(?%*c"$$r#  /B  &C  DX!#%-a[^c"$$r#l8A;q>:J!HUVKXYNK[[e%fgX!#=EWXkdEQVT5MXWx=A%)1!QM#&!((2cU,x{1~>NaPXYZP[\]P^O__i)jk99$T8$TTD!((2cU2HNg)hi */M#&!((2cU2f)gh= !@ 	-  55I }}\15H''++B3E&+E
1O1OIH N--immM1.MNC !$ 3 3rd!C5M B HbQ[\mosQt 1 5 56H" Mz|*%@E#O !	!  : # 3 3'*'A'A-0-M-M'*'A'A%-%B%B'/'F'F-5-R-R!3N %2$iy-

 #L12"Y]]=RT5U4VVXZK !}}_5H..222r:>>xH	"8*By}}]B/O.PPS$==<=U9==Q^`bCcBddfhK
  %5hZr#BBKTC,, TL (++OR@!DL(,,-=rBM}NN&j([,W

 (C''+r2C *s3bQKB OI%9#m:L9MUSVWcSdRefoeppr#ssK ( )!,C%9#m:L9MUSVWcSdReeg#hhK -^K $A  #"5"5r8$  $+,x7NN[Hc*>&? Q 	/	:C}-D$S%7$8c,>O=PPYZ^Y__abK  GGO4:Eu:	1"NN l "Hc%j) (,yy 105jy0A1 ( $):<.'KK/ ;0 t#  )}}\9==XY;Z[,'*+;'<$  N#/B#:U:U4U55b9B66"4b9?RD$'EH"%$LLR1 #
 ,x7&--b1 : '6)8'6  02#Ds+"%,,"2"2<"DB!vvj1T9CGRVVFB-? @ % #E $ ??(B ff^R8G(,,Wg>E"5'BFF<,D+EFF "'7 <I &2&6&6y"&E&"9+SQ&GGF"9+Q&77FfXWRVVM25N4Ot#TTK )( ;tyyI[?\\_eeeK  t  tK  CG  CL  CL  MZ  C[  [  ^d  d  dK D)--immM[\>]"^!_`Koo))+6G!++&8"=OR8";;'8"=%kk-<";;;..9]"> \(  <J  $K  KK$[[):HE
*"8 3:>G_ZLgYb#QQK( r::!H% (>$%EE)R$8"#%%"4'(uu\2'>()mR(@127Mr1R)  &  %<		(@S#SVZ#ZZK%kk*<bA(+%!E!$!1!1%!<A uuV} 1 5 5ai @ "= "F #%K(!"{B!7!"w!3 !k2 6 !""7 8%&UU+;%<%B
!"?!3&-eW$5z3zl9K_a$b	'/x%8
!f4E&E'..F8;xy  yA  AC  DI  CJ  KT  JU  V`  Ua  0b  c'..F82fXRwykZdYe/fg )  E r7<<45R9K t#%% U**+-34
 
^K  #2266r2>"$*B#**!"\"2!32b6G5HI + 7$))O:TTW]]]''++B3E
"+E1"5JBRXR\R\]jRkgfjj.K-LA$NqsM%%&rd=/FJJvr<R;SSXY_YcYcduwyYzX{  |E  FL  FP  FP  QX  Z\  F]  E^  ^_  ` #6
  Zii
+,.45 !)!5!`2x.CL`L`:`!23  j  k>K:Hgy(MMBwiq
&
!I;Va"bc ?LTUYZ 33\]>K:Hgy(MMBwiq
&
!I;Va"bc ?Lb!  T  U  E  Fpq  q  r  E  FMM"%MM"TUMM"mnMM  #O  PMM  #O  P499V,v55K* +4F	<
 u~  BE  BU  BU  BY  BY  Z\  B]n  dfg #:,/0	
	
 	> +2-R-)
*7##s#s(; IIcN1- 	! )
 46	"0"6"6"8Hh77$, 	5(I!6i!G&--)1K MM8Y"78 %- #9 $1#6#6#8Hi77h	2)C"))%-YG x34 $9 hY*7KOOI&T 	  + /t5
C !	!  	  
X1117R<<b(^LJ
 !$ : : > >H~ N%*;*C2""$WXJ 7!!+ /IJ	 	 ,=+H&'b  #&"B"B"F"FX#  66::B>JMM"& M))-JJ 	  2?KOO,-D 	 02"~6<"<%%#%66-#8"$&&"6!#R!8&	'  = #7=2=%%#%66-#8"$&&"6!#R!8'	'  > 8EKOO23$ 	 #,"* (@&F# 0(?$,)%+$7+*6$7(6*;
( .335
5TQeAqD5 	 
 #-"C/B"C'01H'I$ 5::< 
<TQ$$ qD< 	  

 &jj$Uz
  	 $>S))3+A+A$
  --/H(!2B7;;RNBOIJ!XXi,006!!<RVVDUWY=Z<[ \'')vvor'B&C D((*/?(D'EG	 " B
 ii
+,
 /55 88:, !%6;??XPRSI!J!ll#5r:>>tD(? @ FBGMMO	  4b9''":;ArFFs8}UUG2cggor&B%C3ykRI&RRE!!%( " R
 ii
+,
 /55 &(@@;N RWW$E$EEFHLMOZ[Kh		 g,K
 **%JKKJJ7M *-!,224
d $HE1-114-@ !% 5  -224e)--tUm<  503 03 0!8 %#$' &#22+B40"66}VXFY6ZF 'v.I #77NPV"W/ &&		&BS8T7U'VW,A&&%a&8%9 :##$Y<.'
| D%%&~%6$7q: - 7UZZ=TU (ii>+>>?*+ 
 )'"( ,$*#'#6#6"/t6 :"&":":wianpr`s":"t -V4..22B>2FC"$K"%%K*"/"4#&777#3#G(;3;K0'OOC0  +  &zz*:B?!$r!:
 /:.?.?.ANFF%'VV %		&(9 9<P P & &&F /B '%8%MF,2L)"%''*>"CB"$&&"<C"k15@5E> 2 #D 'CN(O;aq)9;I(O-0WW5I2-N9-Nr#%66."#=Y#N !#-N9C 457  @@ #$KRQY[fg"'^`bdop 00GP"P,RDhZ @E F ".x 8  /4"99:KLRPRG{{#;<H##+<<#>#D" !::&6;ArAC1#'',2KM]^D!4#''/*BA " B **4b9aVZZH\^`=aC-8-@-@-Eo-EcWZI[cnInc-E
o-c:>C25./

/6$&F=!09HtF=!)0F%&
 ,CF'(0LF,-zz"2B7H8}y(+3JY+?'(!"23 -/M*002S:=%>#wtQd#%>c" 3  !gg&:B?.9"fkQUU>SU=VZe=e1kK"f&(#$A%%3C%%b1C)--c359E 'VXZ\_` %'"0"5a"8";'egiknpsu|}*1,'..q1 % 2,7() FQ!Q[!%%";[!Q"2::.Dcggl\^F_#`a&7B]*#**Bm\^N_+`a~-.2D#**Bn]_N`abNcdeNf+gh'OQSUWYghjYklmYnopYqr'hjlnpq 8 +6c+Q~r9RVb9bq+c,7() r2%CN  A;aRSRWRWXfRglmlqlqr~la'<9;  A,  !-HCVVbiin-Y7%uF !- %+L!s  x ,,00R8JZ,6 [Jq!%%:NRZ:ZJ [(2GBC..+8+<+@+@+LK(  3 (2GBC
O++5b>+=+=fb+IK(  3 6h?K:6  & )*5+;L %T<A+7F'(09,0GF,- 1<H 017R<<[IORc,-413 
 ""!Aa&EE(O'99quu\2C8	 ,!  " "!)RI!)T2z!l3S8 H ), 79K ",/	 "0      .   77<(H4	 -c>B  8   D H$4!)RI
 $<!"67). ;B4~N$L
 7CI233 2#,==r#B).2+7	 H 0  ;B4~N 5=I01+ "0. 2266H~FL"
 -2.) :2$eH:N %	%  &+"!)RI%MM,;E $<!"67)1 .rd%zB$L
 7CI23 $8"'(',-1b/;	$ 9($@E("T ##7"$x',-2r/;	$ &5',E/;-5/>+/+>+>*.*B*B,9??AQY/Z +C + !'

+B C !J#)::.B#C!JGI&
6 &L'1/;',-4*1#*M&
 0
; (,'H'H*3-2&+1;(0'-141E1E1?#%18.5c?D.Q (I (H  (3BJ ?|Z/
; 26.#7"'(',-2z/;	$ 0  ;B4uXJO 5=I01m "0p & (<F8$$ /vs7K7KLM{) HR \@ X| z* ,'+$, !*:6 %$%j)
Z
 
j )P9j p4 &? #g2 "R d A !\~"H    xx#<<NNfHcggo&>   h  	
  	LL5r3?	s  $BL;BLBL BL ;BL&	BL&4BL, 	BMC:<BMPBMP$BMQBM%[ABO8 \&/BO8 ]I5BO8 gBO8 gBO8 gBM+g,BO8 hBM0hEBO8 m!BM5m1BM5m5BBO8 pBM:p'BO8 p6BN qBN qB.BO8 tBNtB=BO8 wBN
w2BN
w6"BO8 xBN
x/BN
yBN
yA4BO8 {BN{BN{!BO8 |=BO8 |?B5BO8 4ABNABO8 A%BO8 BABO8 C
BNCIBO8 L,BMM BMMBMMBMM+3BO8 N	BO5N'ABO0O*BO8 O0BO5O5BO8 O8BP,P
BP'P'BP,resumerv   )r  r  r  __doc__r  r
   r   r   r   __annotations__r  r  r  r  r  r	   r   r  floatr_  r|  r  r  r0  rD  rp   r9   r7   r  r    s   l
 59$sCx.18 <@c3h 8?(4S> (/$sCx. /@c @D
$sCx. 
4 $%!"%(P+ E$sCx.(4S>*BBCDP+
 P+ S#XP+ d38n%P+ P+ !P+ P+ #P+ 
tDcN#S$tCH~*>>	?P+d9c4S>.A(B 9vpc3h pd	f
V TO7bsr9   r  c                   4    e Zd ZdZdefdZddeeef   fdZ	y)SceneVerifyStepuR   Step 17: 교차 검증 (앞2씬 컨텍스트). 조건부: 다중 캐릭터 씬만.r"   c                     | j                   j                  dd      }|dk7  ry| j                  d      }|sy|j                  di       j                  dg       }t        d	 |D              S )
Napplicabilityalwaysif_multi_char_scenesTra  Frp  r~  c              3   X   K   | ]"  }t        |j                  d g             dk\   $ yw)r\   r   N)r   r,   r   s     r7   rs   z6SceneVerifyStep.check_applicability.<locals>.<genexpr>  s'     KFq3quu/45:Fs   (*)manifestr,   rU   r   )r  rule	detail_cpr~  s       r7   check_applicabilityz#SceneVerifyStep.check_applicability  sh    }}  (;))..~>	vr*..x<KFKKKr9   c                      j                  d      }|r"|j                  di       j                  dg       ng } j                  d      }|r"|j                  di       j                  dg       ng t        dd      t        dd      g }d	} fd
}t	        d      5 }|D 	ci c]  }	|j                  ||	      |	 }
}	t        |
      D ]+  }|j                         }|r|j                  |       '|dz  }- 	 d d d        |j                  d        t        |      t        |      |d|idS c c}	w # 1 sw Y   <xY w)Nra  rp  r~  
scene_saver  scene_verifyr  verify_schemar   c                 4   | j                  dd      | j                  d      }| j                  dg       }t        |      dk  r| S g }t        t        ddz
              D ];  t	        fdD        d       }|s|j                  |j                  dd	             = t	        fd
D        d       }|r|j                  dd	      nd	}d	}|/| j                  dd	      xs | j                  dd	      }d| d| d}ddj                  |       d d| d| ddj                  |       d}		 t        d|	j                  d j                  dgdi            }
|
| d<   | S # t        $ r#}t        j                  d|       | cY d }~S d }~ww xY w)Nr    r   r4  r\   r   r%   c              3   L   K   | ]  }|j                  d       k(  s|  ywr    Nrw   )rq   r   js     r7   rs   z@SceneVerifyStep._execute.<locals>._verify_one.<locals>.<genexpr>  s"     F1AEE-,@A,EQ   $$r   r(   c              3   L   K   | ]  }|j                  d       k(  s|  ywrd  rw   )rq   r   rc   s     r7   rs   z@SceneVerifyStep._execute.<locals>._verify_one.<locals>.<genexpr>  s"     CHqm(<(BHrf  r!  r"  u   
[검증 대상 Shot r@  rl   u   앞 씬 컨텍스트:
z---u   

현재 씬 z:
u   
확정 엔티티: r  u]   
위 엔티티가 물리적으로 이 씬(또는 shot)에 존재하는지 검증해주세요.r`  scene_verify_verifyr  r  verificationzScene %d verify failed: %s)r,   r   ranger  r  r/   r   r   rC   r/  rW   rv  rw  )scenerR  r   
prev_textsprev_segcur_segr  shot_context	shot_descr  verify_resultr  re  rc   r   r  r  r  s               @@r7   _verify_onez-SceneVerifyStep._execute.<locals>._verify_one  s   =!,Byy/Hii 2B7G7|a J3q"q&>2.FF %%hll62&>? / CHCTG 5<VR0J L#!II&=rBieiiPdfhFi	!7zYKrR *%**Z*@)A B TZL. !%%)YYw%7$8 9oo  /'"( +$*#'#6#6"/t 4"&":":xjboqsat":"u! )6n% ;REs   19E+ +	F4FFFr  r.  r%   c                 &    | j                  dd      S )Nr    r   rw   r  s    r7   rM   z*SceneVerifyStep._execute.<locals>.<lambda>  s    155#:r9   r  )r  r  r  rp  )rU   r,   r   r   r   rE  r   r   r/   r  r   )r  r  r\  r~  save_cpr!  r"  rs  rL  r   rO  rB  rJ   r   r  r  s   `            @@@r7   r  zSceneVerifyStep._execute  s9   ..~>	@Ivr*..x<r,,\:BI7;;vr*..z2>r^X6^_=2	h  A.$?EFv!t{{;2A5vGF!'*HHJNN1%aKF + / 	:;  #7| #F"w'	
 	
 G /.s   #D9(D4;D94D99ENrN  )
r  r  r  rP  rf  r]  r   r   r   r  rp   r9   r7   rT  rT    s)    \LT LQ
c3h Q
r9   rT  )NN)r(   )UrP  rG  loggingr   rI  concurrent.futuresr   r   pathlibr   typingr   r   r   r	   r
   r   r   sqlalchemy.excr   app.core.dtor   app.core.errorsr   app.core.perception_moder   app.core.frame_spatial_contractr   r  r   r  app.core.framing_scaler   r   app.core.step_runnerr   app.core.subject_stater    app.core.steps._evidence_helpersr   r   r  r   app.modules.llm.llm_clientr   app.modules.prompt_loaderr   r   	getLoggerr  rv  r   r  r8   r  r  r`   rf  r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r[  rj  r  r  r  r  r  r.   r  r  r  rT  rp   r9   r7   <module>r     s(   <   	  ?  B B B * - $ 9 , 7 C 6 >			8	$A4S#X/0A#tDcN334A A 	A
 
$sCx.AL ! / ' 'h UYIMW( */"W(W(	W( %S/4945W( !sCx#!56	W(
 #+4c3hc3h0O+P"QW( %T%S/49*D%EFW( W( !W( #'W( W( 	W(t	5c 	5 	5$s) 	5S T#t)_ N #

#:; T$sCx.12sCx2"c "# "3 "4$LT$sCx.12$L c3h0$L 
$LT #

#GH 4MT$sCx.124M c3h04M 
4Mn"d38n%"s8" 	X	"J22}2	$sCx.2j}	$sCx.8%%}%	$sCx.%P| 
$sCx.	!| S#X'	|
 
#s(^|H    $"&#x 
$sCx.	!x S#X'	x$ 
#s(^%xv:: : 		:
 
#: : : c]:z($sCx.)( #( c3h	(
 c](V2CH~2I2  #2 	2
 
2r		. , !#  	
  	:?%?%?% ?% 		?%D) )2\+&
 \+@W^
&
 ^
r9   