
    ji*@                    ^   d Z ddlmZ ddlZddlZddlmZ ddlmZ  ee	      j                         j                  d   dz  Z ee      ej                  vr"ej                  j                  d ee             ddZdd	Z	 	 	 	 	 	 	 	 	 	 dd
Z e       ZddZddZddZddZddZddZy)u  Shared preflight helpers for G4.5a canary scripts (Wave 3).

All 4 metric scripts (`g4_5a_camera_frame_consistency.py`,
`g4_5a_fg_bg_shared_anchor.py`, `g4_5a_primary_framing.py`,
`g4_5a_view_mixing_extension.py`) import the helpers from this module and
apply them identically. The token-count script (`g4_5a_token_count.py`)
does NOT use these helpers — per PR4-B4 it reads prompt files, not CP, and
must remain CP-free (no `compute_render_strategy_snapshot_hash` import).

Trap #1 silent-absorb ban (G4.3 R3R4-B2 + G4.4 carry):
- `or [] / or {}` 패턴 금지.
- `data.scenes` / `t2i_variations` / `t2i_prompt` /
  `render_prompt_card` / `render_strategy.spatial_consistency` 누락은
  measurement_failures 로 적재 후 caller 가 exit 1.
- `compute_render_strategy_snapshot_hash()` 호출 실패도 measurement_failures
  로 적재 (silent skip 금지).

Trap #3 — CP shape `scene["render_prompt_card"]["render_strategy"]` (NOT
flat). G4.2 carry — `detail_steps.py` authoritative.

Trap #8 — module-level constants single source. Imports happen in
`render_prompt_card_imports()` only — no inline duplication of the
9-entry close-framing keyword tuple, 7-entry interaction verb tuple, etc.

PR4-B4 — token script CP-free: this module is NOT imported by
`g4_5a_token_count.py`.
    )annotationsN)Path)Any   backendc                 L    ddl m} m}m}m}m}m}m}m}m	}m
}	 | |||||||||	d
S )u  Return the G4.5a + 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 tuple
    duplication of the 9-entry close-framing keywords / 7-entry interaction
    verbs / 8-entry frame-edge tokens / etc.

    RO-11 binding — G4.5a reuses G4.3 `_ID_BODY_PART_TRIGGERS` directly.
    NO `_SPATIAL_BODY_PART_TRIGGERS` alias.
    r   
_ID_BODY_PART_TRIGGERS_SPATIAL_CAMERA_HIGH_TOKENS_SPATIAL_CAMERA_HIP_TOKENS_SPATIAL_CAMERA_LOW_TOKENS _SPATIAL_FG_BG_SEPARATION_TOKENS#_SPATIAL_FRAME_EDGE_POSITION_TOKENS_SPATIAL_FRAMING_CLOSE_KEYWORDS_SPATIAL_INTERACTION_VERBS_SPATIAL_SHARED_ANCHOR_KEYWORDS%compute_render_strategy_snapshot_hash)!app.core.steps.render_prompt_cardr
   r   r   r   r   r   r   r   r   r   r	   s
             J/Users/manta/Documents/Projects/TheRoad-I1/scripts/canary/_g4_5a_common.pyrender_prompt_card_importsr   ,   s;       #9'B&@&@,L/+J&@+J1     c                    d| vr|j                  d       y| d   }t        |t              s|j                  d       yd|vr|j                  d       y|d   }t        |t              s|j                  d       y|S )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 (NO `.get(..., default)`
    fallback).
    datazcp.data key missingNzcp.data not dictsceneszcp.data.scenes key missingzcp.data.scenes not list)append
isinstancedictlist)cpmeasurement_failuresr   r   s       r   _load_scenes_or_failr!   X   s     R##$9:f:DdD!##$67t##$@A(^Ffd###$=>Mr   c           	        d| vr|j                  d| d| d       y| d   }t        |t              s|j                  d| d| d       yd|vr|j                  d| d| d       y|d   }t        |t              s|j                  d| d| d	       yd
|vr|j                  d| d| d       y|d
   }t        |t              s|j                  d| d| d       y	 ddlm}  ||       d| vr|j                  d| d| d       y| d   }	t        |	t              s|j                  d| d| d       y|	s|j                  d| d| d       y|	S # t
        $ r$}|j                  d| d| d|        Y d}~yd}~ww xY w)u  Validate per-scene shape — render_prompt_card / render_strategy /
    spatial_consistency / 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.render_strategy must be dict.
      - render_prompt_card.render_strategy.spatial_consistency must be dict.
      - compute_render_strategy_snapshot_hash(rpc) must succeed (no
        silent skip).
      - t2i_variations must be present, list, AND non-empty (R3-B1 carry —
        empty must fail, never silently absorbed).
    render_prompt_cards_shz : render_prompt_card key missingNz: render_prompt_card not dictrender_strategyz0: render_prompt_card.render_strategy key missingz-: render_prompt_card.render_strategy not dictspatial_consistencyzD: render_prompt_card.render_strategy.spatial_consistency key missingzA: render_prompt_card.render_strategy.spatial_consistency not dictr   )r   z: render_strategy hash failed: t2i_variationsz: t2i_variations key missingz: t2i_variations not listz: t2i_variations empty)r   r   r   r   r   	Exceptionr   )
