
    Qj              	         d Z ddlmZ ddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZmZmZmZmZmZ  ej$                  d      ZddlmZ dd	lmZ dd
lmZ ddlmZmZ  ej:                  e      ZdZ ddddddZ! e"h d      Z# e"h d      Z$ e"ddh      Z% e"dh      Z&dZ'dZ(dZ)ee*e*eee*e	f      e	ge	f   Z+d/dZ,	 d0	 	 	 	 	 d1dZ-	 	 	 	 	 	 	 	 d2dZ.ddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d3d Z/d!d"	 	 	 	 	 	 	 d4d#Z0	 d5	 	 	 	 	 	 	 d6d$Z1d7d%Z2d8d&Z3d9d'Z4dd(	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d:d)Z5	 	 	 	 	 	 	 	 	 	 	 	 d;d*Z6	 	 	 	 	 	 	 	 d<d+Z7ddd,dddd,dd-	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d=d.Z8y)>u  shot_conti_light — 샷별 경량 콘티 (2026-07-13 레시피 이식).

s41 확정: bgonly·prev 샷을 제외한 인물 샷마다, 해당 샷의 배경 플레이트
1장을 **느슨한 공간 참고 전용**(BG_REF_CLAUSE — 카메라·조명·디테일 복사
금지, 샷 텍스트가 요구하는 카메라 선택)으로 참조해 LIGHT_FRAME(얇은 균일
윤곽선만) 단일 프레임 16:9 콘티를 gpt-image-2 로 1롤 생성한다(콘티는
multiroll 파이프 미적용 — s41 실증). 정본: scratchpad/forest_exp/
s41_conti_light.py stage_conti + s38 _pose_clauses.

콘티 부재가 스틸을 막지 않는다: 플레이트 없는 샷은 skipped_reason="no_plate"
로 격리(콘티의 정의가 '플레이트 느슨 참조'이므로 텍스트 전용 콘티는 만들지
않는다 — s34 장소 이탈 실측의 교훈), 스틸 단계는 콘티 없이 진행.

실험의 `_soften`(고어 어휘 치환 하드코딩)은 이식하지 않는다 — moderation
대응은 production PromptSanitizer 경로(스텝 어댑터)가 담당.
    )annotationsN)datetimetimezone)Path)AnyCallableDictListOptionalSetTuplez^S(\d+)_Shot(\d+)$)load_prompt)compute_input_fingerprint)carried_clause_for)	parse_tagtag_ofshot_conti_lightz1.202607132300z2.202607201545z3.202607211530z4.202607221110z5.202607222353)12345>   r   r   r   r   >   r   r   r   r   r      zgpt-image-2.5-sunburst1536x864c           	     n    	 t         |    S # t        $ r  t        d| dt        t                d      w xY w)Nz1unknown shot_conti_light prompt version selector z	 (known: ))PROMPT_VERSION_MAPKeyError
ValueErrorsorted)selectors    [/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/shot_conti_light.pyresolve_prompt_versionr#   W   sK    
!(++ 
?l)F+=$>#?qB
 	

s    )4c           	        |xs
 t               }| j                         D cg c]G  \  }}|j                  d      r1|j                  d      r||v st        |j                  d            r|I }}}t	        |t
              S c c}}w )u  콘티 대상 = 인물 샷(person_visible) 중 prev 없는 샷 — 스토리 순서.

    2026-08-07: 좁고 복잡한 실내로 판별된 샷(`confined_structure`, 분류 팩
    v4)은 prev 가 있어도 콘티를 만든다. prev 사진 한 장은 "같은 장소"만
    말해 줄 뿐 이 프레임의 기하 — 어느 좌석에 누가 앉고 카메라가 어디
    있는지 — 를 말해 주지 않는다. 자동차 캐빈 육안 결함 4건이 전부 이
    경로였다. 만든 콘티는 `build_still_refs(geom_authority=True)` 가 prev 와
    함께 싣는다.

    ★판별을 `classify_shots` 에서 **직접 읽는다.** 호출측이 따로 넘기는
    형태로 두면 배선을 빠뜨렸을 때 조용히 기존 집합으로 돌아가고, 그러면
    새 팩 문안이 유료 재생성에 실리지 않은 채 비용만 나간다(Codex 리뷰가
    실제로 그 상태를 잡았다). `geom_tags` 는 그 위에 더 얹고 싶을 때만
    쓰는 보조 인자다.
    person_visibleprevconfined_structurekey)	frozensetitemsgetboolr    r   )classify_shots	geom_tagsgeomtittagss         r"   conti_targetsr4   a   s    & #	D $))++EAr66"#T	RVV012 	
