
    i1                       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j6                  d      Zd	Zdd
ZddZddZ ddZ!e"dk(  r ejF                   e               yy)u  G4.3 canary — STRICT reproduction surface composite-ID counter (Task 4.3).

Detects composite IDs (`C##` OR `C##O##`) within ±50 chars window of any of 9
reproduction-surface keywords. Surface keywords are imported from
`render_prompt_card._ID_REPRODUCTION_SURFACES`.

Override R1R2-B3 carry: char-level ±50 window — NOT sentence boundary regex
(LLM punctuation is variable, sentence split would yield false negatives).
`feedback_no_regex_postprocessing.md` carry.

Override R4-M5 carry: dedup — if a single id occurrence (id_token + id_position
start) is within ±50 chars of multiple surface keywords, count violation
1× per (id_token, id_position[0]) pair.

Scope: ALL shots of pinned (si, shi) tuples (R3-I1).

Exit criteria (analyzer, OUT-OF-BAND):
  candidate.metrics.reproduction_surface_composite_id_count == 0  (STRICT)
  baseline value is informational only.

Usage:
    python scripts/canary/g4_3_reproduction_surface.py \
        --config canary_config.json \
        --cp-root data/projects/<pid>/checkpoints/scene_detail \
        --prompt-version 17.<timestamp> \
        --role candidate \
        --output results/reproduction_surface_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_REPRODUCTION_SURFACESz\bC\d{2}(?:O\d{2})?\b2   c                 L    t        j                         j                         dz   S )NZ)r   utcnow	isoformat     +scripts/canary/g4_3_reproduction_surface.pyiso8601_nowr   =   s    ??&&(3..r   c                   g }| j                         }t        D ]T  }|j                         }t        |      }d}	 |j                  ||      }|dk(  r9|j	                  |||z   |f       |dz   }5 g }t               }	t        j                  |       D ]  }