scenesishir    rpcrsscr   exc
variationss
             r   _validate_scene_preflightr2   p   s1   ( 5(##t3se;<	
 
$
%Cc4 ##t3se89	
 ###t3seKL	
 		Bb$##t3seHI	
 B&##t3se . /	
 	!	"Bb$##t3se + ,	
 	
 	.c2 u$##t3se78	
 '(Jj$'##t3se45	
 ##t3se12	
 -  ##t3se:3%@	
 	s   #E 	F"FFc                    t        | dd      rt        | j                        }|j                         s3t	        d| t
        j                         t        j                  d       	 |j                  dd      5 }t        j                  |      ddd       dfd
} |d       |d       |d       |d       |d       |d       |d      dS 	 t        | dd      rt        j                  | j                        nd}t        | dd      rt        j                  | j                        nd}t        | dd      t        | dd      t        | dd      t        | dd      t        | dd      dS # 1 sw Y   xY w# t        $ r>}t	        d	| t
        j                         t        j                  d       Y d}~&d}~ww xY w# t        $ r=}t	        d| t
        j                         t        j                  d       Y d}~d}~ww xY w)u  Construct pinning dict from --config JSON OR individual CLI flags.

    Returns dict with 7 keys (G4.5a pinning block):
      pid / scene_index_list / shot_index_list_per_scene / model_routing /
      prompt_source_mode / card_commit_hash / render_strategy_card_snapshot_hash

    Trap #1 — NO silent defaults; caller must run validate_pinning() afterwards.
    `_DEFAULT_SENTINEL` is the only allowed default — `None` round-trip is
    enforced explicitly in validate_pinning().
    configNz/[g4_5a_common] ERROR: --config file not found: file   rutf-8encodingz,[g4_5a_common] ERROR: --config parse error: c                H    j                  | t              }|t        u rd S |S )N)get_DEFAULT_SENTINEL)keyvalcfgs     r   _cfg_or_nonez,load_pinning_from_args.<locals>._cfg_or_none   s'    ''#01C"334<<r   pidscene_index_listshot_index_list_per_scenemodel_routingprompt_source_modecard_commit_hash"render_strategy_card_snapshot_hashrC   rD   rE   rF   rG   rH   rI   z;[g4_5a_common] ERROR: failed to parse list/dict JSON args: )r?   strreturnr   )getattrr   r4   existsprintsysstderrexitopenjsonloadr)   loadsrD   rE   )argscfg_pathfpr0   rB   rD   rE   rA   s          @r   load_pinning_from_argsrZ      s    tXt$$ A(LZZ HHQK	sW5iim 6	=
  & ,-? @)5+* */:"./C"D ,-? @2>43
 	
 t/6 JJt,,- 	 t8$? JJt556 	" tUD),%> =%d,@$G#D*<dC.56/

 
Y 65 	>seDZZ HHQKK	H  I#O	
 	sI   (E= ;E1E= AG 1E:6E= =	G3F??G	H3HHc                   d}g }|D ]6  }|| vr|j                  |       | |   }||dk(  s&|j                  |       8 |r4t        d| dt        j                         t        j                  d       t        | d   t              s0t        d	t        j                         t        j                  d       t        | d
   t              s1t        dt        j                         t        j                  d       yy)uD   Fail-fast on missing pinning fields. Trap #1 — NO silent defaults.rJ   N z9[g4_5a_common] ERROR: pinning missing required field(s): zF. Provide via --config <json> or individual flags. NO silent defaults.r5   r7   rD   z@[g4_5a_common] ERROR: pinning.scene_index_list must be list[int]rE   zD[g4_5a_common] ERROR: pinning.shot_index_list_per_scene must be dict)r   rO   rP   rQ   rR   r   r   r   )pinningrequiredmissingkvs        r   validate_pinningrb     s    H GGNN1AJ9RNN1  Gi "# 		
 	g0148N	
 	g9:DA	

 	 Br   c           	        t               }| d   }| d   D ]:  }t        |      }|j                  t        |      t              }|t        u r|j                  |t              }|t        u r4t        d| dt        j                         t        j                  d       t        |t              sJt        d| dt        |      j                   d	t        j                         t        j                  d       |s4t        d| d
