
    jl                    \   d Z ddlZddlZddlZddlmZmZmZ ddlm	Z	m
Z
 ddlmZ ddlmZmZ ddlmZ ddlmZ  ej*                  e      Z G d	 d
      Z G d dee      Z G d dee      ZdedefdZdededefdZ G d dee      Z G d dee      Z G d dee      Z eeee edZ!y)u   이미지 Phase StepRunner 서브클래스 — Steps 11-14.

force 모드: 기존 이미지 삭제 안 함. 새 이미지 추가 생성 (is_primary 이동).
resume 모드: 미완성분만 이어서 생성.
    N)AnyDictOptional)build_name_indexlookup_name)
StepRunner)get_visual_descriptoris_immobilized_state)annotate_generated_asset)PRESENCE_SHELVEDc                   >    e Zd ZdZd Zdedee   fdZdefdZ	d Z
y)	_ImageStepMixinu   이미지 단계 공통.c                     	 ddl m}  |       j                  | j                                y# t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)uJ   ImageTracer에 Opik context 전달 — thread pool worker에서도 유지.r   )get_image_traceru;   image tracer context setup failed: %s — 본 처리 계속N)app.modules.llm.image_tracerr   set_contextbuild_opik_metadata	Exceptionloggerdebug)selfr   excs      P/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/image_steps.py_setup_image_tracer_contextz+_ImageStepMixin._setup_image_tracer_context   sJ    	]E**4+C+C+EF 	]LLVX[\\	]s   *- 	AAAstep_idreturnc                     ddl m} ddlm}  ||j                        | j
                  z  dz  dz  | j                  z  |z  dz  }|j                         r%t        j                  |j                  d            S y )	Nr   Pathsettingscheckpointsepisodeszmanifest.jsonutf-8encoding)pathlibr   app.core.configr!   projects_dir
project_id
episode_idexistsjsonloads	read_text)r   r   r   r!   cps        r   _load_prev_checkpointz%_ImageStepMixin._load_prev_checkpoint!   sp     ,(''(4??:]JZWZ^ZiZiilss  wF  F99;::bllGl<==    c                     ddl m} | j                  j                  |      j	                  |j
                  dk(        j                         }|s)| j                  j                  |      j                         }|st        d      |j                  S )u;   시스템 작업용 actor_id — admin 사용자 ID 반환.r   )UserAccountadminz8No user accounts exist; cannot determine system actor_id)	app.models.catalogr4   dbqueryfilterrolefirstRuntimeErrorid)r   r4   users      r   _get_system_actor_idz$_ImageStepMixin._get_system_actor_id)   sl    2ww}}[)001A1AW1LMSSU77==-335DYZZwwr2   c                 `    ddl m}  || j                  | j                  | j                         y)ub  StepRunner 분석 체크포인트 → DB 동기화.

        이미지 서비스는 episode.status='analyzed' + EntityCanon 존재를 요구.
        StepRunner는 체크포인트에만 저장하므로 이미지 단계 전에 DB에 반영.
        Phase 2.1 Service 오케스트레이터에 위임 (API private 역의존 해소).

        W4 P3-2 (Codex High 반영): 이 경로는 image step **실행 전** 선행 sync 이므로
        step_id를 전달하지 않는다. post-step 신호가 아님. 실패 시 별도 분석 step의
        sync_status에 영향을 주면 잘못된 해석을 유발.
        r   )orchestrate_full_syncN)app.services.checkpoint_syncrA   r*   r+   r7   )r   rA   s     r   _sync_analysis_to_dbz$_ImageStepMixin._sync_analysis_to_db3   s      	GdootHr2   N)__name__
__module____qualname____doc__r   strr   r   r1   r?   rC    r2   r   r   r      s4    "]S Xd^ c Ir2   r   c                   $    e Zd Zddeeef   fdZy)WorldGuideStepr   c                    | j                          ddlm} ddlm} ddlm} ddlm}m}m	}m
} dd l}	dd l}
ddlm}m} | j                  j!                  |      j#                   ||j$                              j'                  |j(                  | j*                  k(        j-                         }ddlm}  || j                  | j2                  | j*                        }|rQ| j                  j!                  |      j'                  |j(                  j5                  |            j7                         ng }|D cg c]5  }|j(                  |j8                  |j:                  |j<                  xs dd	7 }}| j                  j!                  |      j'                  |j2                  | j2                  k(  |j*                  | j*                  k(  |j>                  d
k(  |j@                  dk\  |jB                  dk7        j7                         }|D cg c]  }d|jD                  xs di }}|j$                  xs d} |	jF                  |d d  dtI        |       dtI        |       jK                               jM                         }|dk(  r| j                  j!                  |      j'                  |j2                  | j2                  k(  |j*                  | j*                  k(        jO                  |jP                  jS                               j-                         }|r3|jT                  |k(  r$dddtW        jX                  |jZ                        dS  |       }|j]                  ||j^                  xs d|j`                  xs d||| jb                        }|je                  |jf                        ji                         }| j                  jk                   |tm        |
jo                               | j2                  | j*                  tW        jp                  |d      ||             | j                  js                          ddd|dS c c}w c c}w )Nr   )Episode)undefer)WorldGuideGenerator)
WorldGuideEntityCanonEntityEpisodeLink
SceneStilldatetimetimezoneactive_episode_canon_ids )r=   nameentity_typedescriptionTstalestill_frame_prompti  :resume   completed_countapplicable_countfailed_countdatakoepisode)fulltextlanguagesource_fileentitiesstillsproject_configF)ensure_ascii)r=   r*   r+   
guide_jsonsource_hash
created_at):r   app.models.projectrM   sqlalchemy.ormrN   !app.modules.world_guide_generatorrO   rP   rQ   rR   rS   hashlibuuidrU   rV   r7   r8   optionsri   r9   r=   r+   r;   app.core.entity_identityrX   r*   in_allrZ   r[   r\   is_selectedstill_indexstatusr^   md5lenencode	hexdigestorder_byrr   descrq   r-   r.   rp   generaterj   source_filenamern   nowutc	isoformataddrH   uuid4dumpscommit)r   moderM   rN   rO   rP   rQ   rR   rS   rv   rw   rU   rV   eprX   	canon_idsentities_ormerl   
stills_ormsrm   ftwg_hashexistingwg_genworld_guider   s                               r   _executezWorldGuideStep._executeE   s   ((*.*I]]/WW]]7#++GG4D4D,EFMMgjj\`\k\kNklrrt 	F,GGT__doo7	ajtww}}[1889K9KI9VW[[]prBNPBNQ 44$%MM$7R9BN 	 P WW]]:.55!!T__4!!T__4""d*""a'(
 #% 	 OYYj')=)=)CDjY[[B'++DS
!CM?!CK=IPPRS]]_ 8ww}}Z077%%8%%8 hz,,1134UUW  H00G;+,!UV $

8+>+> ?A A %&oo"++"5**7if..	 & 
 ll8<<(224J4::< T__zz+EBC
 	
 	#$!Q#% 	%UP Zs   4:P8P=Nr`   )rD   rE   rF   r   rH   r   r   rI   r2   r   rK   rK   D   s    =%c3h =%r2   rK   c                   6    e Zd ZdefdZddeeef   fdZd Zy)RefImageGenStepr   c                 `   ddl m} ddlm} ddlm}m}  || j                        } || j                        } ||      s|S ddl} || j                  | j                  | j                  |      }|j                  | d| j                  d            j                         dt        |       S )	u  `v2_chunk` 에서만 canonical ref 입력 전체의 지문을 접는다 (Codex D 2026-09-02).

        사람이 사진을 verified/rejected 로 바꾸면 이 값이 달라져 스텝이 SKIP 으로
        빠지지 않는다 — 실제로 다시 굽는 것은 orchestrator 의 지문 대조가 정한다
        (바뀐 entity 만). legacy/v2 는 base 지문 **그대로**(옛 CP 보존).
        r   )resolve_grounding_modecompute_config_hash)canonical_grounding_digestcentral_cp_is_requiredN)r*   r+   grounding_modez|grounding_inputs=r$   )app.core.grounding_moder   app.core.step_runnerr   3app.modules.pipeline.grounding_canonical_ref_inputsr   r   rn   rv   r7   r*   r+   sha256r   r   r   )	r   r   r   r   r   baser   _hldigests	            r   _config_hashzRefImageGenStep._config_hash   s     	C<	
 #4#6#67%d&9&9:%d+K+GGDOO! zzf&vh/66w?

)+js4y" 	"r2   c                 V   | j                          ddlm} | j                          |dk(  rddlm} | j                  j                   |d      | j                  | j                  d       | j                  j                          ddlm} ddlm}  ||j                        | j                  z  d	z  d
z  | j                  z  dz  }|j!                         r|j#                           || j                  | j                  | j%                               }|j'                  | j                  d      }|j)                  dd      xs |j)                  dd      }	|j)                  dd      }
|j)                  dd      }|j)                  d|	|
z   |z         }|	|
z   |||| j+                         dS )Nr   ReferenceImageServiceforcetextzUPDATE image_asset SET is_primary = 0 WHERE project_id = :pid AND episode_id = :eid AND asset_type = 'reference' AND is_primary = 1 AND COALESCE(prompt_used, '') NOT LIKE '[outfit:%' AND COALESCE(prompt_used, '') NOT LIKE '[composite:%'pideidr   r    r"   imageszreference_checkpoint.jsonr7   r*   actor_idr`   r+   r   reference_count	generatedskippedfailedtotal)rc   rd   re   rf   config_hash)r   $app.services.reference_image_servicer   rC   
sqlalchemyr   r7   executer*   r+   r   r'   r   r(   r!   r)   r,   unlinkr?   generate_base_referencesgetr   )r   r   r   r   r   r!   cp_pathsvcresultr   r   r   r   s                r   r   zRefImageGenStep._execute   s   ((*N 	!!#7? (GGOODH t?A GGNN$08001DOOCmSV^^aeapapp  tO  OG~~ #tww4??UYUnUnUpq
 --X . 
 JJ0!4R

;PQ8R	**Y*Ha(

7I$7&$@A  )72 %",,.
 	
r2   c           	         ddl m} ddlm}m}m}  || j                  | j                  | j                        }|D cg c]  }|j                   }}t        |      }|dk(  r |dg ddddd      S t         || j                  | j                  |            }	 || j                  | j                  |      }
|	|k  r$ |d||	z
   d	| d
|	 dg|	dk(  rdnd||	|
d      S  |dg d||	|
d      S c c}w )uy  이 에피소드의 ref 대상이 **정본 참조를 갖고 있는가**.

        「**대상은 episode, 자산은 canon/project**」 — 게이트와 같은 물음이다.
        대상은 이 에피소드에 연결된 `entity_type ∉ {location, outlook}` 엔티티
        (생산자 `reference_phase1_service.py:92` 와 같은 형태 — prop 누락이
        false-clean 으로 묻히지 않는다), 저빈도 스킵은 뺀다.
        자산은 `project + entity` 의 primary 참조 + **파일 실재**.

        ★`ImageAsset.episode_id` 로 묻지 않는다. 참조의 primary 는 프로젝트
         안에서 canon 당 하나이고(새 참조가 그 범위로 옛 primary 를 내린다),
         에피소드로 물으면 **재사용된 canon 이 거짓 미완료**가 된다.

        cleanup_artifacts override 안 함 → default noop (부분 재생성 보호).
        r   CompletionReport)entities_with_referencereference_asset_row_countreference_targetsTcleanexpectedfoundrowsis_completemissingseveritymetadataFz( character ref images missing (expected=, found=)r   partial)app.core.integrity_reportr   app.core.pipeline_gater   r   r   r7   r*   r+   r=   r   )r   r   r   r   r   expected_entitiesr   expected_idsr   r   r   s              r   verify_completionz!RefImageGenStep.verify_completion   s0    	?	
 	
 .GGT__doo7&78&7&78|$q=# "w&'!Q? 
 +GGT__l4 5 )$//<P8#!%'( )!!)
