
    !i6                       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 ddl	m
Z
mZmZmZmZ  ee      j!                         j"                  d   Zedz  Z ee      ej*                  vr"ej*                  j-                  d ee             dd	Zddd
ZddZddZddZddZddZddZedk(  r ej@                   e              yy)u  G4.2 canary — owned-violation cumulative count (G3.2 sentinel CP read).

Reads pre-computed `t2i_variations[*].owned_validation` sentinel objects from
the scene_detail checkpoint. Each sentinel `violations` list length is summed
per shot (and per scene_set). NO LLM calls. Uses the strict shape validator
from `app.core.steps._owned_helpers.assert_owned_sentinel_shape` (plan-R2-I5).

Exit criteria (analyzer):
  candidate.metrics.owned_violations_total <= baseline.metrics.owned_violations_total
  AND both runs' measurement_failures lists are empty.

Usage (config mode):
    python scripts/canary/g4_2_owned_violations.py \
        --config canary_config.json --role candidate \
        --output results/owned_violations_candidate.json
    )annotationsN)datetime)Path)AnyDictListOptionalTuple   backendc                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     'scripts/canary/g4_2_owned_violations.pyiso8601_nowr   #   s    ??&&(3..r   c                j    t        d|  t        j                         t        j                  |       y )Nz[g4_2_owned_violations] ERROR: file)printsysstderrexit)msgcodes     r   _failr   '   s#    	+C5
1

CHHTNr   c           	        | j                   rt        | j                         }|j                         st        d|        	 |j	                  dd      5 }t        j                  |      }d d d        j                  d      |j                  d      |j                  d      |j                  d	      |j                  d
      |j                  d      |j                  d      dS 	 | j                  rt        j                  | j                        nd }| j                  rt        j                  | j                        nd }| j                  | j                  | j                  | j                  | j                   dS # 1 sw Y   xY w# t        $ r}t        d|        Y d }~4d }~ww xY w# t        $ r}t        d|        Y d }~d }~ww xY w)Nz--config file not found: rutf-8encodingz--config parse error: pidscene_index_listshot_index_list_per_scenemodel_routingprompt_source_modecard_commit_hashchain_bg_card_snapshot_hashr$   r%   r&   r'   r(   r)   r*   z%failed to parse list/dict JSON args: )configr   existsr   openjsonload	Exceptiongetr%   loadsr&   r$   r'   r(   r)   r*   )argscfg_pathfpcfgexcr%   r&   s          r   load_pinningr9   ,   s   {{$ -hZ89	2sW5iim 6
 775> #(: ;),1L)M WW_5"%''*>"? #(: ;+.773P+Q
 	
	=151F1FDJJt,,-D 	
 -- JJt55637 	" xx,%>++"55 11'+'G'G / 65 	2*3%011	2&  =5cU;<<=sI   F	 E<*F	 &AF. <FF	 		F+F&&F+.	G7G

Gc                    g d}|D cg c]  }| j                  |      dv s| }}|rt        d| d       t        | d   t              st        d       t        | d   t              st        d       y y c c}w )	Nr+   )N z#pinning missing required field(s): zF. Provide via --config <json> or individual flags. NO silent defaults.r%   z*pinning.scene_index_list must be list[int]r&   z>pinning.shot_index_list_per_scene must be dict[str, list[int]])r2   r   
isinstancelistdict)pinningrequiredkmissings       r   validate_pinningrC   T   s    H
 #C(Qgkk!n