t        j                         t        j                  d       |D ]  }|j                  |t        |      f         = |S )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.
    rE   rD   z>[g4_5a_common] ERROR: pinning.scene_index_list contains scene z? but shot_index_list_per_scene has no entry. NO silent default.r5   r7   z8[g4_5a_common] ERROR: pinning.shot_index_list_per_scene[z] must be list[int], got .u4   ] is empty — pinned scene must enumerate >=1 shot.)setintr=   rK   r>   rO   rP   rQ   rR   r   r   type__name__add)r]   outshot_mapr+   si_int	raw_shotsr,   s          r   build_pinning_scene_setrn   G  sD    !$C23H()R LLV.?@	)) V->?I)) !%& ZZ	 HHQK)T*84	?++,A/ ZZ	 HHQK8  ZZ	 HHQKCGGVSX&' A *D Jr   c                   | dz  }|j                         s3t        d| t        j                         t        j                  d       	 |j                  dd      5 }t        j                  |      cddd       S # 1 sw Y   yxY w# t        $ r=}t        d	| t        j                         t        j                  d       Y d}~yd}~ww xY w)
zELoad `manifest.json` under cp_root. fail-fast on missing/parse error.zmanifest.jsonz1[g4_5a_common] ERROR: manifest.json not found at r5   r7   r8   r9   r:   Nz1[g4_5a_common] ERROR: manifest.json parse error: )	rN   rO   rP   rQ   rR   rS   rT   rU   r)   )cp_rootmanifest_pathrY   r0   s       r   load_cp_manifestrr   u  s    o-M!?o	

 	g6"99R= 766 ?uE	
 	s6   
B A<2	B <BB B 	C3C		Cc                "   d}t        | dd      rdt        | j                        j                  dd      5 }t	        j
                  |      }ddd       j                  dt              }|t        ur|}|st        | dd      }|s0t        dt        j                         t        j                  d	       t        |      }|j                         s3t        d
| t        j                         t        j                  d	       |S # 1 sw Y   xY w)up   Resolve cp_root from --config or --cp-root flag. Fail-fast on missing.

    Trap #1 — NO silent defaults.
    Nr4   r8   r9   r:   rp   z<[g4_5a_common] ERROR: --cp-root (or config.cp_root) requiredr5   r7   z+[g4_5a_common] ERROR: --cp-root not found: )rM   r   r4   rS   rT   rU   r=   r>   rO   rP   rQ   rR   rN   )rW   cp_root_strrY   rA   cfg_valrp   s         r   resolve_cp_rootrv     s    
 #KtXt$$++##C'#:b))B-C ;'')%67++!KdIt4J	
 	;G>>9'C	
 	N) ;:s   DDc                   |j                   dk(  rHt        j                  | t        j                  ddd       t        j                  j                  d       yt        |j                         }|j                  j                  dd       |j                  dd	
      5 }t        j                  | |ddd       ddd       y# 1 sw Y   yxY w)z2Write JSON output to args.output ('-' for stdout).-Fr   T)ensure_asciiindent	sort_keys
)parentsexist_okwr9   r:   N)
outputrT   dumprP   stdoutwriter   parentmkdirrS   )rj   rW   out_pathrY   s       r   emit_outputr     s    {{c		#szza4P

$dT:]]3]1RIIc2E!tL 211s   CC
)rL   r   )r   r   r    r   rL   list | None)
r*   r   r+   
int | Noner,   r   r    r   rL   r   )rW   r   rL   r   )r]   r   rL   None)r]   r   rL   zset[tuple[int, int]])rp   r   rL   r   )rW   r   rL   r   )rj   r   rW   r   rL   r   )__doc__
__future__r   rT   rP   pathlibr   typingr   __file__resolver}   _BACKEND_PATHrK   pathinsertr   r!   r2   objectr>   rZ   rb   rn   rr   rv   r    r   r   <module>r      s   6 #  
   X&&(003i?}SXX%HHOOAs=)*)X0TTT 
T 	T
 Tv H JZ)X+\*<	Mr   