
    ki7                       U 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j                  j                  d e
 ee      j                         j                               ddlmZmZmZmZmZmZmZmZmZ  e       Zed   Zed   Zed	   Zed
   ZddlmZ dZdZ de!d<   dZ"de!d<   dZ#de!d<   ddZ$ddZ%	 	 	 	 	 	 ddZ&ddZ'	 	 	 	 	 	 	 	 ddZ(e)dk(  r ejT                   e'              yy)u  G4.5a canary — camera/frame consistency (Rule F, Task 4.1).

Detect Rule F violations: `camera_direction` vertical token (low / high /
hip) + frame-edge surface position token (chest / waist / floor / ceiling)
co-occurrence in t2i_prompt with **±50 chars proximity window** that forms
a contradiction pattern (low-camera + chest/waist height frame-edge ⇒
camera height contradicts frame-edge surface presence).

Scope: pinned shots whose `render_strategy.camera_direction` contains 1+
camera vertical token (low / high / hip). Other shots are out-of-scope
(分母 from total_shots_with_camera_vertical only).

PR4-B7 / RO-8 binding: STRICT 0 — exit 1 on any violation, NOT soft warning.

PR-fix-iter-1-3 disambiguation: ±50 chars proximity window is THIS script's
algorithm (G4.4 carry style — char-level, not sentence regex). RO-8 ±20
tokens applies ONLY to `g4_5a_primary_framing.py`.

Trap #1 silent-fallback ban — NO `.get(key, default)` with implicit default;
only `_DEFAULT_SENTINEL` is permitted (PR-fix-iter-1-1 strengthened).
Trap #3 CP shape `scene["render_prompt_card"]["render_strategy"]` (NOT flat).
Trap #4 pinned-tuple post-loop `seen_in_cp` validation (Wave 3 carry).
Trap #8 module-level constants — direct import via render_prompt_card_imports.

Override O-17 — regex/substring detector is 1차 only. baseline 비0 시 P1
follow-up: LLM-validator (gpt-5.4-mini judge) 전환. brittle pattern carry.

Usage (config mode):
    python scripts/canary/g4_5a_camera_frame_consistency.py \
        --config canary_config.json \
        --cp-root data/projects/<pid>/checkpoints/scene_detail \
        --prompt-version 20.<timestamp> \
        --role candidate \
        --output results/g4_5a_camera_frame_candidate.json
    )annotationsN)datetime)Path)	_load_scenes_or_fail_validate_scene_preflightbuild_pinning_scene_setemit_outputload_cp_manifestload_pinning_from_argsrender_prompt_card_importsresolve_cp_rootvalidate_pinning_SPATIAL_CAMERA_LOW_TOKENS_SPATIAL_CAMERA_HIGH_TOKENS_SPATIAL_CAMERA_HIP_TOKENS#_SPATIAL_FRAME_EDGE_POSITION_TOKENS)%compute_render_strategy_snapshot_hash2   )chestwaistu   가슴u   허리ztuple[str, ...]_LOW_CONTRADICTION_TOKENS)floor   바닥z
below feetu   아래_HIGH_CONTRADICTION_TOKENS)ceilingu   천장r   r   _HIP_CONTRADICTION_TOKENSc                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     0scripts/canary/g4_5a_camera_frame_consistency.pyiso8601_nowr$   [   s    ??&&(3..r"   c                L    | j                         t        fd|D              S )z*case-insensitive substring presence check.c              3  B   K   | ]  }|j                         v   y wNlower).0t
text_lowers     r#   	<genexpr>z!_has_any_token.<locals>.<genexpr>b   s     71qwwyJ&   )r)   any)texttokensr,   s     @r#   _has_any_tokenr2   _   s    J7777r"   c                   | j                         }|j                         g }t        fdt        D              r|j                  dt        f       t        fdt
        D              r|j                  dt        f       t        fdt        D              r|j                  dt        f       |syd}t        D ]  }|j                         }d}	 |j                  ||      }|dk(  r.t        d|t        z
        }	t        t        |       |t        |      z   t        z         }