&Bq(GC1' ;S T	
 g0148:;g9:DANO B Ds
   A7A7c           	        g }| d   }| d   D ]  }t        |      }|j                  t        |            }||j                  |      }|t        d| d       t	        |t
              s%t        d| dt        |      j                   d       |st        d| d       |D ]  }|j                  |t        |      f          |S )	u(  Cartesian (scene_index, shot_index) tuples from pinning block.

    Strict — pinning enumerates EXACT (si, shi) tuples. A scene_index without
    a corresponding shot_index_list entry (or with a non-list / empty list)
    is a contract violation, not a silent default. fail-fast via _fail.
    r&   r%   z(pinning.scene_index_list contains scene zF but shot_index_list_per_scene has no entry for it. NO silent default.z"pinning.shot_index_list_per_scene[z] must be list[int], got z. NO silent default.uR   ] is empty — a pinned scene must enumerate at least one shot. NO silent default.)	intr2   strr   r<   r=   type__name__append)r?   outshot_mapsisi_int	raw_shotsshis          r   build_pinning_scene_setrP   f   s     "$C23H()RLLV-	 V,I:6( CT U )T*4VH =I//00DF 4VH =V W CJJC)* ) *, Jr   c                   | dz  }|j                         st        d|        	 |j                  dd      5 }t        j                  |      cd d d        S # 1 sw Y   y xY w# t
        $ r}t        d|        Y d }~y d }~ww xY w)Nzmanifest.jsonzmanifest.json not found at r    r!   r"   zmanifest.json parse error: )r-   r   r.   r/   r0   r1   )cp_rootmanifest_pathr6   r8   s       r   load_cp_manifestrT      s{    o-M!+M?;<3g6"99R= 766 3+C51223s4   A# A	A# A A#  A# #	B,A??Bc                    | j                  d      }t        |t              sy|j                  d      }t        |t              sy|j                  d      dk(  ry|j                  d      d	u ryy
)u  Return (cf_flag, error_message).

    Reads from `scene["render_prompt_card"]["background_binding"]` — the actual
    CP shape produced by detail_steps.py (scene["background_binding"] does NOT
    exist as a flat field). When the scene record is missing the
    `render_prompt_card` envelope or its `background_binding` field, returns
    (False, reason) so the caller can append to measurement_failures
    (NO silent absorb — Codex C1 fix).

    error_message is None on success.
    render_prompt_card)Fz&render_prompt_card missing or not dictbackground_binding)Fz9render_prompt_card.background_binding missing or not dictmodeskipped_close_framing)TN"close_framing_skips_background_refT)FN)r2   r<   r>   )scenerpcbbs      r   is_close_framingr^      sh     ))(
)Cc4 @	%	&Bb$S	vvf~00	vv23t;r   c                    t        j                  d      } | j                  dt        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dlm} d }|j                  rUt        |j                        j                  dd      5 }t        j                  |      }d d d        j!                  d       }|s|j"                  }|st        d!       t        |      }|j%                         st        d"|        t'        |      }	|	j!                  d#      }
t)        |
t*              st        d$       |
j!                  d%      }t)        |t,              st        d&       t/        t1        |            }t/               }d}i }g }g }d}|D ]_  }t)        |t*              s|j!                  d'      }|j!                  d(      }||<||f|vrC|j3                  t5        |      t5        |      f       |d)z  }t7        |      \  }}|r|j9                  d*| d+| d,|        |j9                  |||d-       |j!                  d.      }t)        |t,              r|s|j9                  d*| d+| d/       d}|D ]b  }|j!                  d0      }||j9                  d*| d+| d1       /	  |d2| d+| 3       |j!                  d5      xs g }|t=        |      z  }d ||z  }||| d6| <   b |D ]$  \  }}||f|vs|j9                  d7| d+| d8       & t?               |j@                  |jB                  |||||d9|d:d;id<}|jD                  d=k(  rHt        jF                  |tH        jJ                  d>d?d@       tH        jJ                  jM                  dA       nht        |jD                        }|jN                  jQ                  ddB       |j                  dCd      5 }t        jF                  ||d>d?d@       d d d        tS        dD|jB                   dE|j@                   dF| dG| dHt=        |       dItH        jT                  J       |ry)y# t        $ r}t        d| d       Y d }~d }~ww xY w# 1 sw Y   xY w# t:        $ r%}|j9                  d*| d+| d4|        Y d }~4d }~ww xY w# 1 sw Y   xY w)KNzFG4.2 canary: owned-violation cumulative count (G3.2 sentinel CP read).)descriptionz--config)rG   defaultz--pidz	--episodez	--cp-rootz/Directory containing scene_detail manifest.json)rG   ra   helpz--prompt-versionT)rG   r@   z--role	candidatebaseline)rG   ra   choicesz--scene-index-listz--shot-index-list-per-scenez--model-routingz--prompt-source-mode)Nr   dbz--card-commit-hashz--chain-bg-card-snapshot-hashz--outputr   )assert_owned_sentinel_shapezHCannot import app.core.steps._owned_helpers (backend not on sys.path?): z6. Run from repo root or set PYTHONPATH=<repo>/backend.r    r!   r"   rR   z)--cp-root (or config.cp_root) is requiredz--cp-root path does not exist: dataz!cp['data'] is missing or not dictscenesz+cp['data']['scenes'] is missing or not listscene_index_shot_index   s_shz: )rj   
shot_indexr^   t2i_variationsz,: t2i_variations missing, empty, or not listowned_validationu"   : owned_validation sentinel 부재zcanary.owned_violations s)whereu   : sentinel shape 위반 — 
violations_z
pinning (sz ): not present in CP data.scenes)total_shotsowned_violations_totalowned_violations_per_shotowned_violationsuN   scripts/canary/g4_2_owned_violations.py (G3.2 sentinel CP read — plan-R1-B2))	timestampprompt_versionroler?   	scene_setmetricsmeasurement_failuresmeasurement_scripts-Fr   )ensure_asciiindent	sort_keys
)parentsexist_okwz[g4_2_owned_violations] role=z prompt_version=z
  total_shots=z  owned_violations_total=z
  measurement_failures: z
