+
    u+i4                    x   R t ^ RIHt ^ RIt^ RIt^ RIHt ^ RIHt ]! ]	4      P                  4       P                  ^,          R,          t]! ]4      ]P                  9  d#   ]P                  P                  ^ ]! ]4      4       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# )uY  Shared preflight helpers for G4.4 canary scripts (Wave 3 carry).

All 4 metric scripts (`g4_4_double_description.py`,
`g4_4_zoom_in_detail_no_new_entity.py`, `g4_4_atmosphere_no_layout_import.py`,
`g4_4_view_mixing.py`) import the helpers from this module and apply them
identically. The token-count script (`g4_4_token_count.py`) does NOT use
these helpers — it reads prompt files, not CP, and per R3-M2 must remain
CP-free (no `compute_continuity_snapshot_hash` import).

Trap #1 silent-absorb ban (Wave 3 carry — G4.3 R3R4-B2 carry):
- `or [] / or {}` 패턴 금지.
- `data.scenes` / `t2i_variations` / `t2i_prompt` /
  `render_prompt_card` / `continuity_elements_used` 누락은
  measurement_failures 로 적재 후 caller 가 exit 1.
- `compute_continuity_snapshot_hash()` 호출 실패도 measurement_failures 로
  적재 (silent skip 금지).
)annotationsN)Path)Anybackendc                   V ^8  d   QhRR/# )   returndict )formats   "scripts/canary/_g4_4_common.py__annotate__r   "   s      D     c                 F    ^ RI Hp HpHpHpHpHpHp RV RVRVRVRVRVRV/# )	u  Return the G4.4 + G4.3-cross module symbols Wave 1-A exposes.

Centralised to give every canary script a single import boundary; if
Wave 1-A renames a constant, only this module needs adjustment.

Trap #8 carry — module-level constants direct import; NO inline list
duplication of the 7-entry generic person nouns / 7-entry furniture
layout tokens / 4-entry body-part triggers / etc.
!_CONTINUITY_FRAMING_SCOPE_OPTIONS#_CONTINUITY_FURNITURE_LAYOUT_TOKENS _CONTINUITY_GENERIC_PERSON_NOUNS'_CONTINUITY_ID_POLICY_CROSS_REF_LITERAL_CONTINUITY_REF_USAGE_VALUES_ID_BODY_PART_TRIGGERS compute_continuity_snapshot_hashr   r   r   r   r   r   r   )!app.core.steps.render_prompt_cardr   r   r   r   r   r   r   r   s          r   render_prompt_card_importsr   "   sG       	,-N-/R*,L13Z&(D "8*,L r   c               $    V ^8  d   QhRRRRRR/# )r   cpr	   measurement_failureslistr   list | Noner
   )r   s   "r   r   r   A   s!      T  + r   c                6   RV 9  d   VP                  R4       R# V R,          p\        V\        4      '       g   VP                  R4       R# RV9  d   VP                  R4       R# VR,          p\        V\        4      '       g   VP                  R4       R# V# )u   Validate CP envelope shape and return scenes list (or None on failure).

Caller must check None and fail-fast (sys.exit(1)) — measurement_failures
is appended in-place. Trap #1 — silent-absorb ban.
datazcp.data key missingNzcp.data not dictsceneszcp.data.scenes key missingzcp.data.scenes not list)append
isinstancer	   r   )r   r   r    r!   s   &&  r   _load_scenes_or_failr$   A   s     R##$9:f:DdD!!##$67t##$@A(^Ffd####$=>Mr   c          
     ,    V ^8  d   QhRRRRRRRRRR	/# )
r   scener	   siz
int | Noneshir   r   r   r   r
   )r   s   "r   r   r   X   sA     G GGG 
G 	G
 Gr   c           	        RV 9  d   VP                  RV RV R24       R# V R,          p\        V\        4      '       g   VP                  RV RV R24       R# RV9  d   VP                  RV RV R24       R# VR,          p\        V\        4      '       g   VP                  RV RV R	24       R#  ^ R
IHp V! V4       RT 9  d   TP                  RT RT R24       R# T R,          p\        T\        4      '       g   TP                  RT RT R24       R# T'       g   TP                  RT RT R24       R# T#   \
         d&   pTP                  RT RT RT 24        Rp?R# Rp?ii ; i)u  Validate per-scene shape — render_prompt_card / continuity_elements_used /
variations.

Returns variations list when valid; None otherwise (caller continues
loop). Per spec §5.3 + Trap #1 silent-absorb ban + Trap #3 CP shape:

  - render_prompt_card must be dict.
  - render_prompt_card.continuity_elements_used must be dict.
  - compute_continuity_snapshot_hash(rpc) must succeed (no silent skip).
  - t2i_variations must be present, list, AND non-empty (R3-B1 carry —
    empty must fail).