(5'< ',qji&.M   b7"*UDI
 	
1 9s   C0Nr   )	rD   rE   rF   rH   r   r   r   r   r   rI   r2   r   r   r      s(    "c "00
c3h 0
d9
r2   r   r   mismatch_reasonc                 0   ddl m} ddlm}m} t        t        |dd            j                  d      D ch c]"  }|j                         r|j                         $ }}| |vryt        j                  d| |        ||j                  d	| d
      S c c}w )u&  `STEP_CONFIG_DRIFT_ACK` 에 이 스텝이 적혀 있으면 **비파괴 재실행**.

    ## 왜 공용 함수인가

    이 규칙은 `SceneImagePipelineStep` 안에만 있었다. 합성·상태변형에
    지문을 넣자(`a58417b2`) 그 둘이 stale 이 됐는데 승인 문이 없어
    「force 로 재실행하라」만 나왔다 — force 는 **멀쩡한 자산까지 전부**
    다시 굽는다(합성 24장 중 고칠 것은 4장뿐이었다).

    같은 규칙을 세 곳에 베껴 두면 한 곳만 고쳐진다. 그래서 한 곳에 둔다.

    ★`RERUN_SELF` 는 cleanup/invalidate 없이 `_execute(resume)` 를 다시
     부르는 것이라 **완료 자산이 안 지워진다**. 서비스가 파일이 있는 것은
     건너뛰므로, 다시 만들 것만 다시 만든다.
    r   r    ResumeActionResumeDecisionstep_config_drift_ackrY   ,NuU   Step %s: config drift 를 env 승인으로 통과 — 비파괴 resume 재실행 (%s)uR   config drift — step_config_drift_ack 명시 승인, 비파괴 resume 재실행: config_drift_ackactionreasonorigin)r(   r!   r   r   r   rH   getattrsplitstripr   warning
RERUN_SELF)r   r   	_settingsr   r   xacks          r   _drift_ack_decisionr     s      6A WY(?DEKKCPPA779 	
	P  
 c
NN_" &&//>.?A!	 s   'Br   moduler   c                 l   ddl }ddlm} g }|D ](  }	 |j                  | d| d |||d      d           * |j                  d
j                  | gt        |      z         j                  d            j                         dt        |        S # t        $ r |j                  | d| d	       Y w xY w)u  `base` 지문에 팩 **버전**을 접는다 (2026-09-19).

    ## 왜 필요한가

    합성·상태변형 단계에는 `_config_hash` 가 **아예 없었다**. 둘 다 빈 dict
    지문(`99914b932bd37a50`)을 쓰고 있어서, `ref_image_prompts` 팩을 고쳐도
    스텝이 무효화되지 않았다 — 고친 문안이 **나가는 프롬프트까지 못 내려간다.**
    `#94`(cine_stage_direction)·`1-E`(prev_usage_clause)와 같은 부류다.

    실측으로 그 자리를 밟았다: 옷 참조의 마네킹이 같이 그려지는 결함을
    팩 v7 로 고쳤는데, 지문이 안 움직여 보통 resume 에서는 옛 그림이 그대로
    남았을 것이다.

    ★버전 문자열만 접는다. 바이트를 접으면 주석 한 줄만 고쳐도 유료 단계가
     통째로 다시 돈다 — 발행된 팩은 덮어쓰지 않는 것이 이 저장소 규칙이라
     버전이 곧 내용이다.
    r   N)resolve_effective/=prompt)kindversionz=<unresolved>|r$   )rv   app.modules.prompt_loaderr   appendr   r   joinsortedr   r   r   )r   r   stemsr   r   partsstems          r   _fold_prompt_packsr  5  s    $ ;E	:LL(!D6$VTA)LMOP  ::	4&6%=(	)11':ik*3t9 	  	: LLF81TF-89	:s   %B B32B3c                   N     e Zd Zdef fdZdefdZddeeef   fdZd Z	 xZ
S )CompositeImageGenStepr   c                 V    t        | j                  |      }||S t        |   |      S )u   합성 팩 수리로 지문이 움직였을 때, env 승인이 있으면 **비파괴**
        재실행. 없으면 기존대로 BLOCK — 승인 없는 drift 는 막는다.r   r   super_evaluate_contract_driftr   r   _acked	__class__s      r   r  z.CompositeImageGenStep._evaluate_contract_drift[  0     %T\\?CMw/@@r2   r   c                 H    ddl m} t         || j                        dd      S )uZ   합성이 쓰는 **팩 버전**을 접는다 — 없으면 팩 수리가 안 내려간다.r   r   ref_image_prompts)character_outlook_refcharacter_composite_refcharacter_composite_derive_refr   r   r  rn   r   r   s     r   r   z"CompositeImageGenStep._config_hashc  s(    <! 3 346I/0 	0r2   c           	         | j                          ddlm} ddlm}m}m}m} ddlm	} ddl
m} dd l}	| j                          ddlm}
  |
| j                   | j"                  | j$                        }t'               }|D ]s  }| j                   j)                  |      j+                  |j,                  |k(  |j.                  dk(        j1                         }|sY|j3                  |j,                         u | j                   j)                  |      j+                  |j"                  | j"                  k(  |j4                  dk(  |j.                  d	k(        j7                         D ch c]  }|j,                   c}|ri| j                   j)                  |      j+                  |j"                  | j"                  k(  |j8                  j;                  |            j7                         ng }dd
lm}  || j"                  | j$                        tA        fd|D              }|dk(  r| j                   j)                  |      j+                  |j"                  | j"                  k(  |jB                  dk(  |jD                  jG                  d      |r|jH                  j;                  |      nd      j7                         }|D ]?  }|jD                  sd|jD                  vs|jD                  jK                  dd      |_"        A | j                   jM                          tN        jQ                  dtS        |              || j                   | j"                  | jU                               }|jW                  | j$                  d       |r| j                   j)                  |      j+                  |j"                  | j"                  k(  |jB                  dk(  |jD                  jG                  d      |r|jH                  j;                  |      nd      j7                         ng }t'               }|D ]g  }|	jY                  d|jD                  xs d      }|s&|j[                  d      |j[                  d      }}|v rM|v rR|j3                  | d|        i | j                   j)                  |      j]                  ||j^                  |j,                  k(        j+                  |j"                  | j"                  k(  |j$                  | j$                  k(  |j.                  d	k(  |j4                  dk7        j7                         D cg c]4  }|j,                  |D ch c]  }|j`                   c}vr|j,                  6 }}}tS        |      |tc        d|tS        |      z
        tS        |      |tS        |      te        |      ddS c c}w c c}w c c}}w )Nr   r   )CharacterOutlook
ImageAssetrR   rQ   r   r   rW   	characterO00outlookload_low_freq_skip_idsc              3   Z   K   | ]"  }|j                   vr|j                  vrd  $ ywra   N)
outlook_idcharacter_id).0c_low_freq_skip_ids_o00_idss     r   	<genexpr>z1CompositeImageGenStep._execute.<locals>.<genexpr>  s0      
"!||8+FX0X {s   (+r   	reference%composite:%F_old_z
composite:zcomposite_old:z5Composite force: marked %d existing composites as oldr   r`   r   #composite:([a-f0-9-]+):([a-f0-9-]+)rY   ra      r_   )composite_donecombo_totaloutlook_no_wearer_countoutlook_no_wearer_idsrb   )3r   r   r   rs   r  r  rR   rQ   r   r   r'   r   rerC   ry   rX   r7   r*   r+   setr8   r9   r=   r[   r;   r   short_idr{   r#  rz   app.core.low_freq_skipr  sum
asset_typeprompt_usedlike	entity_idreplacer   r   infor   r?   generate_compositessearchgroupr  canon_idr"  maxr  ) r   r   r   r  r  rR   rQ   r   r   r2  rX   _activechar_ids_cidecr   _all_combosr  r/  
compositescar   composite_assetsexisting_keysmchar_idr"  or%  
_no_wearerr&  r'  s                                  @@r   r   zCompositeImageGenStep._executel  s   ((*Ncc# 
 	!!# 	F*477DOO+/??<5D{+22$&(?(?;(Neg  RUU#  ''--4;;&&$//9$$-''94 ce	
QADD 
  ggmm$45<<''4??:))--h7
 #% 	 	B"8$//"Z 
"
 

 7?
 z299%%8%%4&&++N;6>
$$((2E	
 ce  !>>gR^^&C%'^^%;%;LJZ%[BN ! GGNNKKOQTU_Q`a
 $tww4??UYUnUnUpq4??J   77==4;;!!T__4!![0""''72:J  $$X.	

 #% 	 "B		@"..BVTVWA"#''!*aggajZGX%,,	:,78 #$ ''--499!!**knn< f!,,?!,,?''94$$-	
 ce
Q tt+>+QALL+>> DD  	 
  #=1 +;]1C#CD"%m"4*+.z?)/
);		

 
	