Exit criteria:
  candidate.metrics.owned_violations_total <= baseline.metrics.owned_violations_total
  AND both runs' measurement_failures must be empty.r   )+argparseArgumentParseradd_argumentrF   
parse_argsr9   rC   app.core.steps._owned_helpersrg   ModuleNotFoundErrorr   r,   r   r.   r/   r0   r2   rR   r-   rT   r<   r>   r=   setrP   addrE   r^   rI   r1   lenr   rz   r{   outputdumpr   stdoutwriteparentmkdirr   r   )parserr4   r?   rg   r8   cp_root_strr6   r7   rR   cprh   ri   pinning_scene_set
seen_in_cprv   rw   r~   scene_set_outru   r[   rL   rO   cfcf_err
variations
shot_countvarsentinelrs   rJ   out_paths                                  r   mainr      s&   $$\F 
d;
c48
#t<
#tN  P
*tD
sK!+[ 9  ;
,3E
5CN
)TB
.S$ 4  6
,3E
7c4P

t<D4 GW
M #K{{$++##C'#:b))B-C ;ggi(ll9:;G>>/y9:	'	"B 66&>DdD!12XXhFfd#;<3G<='*uJ02&(*,MK%&YY}%ii&:9--BS*+q%e,
F ''B4s3%r&* 	Sb
 	 YY/0
*d+: ''B4s3%KL 
Cww12H$++t3se#EF +&?t3se$L "l39rJ#j/)J# $ 	*,3=!RD#-0c l '	S9J& ''RDC5(HI ' !]--		"&&<)B

 !5 p 
C" {{c		#szza4P

$dT:]]3]1RIIc2E!tL 2 

'		{2B4CVCVBW X$%>?U>V W##&';#<"= >?	@ ZZ Y  
VWZV[ \C D	
 	

 ;:P  $++t3se#?uE 	T 21sH   =U; 7V!V.W;	VVV!V+.	W7WWW(__main__)returnrF   )rl   )r   rF   r   rE   r   None)r4   zargparse.Namespacer   Dict[str, Any])r?   r   r   r   )r?   r   r   zList[Tuple[int, int]])rR   r   r   r   )r[   r   r   zTuple[bool, Optional[str]])r   rE   )!__doc__
__future__r   r   r/   r   r   pathlibr   typingr   r   r   r	   r
   __file__resolver   
_REPO_ROOT_BACKEND_PATHrF   pathinsertr   r   r9   rC   rP   rT   r^   r   rH   r   r   r   r   <module>r      s     #   
   3 3 (^##%--a0
Y&}SXX%HHOOAs=)*/
%PP$D32hV zCHHTV r   