
     j                         d 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  e ee      j                         j                  j                        Zeej"                  vrej"                  j%                  de       ddlmZmZmZmZmZ ddlmZmZmZ dZd	Zd
Ze G d d             Z de de!de fdZ"de de fdZ#de de fdZ$de de fdZ%de de fdZ&de de fdZ'de de fdZ(edde)de)de)de!fdZ*ddddddede)ded ede d!e
e	eef      d"e
e   d#e
e)   d$e
e   de	eef   fd%Z+d&ed'ed(e)de deeef   f
d)Z,d-d*e
e-   de)fd+Z.e/d,k(  r ej`                   e.              yy).u  scripts/ci_watch_handoff_runner.py — CI_WATCH_HANDOFF progress watcher runner.

task-2729 Phase 1 — progress watcher dispatch gate (watcher 골격).

회장 목표:
  dispatch 직후 등록되는 progress watcher 의 러너 골격.
  6 상태(head change · non-force push · CI · finish-task .done · normal callback ·
  fallback prune) 추적 + review-settle quiet-window 골격 + terminal 도달 시
  ANU normal callback 발사를 담당한다.

  ★ ACTIVE=false (Phase 1):
    본 러너는 --dry-run/--once 모드에서 네트워크 호출(gh 폴링) 없이 import-safe 하게
    동작하는 골격이다. 실제 live gh polling 루프는 골격만 두고 미실행한다
    (watcher 위임 골격). production ACTIVE 전환은 별도 회장 승인 대상.

  ★ fallback 은 progress trigger 아님:
    fallback_prune 추적 상태는 dead-man safety-net 의 관측 항목일 뿐이며
    progress trigger 가 아니다. progress 판단은 watcher 의 6 상태/terminal classify 가
    담당한다.

기존 classifier(utils.pr_watcher_terminal_state_classifier) 를 재사용하며
raw ANU 키 문자열을 절대 작성하지 않는다 (anu_key=None → registrar 가 env→default 해결).
    N)	dataclass)Path)AnyDictOptionalTuple)
PRSnapshotclassifyTERMINAL_STATESbuild_callback_enveloperegister_terminal_callback)WATCHER_TRACKED_STATES watcher_terminal_callback_statusall_states_trackedx      i  c                       e Zd ZU dZdZeed<   dZeed<   dZeed<   dZ	eed<   dZ
eed<   dZeed<   d	Zeed
<   d	Zeed<   deeef   fdZdefdZy)WatcherStateu.   watcher 6 상태 추적 + poll/elapsed 메타.Fhead_changenon_force_pushcifinish_donenormal_callbackfallback_pruner   pollselapsed_secreturnc                     | j                   | j                  | j                  | j                  | j                  | j
                  dS )u+   WATCHER_TRACKED_STATES 키 → bool 매핑.r   r   r   r   r   r   r   selfs    "scripts/ci_watch_handoff_runner.pyas_tracked_dictzWatcherState.as_tracked_dictI   sB      ++"11''++#33"11
 	
    c                 4    t        | j                               S )u;   6 상태가 모두 True 인지 (all_states_tracked 사용).)r   r#   r    s    r"   all_trackedzWatcherState.all_trackedT   s    !$"6"6"899r$   N)__name__
__module____qualname____doc__r   bool__annotations__r   r   r   r   r   r   intr   r   strr#   r&    r$   r"   r   r   <   st    8K ND BK!OT! ND E3NK	
c4i 	
:T :r$   r   stateflagsr   c                 t    |j                         D ]$  \  }}|t        v st        | |t        |             & | S )u7   6 상태 갱신 (WATCHER_TRACKED_STATES 키만 허용).)itemsr   setattrr+   )r0   r1   keyvalues       r"   update_stater7   Y   s;    kkm -
U((E3U,- Lr$   c                     d| _         | S NT)r   r0   s    r"   mark_head_changer;   a       ELr$   c                     d| _         | S r9   )r   r:   s    r"   mark_non_force_pushr>   f       ELr$   c                     d| _         | S r9   )r   r:   s    r"   mark_cirA   k   s    EHLr$   c                     d| _         | S r9   )r   r:   s    r"   mark_finish_donerC   p   r<   r$   c                     d| _         | S r9   )r   r:   s    r"   mark_normal_callbackrE   u   s     ELr$   c                     d| _         | S r9   )r   r:   s    r"   mark_fallback_prunerG   z   r?   r$   )quiet_window_seclast_event_secnow_secrH   c                     || z
  |k\  S )u   review-settle quiet-window 판정 골격.

    마지막 이벤트 이후 quiet_window_sec 이상 무이벤트면 settled.
    r/   )rI   rJ   rH   s      r"   is_quiet_window_settledrL      s     n$)999r$   )last_snapshotanu_keychat_idrunnertask_id	pr_numberterminal_statereasonrM   rN   rO   rP   c        	   	          t        | ||||j                  |j                  |      }	t        |	|||      }
|
j                  rd|_        |
j                  |t        d|
j                        dS )u  terminal 도달 시 ANU normal callback 발사 (classifier 재사용).

    - build_callback_envelope 로 envelope 생성 → register_terminal_callback 호출.
    - ★ anu_key=None 이면 registrar 가 env→ANU_KEY_DEFAULT 로 해결 (self-key 차단은
      그쪽 책임). raw 키 문자열 절대 작성 금지.
    - result.fired=False 면 callback_status == WATCHER_TERMINAL_CALLBACK_NOT_WIRED.
    )rQ   rR   rS   rT   polls_completedr   rM   )enveloperN   rO   rP   T)terminal_reachedcallback_fired)firedrS   callback_status)r   r   r   r   rZ   r   r   )rQ   rR   rS   rT   r0   rM   rN   rO   rP   rW   results              r"   fire_terminal_callbackr]      s|    & '%%%#H (	F || $(;!!<<
 r$   snapexpected_headelapsed_watcher_secc                    |xj                   dz  c_         ||_        t        | ||      \  }}|r|t        v r||fS d|fS )u   단일 classify 평가 → (terminal_state_or_empty, reason).

    terminal 이면 반환 state 는 TERMINAL_STATES 에 포함된다.
       )r`   r_    )r   r   r
   r   )r^   r_   r`   r0   rS   rT   s         r"   run_oncerd      sS     
KK1K+E%/#NF
 .O;v%%v:r$   argvc                 l   t        j                  dd      }|j                  ddd       |j                  dt        d	d
       |j                  ddd       |j                  ddd       |j                  ddd       |j	                  |       }t               }|j                  s|j                  ry	y	)u   argparse 엔트리.

    --dry-run/--once 모드는 실제 gh 폴링 없이 import-safe 하게 동작(네트워크 호출 없이
    0 반환). 실제 live 폴링 루프는 골격만 둔다.
    ci_watch_handoff_runnerzItask-2729 Phase 1 CI_WATCH_HANDOFF progress watcher runner (ACTIVE=false))progdescriptionz	--task-idrc   ztask id (e.g. 2729))defaulthelpz--prr   z	PR number)typerj   rk   z--expected-headzexpected head oidz--once
store_trueu(   단일 평가 후 종료 (폴링 없음))actionrk   z	--dry-runu.   네트워크 호출 없이 import-safe dry-run)argparseArgumentParseradd_argumentr-   
parse_argsr   dry_runonce)re   parserargsr0   s       r"   mainrw      s     $$&_F R6KL
S!+F
)2<OP
<fg
L?opT"DNE||tyy  r$   __main__)N)1r*   ro   sysdataclassesr   pathlibr   typingr   r   r   r   r.   __file__resolveparent
_REPO_ROOTpathinsert*utils.pr_watcher_terminal_state_classifierr	   r
   r   r   r   dispatch.progress_watcher_gater   r   r   REVIEW_SETTLE_QUIET_WINDOW_SECDEFAULT_POLL_INTERVAL_SECDEFAULT_MAX_WATCH_SECr   r+   r7   r;   r>   rA   rC   rE   rG   r-   rL   r]   rd   listrw   r'   exitr/   r$   r"   <module>r      si  .  
 !  - -
 h'')00778
SXXHHOOAz"   "%    : : :8 t  L \ 
|  
< L 
L \ 
  
|   ;	
:
: 
: 	
:
 

:( /3!! ++ + 	+
 + + DcN++ c]+ c]+ SM+ 
#s(^+\
  	
  38_.x~  @ zCHHTV r$   