s
n ?
s   "V=;WW"WWc                   #$% ddl }ddlm} ddlm} ddlm} ddlm}m	}m
}m} | j                  j                  |j                        j                  ||j                   |j                  k(        j#                  |j$                  | j$                  k(  |j&                  | j&                  k(  |j(                  t*        k7  |j,                  dk(        j/                         D 	ch c]  }	|	d   	 }
}	| j                  j                  |j                        j                  ||j                   |j                  k(        j#                  |j$                  | j$                  k(  |j&                  | j&                  k(  |j(                  t*        k7  |j,                  dk(  |j0                  d	k7        j/                         D 	cg c]  }	|	d   	 }}	| j                  j                  |      j#                  |j$                  | j$                  k(  |j0                  d	k(  |j,                  dk(        j/                         D ch c]  }|j                    }} || j$                  | j&                        $g }i }dd
lm} |
rD || j                  | j$                  | j&                        D cg c]  }|j6                  |
v r| c}ng }|D cg c]6  }|j8                  |vr&|j6                  $vr|j6                  |j8                  f8 }}t;        |      }d}|dkD  r8| j                  j                  |      j#                  |j$                  | j$                  k(  |j<                  dk(  |j>                  jA                  d            j/                         }tC               #|D ]|  }	|jE                  d|	j>                  xs d      }|s& ||	jF                        }|s;|jI                         sL#jK                  |jM                  d      |jM                  d      f       ~ tO        #fd|D              }||k  r|jQ                  ||z
   d| d| d       ||d<   ||d<   |rD || j                  | j$                  | j&                        D cg c]  }|j8                  |v r| c}ng }i %|D ]?  }%jS                  |j8                  tC                     jK                  |j6                         A dtT        dtV        f$%fd}|D cg c]  } ||      s| }}t;        |      }tO        %fd|D              |d<   d}|dkD  r| j                  j                  |      j#                  |j$                  | j$                  k(  |j<                  dk(  |jX                  j[                  |            j/                         } | D ]-  }	 ||	jF                        }|s|jI                         s)|dz  }/ ||k  r|jQ                  ||z
   d| d| d       ||d<   ||d<   ||z   }!||z   }"|!dk(  r |dg d |!      S |"|!k  r |d"||"dk(  rd#|!      S d$|!      S  |dg d |!      S c c}	w c c}	w c c}w c c}w c c}w c c}w c c}w )%uy  Composite step의 두 산출물 검증:
          1. char×outlook composite (prompt_used~'composite:char:outlook') — Phase 1
          2. outlook standalone (asset_type='reference', entity_type='outlook',
             O00 제외) — Phase 2

        O00 + low_freq skip 제외. cleanup_artifacts override 안 함 → default
        noop (부분 재생성 보호).
        r   Nresolve_image_pathr   r  )r  rQ   rR   r  r  r  r  )episode_outlook_rowsr)  r*  r,  rY   ra   r-  c              3   ,   K   | ]  }|v sd   ywr!  rI   )r$  pairexisting_pairss     r   r(  z:CompositeImageGenStep.verify_completion.<locals>.<genexpr>E  s     T>4T^=SQ>   	z composites missing (expected=r   r   composite_expectedcomposite_foundoidr   c                 P    j                  |       }|sy|j                         S )NF)r   issubset)rY  wearersr   wearers_by_outlooks     r   _outlook_keptz>CompositeImageGenStep.verify_completion.<locals>._outlook_kepti  s-    (,,S1G''000r2   c              3   F   K   | ]  }j                  |      rd   ywr!  )r   )r$  rY  r]  s     r   r(  z:CompositeImageGenStep.verify_completion.<locals>.<genexpr>s  s$      4I$#,>,B,B3,GAs   !!outlook_no_wearer_skippedz' outlook standalones missing (expected=outlook_expectedoutlook_foundTr   r   Fr   r   ).r2  app.core.file_pathsrQ  r   r   r5  r  rs   r  rQ   rR   r  r7   r8   r@  r  r=   r9   r*   r+   presence_status_PRESENCE_SHELVEDr[   r{   r4  ry   rR  r#  r"  r   r7  r8  r9  r3  r>  	file_pathr,   r   r?  r6  r   
setdefaultrH   boolr:  rz   )&r   _rerQ  r   r  r  rQ   rR   r  rrC  outlook_idsr   o00_idsmissing_messagesr   rR  r%  
all_combosexpected_pairspair_expected
pair_foundcomposite_rowsrK  pwearer_linkslinkr^  rY  outlook_expected_idsra  rb  r   total_expectedtotal_foundrU  r   r]  s&                                      @@@r   r   z'CompositeImageGenStep.verify_completion  s    	:>A	
 	
 ''--(9(B(BCHH[^^/@/I/IIf!,,?!,,?!115FF'';6 ce

QAaD  	 

 ''--(9(B(BCHH[^^/@/I/IIf!,,?!,,?!115FF''94$$- ce	
	QAaD 	 	 
 ''--4;;&&$//9$$-''94 ce	
QADD  	 
 )$//J&(#% 	B  ,DGGT__,0OO=
=!~~)  =
 	 	 5?
4>q||7*q~~W/L ^^Q\\*J 	 
 N+
1!WW]]:6==%%8%%4&&++N; ce	 
 !UN#JJEq}}GZXZ[&q{{3"&&
AGGAJ'?@ $ T>TTJM) ''$z12 3!!.x
|1F
 *7%&&0"#.  ,DGGT__,0OO=
=!||{*  =
 !	 	
 .0 D))$//35AEEdFWFWX !	1s 	1t 	1 0;Q{mC>P{Q34 14 4I$4I 1I,- a77==,33%%8%%4$$(()=> ce	 
 &q{{3!Q&M  // '''-78 9!!1 2(=/L
 (8#$$1! ')99 =0Q# "w! 
 '#!(&1Q&6!	  =F!	   b7
 	
w



"


`
  Rs0    Y6Y1YY9;Y7Y 0Y%>Y%r   )rD   rE   rF   rH   r  r   r   r   r   r   __classcell__r  s   @r   r  r  Z  s:    A A0c 0
c3h 
Bq
r2   r  c                        e Zd ZdeddfdZdef fdZdefdZdefdZddeee	f   fd	Z
d
eee	f   ddf fdZd ZdefdZd Zd Zd Z xZS )SceneImagePipelineStepr   r   Nc                 l   ddl m} t        t        |dd            rbddlm} ddlm}m}  ||j                        | j                  z  dz  | j                  z  dz  d	z  |z  }|j                         r |d
| d      |dk7  ryddl m} ddlm}  |t        |dd            rddlm}	  |	ddd      y)u  표적 씬 슬라이스+force 병용 금지 (Codex 재리뷰 BLOCKING-1).

        run() 최선두(claim/cleanup_artifacts/invalidate_downstream/
        clear_checkpoint 이전) preflight — _execute 내부 가드만으로는
        public run(force) 의 파괴 경로가 먼저 지나간다. force 는 에피소드
        전체 primary 해제+checkpoint 삭제+recipe 아카이브라 표적 모드와
        병용 시 비표적 자산까지 무효화 (부분 force=별도 범위).

        #77-B 상한 래치(Codex 3차 리뷰): 같은 이유로 래치 검사도 여기 —
        _execute 첫 문장 검사만으로는 public run(force) 이 그 전에
        invalidate_downstream·clear_checkpoint 로 기록을 변형한다.
        래치가 있으면 mode 무관, 어떤 변형·지출보다 먼저 멈춘다.
        (_execute 초입·recipe 걷기 초입 검사는 다른 진입로 방어로 유지)
        r   r    still_jit_verify_enabledTr   JIT_LATCH_FILENAMEStillJitRegenLimitExceededr   scenerecipe.   JIT 재생성 상한 래치가 남아 있다: u    — 사람 확인 전에는 mode 와 무관하게 이 스텝을 실행하지 않는다(선행 유료 구간·downstream 무효화·checkpoint 삭제 전부 차단). 원인 확인 후 래치 파일을 삭제하고 resume.r   Nparse_target_scenesscene_image_target_scenesrY   AppError"scene_image.target_force_forbidden   scene_image_target_scenes 설정과 mode=force 병용 금지 — force 는 에피소드 전체 무효화라 비표적 자산을 파괴한다 (fail-closed)  codemessagestatus_code)r(   r!   rh  r   r'   r   !app.services.still_recipe_servicer  r  r)   r*   r+   r,   &app.modules.pipeline.scene_image_scoper  app.core.errorsr  )
r   r   _s77_P77
_JIT_LATCH_JitLatchErr_latchr!   r  r  s
             r   validate_modez$SceneImagePipelineStep.validate_mode  s     	58$?@, T&&'$//9HD//"$+,.679CD  }}"DVH M     7?,	
 "=rBD091   	Dr2   r   c                 @   d|v rddl m}m} | j                         }|xs i j	                  d      }|s|xs i j	                  d      }|r+|| j                         k(  r ||j                  d| d      S t        | j                  |      }||S t        | )  |      S )	u  표적 목록 변경만의 drift = 비파괴 RERUN_SELF (Codex 재리뷰
        BLOCKING-2).

        표적은 config_hash 에 접혀 있어(아래 _config_hash) 목록 변경 시
        cp_mismatch → 기본 정책은 BLOCK+force 요구인데 표적+force 는
        금지 — A scope 완주 후 B scope 로 바꾸면 영구 BLOCK 이던 교착
        해소. CP 에 영속된 target_scope_base_hash(표적 무관 base)가 현재
        base 와 같으면 '표적 목록만 바뀐 drift' 로 분류 — RERUN_SELF
        (origin=contract_drift) 는 cleanup/invalidate/clear 없이
        _execute(resume) 만 수행하므로 비표적 자산 무손실. base 자체가
        다르면(팩·모델 등 실질 변경) 기본 BLOCK 유지.
        zconfig_hash mismatchr   r   target_scope_base_hashr   uK   target-scope drift only (base hash 동일) — 비파괴 resume 재실행: contract_driftr   )r   r   r   load_checkpointr   _config_hash_baser   r   r   r  r  )r   r   r   r   r0   stored_baser  r  s          r   r  z/SceneImagePipelineStep._evaluate_contract_drift  s     "_4I%%'B8..)ABK  "xRnn];{d.D.D.FF%'2277F6GI ,  )GF!w/@@r2   c           
      2   ddl m} ddlm}m} | j                         } |t        |dd            }|s|S ddl}|j                  | ddj                  t        t        |             d	| j                  d
            j                         dt        |       S )uV  표적 씬 슬라이스 스탬프 (Codex NARROW-6): 표적 설정은 산출
        집합의 실질 입력 — recipe OFF(legacy) 경로 포함 전 경로에서 표적
        변경=stale. 빈 설정=_config_hash_base byte-identical. scope 계약
        버전(SCOPE_CONTRACT_VERSION)도 병행 스탬프 (Codex 재리뷰 HIGH-3).
        r   r    )SCOPE_CONTRACT_VERSIONr  r  rY   Nz|scene_targets=r   z|scope_contract=r$   )r(   r!   r  r  r  r  r   rv   r   r  maprH   r   r   r   )r   r!   r  r  r   targetsr   s          r   r   z#SceneImagePipelineStep._config_hash  s     	-	

 %%'%H92>@Kzz&S'1B(C'D"#9":<fWo	

 )+js4y" 	"r2   c                    ddl m} ddlm}  || j                        }t        |dd      rSddl}ddl}ddlm	} |j                  |j                  ||dd	
      j                  d            j                         dd }t        |dd      dk(  r|S ddl}ddl}ddlm}	m}
m} ddlm} ddlm}m}m} ||j0                  ||j2                  |j4                   |d       |d       ||	       |
|	      |j6                  dt9        t        |dd            d}t9        t        |dd            r(ddlm}m} d	|d<   ||d<    ||      |d<    |
|      |d<   t9        t        |dd            rFddlm}m } d	|d<   ||d<   |jB                   d|jD                   |d<    ||      |d <    |
|      |d!<   t9        t        |d"d            rrdd#lm#}m$} d	|d"<   ||d$<   dd%lm%} ||d&<   dd'lm&}  |       |d(<   t9        t        |d)d            r/ ||      |d*<    |
|      |d+<   tO        t        |d,d-            |d.<   t9        t        |d/d            rCt9        t        |d)d            r-dd0lm(}m)} d	|d/<    ||      |d1<    |
|      |d2<    |       |d3<   tO        t        |d4d5      xs d5      d5k7  rt9        t        |d)d            rdd6lm*}m+ dd7l,m-} tO        |j\                        |d4<   ||d8<   ||d9<    |j                  d:j_                  fd;ta                      D              j                  d            j                         dd |d<<   t9        t        |d=d            rzdd>l1m2}m3}m4} d	|d=<   ||d?<    |       |d@<    |       |dA<   ddBl1m5} m6}!m7}" | |dC<   |!|dD<    |"|       |dE<    |"|!      |dF<   ddGl8m9}#  |#       |dH<   ddIl:m;}$ ddJl<m=}%  |$|%K      |dL<   t9        t        |dMd            r`ddNl>m?}&m@}'mA}(mB}) ddOlmC}*mD}+ d	|dM<   |'|dP<    |(       |dQ<    |)       |dR<   |&|dS<   tO        t        |dTd-      xs d-      |dU<    |+|*dV      |dW<   t9        t        |dXd            r/ddYlEmF}, ddZlmG}-mH}. d	|dX<   d[|d\<   |-|.   |d]<    |,d^|-|.         |d_<   dd`lmI}/mD}0  ||/      |da<    |0|/db      |dc<   t        |ddde      }1|1dfk(  r0|1|dd<   t        |dgd-      |dg<   ddhlJmK}2 |2j                  did-      |dj<   |1dkk(  rxddllmM}3mN}4 |j                  |dd<   |j                  |d,<    ||3      |dm<    ||3      |dn<    ||4      |do<    ||4      |dp<   ddqlQmR}5mS}6 ddrlmT}7 |6|ds<   |5|dt<   t        |7      |du<   ddvlmV}8mW}9m}:m};  |;|9      |dw<    |:|9      |dx<   t        |ddde      dkk(  r |;|8      |dy<    |:|8      |dz<   t        |d{d      rdd|l,mX}< |<|d}<   dd~lmY}=mZ}>  |;|>      |d<    |:|>      |d<   t        |ddde      dkk(  r |;|=      |d<    |:|=      |d<   ddlm[}?mM}@mD}Am\}B t        |ddde      dkk(  }C |B|Ct9        t        |dd                  rCr|?@k7  r ||?      |d<    A|?d      |d<   t9        t        |dd            rddl]m^c m_c m`}D ddlma}EmD}F  |E       }Gt9        t        |dd            rd	|d<   ddlbmc}Hmd}I  |I       }Jd	|d<   |Jd   |d<   |Jd   |Hk7  rJd   |d<   |Jd   |d<    |G      |d<    F|GDj                        |d<   t9        t        |dd            r FGDj                        |d<   Dj                  |d<   t9        t        |dd            r*ddlhmi}Kmj}L d	|d<    |K       |d<   t         |L             |d<   ddlmk}Mml}NmD}Omm}P  |N |Pt        |ddde      dkk(              \  }Q}Rt        |j2                        |d<   |Q|d<    |O|R|Q      |d<    |O|Md      |d<   t9        t        |dd            r!ddlmo}S d	|d<    ||S      |d<    ||S      |d<   t9        t        |dd            rZddlpmq}Tmr}Ums}V d	|d<    |V       |d<    |U       |d<   tO        t        |dgd-            |d<   tO        t        |dd-            |d<   |T|d<   ddltmu}W  |W       r`ddlvmw}Xm}Y ddlmx}Zmy}[mz}\m{}]m}^ d	|d<    |^|\      |d<    |^|[      |d<    |^|]      |d<   |Z|d<    |Yd      |d<   |X|d<   |jD                  |d<   t        |dd      r,ddlvmw}_m}` d	|d<    |`d      |d<   |_|d<   |jD                  |d<   t        |dd      r&ddl|m}a d	|d<    |ad      |d<   d|d<   ddl|m}}b |b|d<   t        |dd      rUddl~m?}cm}dm}e d	|d<    |ed      |d<   |d|d<   |c|d<   tO        t        |dd-            |d<   tO        |jD                        |d<   t        |dd      dk(  r ||      |d<    ||      |d<   t        |dd      rddlm}fm}gm}hm}i d	|d<    |i|h      |d<   |g|d<   |f|d<   ddlm}j  |i|j      |d<   tO        |jD                        |d<   t        |dd      rJddlm}km}l d	|d<   |k|d<    i|l      |d<   t        |dd      r ddlm}mm}nm}j d	|d<    i|n      |d<   |m|d<   	 ddlm}ommDm  |o      |d<          |d<   ddlm}pm}q dj_                  fd݄|q|pfD              |d<   dj_                  fd߄|q|pfD              |d<   dj_                  fddD              |d<   t        |dd      rddlm}rm}s d	|d<    |s|r      |d<   t        |dd      rTt        |dd      rGddlm}tmD}umm}v  |t |vt        |ddde      dkk(        d	      \  }w}x s|x      |d<   |w|d<    |u|x|w      |d<    W       r-ddlm}ym}zmD}{  s|y      |d<    |{|yd      |d<    |{|zd      |d<   t        |dd      rddlm}|m}} d	|d<    |}||      |d<   t        |dd      rUddlm}~m} d	|d<    ||~      |d<   ddlm}mD}mm}  | |t        |ddde      dkk(              } ||      |d<    ||d      |d<   t        |dd      rFt9        t        |d)d            r0ddl,m} ||d<   dd'lm&}  |       |d<   dd lm}m}  ||      |d<   t        |dd      r<ddlm}m} ddl,m} ||d<    ||      |d<   tO        t        |dd-            |d<    |j                  |j                  |d	
      j                  d            j                         dd S (  u3  still_recipe(2026-07-13) opt-in stamping — mode OFF 는 base hash
        byte-identical(legacy cp 보존), "v1" 일 때만 레시피 키를 섞는다.
        표적 목록은 여기 접지 않는다 — target-scope drift 판별의 비교
        기준으로 CP(target_scope_base_hash)에 영속된다.r   r    r   zoom_continuity_anchor_enabledFN)VISION_MODEL_DEFAULT)r   zoom_bbox_modelT)	sort_keysr$      still_recipe_modeoff)STILL_JUDGE_PACK_VERSIONjudge_pack_content_hashresolve_judge_pack_version)!CARRIED_PERSON_PROJECTION_VERSION)!TEXT_ONLY_LOCATION_PROMPT_VERSIONrecipe_pack_content_hashresolve_prompt_version1z
gemini-prooutdoor_map_conti_enabled)r   r  carried_person_projectionstill_recipe_roll_countstill_recipe_critique_enabledstill_recipe_packstill_recipe_pack_contentmultiroll_judge_packmultiroll_judge_pack_content	nb2_modeljudge_modelr  multiroll_gq_judge_enabled)GQ_CRITIQUE_PACK_VERSIONGQ_SELECT_POLICY_VERSIONgq_select_policygq_critique_packgq_critique_pack_contentmultiroll_qk_judge_enabled)QK_CRITIQUE_PACK_VERSIONQK_SELECT_POLICY_VERSIONqk_select_policyr   qk_judge_modelsqk_critique_packqk_critique_pack_contentmultiroll_gg46_judge_enabled)GG46_CRITIQUE_PACK_VERSIONGG46_SELECT_POLICY_VERSIONgg46_select_policy) CROSS_MODEL_ORDER_POLICY_VERSIONselect_order_policy)#resolve_select_judge_model_physicalselect_judge_models_physicalr  gg46_critique_packgg46_critique_pack_contentgrok_image_modelrY   gg46_fix_image_modelstill_fix_ref_gate_enabled)FIX_MISSING_PACK_VERSIONfix_ref_contract_shafix_missing_packfix_missing_pack_contentfix_ref_contractstill_repair_methodedit)STILL_REGEN_PACK_VERSIONresolve_still_regen_texts)REPAIR_METHOD_POLICY_VERSIONstill_repair_policystill_regen_pack
c              3   2   K   | ]  }        |     y wNrI   )r$  k_regen_textss     r   r(  z;SceneImagePipelineStep._config_hash_base.<locals>.<genexpr>  s!      % 6$,.+ 6$s   still_regen_pack_contentera_research_enabled)ERA_RESEARCH_POLICY_VERSIONera_pack_content_hashresolve_era_packera_research_policyera_research_packera_research_pack_content)ASSESS_MODEL
PICK_MODELresolve_model_physicalera_assess_modelera_pick_modelera_assess_model_physicalera_pick_model_physical)search_contract_shaera_search_contract)acquisition_contract_sha)CENTRAL_ROUNDS)roundsreference_acquisition_contractsignage_author_enabled)AUTHOR_MODELSIGNAGE_POLICY_VERSIONresolve_signage_packsignage_pack_content_hash)SIGNAGE_SECTION_PROMPT_VERSIONrecipe_stem_content_hashsignage_policysignage_packsignage_pack_contentsignage_author_modelgemini_flash_modelsignage_author_model_resolvedsignage_sectionsignage_section_stem still_cast_wardrobe_lock_enabled)pack_dir_content_hash)PROMPT_VERSION_MAPPREV_CAST_SCOPE_PROMPT_VERSIONcast_wardrobe_lock_v1cast_wardrobe_lock_policycast_scope_packstill_recipecast_scope_pack_content) PREV_USAGE_CLAUSE_PROMPT_VERSIONr  recipe_prev_usage_packprev_usage_clauserecipe_prev_usage_stem_contentstill_image_backendnb2gpt25openai_image_model)SIZE_BY_ASPECTz16:9gpt_image_size_16_9grok2)STILL_COMPACT_PROMPT_VERSIONSTILL_GROK_STEM_VERSIONstill_compact_packstill_compact_pack_contentstill_grok_stem_packstill_grok_stem_pack_content)GROK_TEXT_BYTE_HARD_LIMITGROK_TEXT_BYTE_LIMIT)GROK_SHED_ORDERstill_grok_text_limitstill_grok_text_hard_limitstill_grok_shed_order)TEXT_POLICY_GROK_PROMPT_VERSIONTEXT_POLICY_PROMPT_VERSIONr  r  text_policy_packtext_policy_pack_contenttext_policy_grok_packtext_policy_grok_pack_contentstill_winner_gate_enabled)GATE_POLICY_VERSIONwinner_gate_policy)$NONHUMAN_CAST_COMPACT_PROMPT_VERSIONNONHUMAN_CAST_PROMPT_VERSIONnonhuman_cast_packnonhuman_cast_pack_contentnonhuman_cast_compact_pack"nonhuman_cast_compact_pack_content)CINEMATIC_FINISH_PROMPT_VERSIONr!  r  should_attach_cinematic_finishstill_cine_transform_enabled)grok_backendcine_onstill_cinematic_finish_packcinematic_finish#still_cinematic_finish_stem_content)cine_pack_selectorr  "still_cine_stage_direction_enabled)LEGACY_PROVIDERcine_provider_identitymodelcine_transform_modelprovidercine_transform_providerendpointcine_transform_endpointcine_transform_packcine_transform_stem_content!cine_stage_direction_stem_contentcine_transform_contractstill_cine_verify_enabled)verify_contract_shaverify_modelscine_verify_contractcine_verify_models) SUPPORT_STILLNESS_PROMPT_VERSIONbroll_variation_stemr  still_guidance_selectorstill_roll_countrX  broll_variation_stem_contentsupport_stillness_clausesupport_stillness_stem_contentstill_identity_ref_role_enabled)IDENTITY_ROLE_PROMPT_VERSIONidentity_role_packidentity_role_pack_contentstill_confined_fp_enabled)CONFINED_FP_POLICY_VERSIONconfined_fp_pack_content_hashresolve_confined_fp_packconfined_fp_packconfined_fp_pack_contentconfined_fp_modelgemini_text_modelconfined_fp_readback_modelconfined_fp_policy)outdoor_lane_pipe_on)AB_JUDGE_MODELr  )COMPLEX_AB_CONTRACT_VERSIONCOMPLEX_PROMPT_VERSIONLANE_PROMPT_VERSIONSEED_BG_PROMPT_VERSIONr  outdoor_lane_pipe_enabledstill_recipe_lane_packstill_recipe_complex_packstill_recipe_seed_bg_packcomplex_ab_contractconti_ab_judge_packconti_ab_judge_modelconti_ab_judge_model_physicalstill_conti_ab_enabledstill_plate_select_enabled)r  plate_select_packplate_select_model)PLATE_AUTHORITY_VERSIONplate_authority_versionstill_variants_enabled)r  STILL_VARIANTS_CONTRACT_VERSIONresolve_variants_pack_versionstill_variants_packstill_variants_contractstill_variants_author_modelopenai_model$still_variants_author_model_physical#still_variants_judge_model_physicalv1still_recipe_text_only_loc_pack"still_recipe_text_only_loc_contentstill_bgfirst_enabled)BGFIRST_BG_IMAGE_MODELBGFIRST_CONTRACT_VERSIONBGFIRST_PROMPT_VERSIONr  bgfirst_packbgfirst_contractbgfirst_bg_model) CHAIN_BG_LOCATION_PROMPT_VERSIONchain_bg_location_packbgfirst_judge_model_physicalstill_bgfirst_full_enabled)BGFIRST_FULL_CONTRACT_VERSIONBGFIRST_FULL_PROMPT_VERSIONbgfirst_full_contractbgfirst_full_packstill_lane_prev_bgfirst_enabled)BGFIRST_LANE_CONTRACT_VERSIONBGFIRST_LANE_PROMPT_VERSIONr  bgfirst_lane_packbgfirst_lane_contract)GEOM_AUTHORITY_PROMPT_VERSIONHANDLED_OBJECT_PROMPT_VERSIONr  r  recipe_geom_authority_packrecipe_handled_object_pack)REALIZE_COMPACT_PROMPT_VERSIONREALIZE_PROMPT_VERSIONc              3   .   K   | ]  } |        y wr  rI   )r$  r   
_geom_packs     r   r(  z;SceneImagePipelineStep._config_hash_base.<locals>.<genexpr>  s      21#/aJqM<s   recipe_realize_packc              3   >   K   | ]  }d D ]  } ||         yw))realize_still
human_formNrI   )r$  selr  
_hand_stems      r   r(  z;SceneImagePipelineStep._config_hash_base.<locals>.<genexpr>  s-      59#7 sD!7 "#s   recipe_realize_contentc              3   0   K   | ]  } |        y wr  rI   )r$  r  	_hand_selr  s     r   r(  z;SceneImagePipelineStep._config_hash_base.<locals>.<genexpr>  s!      <A? -1Jy$'?s   )handled_object_clausehandled_object_framingrecipe_handled_object_content!still_recipe_camera_frame_enabled)CAMERA_FRAME_PROMPT_VERSIONr  recipe_camera_frame_pack)camera_frame_stemr  rY  )omit_camera_directionrecipe_camera_frame_no_dir_packrecipe_camera_frame_no_dir_stem'recipe_camera_frame_no_dir_stem_content)FRAMING_SCALE_PROMPT_VERSION LANE_SKETCH_LABEL_PROMPT_VERSIONr  recipe_framing_scale_packframing_scale_clause!recipe_framing_scale_stem_contentsketch_label_lane%recipe_lane_sketch_label_stem_contentstill_recipe_lighting_enabled)LIGHTING_MOOD_PROMPT_VERSIONr  recipe_lighting_packstill_recipe_conduct_enabled)STILL_CONDUCT_PROMPT_VERSIONr  recipe_conduct_pack)naturalism_packr  rY  recipe_naturalism_packnaturalism_clauserecipe_naturalism_contentmultiroll_fix_rejudge_enabled)FIX_REJUDGE_POLICY_VERSIONmultiroll_fix_rejudge fix_rejudge_judge_model_physical)%STILL_FIX_REJUDGE_HEADER_PACK_VERSIONr  fix_rejudge_header_pack!multiroll_gpt_composition_enabled)GPT_COMPOSITION_PACK_VERSIONr  )GPT_COMPOSITION_POLICY_VERSIONmultiroll_gpt_compositiongpt_composition_packgpt_composition_model_physical)r(   r!   r   r   rn   r   rv   r-   +app.services.zoom_continuity_render_servicer  r   r   r   r   %app.modules.pipeline.multiroll_geminir  r  r  +app.modules.pipeline.shot_continuity_authorr  !app.modules.pipeline.still_reciper  r  r  r  r  r  gemini_image_modelrh  r  r  r  r  qwen_vlm_modelri  r  r  r  r  rH   r  r  r  r  %app.modules.pipeline.multiroll_selectr  r  r  r  !app.modules.pipeline.era_researchr  r  r  r  r  r  (app.modules.pipeline.search_grounded_refr  *app.modules.pipeline.reference_acquisitionr  )app.core.steps.reference_acquisition_stepr  #app.modules.pipeline.signage_authorr  r   r  r  r  r  r   r  r  r  r   app.modules.llm.gpt_image_clientr  r   r!  r"  r  r  !app.modules.llm.grok_image_clientr'  r(  r)  listr-  r.  r4  r6  r7  r<  r=  #app.modules.pipeline.cine_transformmodulespipelinecine_transformrD  "app.modules.pipeline.cine_providerrF  rG  CINE_TRANSFORM_STEMCINE_STAGE_DIRECTION_STEMCINE_CONTRACT_VERSION app.modules.pipeline.cine_verifyrS  rT  rW  rX  rY  intr_   app.modules.pipeline.confined_fprc  rd  re  app.core.applicabilityrl  app.modules.pipeline.conti_abrm  rn  ro  rp  rq  !app.modules.pipeline.plate_selectr~  #app.modules.pipeline.still_variantsr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  )r   r!   r   r   _zh_zj_zoom_bbox_modelrv   _json_still_judge_sel_judge_pack_content_judge_pack_resolved_carried_person_proj_TEXT_ONLY_LOC_SEL_recipe_pack_content_recipe_pack_resolvedpayload_gq_pack_sel
_gq_policy_qk_pack_sel
_qk_policy_gg46_pack_sel_gg46_policy_order_policy	_sel_phys_fix_missing_sel_fix_ref_contract
_regen_sel_repair_policy_era_policy_era_pack_content_era_pack_resolved_era_assess_model_era_pick_model	_era_phys_search_contract_acq_contract_acq_rounds	_sg_model
_sg_policy_sg_pack_resolved_sg_pack_content_SIGNAGE_SECTION_SEL_sg_section_stem_pdc_sr_map	_cast_sel_pu_sel_pu_stem_content_img_backend
_gpt_sizes_compact_sel_grok_stem_sel_grok_hard_limit_grok_limit
_grok_shed_tp_grok_sel_tp_sel_tp_pack_content_tp_resolved_gate_policy_nh_compact_sel_nh_sel_fin_sel_fin_compact_sel_fin_stem_content_fin_attaches	_fin_grok	_cine_mod_cine_pack_selector_cine_stem_content	_cine_sel_CINE_LEGACY_cine_identity_of_cine_ident_cine_verify_sha_cine_v_models_sup_sel_broll_stem_of_recipe_stem_content_broll_sel_of_backend_broll_stem_name_broll_pack_idr_sel_cfp_policy_cfp_content_cfp_resolvedrl  _ab_judge_model_ab_pack_lanern  ro  rp  rq  _recipe_packrm  _ab_pack
_psel_pack_pa_ver_sv_author_model_sv_contract_sv_pack
_bgf_model_bgf_contract_bgf_sel	_bgf_pack_chain_loc_sel_bgf_full_contract_bgf_full_sel_bgf_lane_contract_bgf_lane_sel	_geom_sel_rz_c_rz_cam_sel	_cam_pack_cam_stem_of_cam_stem_content_cam_sel_of_backend_cam_stem_name_cam_stem_pack_fs_sel_lsk_sel_stem_content
_light_sel_light_pack_conduct_sel_conduct_pack_nat_pack_of_nat_stem_content_nat_sel_of_backend	_nat_pack_frj_policy_frj_sel_phys_frj_hdr_sel_frj_hdr_pack_gc_sel_gc_pack
_gc_policyr  r  r  r  s                                                                                                                                              @@@@r   r  z(SceneImagePipelineStep._config_hash_base(  s   
 	-<"4#6#67 8=uE! ::cii2BC (   &w11:SbBD 80%8EAK	
 	

	
	
 	
 !)!;!; *>'/'G'G66 "7s!;)=c)B
 %99I$J,? -"!44')-"=uE*E%
V ">FG
 59G01*4G&'*>|*LG&'2E3G./ ">FG
 59G01*4G&' **+1X-G-G,HI %&*>|*LG&'2E3G./ "@%HI
 7;G23,8G()
 .;G)* 7@kG23 GH;UD E0D"1$,-8K"9$4525H&8"=3?./ #?GH!@%I J
 59G01*> +"G&'2E 3"G./
 +<*=G&' "7@JFK!@%I J .1,,..G)*-;G)**4G&' 3A'..		 %V N6$ % %%+VG_366?ik#23GG./ "8%@A  /3G*+-8G)*+=+?G'(3D3FG/0  +<G&'(7G$%3<!4#G/01:?1KG-. .>-?G)* 9F"9$G45 ":EBC 
 15G,-(2G$%&7&9GN#.>.@G*+ /8G*+7:"6;Ar8CG34
 /?$&7/9G*+ <eE F
 ;?G673JG/0 *1);G%&15	 224G-.	

 -B',J()4D(5*01 x)>F
 7"-9G)*,3.-4G() .8^^FB-GG)*7"
 .6-I-IG)**2*C*CG&',A-G()4H5G01 /D/ G*+6J7 G23 0;G+,4DG01/3J/?G+,	
 	
 '37&;"#.>w.G*+82E:gE/;L/IG+,7G8G34 88%@ -9G()	

 )5W(=$%0@0I,-82E:gE4@5!G01<L=!G89&	
 	
 H&;UCwN	&W<eE F
 (.>">5J6G12 "(,>? 9: "@%HICC ,-IGBEK L
 AE<=
 ,-K6:G23
 /:'.BG*+:&,6 6A5L125@5L12-B9-MG)*5G9886:G12 GBEK L '!9#F#FH ;< // -. GH&A5IJ
 8<34 3C2D./ 15^5E0F,- 	
 	
 )7!"7?7JL)M%+ '*(*J*J&K"#*:&'2F)3+./4H05201
 "CUKL :>G56,A(,KG()4H5G01
 "=uEF  48G/0*7/G&'2>.G./+."6;,=G'( 58"5r:5<G01,7G() 	@!  48G/00<#1G,- 4@&4G/0
 4@&4G/0 .IG)*-:3-?G)*.=G*+** 34 85u=
 15G,--5c]G)*.<G*+
 ** 34
 895A 59G01+5c?G'(,8G() 29G-.
 85u=  15G,--5c]G)*1=G-.5EG12>A."5?G:;
 >A**>G9: 80%8D@%&89 56 %%78 89 84e<  04G+,&/&9GN#*7G&'*4G&' 1:.0IG,-69**7G23
 x!=uE
 9=453E/0/8/G+, ?  BFG=> 4=]3KG/07IG34 	
 	
 1;90E,-
 1;90E,-	

 *- 21$'<21 *1%&,/HH 59U|59 -9() 4788 <A?<A 4A/0 8@%H
 <@G782;H2EG./ "@%H "F . 2>'*?G"#$ +/	20.
 >G">$9:=K9: &nnE AB* $%  8A7I34!'+AB ;< "(,?@ ?@ 8<eD
 8<G34.9*.EG*+ 8;UC
 7;G23-:<-HG)*  %#H&;UC !I 1>i0HG,-3D.40G/0 H=uE!@%I J 0;G+,
 ;H/G67 
 2?|1LG-. 8@%H  4>G/0.6w.?G*+8;."59G45 w~~KK4K077@

)+cr 	r2   c           	      
   ddl m} t        t        |dd            rbddlm} ddlm}m}  ||j                        | j                  z  dz  | j                  z  dz  d	z  |z  }|j                         r |d
| d      | j                          ddlm} ddlm} ddl m}	 ddlm}
  |
t        |	dd            }|r|dk(  rddlm}  |ddd       || j,                  | j                  | j                          || j,                  | j                  | j/                               }|dk(  rvddlm} ddlm} | j,                  j9                   |d      | j                  | j                  d       | j,                  j;                          ddlm} ddl m}  ||j                        | j                  z  dz  dz  | j                  z  dz  }|j                         r|j=                          t        |dd      dk7  rdd lm}  ||j                        | j                  z  dz  | j                  z  dz  d	z  }|j                         rX|jA                  d!|jC                         jE                  d"             }|jG                  |       tH        jK                  d#|       d$}nd$}|jM                  | j                  |%       dd&lm'}m} | j,                  jQ                  |jR                        jU                  |j                  | j                  k(  |j                  | j                  k(  |jV                  dk(  |jX                  dk\  |jZ                  d'k7        j]                         D cg c]  }|d   	 }}|rdd(lm/}m0}m1}  | ||	j                  | j                  | j                        |)      }| j,                  jQ                  |jR                  |jd                        jU                  |j                  | j                  k(  |j                  | j                  k(  |jV                  dk(  |jX                  dk\  |jZ                  d'k7        j]                         D ci c]  }|d   |d*    }} |||       tg        |d+         }|D  cg c]	  } | |v s|  }} d}!|r| j,                  jQ                  |      jU                  |j                  | j                  k(  |j                  | j                  k(  |jh                  dk(  |jj                  d*k(  |jl                  jo                  |            jq                         }!| js                  ||!      }"|r| ju                         |"d,<   |"S c c}w c c}w c c} w )-Nr   r    r~  Tr   r  r   r  r  r  u    — 사람 확인 전에는 mode 와 무관하게 이 스텝을 실행하지 않는다(선행 유료 구간·force 아카이브 우회 차단). 원인 확인 후 래치 파일을 삭제하고 resume.)SceneImageService)check_scene_images_readyr  r  rY   r   r  r  r  r  r  r   r  r   zUPDATE image_asset SET is_primary = 0 WHERE project_id = :pid AND episode_id = :eid AND asset_type = 'scene' AND is_primary = 1r   r"   zscene_checkpoint.jsonr  r  )rU   recipe_archived_z%Y%m%d_%H%M%Su=   scene_image_pipeline force: recipe 산출 아카이브 → %sr`   r   )rS   r  r]   )check_scope_universeload_scope_auditscope_audit_pathexpected_scenesra   effective_idsr  );r(   r!   rh  r   r'   r   r  r  r  r)   r*   r+   r,   r    app.services.scene_image_servicerx  r   ry  r  r  r  r  r7   r?   rs   r  r   r   r   r   r   rU   	with_namer   strftimerenamer   r<  generate_imagesrS   r8   r=   r9   r|   r}   r~   r{   r|  r}  r~  scene_indexr3  r7  
is_primarystill_idrz   count_build_result_countsr  )#r   r   r  r  r  r  r  rx  ry  r   _parse_targets_target_scenesr  r   r  r   r   r!   r   _dt
recipe_dirarchivedgen_moderS   rj  selected_still_idsr|  r}  r~  _audit	_universe
_effectiver   primary_countr   s#                                      r   r   zSceneImagePipelineStep._execute[  sH    	58$?@, T&&'$//9HD//"$+,.679CD  }}"DVH MN OP P
 	((*FC 	:	
 (I:B?Adgo091    	!$//4??K477tQUQjQjQlm 7?5'GGOODl t?A GGNN$08001DOOCmSV^^aeapapp  tK  KG~~ 
 x!4e<E4../$//AHLoo&(/02:;  $$&)33*3779+=+=o+N*OP H %%h/KKW   HHtXF
 	>''--
6==%%8%%8&&$.&&!+!!W, ce
QAaD  	 
   & **DOOOO% !/	F MM:#9#9&))T__<))T__<**d2**a/%%0 #%A !ad
   !3VO45J-"B-aj-  "B GGMM*5<<%%8%%8%%0%%*##''(:; eg " "&!:!:"/ 04/E/E/GF+,M
0"Bs   'U!U&	U+U+rf   c                 R    d|vr| j                         |d<   t        | 	  |       y)u?  step-local config_hash 저장 대칭 (Codex 3차 리뷰 HIGH).

        StepRunner.save_checkpoint 의 fallback 은 project_config md5 인데
        _check_cp_mismatch 는 이 스텝의 step-local hash(_config_hash)와
        비교 — 저장/비교 비대칭으로 recipe ON 무표적 CP 의 표적 전이
        fallback(stored config_hash==현재 base)이 원리적으로 작동하지
        않았다. config_hash 키는 CP 에 항상 존재하므로 shape 불변 — 값만
        저장/비교 대칭이 된다(무표적=base 그대로, 표적=fold 값).
        r   N)r   r  save_checkpoint)r   rf   r  s     r   r  z&SceneImagePipelineStep.save_checkpoint  s-     $"&"3"3"5D%r2   c                 :   t        |      }|}i }|rr| j                         rb| j                  |      \  }}}}	}