+ 	  $I&&s   ABc           
        i }dD ]  }t        |       |z  dz  dz  |z  |z  dz  }|j                         s2	 t        j                  |j	                  d            j                  di       }|j                  d
      xs i j                         D ]  \  }}	t        |	t              r|	j                  d      dk7  r,|	j                  d      xs d}
|
sDt        |
      }|j                         st        j                  dt        ||
       ||	j                  d      xs g D ]o  }t        j                  t        |            }|r8t!        |j#                  d             dt!        |j#                  d             }nt        |      }||vsk|||<   q   |S # t        $ r'}t        j                  dt        ||       Y d	}~d	}~ww xY w)u$  shot key("si_shi") → 배경 플레이트 PNG 경로.

    background_render(Phase 7) 우선, background_chain_render(Phase 5) fallback —
    load_background_chain_bg_map 과 동일 shape(data.groups[bg_id].{status,
    png_path, shot_ids[]})를 경로 기반으로 읽는다(레시피는 bytes 아닌 경로
    참조가 필요). background_chain_enabled 게이트는 여기 적용하지 않는다 —
    레시피 자체가 still_recipe_mode 로 게이트되고, 플레이트 부재는 하류에서
    no_plate 로 자연 격리된다.
    )background_renderbackground_chain_rendercheckpointsepisodeszmanifest.jsonutf-8encodingdatau   %s: %s 로드 실패: %sNgroupsstatusokpng_path u   %s: %s png 부재 — skip (%s)shot_ids   _r   )r   existsjsonloads	read_textr,   	Exceptionloggerwarning_MODULEr+   
isinstancedict_SHOT_ID_REmatchstrintgroup)projects_dir
project_id
episode_idoutstepcpr=   excbg_idgpngpsidmr)   s                  r"   resolve_shot_plate_maprb      s    C@+m;jH "12 	 yy{	::bllGl<=AA&"MD (+1r88:HE1a&!%%/T*A%%
#)rCS	A88:5ws uuZ(.B.  %%c#h/ _-Qs1771:.?@Cc(Cc> CH / ; AD J3  	NN5wcJ	s   5F	G$GGrB   r   )camera_frame_enprompt_versionc        	           |r|t         vrt        d| d      t        |      }	t        t        d|	      j                         t        t        d|	      j                         g}
|r/|
j                  t        t        d|	      j                                |
j                  d|        |
j                  d|         |r|
j                  d	|z          |r|
j                  d
|z          |r |
j                  dj                  |             |r|
j                  d|z          |t        v r^|
j                  t        t        d|	      j                                |
j                  t        t        d|	      j                                |t        v r/|
j                  t        t        d|	      j                                |
j                  t        t        d|	      j                                |t         v rB|r|
j                  |       |
j                  t        t        d|	      j                                dj                  |
      S )u  s41 stage_conti 조립 순서 이식 (head → LIGHT_FRAME → BG_REF →
    LOCATION → SHOT TEXT → MOVEMENT/FIGURES → pose → carried → no_text).

    camera_frame_en (v2, BGFIRST2): still_recipe.build_camera_frame_clause
    렌더 완문 — staging 구도·스케일 계약을 콘티 저작에 주입. 정본 순서
    (exp_conti_persp): v1 전체 조립 뒤에 CAMERA/FRAME 절 → PERSPECTIVE &
    SCALE GUIDES. v1 팩에는 원근 계약 스템이 없어 상호 배타(ValueError).
    shot_conti_light vuO    팩에는 원근 가이드 계약이 없음 — camera_frame_en 은 v2+ 전용