||	|
 }|D ];  \  }}|D ]/  }|j                         |k(  r|j                         |v s*|d	z  } n ; n |t        |      z   } |S )
u  Detect Rule F contradiction patterns within ±50 char windows.

    For each frame-edge position token occurrence in prompt, scan ±50 chars
    around it for a contradicting camera-direction-implied position. Returns
    count of distinct violation occurrences in the t2i_prompt.

    Note: cam_dir tells us camera intent (low/high/hip), but the contradiction
    must be observed inside the t2i_prompt itself — the window centred on each
    frame-edge token.
    c              3  B   K   | ]  }|j                         v   y wr'   r(   r*   r+   cam_dir_lowers     r#   r-   z/_detect_violations_in_window.<locals>.<genexpr>x        
J/I!1779%/Ir.   lowc              3  B   K   | ]  }|j                         v   y wr'   r(   r5   s     r#   r-   z/_detect_violations_in_window.<locals>.<genexpr>z   s     
K/J!1779%/Jr.   highc              3  B   K   | ]  }|j                         v   y wr'   r(   r5   s     r#   r-   z/_detect_violations_in_window.<locals>.<genexpr>|   r7   r.   hipr      )r)   r/   r   appendr   r   r   r   r   r   findmax_WINDOW_CHARSminlen)promptcam_dirr,   contradictions
violationsfe_tokfe_loweridxpos	win_startwin_endwindow_camera_kindcontradict_tokensctr6   s                  @r#   _detect_violations_in_windowrS   e   sw    JMMOM 9;N

J/I
JJu&?@A

K/J
KKv'ABC

J/I
JJu&?@AJ 6<<>//(C0CbyAs]23I#f+sS]':]'JKG	'2F3A// ,BxxzX- xxzV+"a
 ,  4B  H%C/  66 r"   c                  	   t        j                  d      } | j                  dt        d        | j                  dt        d        | j                  dt        d        | j                  dt        d	       | j                  d
t        dddg       | j                  dt        d        | j                  dt        d        | j                  dt        d        | j                  dt        d g d       | j                  dt        d        | j                  dt        d        | j                  dt        d	       | j	                         }t        |      }t        |       t        |      }t        |      }g }t        ||      }|t        |||      }t        ||       yt        |      }t               }	d}
d}i }g }|D ]  }t        |t              s|j!                  d       &d|vsd|vr|j!                  d       @|d   }|d   }||Ot#        |      }t#        |      }||f|vrl|	j%                  ||f       t'        ||||      }||d   }|d   }d|vr|j!                  d| d | d!       |d   }t        |t              s|j!                  d| d | d"       t)        |t*              xs" t)        |t,              xs t)        |t.              }|j!                  |||d#       |s2|
dz  }
d}t1        |      D ]  \  }}t        |t              s|j!                  d| d | d$| d%       2d&|vr|j!                  d| d | d$| d'       R|d&   }t        |t              r|j3                         s|j!                  d| d | d$| d(       |t5        ||      z  } ||z  }|dkD  s||| d)| <     |D ]$  \  }}||f|	vs|j!                  d*| d | d+       & t7               |j8                  |j:                  |||
||d,|d| xr |dk(  d-d.id/
}t        ||       t=        d0|j:                   d1|j8                   d2|
 d3| d4t?        |       d5t@        jB                  6       |ry|j:                  dk(  r|dkD  ryy)7Nuy   G4.5a canary: camera/frame-consistency detection (camera vertical token shots only, Rule F, ±50 chars window, STRICT 0).)descriptionz--config)typedefaultz--pidz	--cp-rootz--prompt-versionT)rV   requiredz--role	candidatebaseline)rV   rW   choicesz--scene-index-listz--shot-index-list-per-scenez--model-routingz--prompt-source-mode)Nfiledbz--card-commit-hashz$--render-strategy-card-snapshot-hashz--outputr>   r   zscene entry is not dictscene_index_shot_indexz%scene missing scene_index/_shot_indexrender_prompt_cardrender_strategycamera_directions_shz.: render_strategy.camera_direction key missingz*: render_strategy.camera_direction not str)r^   