t        |      }t        |      }|t        |      |dd t        |      dkD  t        |
      |	rdndd}|	rd|d<   ||t	        d||z
        ||d	|d
S )u  실행 계수 — 게이트에 **붙잡힌 샷은 완료가 아니다**.

        ★종전에는 DB 대표 행 수 그대로라, 게이트가 절반을 붙잡아도
         「completed=239 / failed=0」으로 닫혔다 — UI·감시·복구 판단을
         오도한다. StepRunner 는 exit verify 가 false 면 **상태만** partial
         로 바꿀 뿐 계수를 다시 세지 않는다.
        ★`대표 수 - len(보류)` 로 빼면 **원래 대표가 없던** 보류 샷을
         두 번 뻐다 — 그래서 verify 와 **같은 집합 계산**을 쓴다.
        ★게이트가 꺼진 판의 집계는 **그대로** 둔다(result shape 포함).
        N   gate_state_unreadablewinner_violation_gate)gate_policy_completedgate_held_countgate_held_still_ids_previewgate_held_still_ids_truncatedgate_unjudged_countgate_held_reasonTgate_unverifiabler   )scene_totalr  rb   )r   _gate_counts_apply_policy_completed_still_idsr  rA  )r   r  r  r  rc   	gate_datadoneheld_unverifiableunjudged_previews               r   r  z+SceneImagePipelineStep._build_result_counts  s     ,-'$&	$"9"9";001CD 2D$<!$iOd|H)8#&t9 08~14X1D'*8}@L%<*A
