+
    ti/                      a  0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
 ]! ]4      P                  4       P                  ^,          t]R,          t]P                   P#                  ^ ]! ]4      4       ^ RIHt ^ RIHtHtHt R R	 ltR
 R ltR R ltR R ltR R ltR R ltR R ltR R lt R R lt!R R lt"RRRRRR R!R"/t#R#]$R$&   R% R& lt%R' R( lt&R) R* lt'](R+8X  d
   ]'! 4        R# R# ),uR  Generic raw-failure fixture capture (spec phase fixture extractor).

config JSON 으로 fixture 명세를 받고 그에 따라 PID/EID 의 step manifest +
DB row 를 immutable JSON 으로 추출. 스크립트 본문에는 시나리오 의존
(scene/shot index, short_id 등) 일체 없음.

사용 (G4.6 예):

    backend/.venv/bin/python scripts/g4_6_capture_fixtures.py       --pid 298d86d9-615b-4b87-8040-21144c0731c1       --eid b6544514-babb-4781-95a2-6a0ff1da342f       --config scripts/configs/g4_6_fixtures.json       --out backend/tests/fixtures/g4_6/

config 형식 (JSON):

    {
      "fixtures": [
        {"name": "<file>.json", "type": "dependency_single", "scene": <int>, "shot": <int>},
        {"name": "<file>.json", "type": "dependency_pair", "scene": <int>, "shots": [<int>, <int>]},
        {"name": "<file>.json", "type": "image_asset", "scene": <int>, "shot": <int>},
        {"name": "<file>.json", "type": "scene_detail_shot", "scene": <int>, "shot": <int>},
        {"name": "<file>.json", "type": "shot_validator_shot", "scene": <int>, "shot": <int>},
        {"name": "<file>.json", "type": "entity_canon", "short_id": "<id>"}
      ],
      "low_freq_skip": {
        "pre_filename": "<file>.json",
        "post_filename": "<file>.json",
        "protected_short_ids": ["<id>", ...],
        "post_note": "<설명>"
      }
    }
)annotationsN)Path)Anybackend)SessionLocal)EntityCanon
ImageAsset
SceneStillc               (    V ^8  d   QhRRRRRRRR/# )   pidstreidstepreturnr    )formats   " scripts/g4_6_capture_fixtures.py__annotate__r   2   s.     d ds d dC dD d    c                p    \         R ,          V ,          R,          R,          V,          V,          R,          # )projectscheckpointsepisodeszmanifest.json)	REPO_ROOT)r   r   r   s   &&&r   manifest_pathr   2   s,    z!C'-7*DsJTQTcccr   c               (    V ^8  d   QhRRRRRRRR/# )r   r   r   r   r   r   dictr   )r   s   "r   r   r   6   s(     8 8C 8c 8 8 8r   c                    \        WV4      pVP                  4       '       g   \        R V 24      h\        P                  ! VP                  RR7      4      # )zmanifest missing: utf-8encoding)r   existsFileNotFoundErrorjsonloads	read_text)r   r   r   paths   &&& r   load_step_manifestr(   6   sF    4(D;;=="4TF ;<<::dnngn677r   c               (    V ^8  d   QhRRRRRRRR/# )	r   out_dirr   namer   payloadobjectr   Noner   )r   s   "r   r   r   =   s.     V V VC V& VT Vr   c                    W,          pVP                  \        P                  ! VR ^R7      RR7       \        RVP	                  \
        4       RVP                  4       P                   R24       R# )F)ensure_asciiindentr   r    z  wrote z (z bytes)N)
write_textr$   dumpsprintrelative_tor   statst_size)r*   r+   r,   targets   &&& r   
write_jsonr9   =   sZ    ^F
djjuQGRYZ	HV''	232fkkm6K6K5LG
TUr   c               (    V ^8  d   QhRRRRRRRR/# )r   depslistsceneintshotr   dict | Noner   )r   s   "r   r   r   C   s(      $ s # + r   c                |    V  F5  pVP                  R 4      V8X  g   K  VP                  R4      V8X  g   K3  Vu # 	  R# )scene_index
shot_indexNget)r;   r=   r?   ds   &&& r   find_dependencyrG   C   s7    555(QUU<-@D-HH  r   c               (    V ^8  d   QhRRRRRRRR/# r   scenesr<   r=   r>   r?   r   r@   r   )r   s   "r   r   r   J   s(      4  3 ; r   c                |    V  F5  pVP                  R 4      V8X  g   K  VP                  R4      V8X  g   K3  Vu # 	  R# )rB   _shot_indexNrD   )rJ   r=   r?   entrys   &&& r   find_scene_detail_shotrN   J   s7    99]#u,=1IT1QL  r   c               (    V ^8  d   QhRRRRRRRR/# rI   r   )r   s   "r   r   r   Q   s(       S   r   c                    V  Fe  pVP                  R 4      V8w  d   K  VP                  R. 4       F4  pVP                  R4      V8X  g   K  R VRVP                  R4      RV/u u # 	  Kg  	  R# )rB   shotsrC   scene_headingr?   NrD   )rJ   r=   r?   scshs   &&&  r   find_validator_shotrU   Q   si    66- E)&&"%Bvvl#t+%uorvvo?VX^`bcc &  r   c                    V ^8  d   QhRRRR/# )r   rowr   r   r   r   )r   s   "r   r   r   [   s     
 
k 
d 
r   c                    R V P                   RV P                  RV P                  RV P                  RV P                  RV P
                  RV P                  RV P                  /# idshort_identity_typer+   descriptionstable_traits
t2i_promptstatusrY   rW   s   &r   entity_canon_to_dictrb   [   sV    cffCLLss**cnn#**	 	r   c                    V ^8  d   QhRRRR/# )r   rW   r   r   r   r   )r   s   "r   r   r   h   s      Z D r   c                >   R V P                   RV P                  RV P                  RV P                  RV P                  RV P
                  RV P                  RV P                  RV P                  R	V P                  R
V P                  RV P                  RV P                  /# rZ   
asset_typestill_id	entity_idvariant_indexvariant_labelrC   prompt_usedreference_image_idsgeneration_modelcode_versionprompt_file_versionr`   re   ra   s   &r   image_asset_to_dictrp   h   s    cffcnnCLLS]]****cnnss66C00((s66#** r   c          
     ,    V ^8  d   QhRRRRRRRRRR/# )	r   r   r   r   r=   r>   r?   r   r   r   )r   s   "r   r   r   z   s0       # c  QU r   c                   V P                  \        4      P                  \        P                  V8H  \        P                  V8H  \        P
                  V8H  \        P                  V8H  4      P                  4       pVf   \        RV RV RV RV 24      hV P                  \        4      P                  \        P                  V8H  \        P                  V8H  \        P                  VP                  8H  4      P                  \        P                  4      P                  4       pRTRTRVP                  RVP                  R	V Uu. uF  p\!        V4      NK  	  up/# u upi )
NzSceneStill missing for scene= shot=z pid=z eid=rB   rC   rg   still_frame_promptimage_assets)queryr	   filter
project_id
episode_idrB   rC   firstRuntimeErrorr   rg   rZ   order_byri   allrt   rp   )dbr   r   r=   r?   stillassetsas   &&&&&   r   fetch_image_asset_for_shotr   z   s;   
	!!S(!!S(""e+!!T)	

 
 
 }:5'vUSVRWW\]`\abcc
	!!S(!!S(588+


 
***	+	  	udEHHe66@A,Q/@ 
 As   Ec               $    V ^8  d   QhRRRRRR/# )r   r   r   r[   r   r   r   )r   s   "r   r   r      s!     % % %s %t %r   c                    V P                  \        4      P                  \        P                  V8H  \        P                  V8H  4      P                  4       pVf   \        RV: RV 24      h\        V4      # )NzEntityCanon short_id=z missing for pid=)rv   r   rw   rx   r[   rz   r{   rb   )r~   r   r[   rW   s   &&& r   fetch_entity_canonr      si    
	&&#-{/C/Cx/O	P	 
 {28,>OPSuUVV$$r   dependency_singleshot_dependency_t2idependency_pairscene_detail_shotscene_detailshot_validator_shotshot_validatorzdict[str, str]CAPTURE_HANDLERSc               0    V ^8  d   QhRRRRRRRRRR	R
R/# )r   specdict[str, Any]r   r   r   r*   r   manifest_cachezdict[str, dict]r   r.   r   )r   s   "r   r   r      sN     :D :D
:D 
:D 
	:D :D $:D 
:Dr   c                  V R ,          pV R,          pV\         9   d*   \         V,          pW9  d   \        WV4      WX&   WX,          p	VR8X  dt   X	P                  R/ 4      P                  R. 4      p
\        WR,          V R,          4      pVf"   \	        V RV R,           R	V R,           24      h\        WFR
RRV/4       R# VR8X  d   X	P                  R/ 4      P                  R. 4      p
V R,          pV R,          p\        V4      ^8w  d   \        V RV 24      hV Uu/ uF  pRV 2\        WV4      bK  	  ppVP                  4        UUu. uF  w  ppVe   K  VNK  	  pppV'       d   \	        V RV 24      h\        WFR
R/VC4       R# VR8X  du   X	P                  R/ 4      P                  R. 4      p\        VV R,          V R,          4      pVf"   \	        V RV R,           R	V R,           24      h\        WFR
RRV/4       R# VR8X  du   X	P                  R/ 4      P                  R. 4      p\        VV R,          V R,          4      pVf"   \	        V RV R,           R	V R,           24      h\        WFR
R/VC4       R# VR8X  d(   \        WF\        W1W R,          V R,          4      4       R# VR8X  d    \        WF\        W1V R,          4      4       R# \        V RV: 24      hu upi u uppi )r+   typer   datadependenciesr=   r?   Nz: dependency missing scene=rs   sourcez!shot_dependency_t2i.manifest.json
dependencyr   rQ   z0: dependency_pair requires exactly 2 shots, got z: missing entries r   rJ   z#: scene_detail entry missing scene=zscene_detail.manifest.jsonr   z%: shot_validator entry missing scene=zshot_validator.manifest.jsonimage_assetentity_canonr[   z: unknown fixture type )r   r(   rE   rG   r{   r9   len
ValueErroritemsrN   rU   r   r   )r   r   r   r~   r*   r   r+   ftyper   manifestr;   rM   r=   rQ   sentrieskvmissingrJ   s   &$$$$$              r   dispatch_fixturer      s    <DLE  &%#5c#EN !'##||FB'++NB?7mT&\B=$'B4=/QWX\]cXdWefgg78-PR^`e"fg	#	#||FB'++NB?WWu:?v%UV[U\]^^HMN1T!:tA>>N!(>AA11>$'9'CDD78-P"\T["\]	%	%fb)--h;&vtG}d6lK=$'J4PW=/Y_`dek`l_mnoo78-I6SX"YZ	'	'fb)--h;#FDM4<H=$'LTRY]O[abfgmbnaopqq78-K"Uu"UV	-	7"<RcPW=Z^_eZf"gh	.	 7"4Rd:>N"OP D6!8	BCC7 O>s    K*	K/K/c          
     ,    V ^8  d   QhRRRRRRRRRR	/# )
r   cfgr   r   r   r   r*   r   r   r.   r   )r   s   "r   r   r      s<     4 4	4 
4 
	4 4 
4r   c               Z   \         R,          V,          R,          R,          V,          R,          pVP                  4       '       g   \        RV 24      h\        P                  ! VP                  RR7      4      p\        V\        4      '       g   \        R4      hVP                  \        P                  \        P                  \        P                  \        P                  4      P                  \        P                  V8H  \        P                  P!                  V4      4      P#                  4       pV Uu/ uF@  pVP                  R	VP                  R
VP                  RVP                  RVP                  /bKB  	  p	pV U
u. uF  qV	9  g   K  V
NK  	  pp
V'       d    \        R\%        V4       RV RV: R24      hV U
u. uF  qV
,          NK  	  pp
\'        W@R,          R\)        VP+                  \         4      4      RVRV/4       \-        V P/                  R. 4      4      pV Uu. uF  qR
,          V9  g   K  VNK  	  ppV Uu. uF  qR	,          NK  	  pp\'        W@R,          RV P/                  RR4      R\1        V4      RVRV/4       R# u upi u up
i u up
i u upi u upi )u   ref_low_freq_skip pre + post 쌍 생성.

pre: 현재 ref_low_freq_skip.json (UUID list) + short_id resolved.
post: protected_short_ids 가 제거된 expected 결과.
r   r   imageszref_low_freq_skip.jsonz"ref_low_freq_skip.json missing at r   r    z$ref_low_freq_skip.json is not a listrZ   r[   r\   r+   u   ref_low_freq_skip 의 u&    UUID 가 EntityCanon 에 없음 (pid=z): u'    — fail-fast (silent fixture 차단).pre_filenamer   uuidsresolvedprotected_short_idspost_filenamenote	post_note N)r   r"   r{   r$   r%   r&   
isinstancer<   rv   r   rZ   r[   r\   r+   rw   rx   in_r}   r   r9   r   r5   setrE   sorted)r   r   r   r~   r*   	skip_path
skip_uuids
canon_rowsrresolved_indexu
unresolvedordered_resolved	protectedpost_resolved
post_uuidss   &$$$$           r   write_low_freq_skip_pairr      st    	J$}4x?#EH``  ?	{KLLI///ABJj$''ABB
!5!5{7N7NP[P`P`a	&&#-{~~/A/A*/M	N	  A 	
tQTT:qzz=!--QWYZY_Y_``   (CZN+B!!ZJC$S_$5 65J>)PR
 	
 4>>:aq)):>wN+#i++I67$.  CGG1267I 0S 01jM4RQQ 0MS#01=aD''=J1wO,R(vi0M	/ + D ? T1s+   8AJJJJ+J#?J#J(c                   V ^8  d   QhRR/# )r   r   r.   r   )r   s   "r   r   r   !  s     #Y #Yd #Yr   c                    \         P                  ! \        R 7      p V P                  RRRR7       V P                  RRRR7       V P                  RRRR7       V P                  R	RR
R7       V P	                  4       p\        VP                  4      P                  4       '       g   \        VP                  ,          M\        VP                  4      p\        P                  ! VP                  RR7      4      pVP                  R4      p\        V\        4      '       d	   V'       g   \        RV R24      hVP                  R4      p\        V\         4      '       g   \        RV R24      hR F  pWe9  g   K  \        RV RV R24      h	  \        VP"                  4      P                  4       '       g   \        VP"                  ,          M\        VP"                  4      pVP%                  RRR7       \'        RVP(                   RVP*                   RVP-                  \        4       RVP-                  \        4       24       / p\/        4       p	 V F&  p
\1        WP(                  VP*                  WVR7       K(  	  \3        WQP(                  VP*                  WR7       V	P5                  4        \7        V4      ^,           p\'        RV RVP-                  \        4       24       R#   T	P5                  4        i ; i))r]   z--pidTrx   )requiredhelpz--eidry   z--configzfixture config JSON pathz--outz"output dir (relative to repo root)r   r    fixtureszconfig z%: 'fixtures' must be a non-empty listlow_freq_skipz : 'low_freq_skip' must be a dictz: low_freq_skip.z missing)parentsexist_okzcapture: pid=z, eid=z	, config=z, out=)r   r   r~   r*   r   )r   r   r~   r*   u
   
DONE — z fixtures written to N)r   r   r   )argparseArgumentParser__doc__add_argument
parse_argsr   configis_absoluter   r$   r%   r&   rE   r   r<   r   r   outmkdirr4   r   r   r5   r   r   r   closer   )parserargsconfig_pathr   r   lfsr   r*   r   r~   r   expecteds               r   mainr   !  sz   $$9F
$\B
$\B

T8RS
$5YZD373D3P3P3R3R9t{{*X\]a]h]hXiK
**[**G*<
=Cwwz"Hh%%X7;-/TUVV
''/
"Cc4  7;-/OPQQLw{m3CH:XVWW M -1N,F,F,H,Hy488#dSWS[S[nGMM$M.	M$((6$((9[=T=TU^=_<``fgngzgz  |E  hF  gG  H  I&(N	BDTxxTXX"ftu  ((RY

8}q H	Kz!6w7J7J97U6V
WX 	
s   AK( (K:__main__))__conditional_annotations__r   
__future__r   r   r$   syspathlibr   typingr   __file__resolver   r   BACKENDr'   insertr   app.core.databaser   app.models.projectr   r   r	   r   r(   r9   rG   rN   rU   rb   rp   r   r   r   __annotations__r   r   r   __name__)r   s   @r   <module>r      s    B #   
  N""$,,Q/	
i
 3w<   * B Bd8V
$@% .,+	$ . :Dz4n#YL zF r   