render_prompt_cards_shz : render_prompt_card key missingNz: render_prompt_card not dictcontinuity_elements_usedz9: render_prompt_card.continuity_elements_used key missingz6: render_prompt_card.continuity_elements_used not dict)r   z: continuity hash failed: t2i_variationsz: t2i_variations key missingz: t2i_variations not listz: t2i_variations empty)r"   r#   r	   r   r   	Exceptionr   )	r&   r'   r(   r   rpccer   exc
variationss	   &&&&     r   _validate_scene_preflightr4   X   s   $ 5(##t3se;<	
 
$
%Cc4  ##t3se89	
 !,##t3se  	
 	'	(Bb$##t3se  	
 	
 	)- u$##t3se78	
 '(Jj$''##t3se45	
 ##t3se12	
 -  ##t3se5cU;	
 	s   6E E2E--E2c                    V ^8  d   QhRRRR/# )r   argsr   r   r	   r
   )r   s   "r   r   r      s     @ @ @ @r   c                   \        V RR4      '       Ed   \        V P                  4      pVP                  4       '       g5   \	        RV 2\
        P                  R7       \
        P                  ! ^4        VP                  RRR7      ;_uu_ 4       p\        P                  ! V4      pRRR4       R	XP                  R	4      R
TP                  R
4      RTP                  R4      RTP                  R4      RTP                  R4      RTP                  R4      RTP                  R4      /#  \        V R
R4      '       d!   \        P                  ! V P                  4      MRp\        V RR4      '       d!   \        P                  ! V P                  4      MRpR	\        V R	R4      R
XRXR\        V RR4      R\        V RR4      R\        V RR4      R\        V RR4      /#   + '       g   i     EL<; i  \         d@   p\	        RT 2\
        P                  R7       \
        P                  ! ^4        Rp?ELRp?ii ; i  \         d?   p\	        RT 2\
        P                  R7       \
        P                  ! ^4        Rp?LRp?ii ; i)ud  Construct pinning dict from --config JSON OR individual CLI flags.

Returns dict with 7 keys (G4.4 pinning block — Override O-12):
  pid / scene_index_list / shot_index_list_per_scene / model_routing /
  prompt_source_mode / card_commit_hash / continuity_card_snapshot_hash

Trap #1 — NO silent defaults; caller must run validate_pinning() afterwards.
configNz.[g4_4_common] ERROR: --config file not found: filerutf-8encodingz+[g4_4_common] ERROR: --config parse error: pidscene_index_listshot_index_list_per_scenemodel_routingprompt_source_modecard_commit_hashcontinuity_card_snapshot_hashz:[g4_4_common] ERROR: failed to parse list/dict JSON args: )getattrr   r8   existsprintsysstderrexitopenjsonloadr/   getloadsr@   rA   )r6   cfg_pathfpcfgr2   r@   rA   s   &      r   load_pinning_from_argsrT      s6    tXt$$$  @
KZZ HHQK	sW55iim 6 3775>(: ;'1L)MSWW_5 #''*>"?(: ;+SWW/.

 
	
 t/66 JJt,,- 	 t8$?? JJt556 	" 	wtUD),#%>=gd,@$GGD*<dC'14*

 
I 655 	=cUCZZ HHQKK	8  HN	
 	sO   5G. G'G. )A*H; G+	%G. +G. .H894H33H8;J4I??Jc                    V ^8  d   QhRRRR/# )r   pinningr	   r   Noner
   )r   s   "r   r   r      s        d  t  r   c                   R	pV Uu. uF  q P                  V4      R
9   g   K  VNK  	  ppV'       d6   \        RV R2\        P                  R7       \        P                  ! ^4       \        V R,          \        4      '       g2   \        R\        P                  R7       \        P                  ! ^4       \        V R,          \        4      '       g4   \        R\        P                  R7       \        P                  ! ^4       R# R# u upi )uD   Fail-fast on missing pinning fields. Trap #1 — NO silent defaults.r@   rA   Nz8[g4_4_common] ERROR: pinning missing required field(s): zF. Provide via --config <json> or individual flags. NO silent defaults.r9   z?[g4_4_common] ERROR: pinning.scene_index_list must be list[int]zC[g4_4_common] ERROR: pinning.shot_index_list_per_scene must be dict)r?   r@   rA   rB   rC   rD   rE   )N )rO   rH   rI   rJ   rK   r#   r   r	   )rV   requiredkmissings   &   r   validate_pinningr]      s    H #C(Qkk!n
&Bqq(GCFi "# 		
 	g01488M	
 	g9:DAA	

 	 B Ds
   DDc                    V ^8  d   QhRRRR/# )r   rV   r	   r   zset[tuple[int, int]]r
   )r   s   "r   r   r     s     ( (T (.B (r   c           	        \        4       pV R,          pV R,           EF:  p\        V4      pVP                  \        V4      4      pVf   VP                  V4      pVf6   \	        RV R2\
        P                  R7       \
        P                  ! ^4       \        V\        4      '       gL   \	        RV R\        V4      P                   R2\
        P                  R7       \
        P                  ! ^4       V'       g6   \	        RV R	2\
        P                  R7       \
        P                  ! ^4       V F  pVP                  V\        V4      34       K!  	  EK=  	  V# )