I 15	-.. +;#@A$/&3" "	
 	
r2   c                 <    	 ddl m}  |       S # t        $ r Y yw xY w)u   실행 계수를 게이트 기준으로 셀 것인가 — **적용 경계 한 자리**.

        ★`gate_is_on()` 하나만 본다. 소비자마다 설정 규칙을 베껴 두면
         한쪽만 고쳐진다 (2026-09-20 Codex ㅢ).
        r   
gate_is_onF)r  r  r   )r   r  s     r   r  z)SceneImagePipelineStep._gate_counts_applyD  s$    	D< 		s    	c                    ddl m} ddlm} | j                  j                  |      j                  |j                  | j                  k(  |j                  | j                  k(  |j                  dk(  |j                  dk(  |j                  j                  t        |                  j                         }t               }|D ]P  } ||j                         }|s|j#                         s)|j                  s6|j%                  |j                         R | j'                  |      \  }}	}
|	r)t               t        |      t)        |      dt               fS ||z
  |t)        |      d|
fS )u  **정책상 완료**한 still_id — 실행 계수와 verify 가 함께 쓴다.

        Codex ㅣ 의 집합 계산 그대로:
          V = selected 안에서 **실제 파일이 있는** 대표의 고유 still_id
          H = 같은 정책에서 **보류**된 still_id(직접 보류 + 의존 보류)
          완료 = V - H

        ★`대표 수 - len(H)` 로 빼면 **원래 대표가 없던** 보류 샷을 두 번
         뻐다. 그래서 집합으로 센다.
        ★DB 자산을 지우거나 대표를 내리지 않는다 — **세는 자리**에서만 뻐다.

        돌려주는 것: (완료 집합, 보류 집합, 대표 행 수)
        r   rP  rz  r  ra   TF)rc  rQ  rs   r  r7   r8   r9   r*   r+   r7  r  r  rz   r  r{   r3  rf  r,   r   _gate_blocked_still_idsr   )r   selected_idsrQ  r  r   seenrj  pthr  r  r  s              r   r  z2SceneImagePipelineStep._policy_completed_still_idsQ  s&    	;1ww}}Z(//!!T__4!!T__4!!W,!!Q&##D$67
 #% 	 EA$Q[[1Cszz|

$  (,'C'C($lH 5#l+SYceCCtdCIuh>>r2   c                 <   ddl m} ddlm}  |       st	               dt	               fS 	 ddl}ddlm} ddlm	} ddlm
}  ||j                        | j                  z  d	z  | j                  z  d
z  dz  dz  }|j                         sB|r+t        j!                  d|       t	               dt	               fS t	               dt	               fS |j#                  |j%                  d            }	| j&                  j)                  |      j+                  |j,                  j/                  t1        |                  j3                         }
t	               }t	               }|
D ]  }|	j5                  d|j6                   d|j8                         }t;        |t<              r|j5                  d      nd}t;        |t<              rt?        |j5                  d      xs d      nd}|s7|jA                  |j,                         |jA                  |j,                         ||v s|jA                  |j,                          |rt        j!                  dtC        |             |d|fS # tD        $ r5}t        j!                  d|       t	               dt	               fcY d}~S d}~ww xY w)uR  게이트에서 **막힌** 샷 · **검사 불가** 여부 · **판정 없는** 샷.

        돌려주는 것: (보류 집합, 검사 불가 여부, 판정 없는 집합)
        판정 없는 집합은 보류 집합에 **포함**된다.

        ★게이트가 꺼져 있으면 (빈 집합, False, 빈 집합) — 종전 그대로다.
        ★★게이트가 **켜진** 판에서 기록을 못 읽으면 **검사 불가**다
         (2026-09-20 Codex). 종전에는 빈 집합만 돌려줘서, 파일이 남아
         있는 **옛 대표가 다시 완료로 집계**됐다.
        ★★**판정이 아예 없는 샷도 합격이 아니다** (2026-09-20 Codex).
         「명시 비대상(`not_applicable`)」과 「판정 없음」은 다르다 —
         gate 키가 없다는 것만으로 「bgfirst 라 비대상」이라고 알 수 없다.
         켜진 판에서 판정이 없으면 **아직 안 본 것**이다.
        ★검사 불가·미판정을 **재구매로 풀지 않는다** — 세는 자리에서
         보류로 남기고, 파일·대표는 그대로 둔다.
        r   r    r  FNr   rS   )GATE_HOLDING_OUTCOMESr   r  r  zrecords.jsonus   scene_image_pipeline verify: 게이트가 켜져 있는데 판정 기록 파일이 없다(%s) — **검사 불가**Tr$   r%   SshgateoutcomerY   ux   scene_image_pipeline verify: 게이트가 켜져 있는데 **판정이 없는 샷 %d개** — 완료로 세지 않는다u   scene_image_pipeline verify: 게이트가 켜져 있는데 상태를 **못 읽었다** — 검사 불가다(완료 단정 금지): %s)#r(   r!   r  r  r3  r-   r'   r   rs   rS   r  r)   r*   r+   r,   r   errorr.   r/   r7   r8   r9   r=   rz   r  r{   r   r  
