
    gi%                       d Z ddlmZ ddl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  e       Zed   Zed   Zd	j9                  d
 eD              Z ej<                  dez   dz   ej>                        Z  ej<                  d      Z!ddZ"ddZ#ddZ$e%dk(  r ejL                   e#              yy)u  G4.3 canary — STRICT body-part focus pattern counter (Task 4.1).

Detects `<trigger>` + composite ID + `'s` + body part. The 4 trigger phrases
(`focus on`, `close on`, `tight on`, `detail on`) are imported from
`render_prompt_card._ID_BODY_PART_TRIGGERS` (Override R1R2-B2 — drift ban).

Scope: ALL shots of pinned (si, shi) tuples — `focus on` pattern itself is
the intentional close-up trigger, so this is not a close-framing-only metric.

Exit criteria (analyzer, OUT-OF-BAND):
  candidate.metrics.body_part_focus_pattern_count == 0  (STRICT, R1R2-B2 / R3-I1)
  baseline value is informational only.

Usage (config mode):
    python scripts/canary/g4_3_body_part_focus.py \
        --config canary_config.json \
        --cp-root data/projects/<pid>/checkpoints/scene_detail \
        --prompt-version 17.<timestamp> \
        --role candidate \
        --output results/body_part_focus_candidate.json
    )annotationsN)datetime)Path)_load_scenes_or_fail_validate_scene_preflightbuild_pinning_scene_setis_close_framingload_cp_manifestload_pinning_from_argsrender_prompt_card_importsvalidate_pinning_ID_BODY_PART_TRIGGERS_ID_REPRODUCTION_SURFACES|c              #  F   K   | ]  }t        j                  |        y w)N)reescape).0ts     &scripts/canary/g4_3_body_part_focus.py	<genexpr>r   6   s     E.D		!.Ds   !z\b(?:z)\s+C\d{2}(?:O\d{2})?'s\s+\w+z\bC\d{2}(?:O\d{2})?\bc                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     r   iso8601_nowr   @   s    ??&&(3..r   c                 p   t        j                  d      } | j                  dt        d        | j                  dt        d        | j                  dt        d 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        |       d }|j                  rUt        |j                        j                  dd      5 }t        j                  |      }d d d        j                  d      }|s|j                  }|st        dt        j                          yt        |      }|j#                         st        d| t        j                          yt%        |      }g }t'        ||      }	|	Ht)               |j*                  |j,                  |g d d i t/        t0              d!|d"d#id$}
t3        |
|       yt5        |      }t7               }d }d }i }g }|	D ]'  }t9        |t:              s|j=                  d%       &|j                  d&      }|j                  d'      }||Mt?        |      t?        |      f|vrf|jA                  t?        |      t?        |      f       tC        ||||      }|tE        |      \  }}||j=                  d(| d)| d*|        d+}d+}|D ]}  }t9        |t:              s|j                  d,      }t9        |t              s6tF        jI                  |      rd
}|jK                         }tL        D ]  }|jK                         |v sd
} }  |j=                  |||||d-       |dz  }d }tO        |      D ]  \  }}t9        |t:              s|j=                  d(| d)| d.| d/       2|j                  d,      }t9        |t              r|jQ                         s|j=                  d(| d)| d.| d0       tR        jU                  |      } |tW        |       z  } ||z  }||| d1| <   * |D ]$  \  }}||f|vs|j=                  d2| d)| d3       & t)               |j*                  |j,                  |||||t/        t0              d!|d"d#id$}
t3        |
|       t        d4|j,                   d5|j*                   d6| d7| d8tW        |       d9t        j                          |ry|j,                  dk(  r|d kD  ryy # 1 sw Y   xY w):NzEG4.3 canary: body-part focus pattern counter (4 triggers, all shots).)descriptionz--config)typedefaultz--pidz	--cp-rootz/Directory containing scene_detail manifest.json)r!   r"   helpz--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--id-policy-card-snapshot-hashz--outputrutf-8encodingcp_rootzD[g4_3_body_part_focus] ERROR: --cp-root (or config.cp_root) required)r(      z3[g4_3_body_part_focus] ERROR: --cp-root not found: r   )total_shotsbody_part_focus_pattern_countbody_part_focus_per_shottrigger_phrases_usedbody_part_focusuT   scripts/canary/g4_3_body_part_focus.py (all shots — 4 trigger alternation R1R2-B2))	timestampprompt_versionrolepinning	scene_setmetricsmeasurement_failuresmeasurement_scriptszscene entry is not dictscene_index_shot_indexs_shz: F
t2i_prompt)r=   
shot_indexr	   has_composite_idhas_reproduction_surface_vz: variation not a dictz': t2i_prompt missing, empty, or not str_z
pinning (sz ): not present in CP data.scenesz[g4_3_body_part_focus] role=z prompt_version=z
  total_shots=z   body_part_focus_pattern_count=z
  measurement_failures: zM
Exit criteria (STRICT): candidate.metrics.body_part_focus_pattern_count == 0),argparseArgumentParseradd_argumentstr
parse_argsr   r   configr   openjsonloadgetr.   printsysstderrexistsr
   r   r   r6   r7   listr   _emitr   set
isinstancedictappendintaddr   r	   _COMPOSITE_ID_REsearchlowerr   	enumeratestripBODY_PART_FOCUS_PATTERNfindalllen)!parserargsr8   cp_root_strfpcfgr.   cpr;   scenesoutpinning_set
seen_in_cpr0   body_part_focus_countr2   scene_set_outscenesishi
variationscf_flagcf_errrC   rD   varpplsurf
shot_countvipromptmatchess!                                    r   mainr   D   s   $$[F 
d;
c48
#tN  P
*tD
sK!+[ 9  ;
,3E
5CN
)TB
.S$ 4  6
,3E
8sDQ

t<D$T*GW"K{{$++##C'#:b))B-C ;ggi(llT::	;G>>CG9M::		'	"B&(!"&:;F~$"11II 12,.(,-C(D	 %9!#y$
" 	c4)'2K'*uJK/1 "M%& ''(ABYY}%ii&:GSXk1BS*+.ub#?ST
*51 ''!B4s3%r&(BC !#( Cc4(%A!S!#**1-'+$WWY5Dzz|r)370 6  	 ' 0(@
 	 	q
 ,GBc4($++t3se2bT)?@ WW\*Ffc*&,,.$++t3se2bT)PQ -55f=G#g,&J - 	+2< B4q/{ @ !	S9J& ''RDC5(HI ! !]--		"&-B(@$()?$@	
 !5u 
C$ 
#t	
&tyyk1A$BUBUAV W$%EF[E\ ]##&';#<"= >W	X ZZ yyK$9A$=m ;:s   V++V5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)N-F   T)ensure_asciiindent	sort_keys
)parentsexist_okwr+   r,   )
outputrN   dumprR   stdoutwriter   parentmkdirrM   )rl   rf   out_pathrh   s       r   rV   rV      s    {{c		#szza4P

$dT:]]3]1RIIc2E!tL 211s   CC
__main__)returnrJ   )r   r[   )rl   rY   rf   zargparse.Namespacer   None)'__doc__
__future__r   rG   rN   r   rR   r   pathlibr   pathinsertrJ   __file__resolver   _g4_3_commonr   r   r   r	   r
   r   r   r   _CONSTSr   r   join_TRIGGER_ALTcompile
IGNORECASErb   r]   r   r   rV   __name__exitr   r   r   <module>r      s   * #   	 
   3tH~--/667 8	 	 	 %
& !9: #$?@ 
 xxE.DEE$"**|>>MM  2::67 /pfM zCHHTV r   