u  Cartesian (scene_index, shot_index) tuples from pinning block.

Trap #4 — pinning enumerates EXACT (si, shi) tuples. A scene_index without
a corresponding shot_index_list entry (or with non-list / empty list) is a
contract violation, not a silent default.
rA   r@   z=[g4_4_common] ERROR: pinning.scene_index_list contains scene z? but shot_index_list_per_scene has no entry. NO silent default.r9   z7[g4_4_common] ERROR: pinning.shot_index_list_per_scene[z] must be list[int], got .u4   ] is empty — pinned scene must enumerate >=1 shot.)setintrO   strrH   rI   rJ   rK   r#   r   type__name__add)rV   outshot_mapr'   si_int	raw_shotsr(   s   &      r   build_pinning_scene_setrk     s:    !$C23H())RLLV-	 V,I !%& ZZ	 HHQK)T**84	?++,A/ ZZ	 HHQK8  ZZ	 HHQKCGGVSX&' ; *> Jr   c                    V ^8  d   QhRRRR/# )r   cp_rootr   r   r	   r
   )r   s   "r   r   r   3  s      d t r   c                   V R,          pVP                  4       '       g5   \        RV 2\        P                  R7       \        P                  ! ^4        VP                  RRR7      ;_uu_ 4       p\        P                  ! V4      uuRRR4       #   + '       g   i     R# ; i  \         d@   p\        RT 2\        P                  R7       \        P                  ! ^4        Rp?R# Rp?ii ; i)	zELoad `manifest.json` under cp_root. fail-fast on missing/parse error.zmanifest.jsonz0[g4_4_common] ERROR: manifest.json not found at r9   r;   r<   r=   Nz0[g4_4_common] ERROR: manifest.json parse error: )	rG   rH   rI   rJ   rK   rL   rM   rN   r/   )rm   manifest_pathrR   r2   s   &   r   load_cp_manifestrp   3  s    o-M!!>o	

 	g66"99R= 7666 >seD	
 	s6   B% 0B
B% B"	B% "B% %C/04C**C/c                    V ^8  d   QhRRRR/# )r   r6   r   r   r   r
   )r   s   "r   r   r   H  s      # $ r   c                X   Rp\        V RR4      '       d_   \        V P                  4      P                  RRR7      ;_uu_ 4       p\        P
                  ! V4      pRRR4       XP                  R4      pV'       g   \        V RR4      pV'       g2   \        R\        P                  R7       \        P                  ! ^4       \        V4      pVP                  4       '       g5   \        R	V 2\        P                  R7       \        P                  ! ^4       V#   + '       g   i     L; i)
uh   Resolve cp_root from --config or --cp-root flag. Fail-fast on missing.

Trap #1 — NO silent defaults.
Nr8   r;   r<   r=   rm   z;[g4_4_common] ERROR: --cp-root (or config.cp_root) requiredr9   z*[g4_4_common] ERROR: --cp-root not found: )rF   r   r8   rL   rM   rN   rO   rH   rI   rJ   rK   rG   )r6   cp_root_strrR   rS   rm   s   &    r   resolve_cp_rootrt   H  s    
 #KtXt$$$++##C'#::b))B-C ;ggi(dIt4I	
 	;G>>8	B	
 	N% ;:s   DD)	c               $    V ^8  d   QhRRRRRR/# )r   rg   r	   r6   r   r   rW   r
   )r   s   "r   r   r   d  s&     	M 	MT 	M 	M 	Mr   c           
        VP                   R8X  dK   \        P                  ! V \        P                  R^RR7       \        P                  P                  R4       R
# \        VP                   4      pVP                  P                  RRR7       VP                  RRR	7      ;_uu_ 4       p\        P                  ! WR^RR7       R
R
R
4       R
#   + '       g   i     R
# ; i)z2Write JSON output to args.output ('-' for stdout).-FT)ensure_asciiindent	sort_keys
)parentsexist_okwr<   r=   N)
outputrM   dumprI   stdoutwriter   parentmkdirrL   )rg   r6   out_pathrR   s   &&  r   emit_outputr   d  s    {{c		#szza4P

$dT:]]3]11RIIcE!tL 2111s   )CC	)__doc__
__future__r   rM   rI   pathlibr   typingr   __file__resolver|   _BACKEND_PATHrc   pathinsertr   r$   r4   rT   r]   rk   rp   rt   r   r
   r   r   <module>r      s   " #  
   X&&(003i?}SXX%HHOOAs=)*>.GT@F F(V*8	Mr   