shot_index
isinstancedictrH   r   r   r   )r   r  _sr  r  _PathrS   r  rjrf   r   r  r  rj  recgr  r   s                     r   r  z.SceneImagePipelineStep._gate_blocked_still_idsw  s3   " 	3@ |5%&&3	& -5 (4??:XEOO$&-.089;IJB99;  LLSTVX 5$--ueSU**;;r||W|=>D77==,33!!$|"4577:su DEHhh1==/ALL>BC'1#t'<CGGFO$(D1 quuY/52679 LL&HHQTTN 55HHQTTN  TM# (( 	&LLS 5$%%	&s2   B
I 5I 
EI AI 	J&*JJJc                 P   ddl m} ddlm} | j                  j                  |j                        j                  |j                  | j                  k(  |j                  | j                  k(  |j                  dk(  |j                  dk\  |j                  dk7        j                         D cg c]  }|d   	 }}ddlm} ddlm}m}m}m}	  |t+        |dd	            }
|
r	  | |	|j,                  | j                  | j                        |

      }| j                  j                  |j                  |j.                        j                  |j                  | j                  k(  |j                  | j                  k(  |j                  dk(  |j                  dk\  |j                  dk7        j                         D ci c]  }|d   |d    }} |||       t5        |d         }|D cg c]	  }||v s| }}t7        |      }|dk(  r |dg ddddd      S | j9                  |      \  }}}}}|r t:        j=                  dt7        |             n!|rt:        j?                  dt7        |             t7        |      }||k  rD |d|r	d| d| dn||z
   d| d| dg|dk(  rdnd|||t7        |      t7        |      |d      S  |dg d|||t7        |      t7        |      |d      S c c}w c c}w # t0        $ r$} |dt3        |      gddddd      cY d}~S d}~ww xY wc c}w )u  Group 1 #3 (visual_pipeline_contracts_plan): consumer 측 verify_completion.

        scene 이미지 산출물 정합성 — selected SceneStill 수 == ImageAsset(scene, is_primary=1, file 존재).
        producer 측 verify_completion (RefImageGenStep / CompositeImageGenStep / chain_bg) 와
        symmetric. silent fallback 차단 (silent text-only 결과나 disk 누락이 status=completed
        로 통과 안 되도록).
        r   r   r  Tr]   r    )r|  r}  r  r~  r  rY   r  ra   Fr   r   r   Nr  r   ux   scene_image_pipeline verify: 게이트 상태를 **못 읽어** 완료를 단정하지 않는다 — %d샷 전부 보류ul   scene_image_pipeline verify: 게이트에 막힌 %d샷은 옛 대표가 있어도 완료로 세지 않는다u7   게이트 상태를 못 읽어 검사 불가 (expected=r   r   z" scene image(s) missing (expected=r   )r   r   r   	gate_heldgate_unjudgedr  ) r   r   rs   rS   r7   r8   r=   r9   r*   r+   r|   r}   r~   r{   r(   r!   r  r|  r}  r  r~  r   r)   r  
ValueErrorrH   r3  r   r  r   r  r   )r   r   rS   rj  r  r   r|  r}  r  r~  r  r  r  r   r  r   r   seen_still_ids_blocked_rows_n_unverifiable	_unjudgedr   s                          r   r   z(SceneImagePipelineStep.verify_completion  s    	?1 ''--
6==%%8%%8&&$.&&!+!!W, ce
QAaD  	 
 	:	
 	
 (I:B?A)$!..) %3	 "WW]]"z'='=f"--@"--@"..$6"..!3"))W4 ce aD!A$J   %VY7 VO45J'3G|!qJA|LG|$q=# "w&'!Q?  ,,\: 	D'=)LLKLOPXM[ NN78;HG N#8#! %""*8E7!= !5() *""*8E7!=	 ',qji&.")H+.y>/<>   b7"*U")H+.y>/<>
 	

B  ' %Cz&*+aC  HsC   *K)!CK3 9K.	K3 &	L#0L#.K3 3	L <LL L r   )rD   rE   rF   rH   r  r  r   r  r   r   r   r  r  rh  r  r  r  r   ry  rz  s   @r   r|  r|    s    7# 7$ 7r/A /Ab"c "4q3 qf!pc3h pd&DcN &t &'
RD $?LJ&Xp
r2   r|  c                   R     e Zd ZdZdef fdZdefdZddeeef   fdZ	d Z
 xZS )	CharacterStateVariantStepu?   죽은/다친 인물의 상태 variant 참조 이미지 생성.r   c                 V    t        | j                  |      }||S t        |   |      S )u   상태 변형 팩 수리로 지문이 움직였을 때, env 승인이 있으면 **비파괴**
        재실행. 없으면 기존대로 BLOCK — 승인 없는 drift 는 막는다.r
  r  s      r   r  z2CharacterStateVariantStep._evaluate_contract_drift;	  r  r2   r   c                 H    ddl m} t         || j                        dd      S )uk  상태 변형 팩 버전을 접는다.

        ★`prompt_sanitizer` 팩은 **안 접는다.** 안전 필터에 막힐 때만 도는
         갈래라, 접으면 평소에 안 쓰는 팩 때문에 유료 단계가 다시 돈다.
         대신 그 팩을 고칠 때는 이 스텝을 손으로 다시 돌려야 한다 —
         여기 적어 둔다.
        r   r   character_state_variant)systemr  r  s     r   r   z&CharacterStateVariantStep._config_hashC	  s'     	=! 3 34%{4 	4r2   c                 h   | j                          ddlm} ddlm} ddlm} ddlm} ddl	m
} ddlm}m} ddlm}	 dd l}
dd	lm}m} | j'                  d
      }|s	ddddg idS i }i }|j)                  d|      j)                  dg       D ]  }|j)                  dg       D ]  }|d   }t+        |      s|j)                  dd      }|s)|j-                  |t/                     j1                  |       |j-                  ||fg       j3                  |j)                  d      |j)                  d      f         |st4        j7                  d       ddddg idS t4        j7                  dt9        |      t;        |j=                                      ddlm}m } g }| jB                  jE                  |      jG                  |jH                  | jH                  k(  |jJ                  | jJ                  k(  |jL                  tN        k7        jQ                         }|D ]s  }| jB                  jE                  |      jG                  |jR                  |jT                  k(  |jV                  dk(        jY                         }|sc|j3                  |       u t[        |d d       }t[        |d d       }ddl.m/}m0}m1} ddlm2} ddl3m4} m5}! | jB                  jE                  |      jG                  |jH                  | jH                  k(        jQ                         }" | |"D #ci c]@  }#|#jR                  |#jR                  |#jl                  xs d|#jn                  |#jV                  d B c}#      }$|"D #ch c],  }#|#jl                  d!k(  r|#jV                  d"k(  r|#jR                  . }%}#i }&| jB                  jE                  |      jG                  |jH                  | jH                  k(  |jJ                  | jJ                  k(  |jp                  d#k(  |jr                  dk\  |jt                  d$k7        jQ                         D ]H  }' |!tw        |'d%d       |$      xs i }(ty        d& |(D              r.|(|&|'jz                  |'j|                  f<   J |d'k(  r| jB                  jE                  |      jG                  |jH                  | jH                  k(  |jJ                  | jJ                  k(  |j~                  d(k(  |j                  j                  d)            jQ                         })|)D ]?  }*|*j                  sd*|*j                  vs|*j                  j                  d+d,      |*_@        A | jB                  j                          |)rt4        j7                  d-t9        |)              |	|j                        | jH                  z  d.z  }+|+j                  d#d#/        |       }, |d0d1      xs d}-d}.d}/d}0g }1g }2|D 3cg c]  }3t        ||3      |3 }4}3|4r t4        j                  d2t9        |4      |4       dd3lHmI}5  |5| jH                  | jJ                        }6|j                         D 37ci c]"  \  }3}7t        ||3      t        ||3      |6vr|3|7$ }8}3}7|D 3cg c]  }3t        ||3      t        ||3      |6v r|3  }9}3|9r t4        j7                  d4t9        |9      |9       |8j                         D 7:cg c]  \  }}7|7D ]  }:||:f  };}7}}:|;D ]  \  }<}=t        ||<      }>| jB                  jE                  |      jG                  |jH                  | jH                  k(  |jJ                  | jJ                  k(  |j~                  d(k(  |j                  j                  d5|> d6|= d7            jY                         }?|?r ||?j                        nd }@ ||j)                  |<|=fg       D Acg c]*  }A|A|&v r$|&j)                  A      xs i j)                  |>      , c}A|%      }B || jB                  | jH                  |>|B      }C|?r|d8k(  r@r@j                         r ||?j                  CCjR                  nd       rc|?j                  xs dj                  d+d9d:      |?_@        | jB                  j                          t4        j7                  d;|<|=CjR                         n=|/d:z  }/|1j3                  |<|>|=t        @      d<       t4        j7                  d=|<|=       d }Dd }EC@ |Cj                        }F|Fr,Fj                         rFj                         }DCjR                  }EDd n| jB                  jE                  |      jG                  |jH                  | jH                  k(  |j~                  d(k(  |j                  j                  d>|> d7            j                  |j                  j                               jY                         }G|Gr |Gj                        nd }HDnHr-Hj                         rHj                         }DGjR                  }En| jB                  jE                  |      jG                  |j                  |>k(  |j~                  d(k(  |j                  d:k(        jY                         }I|Ir |Ij                        nd }J|Jr,Jj                         rJj                         }DIjR                  }EDst4        j                  d?|<       |0d:z  }0t        |=      }Kt        ||<      xs d}L|-j                  d@|=      j                  dA|<      j                  dB|L      j                  dCK      }MdDDfg}N	  ||,|< dE|= dF|< dG|= dHK d0M|+Nd| jH                  | jJ                  d0|>dI|=J
      }O|j                  |j                        }Pt        |Oj)                  dK            }Q|Oj)                  dL      rdM}QQrdNndO}R |t        |
