
    ki)                       d Z ddlmZ ddl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dd
lmZ  ej>                  d      Z ddZ!ddZ"ddZ#	 	 	 	 	 	 	 	 ddZ$e%dk(  r ejL                   e#              yy)uL  G4.5a canary — fg/bg shared-anchor missing detection (Rule G, Task 4.2).

Detect Rule G violations: when t2i_prompt contains 2+ character IDs
(C##/C##O##) AND fg/bg separation token AND interaction verb co-occurrence,
the prompt MUST also include a shared-anchor keyword. Absence is a Rule G
violation.

Scope: per-shot scope (no proximity window). Each violation = 1 t2i variation
that satisfies (2+ char IDs + fg/bg token + interaction verb) but lacks any
shared-anchor keyword.

PR4-B7 binding: STRICT 0 — exit 1 on any violation.

Trap #1 silent-fallback ban / Trap #3 CP shape / Trap #4 pinned-tuple
post-loop validation / Trap #8 module-level constants.

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

Usage (config mode):
    python scripts/canary/g4_5a_fg_bg_shared_anchor.py \
        --config canary_config.json \
        --cp-root data/projects/<pid>/checkpoints/scene_detail \
        --prompt-version 20.<timestamp> \
        --role candidate \
        --output results/g4_5a_fg_bg_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_FG_BG_SEPARATION_TOKENS_SPATIAL_INTERACTION_VERBS_SPATIAL_SHARED_ANCHOR_KEYWORDS)%compute_render_strategy_snapshot_hashz\bC\d{2}(?:O\d{2})?\bc                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     +scripts/canary/g4_5a_fg_bg_shared_anchor.pyiso8601_nowr   B   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>I   s     71qwwyJ&s   )r   any)texttokensr    s     @r   _has_any_tokenr%   F   s    J7777r   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}t)        |      D ]  \  }}t        |t              s|j!                  d| d| d| d        2d!|vr|j!                  d| d| d| d"       R|d!   }t        |t              r|j+                         s|j!                  d| d| d| d#       t        t,        j/                  |            }t1        |      d$k  rt3        |t4              st3        |t6              sd}t3        |t8              r|dz  } |j!                  |||d%       |r|
dz  }
|dkD  s||z  }| d&| }||v r||xx   |z  cc<   |||<    |D ]$  \  }}||f|	vs|j!                  d'| d| d(       & t;               |j<                  |j>                  |||
||d)|d| xr |dk(  d*d+id,
}t        ||       tA        d-|j>                   d.|j<                   d/|
 d0| d1t1        |       d2tB        jD                  3       |ry|j>                  dk(  r|dkD  ryy)4NzuG4.5a canary: fg/bg shared-anchor missing detection (fg/bg interaction shots only, Rule G, per-shot scope, STRICT 0).)descriptionz--config)typedefaultz--pidz	--cp-rootz--prompt-versionT)r(   requiredz--role	candidatebaseline)r(   r)   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--output   r   zscene entry is not dictscene_index_shot_indexz%scene missing scene_index/_shot_indexFs_sh_vz: variation not a dict
t2i_promptz: t2i_prompt key missingz': t2i_prompt missing, empty, or not str   )r1   
shot_indexhas_fg_bg_pattern_z
pinning (sz ): not present in CP data.scenestotal_shots_with_fg_bg_pattern!fg_bg_shared_anchor_missing_count$fg_bg_shared_anchor_missing_per_shotfg_bg_shared_anchoru   scripts/canary/g4_5a_fg_bg_shared_anchor.py (fg/bg interaction shots only, per-shot scope, Rule G, 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_fg_bg_shared_anchor] role=z prompt_version=z"
  total_shots_with_fg_bg_pattern=z$  fg_bg_shared_anchor_missing_count=z
  measurement_failures: zS
Exit criteria (STRICT 0): candidate.metrics.fg_bg_shared_anchor_missing_count == 0)r.   )#argparseArgumentParseradd_argumentstr
parse_argsr   r   r   r
   r   _empty_outputr	   r   set
isinstancedictappendintaddr   	enumeratestrip	_ID_REGEXfindalllenr%   r   r   r   r   rB   rC   printsysstderr)parserargsrD   cp_rootcprG   scenesoutpinning_set
seen_in_cpr<   r=   r>   scene_set_outscenesishisi_intshi_int
variationsshot_has_fg_bg_patternshot_violation_countvivarpromptchar_idsshot_keys                              r   mainru   L   sn   $$I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%&"()%;=( "M%& ''(AB%e)C ''7 =!M":Rc(GK/().67$8

 
 "'  ,GBc4($++xs7)2bT1GH 3&$++xs7)2bT1IJ &Ffc*&,,.$++xs7)2bT 2( ) 9,,V45H8}q !&*JK!&*DE%)"!&*IJ$)$; -> 	!!!7
 	
 "*a/*!#-1EE- 7),H??4X>(>
 ) 5X>[ d !	S9J& ''RDC5(HI ! !]--		".L1 5
 !5#$$$ 71Q6 "9 
+C< T	
+DII; 7--. /,,J+K L-,
- .##&';#<"= >C	D ZZ
 yyK$E$Ir   c                `    t               | j                  | j                  |g ddi d|ddddid
S )Nr   r;   Fr?   zRscripts/canary/g4_5a_fg_bg_shared_anchor.py (fg/bg interaction shots only, Rule G)r@   )r   rB   rC   )r`   rD   rG   s      r   rP   rP     sO     !]--		./1246

 !5#$#!9 
 r   __main__)returnrN   )r#   rN   r$   tuplerx   bool)rx   rU   )r`   zargparse.NamespacerD   rS   rG   listrx   rS   )'__doc__
__future__r   rK   rer]   r   pathlibr   pathinsertrN   __file__resolveparent_g4_5a_commonr   r   r   r	   r
   r   r   r   r   _CONSTSr   r   r   !app.core.steps.render_prompt_cardr   compilerY   r   r%   ru   rP   __name__exitr   r   r   <module>r      s   6 #  	 
   3tH~--/667 8
 
 
 %
&#*+M#N  $%AB ")*K"L 
 BJJ/0	/8D
  
	8 zCHHTV r   