
    _2iW0                    *   U 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d	Zd
ed<   dj=                  d eD              Z ej@                  dez   dz   ejB                        Z" ej@                  d      Z#dZ$ddZ%ddZ&ddZ'	 	 	 	 	 	 	 	 ddZ(e)dk(  r ejT                   e'              yy)uY  G4.4 canary — STRICT view-mixing detection (Task 4.4).

Detects same-character-ID full-body / partial-body verbs (`stands` /
`seated` / `leaning` / `walking` / `running` / `kneeling` / `lying` —
G4.4 ground-truth) **+** body-part close-up co-occurrence (`focus on` /
`close on` / `tight on` / `detail on` + `<id>'s <body-part>` —
`_ID_BODY_PART_TRIGGERS` cross-import from G4.3) within a single
t2i_prompt = view-mixing 시점 혼합 violation.

Override O-7 — paired-string substring carry: same composite-ID body-part
focus rule cross-references `_CONTINUITY_ID_POLICY_CROSS_REF_LITERAL`
(`id_policy.body_part_focus_rule`). Cross-card paired drift is enforced
by unit + integration tests; this canary inherits the same import boundary.

Override O-17 — regex brittleness: this regex/substring detector is a
1차 detector only. False-positive examples: "C01O02 stands in profile, hand
resting on the table" (full-body + body-part are natural partial
description, not close-up). If baseline >0, escalate to LLM-validator
(gpt-5.4-mini judge) as P1 follow-up.
`feedback_no_regex_postprocessing.md` carry.

Scope: ALL pinned (si, shi) shots — view-mixing pattern detection itself
is intent-agnostic (R2-B4 carry).

Exit criteria (analyzer, OUT-OF-BAND):
  candidate.metrics.view_mixing_count == 0  (STRICT, Override O-1)

Usage (config mode):
    python scripts/canary/g4_4_view_mixing.py \
        --config canary_config.json \
        --cp-root data/projects/<pid>/checkpoints/scene_detail \
        --prompt-version 19.<timestamp> \
        --role candidate \
        --output results/view_mixing_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_ID_BODY_PART_TRIGGERS'_CONTINUITY_ID_POLICY_CROSS_REF_LITERAL)standsseatedleaningwalkingrunningkneelinglyingztuple[str, ...]_VIEW_MIXING_FULL_BODY_VERBS|c              #  F   K   | ]  }t        j                  |        y wN)reescape).0ts     "scripts/canary/g4_4_view_mixing.py	<genexpr>r!   M   s     E.D		!.Ds   !z\b(?:z)\s+C\d{2}(?:O\d{2})?'s\s+\w+z\bC\d{2}(?:O\d{2})?\b2   c                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     r    iso8601_nowr)   W   s    ??&&(3..r(   c           
        g }| j                         }i }t        j                  |       D ]Q  }|j                         }|j	                  |g       j                  |j                         |j                         f       S g }t        j                  |       D ]g  }t        j                  |j                               }|s)|j                  |j                         |j                         |j                         d       i |j                         D ]  \  }}|D ]  \  }	}
t        d|	t        z
        }t        t        |       |
t        z         }||| t        fdt         D              }|sU|D ]I  }|d   |k7  r|d   |	t        z
  k\  r|d   |
t        z   k  r+|j                  ||	|
g|d   |d   gd       K   |S )u  Return list of view-mixing violations.

    Each violation = {id_token, full_body_position, body_part_position}.
    Same composite-ID + full-body verb (within ±50 chars window) + body-part
    trigger close-up of same id elsewhere (outside that window) →
    view-mixing violation.

    `feedback_no_regex_postprocessing.md` carry — char-level proximity
    (NOT sentence regex). Override O-17 — 1차 detector only.
    )id_tokenstartendr   c              3  &   K   | ]  }|v  
 y wr   r'   )r   vwindows     r    r!   z%detect_view_mixing.<locals>.<genexpr>   s       %AV%As   r+   r,   r-   )r+   full_body_positionbody_part_position)lower	_ID_REGEXfinditergroup
setdefaultappendr,   r-   _BODY_PART_FOCUS_PATTERNsearchitemsmax_WINDOW_CHARSminlenanyr   )prompt
violationsprompt_lowerid_positionsmatchr+   bp_focus_matchesid_match	positionsid_startid_end	win_startwin_endhas_full_bodybpr0   s                  @r    detect_view_mixingrO   [   s     J<<>L 68L##F+;;="-44[[]EIIK(	
 , $&)226:##EKKM2##$NN,yy{%  ;  ,113) )HfAx-78I#f+v'=>G!)G4F  %A  M !&j>X-wK8m#;;5	Vm%;; !! (+3V*<+-g;5	*B#  ' !*  40 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 }g }|D ]  }t        |t              s|j!                  d       &|j#                  d      }|j#                  d      }||Mt%        |      }t%        |      }||f|vrj|	j'                  ||f       t)        ||||      }||j!                  ||d       |