conti_headversionlight_framebg_ref_clausezTHE LOCATION: z#SHOT TEXT (authoritative, Korean): zMOVEMENT (follow exactly): u4   FIGURES — apparent size & depth (follow exactly): 
z!CARRIED STATE (persist exactly): prop_orientationnaturalism_clausekeyshot_clauseno_textperspective_guides

)
_PERSP_PACKSr   r#   r   rM   stripappendjoin_CONDUCT_PACKS_KEYSHOT_PACKS)	shot_desc
place_text	has_platepose_clauses
carried_enmovement_en
figures_enrc   rd   resolvedpartss              r"   build_conti_promptr      s   ( >=  0 18 8
 	
 &n5HG\8<BBDG]H=CCEE (CIIK	
 
LL>*./	LL6ykBC2[@ABZO	
 TYY|,-8:EF' 	!3XFUW	
 	!4hGUW	
 ' 	!18DUW	
 
LLWiBHHJK%LL)-xeg	

 ;;u    T)r{   c                    |t         vryt        |      }|s:| sy|t        vrt        d| d      t	        t
        d|      j                         S | rdnd}t	        t
        ||      j                         S )uc  v2+ GPT ref 서두 설명 — 참조 구성(플레이트/캐릭터 유무)에 따라 분기
    (조건=참조 유무, 의미 판단 아님). v1 = ""(기존 프롬프트 불변).

    has_plate=False (v3, E2E10 fix④): no_plate 샷 — 캐릭터 참조만 있으면
    ref_header_char(플레이트 부재 명시), 참조 0 이면 ""(서두 불요).
    rB   rf   uK    팩에는 no_plate ref 헤더가 없음 — has_plate=False 는 v3+ 전용ref_header_charrh   ref_header_plate_charref_header_plate)rs   r#   _NO_PLATE_PACKSr   r   rM   rt   )has_char_refsrd   r{   r   names        r"   build_conti_ref_headerr      s     \)%n5H0$^$4 5F F  &::?%'	B&3"9KDwh7==??r   c                    t        |      }t        t        d|      j                         }g }| xs g D ]C  }||j	                  d      xs g v s|j                  | d|j	                  d      xs d        E |S )uT   s38 _pose_clauses — 이 샷에 걸리는 자세 정본을 계약 헤더와 함께.pose_clause_headrh   shots pose_enrB   )r#   r   rM   rt   r,   ru   )
pose_canontagrd   r   headrX   r2   s          r"   build_pose_clausesr     s     &n5Hw 2HEKKMD
CB266'?(b)JJ$q	!2!8b 9:;  Jr   c                    | j                         r&	 t        j                  | j                  d            S i S # t        $ r t
        j                  d|        Y i S w xY w)Nr:   r;   u'   %s: sidecar 파싱 실패 — 재생성)rF   rG   rH   rI   rJ   rK   rL   )paths    r"   load_sidecar_recordsr   %  sY    {{}	L::dnngn>?? I  	LNNDdKI	Ls   $9 AAc                    | j                  d      }|j                  t        j                  |dd      d       |j	                  |        y )Nz.tmpFrD   )ensure_asciiindentr:   r;   )with_suffix
write_textrG   dumpsreplace)r   r=   tmps      r"   save_sidecar_recordsr   .  sC    


6
"CNN

4eA6   KKr   c                   | j                         syt        j                  t        j                        j                  d      }| j                  | j                   d| | j                         }d}|j                         rE| j                  | j                   d| d| | j                         }|dz  }|j                         rE| j                  |       y)u   지문 mismatch/force 의 기존 산출을 타임스탬프 이름으로 보존 이동.

    3차 리뷰 MINOR: 같은 초 재실행이 이전 stale 을 덮지 않도록
    microseconds + 충돌 시 순번 suffix.
    Nz%Y%m%d_%H%M%S_%fz.stale_rD   rE   )