|
j                         }|
j                         }|
j                         }||f}g }|D ]6  \  }}}t        ||z
  ||z
        }|t        k  s#|j	                  |||f       8 |sw||	v r||	j                  |       |d   \  }}}|j	                  ||||g||g|dd D cg c]  }|d   	 c}d        |S c c}w )u  Return list of violation records.

    R4-M5 dedup: a single id occurrence (keyed by `(id_token, id_position[0])`)
    is counted at most once even if it falls within ±50 of multiple surface
    keywords. The chosen surface is the FIRST one whose window overlaps the
    id; remaining matches are recorded inside the violation under
    `additional_surfaces` (advisory) but contribute 0 to the counted total.

    char-level ±50 window proximity check (NO sentence regex — R1R2-B3 carry):
      id and surface are considered "near" iff the id span [id_start, id_end]
      and the surface span [s_start, s_end] are within `WINDOW_CHARS` of each
      other on the character axis, i.e.
        id_start - s_end <= WINDOW_CHARS  AND  s_start - id_end <= WINDOW_CHARS
    r      N   )id_tokensurfaceid_positionsurface_positionadditional_surfaces)lowerr   lenfindappendset	_ID_REGEXfinditerstartendgroupmaxWINDOW_CHARSadd)promptsurface_positionsplr   s_lowerslenr(   idx
violations	seen_keysmatchid_startid_endr   keynearbys_starts_endgapprimary_s_startprimary_s_endprimary_surfacess                          r   find_reproduction_violationsrB   A   s    57	B,--/7|'''5)Cby$$c3:w%?@!GE 	 -  J&)eI##F+;;=;;=" .0'8#GUG h&&(89Cl"ww78 (9 )c:@)7 &$f-!0- @28*#=*QAaD*#=
 	1 ,@  $>s   E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        |       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'        ||      }	|	<t)               |j*                  |j,                  |g dddi g d|d d!id"}
t/        |
|       yt1        |      }t3               }d}d}d}i }g }g }|	D ]B  }t5        |t6              s|j9                  d#       &|j                  d$      }|j                  d%      }||Mt;        |      t;        |      f|vrf|j=                  t;        |      t;        |      f       t?        ||||      }|tA        |      \  }}||j9                  d&| d'| d(|        d)}d)}|D ]}  }t5        |t6              s|j                  d*      }t5        |t              s6tB        jE                  |      rd}|jG                         }tH        D ]  }|jG                         |v sd} }  |j9                  |||||d+       |dz  }|r|dz  }d}tK        |      D ]  \  } }t5        |t6              s|j9                  d&| d'| d,|  d-       2|j                  d*      }!t5        |!t              r|!jM                         s|j9                  d&| d'| d,|  d.       tO        |!      }"|tQ        |"      z  }|"D ]  }#|j9                  ||| d/|#         ||z  }||| d0| <   E |D ]$  \  }}||f|vs|j9                  d1| d'| d2       & t)               |j*                  |j,                  |||||||d|d d!id"}
t/        |
|       t        d3|j,                   d4|j*                   d5| d6| d7| d8tQ        |       d9t        j                          |ry|j,                  dk(  r|dkD  ryy# 1 sw Y   	xY w):NuN   G4.3 canary: reproduction-surface composite-ID violation counter (±50 chars).)descriptionz--config)typedefaultz--pidz	--cp-rootz--prompt-versionT)rE   requiredz--role	candidatebaseline)rE   rF   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_rootz5[g4_3_reproduction_surface] ERROR: --cp-root required)rK   r   z8[g4_3_reproduction_surface] ERROR: --cp-root not found: r   )total_shotsshots_with_reproduction_surface'reproduction_surface_composite_id_count*reproduction_surface_composite_id_per_shotviolations_detailreproduction_surfaceul   scripts/canary/g4_3_reproduction_surface.py (all shots, ±50 chars window — R1R2-B3 / R3-I1 / R4-M5 dedup))	timestampprompt_versionrolepinning	scene_setmetricsmeasurement_failuresmeasurement_scriptszscene entry is not dictscene_index_shot_indexrA   _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)r`   rd   variation_index_z
pinning (sz ): not present in CP data.scenesz![g4_3_reproduction_surface] role=z prompt_version=z
  total_shots=z  shots_with_surface=z+
  reproduction_surface_composite_id_count=z
  measurement_failures: zW
Exit criteria (STRICT): candidate.metrics.reproduction_surface_composite_id_count == 0))argparseArgumentParseradd_argumentstr
parse_argsr   r   configr   openjsonloadgetrQ   printsysstderrexistsr
   r   r   rY   rZ   _emitr   r%   
isinstancedictr$   intr-   r   r	   r&   searchr!   r   	enumeratestriprB   r"   )$parserargsr[   cp_root_strfpcfgrQ   cpr^   scenesoutpinning_set
seen_in_cprR   shots_with_surfaceviolations_totalviolations_per_shotrV   scene_set_outscenesishi
variationscf_flagcf_errre   rf   varpr0   surf
shot_countvir.   v_violationsvs$                                       r   mainr      sP   $$dF 
d;
c48
#t<
*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E::	;G>>H	R::		'	"B&(!"&:;F~$"11II 34;<>@%' %9&W$
* 	c4)'2K'*uJK*,$& "M%& ''(ABYY}%ii&:GSXk1BS*+.ub#?ST
*51 ''!B4s3%r&(BC !#( Cc4(%A!S!##A&'+$WWY5Dzz|r)370 6  	 ' 0(@
 	 	q#!#
 ,GBc4($++t3se2bT)?@ WW\*Ffc*&,,.$++t3se2bT)PQ 7?L#l++J!!((#%"%')* 	*  " -* 	J&-7rd!C5M*O R !	S9J& ''RDC5(HI ! !]--		"&/A7G:M!2
 !5"S 
C, 
#t	
+DII;6FtGZGZF[ \$%:;M:N O55E4F G##&';#<"= >a	b
 ZZ yyK$4q$8U ;:s   V44V>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-Fr   T)ensure_asciiindent	sort_keys
)parentsexist_okwrN   rO   )
outputrq   dumpru   stdoutwriter   parentmkdirrp   )r   r   out_pathr   s       r   rx   rx   H  s    {{c		#szza4P

$dT:]]3]1RIIc2E!tL 211s   CC
__main__)returnrm   )r.   rm   r   z
list[dict])r   r{   )r   rz   r   zargparse.Namespacer   None)$__doc__
__future__r   rj   rq   reru   r   pathlibr   pathinsertrm   __file__resolver   _g4_3_commonr   r   r   r	   r
   r   r   r   _CONSTSr   compiler&   r,   r   rB   r   rx   __name__exitr   r   r   <module>r      s   8 #   	 
   3tH~--/667 8	 	 	 %
&#$?@  BJJ/0	/>BCLM zCHHTV r   