shot_indexhas_camera_vertical_token_vz: variation not a dict
t2i_promptz: t2i_prompt key missingz': t2i_prompt missing, empty, or not str_z
pinning (sz ): not present in CP data.scenes total_shots_with_camera_verticalcamera_frame_violation_countcamera_frame_violation_per_shotcamera_frame_consistencyu   scripts/canary/g4_5a_camera_frame_consistency.py (camera vertical token shots only, ±50 chars proximity, Rule F, Override O-17 regex 1차 only)
	timestampprompt_versionrolepinning	scene_setmetricsmeasurement_failuresexit_criteria_thresholdexit_criteria_passmeasurement_scriptsz&[g4_5a_camera_frame_consistency] role=z prompt_version=z$
  total_shots_with_camera_vertical=z  camera_frame_violation_count=z
  measurement_failures: zN
Exit criteria (STRICT 0): candidate.metrics.camera_frame_violation_count == 0)r\   )"argparseArgumentParseradd_argumentstr
parse_argsr   r   r   r
   r   _empty_outputr	   r   set
isinstancedictr?   intaddr   r2   r   r   r   	enumeratestriprS   r$   rq   rr   printrD   sysstderr)parserargsrs   cp_rootcprv   scenesoutpinning_set
seen_in_cprk   rl   rm   scene_set_outscenesishisi_intshi_int
variationsrpcrsrF   has_vert
shot_countvivarrE   s                               r#   mainr      s   $$OF 
d;
c48
#t<
*tD
[)	   ,3E
%C   )TB
$	   ,3E
.S$   
t<D$T*GWd#G	'	"B&(!"&:;F~D'+?@C)'2K'*uJ'($#$ 68# "M%& ''(AB%e)C ''7 =!M":Rc(GK/().67$8

 ()"#R' ''F83wi (  '('3' ''F83wi (   7$>? Cg'BCCg'AB 	
 	!!)1
 	
 (A-(
 ,GBc4($++xs7)2bT1GH 3&$++xs7)2bT1IJ &Ffc*&,,.$++xs7)2bT 2( ) 6vwGGJ% -( 	%
2$>  ,(!G9%] f !	S9J& ''RDC5(HI ! !]--		" 1,H/
 !5#$$$ 2,1 '9 
+C< T	
0 <--. /.+
, -((D'E F##&';#<"= >>	? ZZ
 yyK$@1$Dr"   c                `    t               | j                  | j                  |g ddi d|ddddid
S )Nr   rj   Frn   z[scripts/canary/g4_5a_camera_frame_consistency.py (camera vertical token shots only, Rule F)ro   )r$   rq   rr   )r   rs   rv   s      r#   r   r   f  sO     !]--		01,-/1

 !5#$#&= 
 r"   __main__)returnr}   )r0   r}   r1   tupler   bool)rE   r}   rF   r}   r   r   )r   r   )r   zargparse.Namespacers   r   rv   listr   r   )+__doc__
__future__r   rz   r   r   pathlibr   pathinsertr}   __file__resolveparent_g4_5a_commonr   r   r   r	   r
   r   r   r   r   _CONSTSr   r   r   r   !app.core.steps.render_prompt_cardr   rB   r   __annotations__r   r   r$   r2   rS   r   r   __name__exitr!   r"   r#   <module>r      sC  "F #  
   3tH~--/667 8
 
 
 %
&$%AB %&CD $%AB &-)' # . ? / O . ? 
/8;;; 	;|@F
  
	8 zCHHTV r"   