j                               | jH                  d(|>| jJ                   |OdP         dQ|R d6|> d6|= dR|< |Oj)                  dSd      QrdTndUQrdVnd dPW      }S| jB                  j1                  |S       t        |Sd0d0ErEgng X       | jB                  j                          Qr3|2j3                  |<|>|=OdP   dYdZ       t4        j                  d[|<|=       |.d:z  }.|1j3                  |<|>|=OdP   d<       t4        j7                  d\|<|=       
 |.|/z   t9        |;      |0t9        |2      z   |1|2t9        |2      d^dS c c}#w c c}#w c c}3w c c}7}3w c c}3w c c}:}7}w c c}Aw # t        $ rS}Tt4        j                  d]|<|=T       	 | jB                  j                          n# t        $ r Y nw xY w|0d:z  }0Y d }T~Td }T~Tww xY w)_Nr   )GeminiImageClient)generate_and_validate_reference)load_promptrz  r    )rQ  to_relative_image_pathr   rT   shot_stagingstate_variantsrb   rf   shotscharacter_anglessubject_stater  rY   r  r  zEcharacter_state_variant: no dead/injured/unconscious characters foundz>character_state_variant: %d characters with state variants: %s)rQ   rR   c                     | j                   S r  rZ   r%  s    r   <lambda>z4CharacterStateVariantStep._execute.<locals>.<lambda>	      affr2   c                     | j                   S r  r=   r  s    r   r  z4CharacterStateVariantStep._execute.<locals>.<lambda>	  s    r2   key_fnvalue_fnc                     | j                   S r  r  r  s    r   r  z4CharacterStateVariantStep._execute.<locals>.<lambda>	  r  r2   c                 "    | j                   xs dS )NrY   )r\   r  s    r   r  z4CharacterStateVariantStep._execute.<locals>.<lambda>	  s    ATRTATr2   )existing_source_differspick_state_variant_sourcesource_asset_forr  )build_short_id_resolverextract_outfit_assignments)r=   r4  rZ   r[   r  r  Tr]   t2i_variations_jsonc              3   P   K   | ]  }t        |      j                  d          yw)__N)rH   