dz  }
d}t+        |      D ]  \  }}t        |t              s|j!                  d| d| d| d       2|j#                  d       }t        |t              r|j-                         s|j!                  d| d| d| d!       t/        |      }|t1        |      z  }|D ]#  }|j!                  ||||d"   |d#   |d$   d%       %  ||z  }||| d&| <    |D ]$  \  }}||f|	vs|j!                  d'| d| d(       & t3               |j4                  |j6                  |||
|||t9        t:              t9        t<              d)|d| xr |dk(  d*d+t>         d,id-
}t        ||       tA        d.|j6                   d/|j4                   d0|
 d1| d2t1        |       d3tB        jD                  4       |ry|j6                  dk(  r|dkD  ryy)5Nu{   G4.4 canary: view-mixing detection (all shots, cross-import G4.3 _ID_BODY_PART_TRIGGERS — Override O-17 regex 1차 only).)descriptionz--config)typedefaultz--pidz	--cp-rootz--prompt-versionT)rR   requiredz--role	candidatebaseline)rR   rS   choicesz--scene-index-listz--shot-index-list-per-scenez--model-routingz--prompt-source-mode)Nfiledbz--card-commit-hashz--continuity-card-snapshot-hashz--output   r   zscene entry is not dictscene_index_shot_index)r[   
shot_indexs_sh_vz: variation not a dict
t2i_promptz': t2i_prompt missing, empty, or not strr+   r1   r2   )r[   r]   variation_indexr+   r1   r2   _z
pinning (sz ): not present in CP data.scenestotal_shotsview_mixing_countview_mixing_per_shotviolations_detailtrigger_phrases_usedfull_body_verbs_usedview_mixingu   scripts/canary/g4_4_view_mixing.py (all shots — cross-import G4.3 _ID_BODY_PART_TRIGGERS, Override O-17 regex 1차 only — paired ref literal: )
	timestampprompt_versionrolepinning	scene_setmetricsmeasurement_failuresexit_criteria_thresholdexit_criteria_passmeasurement_scriptsz[g4_4_view_mixing] role=z prompt_version=z
  total_shots=z  view_mixing_count=z
  measurement_failures: zA
Exit criteria (STRICT): candidate.metrics.view_mixing_count == 0)rX   )#argparseArgumentParseradd_argumentstr
parse_argsr   r   r   r
   r   _empty_outputr	   r   set
isinstancedictr8   getintaddr   	enumeratestriprO   r?   r)   ro   rp   listr   r   r   printsysstderr)parserargsrq   cp_rootcprt   scenesoutpinning_set
seen_in_cpre   rf   rg   rh   scene_set_outscenesishisi_intshi_int
variations
shot_countvivarrA   shot_violationsr/   s                              r    mainr      s   $$NF 
d;
c48
#t<
*tD
[)	   ,3E
5CN
)TB
$	   ,3E
)T   
t<D$T*GWd#G	'	"B&(!"&:;F~D'+?@C)'2K'*uJK+-$& "M%& ''(ABYY}%ii&:Rc(GK/() /67$8

 !!
 	
 	q
 ,GBc4($++xs7)2bT1GH WW\*Ffc*&,,.$++xs7)2bT 2( ) 08O#o..J$!((#)")') !**+,@*A*+,@*A*  % -2 	Z'6@xq	23m r !	S9J& ''RDC5(HI ! !]--		"&!2$8!2$()?$@$()E$F
 !5#$$$?):a)? I ;;1> 
'C: T	
"499+ .--. /$ &./ 0##&';#<"= >3		4 ZZ	 yyK$5$9r(   c                    t               | j                  | j                  |g ddi g t        t              t        t
              d|ddddid
S )Nr   rd   Frk   z.scripts/canary/g4_4_view_mixing.py (all shots)rm   )r)   ro   rp   r   r   r   )r   rq   rt   s      r    r}   r}   ?  sb     !]--		!"$&!#$()?$@$()E$F
 !5#$#@ 
# r(   __main__)returnr{   )rA   r{   r   z
list[dict])r   r   )r   zargparse.Namespacerq   r   rt   r   r   r   )+__doc__
__future__r   rx   r   r   r   pathlibr   pathinsertr{   __file__resolveparent_g4_4_commonr   r   r   r	   r
   r   r   r   r   _CONSTSr   r   r   __annotations__join_TRIGGER_ALTcompile
IGNORECASEr9   r4   r=   r)   rO   r   r}   __name__exitr'   r(   r    <module>r      s:  "F #  	 
   3tH~--/667 8
 
 
 %
& !9: *1-+ '
1 o  xxE.DEE%2::|>>MM  BJJ/0	/;|cL
  
	< zCHHTV r(   