rF   r   nowr   utcstrftime	with_namestemsuffixrename)r_   tstargetns       r"   archive_stale_outputr   6  s     88:	hll	#	,	,-?	@B[[AFF872$qxxj9:F	A
--/xwrd!A3qxxjAB	Q --/ HHVr   )lane_contisc        	           i dfddd	 	 	 	 	 	 	 	 	 	 	 dfd}	|xs i j                         D ]E  \  }
}t        |t              s |	|
|ddd||j                  d      |j                  d	      g
       G | xs i j                         D ]o  \  }
}t        |t              s|j                  d      }t        |t              r|r|D cg c]  \  }}|	 c}}n|j                  d      g} |	|
|ddd||
       q |xs i j                         D ]  \  }
}t        |t              s|j                  d      r  |	|
|ddd||j                  d      gd        |	|
|ddd||j                  d      xs |j                  d      |j                  d      fD cg c]  }|r| c}
        yc c}}w c c}w )u  콘티/맵 플레이트/lane 스케치 intermediate asset 등록 (순수 로직).

    Codex 3차 리뷰 B1 계약:
      - map_plate 를 **먼저** 등록 — 맵 기반 conti 의 plate 입력이 fresh
        run 에서도 UUID 로 이어진다(canon→map_plate→conti 2번째 edge).
      - 같은 경로의 기존 row 도 현재 입력 기준으로 lineage/prompt 갱신 —
        재생성(지문/force) 후 stale plate edge 잔존 차단.
      - 기대 입력 파일이 실재하는데 UUID 미해결 = unresolved 진단 기록.

    DB 구체는 callable 주입: find_asset_by_rel(rel)->asset|None,
    new_asset(rel=, tag=, asset_type=, model=, prompt=)->asset(add 포함),
    annotate_fn(asset, role=, input_ids=, meta=). 각 entry 에 asset_id 병기.
    c                    | sy t        |             }|v r|   j                  dfS  |      }||j                  nd t        |       j                         fS )N)NFT)rR   idr   rF   )r_   relafind_asset_by_relregistered_by_relrel_fns      r"   _resolve_inputz4register_intermediate_assets.<locals>._resolve_inputb  s_    SVn##$S),,d22c"4$q'..2BBBr   prompt)
prompt_keyc          	        |xs i j                  |      }|rt        |      j                         sy  t        |            }	g }
g }|D ]C  } |      \  }}|r||
vs|
j	                  |       '|s*|j	                  t        |             E | |d}|rt
        j                  dt        | ||        |	      }|F |||
|       ||_        |j                  |      ||   d d |_	        |j                  |d<   ||	<   y  |	| |||j                  |      xs dd d xs d       } |||
|       |j                  |d<   ||	<   y )N)shot_tagunresolved_input_pathsu(   %s: %s(%s) 입력 asset 미해결 — %s)role	input_idsmetai  asset_idrB   )r   r   
asset_typemodelr   )r,   r   rF   rR   ru   rK   rL   rM   generation_modelprompt_usedr   )r   entrypath_keyr   r   r   input_pathsr   r   r   r   
unresolvedr_   aidfile_existsr   existingassetr   annotate_fnr   	new_assetr   r   s                     r"   	_registerz/register_intermediate_assets.<locals>._registerk  s{      *4:,,.SY!	 "
A-a0Ci'$$S)!!#a&)  &0 
 NN:dJ %S)tytL ).H%yy$0',Z'8%'@$ (E*%-c"5IIj)/R%8@D
 	E	E!HHj!&#r   