startswith)r$  r  s     r   r(  z5CharacterStateVariantStep._execute.<locals>.<genexpr>	  s      8Cq3q6$$T*Cs   $&r   r)  %state_variant:%r+  zstate_variant:zstate_variant_old:z5character_state_variant force: marked %d old variantsreference_images)parentsexist_okr  r  zQcharacter_state_variant: skipping %d unregistered names (not in entity_merge): %sr  z~character_state_variant: excluding %d low-frequency-skipped registered character(s) (no base ref per ref_image_gen policy): %sz%state_variant:r_   %r`   zstate_variant_oldsrc:ra   uj   character_state_variant: %s (%s) — 입력이 규칙과 다르다 (→ %s) — 내리고 다시 만든다)	char_name	char_uuid
state_type
image_pathu5   character_state_variant: %s (%s) — skipped (exists)z%composite:u9   character_state_variant: no ref image for %s — skippingz{state_type}z{character_name}z{character_description}z{state_description}z8Reference: this character in their normal, healthy statez (r   z in z state: )r*   r+   operation_typer:  )
gemini_cliententity_nameentity_descriptionr[   
t2i_prompt
output_dirextra_referencesstyle_context
trace_meta
state_hintsanitization_infofallback_providerFstate_variant_unadoptedstate_variantrf  [z] generation_modelrejectedr   u   상태 보존 미확인으로 비채택 — 안전 필터로 프롬프트가 수정됐다(상태가 지워졌다는 판정은 아니다))r=   r*   r7  r:  r+   rf  r8  r  r~   review_notesr  rr   )pipeline_rolestageinput_image_idsuF   상태 보존 미확인 — 안전 필터로 프롬프트가 수정됨)r  r  r  r  r   uH   character_state_variant: %s (%s) — 비채택 (상태 보존 미확인)u.   character_state_variant: %s (%s) — generatedz+character_state_variant: %s (%s) failed: %s)r  unadopted_state_variantsunadopted_count)]r   #app.modules.llm.gemini_image_clientr  'app.modules.pipeline.ref_image_pipeliner  r   r  rs   r  r(   r!   rc  rQ  r  r'   r   rw   rU   rV   r1   r   r
   rg  r3  r   r   r   r<  r   r  keysrQ   rR   r7   r8   r9   r*   r+   rd  re  r{   r=   r@  r[   r;   r   app.core.state_variant_sourcer  r  r  rS   r  r  r  r4  rZ   r|   r}   r~   r   anyr  r  r7  r8  r9  r;  r   r)   mkdirr   r   r5  r  itemsrf  r,   r  rH   
read_bytesr   rr   r   r:  r  r	   r   r   rh  r   r   r   rollback)Ur   r   r  r  r  r  r!   rQ  r  r   _uuidrU   rV   
staging_cpaffectedoccursshotrH  staterZ   rQ   rR   _canons
char_linksru  rE  name_to_uuidname_to_descr  r  r  rS   r  r  	_ent_rowsr   	_resolverr'  _outfit_by_shot_r_omold_variantsovreference_dirr  prompt_templater   r   r   r  	unadoptedn_unregisteredr  _low_freq_skippedstates_affected_registered_low_freq_excludedst
_all_pairsr  r  r  r   existing_pathr  _want_src	ref_bytessource_asset_id	_src_path
comp_asset	comp_path
face_asset	face_path
state_desc	char_descr   
extra_refspipe_resultr   _unverified_marker_state_assetr   sU                                                                                        r   r   z"CharacterStateVariantStep._executeQ	  sf   ((*I[91,R / //?
'(aQR-r24 4 $&$&NN6:6::7BGDhh126?+'.66+r2D ++D#%8<<UC))4-<CC!XXm4dhh|6LMO 7 H KK_`'(aQR-r24 4 	T]D$9	;
 	FWW]]#45<<((DOO;((DOO;--1BB
 #%	 	
 D{+22$--/'';6 eg  r"  (8,~(
 (8,7T(
	
 	

 	2	

 GGMM+.55""doo577:su 	+:C-E:CQ DD1::+;QVV"#--1 1:C-E F	 #, K)QzzU*q}}	/I DD) K79''--
+22%%8%%8&&$.&&!+!!W,.
 /2ce4B -148)E KHJ 8C88?BOR^^R]];<4 7?77==4;;%%8%%8%%4&&++,>?	
 ce  #>>gR^^&C%'^^%;%;<LNb%cBN # GGNNSUXYeUfg X223dooEHZZD48)+%&?JPb	 	 %-UHqL!0L0THU NNcM"M 	B24??DOOT'/~~'7 
'7)!V<+7L!,4EE vI'7 	  
  
!<+7L!,0AA x 	 

 KKU&'); 6J5O5O5Qc5Q\T6\bVXtRj\bj5Q
c%/!Iz#L)<I ww}}Z077%%8%%8%%4&&++oi[*UV,WX	
 eg  GO.x/A/ABTXM- **i%<bA*AQ( "%%a(.B33I>A* 	E
 $DGGT__iODDH,=CWCWCY*8+C+C6:6F477DR -5,@,@,FB+O+O(*A1,FH(GGNN$KKB!:tww8
 qLG"))%.Y&0M@R+  KK WYbdnoI"O.t~~>	!1!1!3 ) 4 4 6I&*ggO!*!6j)))T__<))[8**//+i[0JK
 *//4467  EO*:+?+?@TXI$y//1%002	",-- GGMM*-V",,	9"--<"--2
 UW  IS.z/C/CDX\	!1!1!3 ) 4 4 6I&0mmOZ\ef!
 /z:J#L)<BI%w~z:w19=w8)Dw4jA	  VW`abJa="/#,+R
|1 =*3DHZL'Y 9%,%/"$ '+oo&*oo*C%.	   *), ll8<<0* #;??3F#GH??#67"'K8C4 / )5;;=)#*'#4[5MN"#G9Ai[*R	{ S%0__5G%L*5J; 3>#/CG "   L)( ";39H_$5b	  $$%.Y&0&1+&>"j	&  NN45>
L Q	%%!*",K<T'  LiYcdu &0H  )72 #J #S^3"0,5#&y>	
 	
w-EKb V" 


 d*\  LiYcehiGG$$&  !st   At/(1t4$t96t9't>#uu	/u
E/u5u	v1v,7vv,	vv,vv,,v1c           	      p   ddl }ddlm} ddlm} ddlm}m}m} | j                  d      }|s |dg dddd	
      S i }|j                  d|      j                  dg       D ]h  }	|	j                  dg       D ]Q  }
|
d   }t        |      s|
j                  dd      }|s)|j                  |t                     j                  |       S j |s |dg dddd	
      S | j                  j!                  |      j#                  |j$                  | j$                  k(  |j&                  | j&                  k(  |j(                  t*        k7        j-                         }g }|D ]s  }| j                  j!                  |      j#                  |j.                  |j0                  k(  |j2                  dk(        j5                         }|sc|j7                  |       u t9        |d d       }ddlm}  || j$                  | j&                        }g }|j?                         D ]3  \  }}tA        ||      }|||v r|D ]  }|j7                  ||f        5 tC        |      }|dk(  r |dg dddd	
      S | j                  j!                  |      j#                  |j$                  | j$                  k(  |jD                  dk(  |jF                  jI                  d            j-                         }t               |D ]|  }|jK                  d|jF                  xs d      }|s& ||jL                        }|s;|jO                         sLj                  |jQ                  d      |jQ                  d      f       ~ tS        fd|D              }||k  r# |d||z
   d| d| dg|dk(  rdnd ||d	
      S  |dg d||d	
      S )!uI  shot_staging 체크포인트의 dead/severely_injured/unconscious subject_state 기준
        (char_uuid, state) 쌍 수 == ImageAsset(prompt_used~'state_variant:{char}:{state}')
        row + 파일 존재.

        production _execute()와 동일한 data source(shot_staging manifest.json) 사용 —
        DB에 shot_staging 테이블 없음. entity_merge에 미등록된 이름은 expected에서 제외
        (production이 익명 단역으로 사전 제외하는 정책 일치).

        cleanup_artifacts override 안 함 → default noop (부분 재생성 보호).
        r   NrP  r   )rQ   rR   r  r  Tr   )r   r   r   rf   r  r  r  r  rY   c                     | j                   S r  r  r  s    r   r  z=CharacterStateVariantStep.verify_completion.<locals>.<lambda>  s    QVVr2   c                     | j                   S r  r  r  s    r   r  z=CharacterStateVariantStep.verify_completion.<locals>.<lambda>  s    r2   r  r  r)  r  z state_variant:([a-f0-9-]+):(\w+)ra   r-  c              3   ,   K   | ]  }|v sd   ywr!  rI   )r$  rT  rJ  s     r   r(  z>CharacterStateVariantStep.verify_completion.<locals>.<genexpr>*  s     J.$DM4IA.rV  Fz" state variants missing (expected=r   r   r   r   )*r2  rc  rQ  r   r   rs   rQ   rR   r  r1   r   r
   rg  r3  r   r7   r8   r9   r*   r+   rd  re  r{   r=   r@  r[   r;   r   r   r5  r  r  r   r   r7  r8  r9  r>  rf  r,   r?  r6  )r   ri  rQ  r   rQ   rR   r  r  r  r!  rH  r"  rZ   r$  canonsru  rE  r%  r  low_freq_skippedro  r4  r  r   sv_rowsrj  rK  rs  r   rJ  s                                @r   r   z+CharacterStateVariantStep.verify_completion
  s    	:>QQ //?
# "w&'!4 
 $&NN6:6::7BGDhh126?+'.66+r2D ++D#%8<<UC 7 H # "w&'!4  WW]]#45<<((DOO;((DOO;--1BB
 #%	 	
 D{+22$--/'';6 eg  b!  (8+n(
 	B1$//4??S!$NN,LD&#L$7I ,,%%y%&89   - ~&q=# "w&'!4  ''--
+22!!T__4!![0""''(:;
 #%	 	
 A

>@SQSTA"1;;/AQXXZ!!1771:qwwqz":;  J.JJ8#!%'( )!!)
(5'< ',qji&.?   b7"*U;
 	
r2   r   )rD   rE   rF   rG   rH   r  r   r   r   r   r   ry  rz  s   @r   r  r  8	  s=    IA A4c 4v
c3h v
po
r2   r  )r   ref_image_gencomposite_image_genr  scene_image_pipeline)"rG   rv   r-   loggingtypingr   r   r   app.core.name_matcherr   r   r   r   app.core.subject_stater	   r
   #app.services.image_capture.annotater   ry   r   re  	getLoggerrD   r   r   rK   r   rH   r   r  r  r|  r  IMAGE_STEP_CLASSESrI   r2   r   <module>r[     s       & & ? + N H J			8	$)I )I\>%_j >%FD
oz D
R" "s "J"S "# " "JD
OZ D
R
P
_j P
j4@
 @
L "$082 r2   