plate_path	map_platemap_conti_platesource_master_pngsource_map_png)r   r   r   r   r   	ref_paths
image_pathconti_lightmarker_map_pathlane_marker_mapbase_map_pathmarker_prompt)r   r   r   r   r   r   lane_storyboard_sketchmaster_png_pathN)r_   Optional[str])r   rR   r   Dict[str, Any]r   rR   r   rR   r   rR   r   rR   r   zList[Optional[str]]r   rR   returnNone)r+   rN   rO   r,   list)contis
map_platesr   r   r   r   conti_modelplate_modelr   r   r   r   
_ref_paths_labr_   _inputsr   r   s     ````          @@r"   register_intermediate_assetsr   G  s   2 )+C %-0'!0'),0'580'20' "0' 260' 0'd "'R..0
UeT"U\&->!II12II./	 1 |**,
UeT" ;/J j$/J #--*wtQ*-ii-. 
 U\(}!#	 -* #(b//1
U%&
 99&'U%6,3D!"YY78* 	/) II/0 2yy1II/0a 	  	
' 2 .Ls   F='Gc                &   |j                  |       }|0t        |      j                         rdt        |      dfS ddd| dfS |j                  |       xs d}|r	ddd| dfS |dk(  r)|r't        |      j                         rdt        |      dfS y	)
u  structure_plate 샷의 배경 권위 typed 해석 (Codex seed-bg 조건 2).

    우선순위 고정: **valid selected plate > 구조적 부재 plate + valid
    seed > failure**. '배정 기록이 있는데 파일/그룹 결손' 은 seed 로
    조용히 하강하지 않는다(손상 감지 계약 침식 금지 — fail-closed).
    반환 = (bg_source "plate"|"seed"|"failed", bg_path, reason).
    platerB   failedu   플레이트 파일 결손(u4   ) — seed 하강 금지 (손상 감지 fail-closed)u$   배정 플레이트 결손(assigned=r@   seed)r   rB   uM   플레이트 구조적 부재 + seed 결손 — LOCATION 권위 소스 없음)r,   r   is_filerR   )shot_key	plate_mapassign_by_key	seed_pathseed_statusr_   assigneds          r"   resolve_structure_bg_sourcer     s     	hA}7??CFB&& b)! -* *
 	

   *0bH
 b28* =+ +
 	

 dyT)_-D-D-Fs9~r))r   c           	        dxxd+fd}t        | t              D ]  }| |   }t        |t              r|j	                  d      dk7  r.|j	                  |      xs i }|j	                  d      xs d}|dvr"|j                  dd	|d
       dz  dz  ~|j	                  d      xs d}|j	                  |      xs i }	|j	                  dd      sK|dk(  r1|j                  d|j	                  d      xs d       dz  dz  |j                  dd       |j	                  d      r|j                  dd       +|dk(  r0|j                  d|j	                  d      xs d        ||	       `t        |	j	                  d      xr0 |	j	                  d       xr t        |	d         j                               }
g }|dk(  rt|rt        |      j                         s|j                  d|       n|
rt        |	j	                  d            |k7  r|j                  d|	j	                  d      d       n|	j	                  d      snt        |	d         j                         s|j                  d       t        |j	                  d      d k(  xr3 |j	                  d!      xs dxr t        |d!         j                               }|s$|j                  d"|j	                  d      d       |
sC|j                  d#t        |	j	                  d      xs |	j	                  d$      xs d%      z          |r,|j                  dd&j                  |              ||	       Q|d'k(  r!t        |	j	                  d      xs d      |d<   |j                  d(|	j	                  d      |	j	                  d      )        d*S ),u  복잡 구조물(structure_plate) A/B 정책 entry finalize (Codex R2 +
    seed-bg 조건 2026-07-17).

    2026-07-16 사용자 확정: 복잡 구조물 샷=맵·마커·마커 스케치 제거,
    A(콘티+배경+엔티티) vs B(배경+엔티티) VLM 선택. 배경 권위는 entry 의
    typed 해석(resolve_structure_bg_source — bg_source/bg_path/bg_reason)
    을 소비한다:

      - bg_source="plate": 기존 계약 — ready 조건에 STRUCTURE LOOK 용
        seed 실재 포함 (plate=주변 SOT / seed=구조물 SOT tie-break).
      - bg_source="seed" (플레이트 producer 구조적 부재 그룹): seed 가
        LOCATION 단일 권위 — 콘티가 정확히 그 seed 를 참조했는지
        (conti.plate_path == bg_path) 검증. STRUCTURE LOOK 별도 부착
        없음 (Codex 조건 1: 동일 seed 이중 첨부 금지).
      - bg_source="failed": eligible/bg_only = fail-closed (조용한 하강
        금지), prev bypass 는 bg 불요라 무관.

    분류: bg_only/prev = ab_select_bypass(+reason), eligible 완비 =
    ab_select_ready, 결손 = failed. 반환 = lane 집계 **보정 delta**
    {"applicable", "completed", "failed"} — unique shot 계약(배치 리뷰
    BLOCKING-1): 일반 콘티 생성 실패(error)는 delta 0, completed 가산
    샷의 정책 실패 = completed -1/failed +1, 일반 콘티 비대상 결손 =
    applicable +1/failed +1.
    r   c                    t        | j                  d      xr | j                  d             }t        | j                  d            }|ry |rdz  dz  y dz  dz  y )Nr   errorrD   )r-   r,   )conti_entrycounted_completedcounted_failed
applicable	completedr   s      r"   _fail_deltaz2finalize_structure_ab_entries.<locals>._fail_delta+  sm     OOL)J+//'2J.J
 koog67NIaKF!OJaKFr   r(   r?   ab_select_pending	bg_sourcer   )r   r   r   u   bg_source 허용 밖 값: u    — fail-closed)r?   r   rD   bg_pathrB   r%   T	bg_reasonu   배경 권위 소스 없음ab_select_bypassbg_only)r?   bypass_reasonr&   r   r   r   u   seed-bg 결손: r   u5   콘티 배경 참조가 seed-bg 와 불일치 (conti=r   u   플레이트 파일 결손r   r@   r   u   structure seed 결손(status=u   일반 콘티 결손: skipped_reasonu   파일 부재z; r   ab_select_ready)r?   
conti_pathr   )r   r   r   )r   r   r   r   )r    r   rN   rO   r,   updater-   r   r   ru   rR   rv   )r   r   r.   r   r   r   clsr   r  r   conti_okreasonsseed_okr   r   r   s                @@@r"   finalize_structure_ab_entriesr    s   < '('J'V ky11C 5$'yy"&99  %+IIk*6h	 77LL29-?OP   !OJaKF))I&,"jjo+ww'.H$#))K0Q4Q   a
!LL 2)LL776?LL 2&LI LLii,M0M   $OOL) :OOG,,:[./779

  $w-"7"7"9!1'=>c+//,"?@GK)ool;>aA ."\23;;=;<		-(D0 7YY{+1r7{+,446G
 $yy7:!= NN(OOG, '"'78'& LL		'0BLC$  #;??<#@#FBGE)$"|4"|4 	 	
{ 2D %9 r   F)rd   extra_fingerprintforceclassify_scenesstaging_by_keychar_refs_by_tagno_plate_contivisible_char_sids_by_tagc                   |r|t         vrt        d| d      |t        v }t        | d       }t	        t        |            }|j                  d      xs g }|j                  d      xs i }|j                  d      xs i }|j                  dd	       |d
z  }t        |      }i }dx}x}}|D ][  }t        |d         t        |d         }}t        ||      }|j                  |      xs i }||vr!|j                  d      sdnd} ddd| dd||<   f|j                  | d|       }!|!)|s't        j                  dt        |       dddddd||<   |dz  }|j                  |      xs i }"t        |||"|xs i j                  |      xs
 t	               |j                  dd             }#|j                  |d      }$d}%|ro|j                  d      xs2 |
xs i j                  t        |      i       j                  d      xs |$}$|xs i j                  | d|       }&|&ddlm}'  |'|&      }%g }(|r)t%        |xs i j                  |      xs g       dt&         }(t)        |j                  d      xs d|$|!dut+        |||      |#|"j                  d      xs d|"j                  d      xs d|%|	      })t-        t/        |(      ||!du      }*|*r|*d z   |)z   })|!d!|!fgng |(D +,cg c]  \  }+},d"|+ |,f c},}+z   }-|d#| d$z  }.	 t1        |)|-dd%|&      }/|j                  |      xs i }0|	 xr& |.j3                         xr |0j                  d'      |/k(  }1|1sT|.j3                         r&t        j                  d(t        |       t5        |.        |||)|-|.       d'|/i||<   t7        ||       t        |.      |!t        |!      nd|)ddd||<   |!d||   d<   |r&|-D 2,cg c]  \  }2},|2t        |,      g c},}2||   d)<   |dz  }^ ||||d+S c c},}+w c c},}2w # t8        $ rL}3t        j;                  d*t        |       d|!t        |!      nd|)dt        |3      d||<   |dz  }Y d}3~3d}3~3ww xY w),u  전 샷 콘티 산출(대상 외=skipped_reason 기록).

    no_plate_conti (v3, E2E10 fix④): True 면 플레이트 부재 대상 샷도
    콘티 저작 — 참조=캐릭터 비례만(has_plate=False 조립: bg_ref 절·
    플레이트 헤더 제외), entry 에 no_plate=True/plate_path=None 병기.
    이후 스틸 단계가 이 콘티로 장소 단위 배경(groupbg)을 생성한다.
    False(default)=기존 skipped_reason="no_plate" byte-identical.

    재개(Codex 2차 리뷰 B3): 파일 존재만으로 skip 하지 않는다 — sidecar
    (conti_records.json)의 입력 지문(프롬프트+플레이트 내용+모델·팩)이
    일치할 때만 재사용. mismatch/force/무기록 = 기존 산출 stale 아카이브
    후 재생성 (맵 플레이트 전환·프롬프트 변경이 실제 이미지에 반영).

    v2 (BGFIRST2, prompt_version in _PERSP_PACKS):
      - THE LOCATION = 샷별 서브공간(classify v3 shots[tag].place_en →
        scenes[si].place_en → location_by_scene fallback — fix2 계약과 동일
        우선순위).
      - CAMERA/FRAME 절 = staging_by_key("si_shi"→shot_staging entry)를
        still_recipe.build_camera_frame_clause 로 결정론 렌더(부재="" —
        보강 절이라 fail-safe).
      - 참조 = [플레이트] + 캐릭터 비례 참조(char_refs_by_tag, 상한
        CONTI_CHAR_REF_MAX) + GPT ref 서두 설명. 지문에 참조 내용 자동
        기여(labeled_refs).
    v1 = 세 param 전부 무시 — 기존 프롬프트·지문 byte-identical.

    Returns {"contis": {tag: {image_path, plate_path, prompt,
                               skipped_reason, error}},
             "applicable_count", "completed_count", "failed_count"}
    rf   u[    팩에는 no_plate 콘티 계약이 없음 — no_plate_conti 는 v3+ 전용 (fail-closed)c                :    t        | d         t        | d         fS )Nscene_index
shot_index)rS   )ss    r"   <lambda>z&run_shot_conti_light.<locals>.<lambda>  s    c!M"23S<5IJr   r(   r   carriedpose_fixT)parentsexist_okzconti_records.jsonr   r  r  r%   bgonlyr&   N)r   r   r   r  r   rE   u,   %s: %s 플레이트 없음 — 콘티 생략no_platerD   )visible_short_idsr  rB   place_en)build_camera_frame_clausedescriptionr~   r   )	ry   rz   r{   r|   r}   r~   r   rc   rd   )r   rd   r{   rr   r   zchar:conti_z.pngF)r   labeled_refs
roll_countcritique_enabledextrainput_fingerprintuB   %s: %s 지문 불일치/force — stale 아카이브 후 재생성r   u   %s: %s 콘티 생성 실패)r   applicable_countcompleted_countfailed_count)r   r   rs   r    setr4   r,   mkdirr   rS   r   rK   rL   rM   r   rR   !app.modules.pipeline.still_reciper$  r   CONTI_CHAR_REF_MAXr   r   r   r-   r   rF   r   r   rJ   	exception)4r   r.   
continuitylocation_by_scener   out_dirgen_fnrd   r  r  r  r  r  r  r  persporderedtargetsr   carried_mapr  records_pathsidecarr   r   r   r   r  sishir   r
  reasonr   fixr  rz   rc   stagingr$  	char_refsr   headerr   r_   r'  out_pathfingerprintprev_recreuselabr[   s4                                                       r"   run_shot_conti_lightrJ    sX   ^ .?  0 1O O
 	
 l*EJG -/0G-3J..+1rK~~j)/RHMM$M.11L"<0GF&''J'Va&'Q|_)=CRo  %+g%(WW-=%>XFF"$$"(4F3K AcUm,=NNI7TWX"$$",tF3K a
ll3%2 %c7=2BB3G u 0$77	9 '**2r2

# #)r..s2w;??
K 
 &+00B4q?G" #<G"D,.	!'R,,S17R!!#I $eeM*0b!4'+J^L.4"ww|,2+)

 (y/.4'
 f_v-F"'"3gu/8
/8GD!uTF^Qy
0

 vcU$//4	3)!&'K {{3'-2H	 EOO%ELL!45D 
 ??$NN\ )2sFL(; 3[A$\7;!(m,1,=c%j4 "&F3K }*.sJ' 1=,0<fc1S#a&M,sK( NIi B &$	 u
V,  		:GSI",1,=c%j4 "&SF3K aKF		s2   -P
CP"&P=P"P""	Q7+AQ22Q7)r!   rR   r   rR   )N)r.   Dict[str, Dict[str, Any]]r/   zOptional[Set[str]]r   	List[str])rU   rR   rV   rR   rW   rR   r   Dict[str, Path])ry   rR   rz   rR   r{   r-   r|   rL  r}   rR   r~   rR   r   rR   rc   rR   rd   rR   r   rR   )r   r-   rd   rR   r{   r-   r   rR   )r   )r   List[Dict[str, Any]]r   rR   rd   rR   r   rL  )r   r   r   r   )r   r   r=   r   r   r   )r_   r   r   r   )r   r   r   r   r   zCallable[[str], str]r   zCallable[[str], Any]r   zCallable[..., Any]r   zCallable[..., None]r   rR   r   rR   r   Optional[Dict[str, Any]]r   r   )r   rR   r   rM  r   zDict[str, str]r   r   r   rR   r   zTuple[str, str, str])r   r   r   r   r.   rK  r   zDict[str, int]) r   rN  r.   rK  r4  r   r5  zDict[int, str]r   rM  r6  r   r7  
ContiGenFnrd   rR   r  rO  r  r-   r  rO  r  rO  r  z+Optional[Dict[str, List[Tuple[str, Path]]]]r  r-   r  zOptional[Dict[str, set]]r   r   )9__doc__
__future__r   rG   loggingrer   r   pathlibr   typingr   r   r	   r
   r   r   r   compilerP   app.modules.prompt_loaderr   %app.modules.pipeline.multiroll_selectr   +app.modules.pipeline.shot_continuity_authorr   &app.modules.pipeline.shot_ref_classifyr   r   	getLogger__name__rK   rM   r   r*   rs   r   rw   rx   r2  CONTI_IMAGE_MODEL
CONTI_SIZErR   rP  r#   r4   rb   r   r   r   r   r   r   r   r   r  rJ   r   r"   <module>ra     s    #   	 '  B B B bjj./ 1 K J D			8	$
 
 
 
 

 
- 4 -.O,C:&C5!   - 

 sCeCI&6!7>DE

 %)'-'!' '>//#&/47//v II I 	I
 I I I I I I 	IZ DH@@,/@<@@@6 GJ
$
+.
@C

6 -1U
U
 U
 !	U

 ,U
 "U
 %U
 U
 U
 *U
 
U
p)) ) "	)
 ) ) )XRR R .	R
 R| 2604/3DH 9=!GG .G 	G
 &G G G G G 0G G .G -G BG G  7!G" #Gr   