
    4j7                       d Z ddlmZ ddlZddlZddlmZmZ ddlmZm	Z	 ddl
mZ ddlmZ ddlmZmZ d	Zd
ZdZddZ G d dee      Zeg ef   ZddZ G d d      Ze G d d             ZddZddZded	 	 	 	 	 	 	 	 	 	 	 ddZy)u  utils/live_cron_state_verifier.py

task-2553+23 — CALLBACK_CANCEL_ON_SUCCESS_OPERATIONAL_INTEGRATION.

목적: 운영 collector 가 사전등록 fallback callback cron 을 실제로 제거하기
**직전**, 실 cron 상태를 live 조회하여 5조건 AND 교차검증한다. dispatch-fired
marker 의 ``fallback_cron_id`` 만으로 제거하지 않는다 — live 조회가 권위,
marker 는 교차입력(cross-input)일 뿐이다 (task-2553+23 §2, §4.2).

5조건 (전부 충족 시에만 remove 허용):
  ① live cron entry.task_id  == 처리 task_id
  ② live cron entry.chat_id  == 6937032012 (ANU 소유)
  ③ live cron entry.role     == "fallback"
  ④ dispatch-fired marker.fallback_callback_cron_id == target_cron_id
                                                    (== live entry.id)
  ⑤ 해당 cron 미발화(pending) AND 미제거

하나라도 불충족 / live 조회 실패 → SKIP(보존), remove 0.
idempotency: 이미 발화/제거된 fallback → no-op (이중제거 0, wrong id 제거 0).

설계 규율 (task-2553+9a 안전성 상속):
  * callback orchestrator(utils/anu_delegation_completion_callback.py) 무접촉
    — 본 모듈은 그것을 import/호출하지 않는다.
  * 본 모듈은 **판정만** 한다. cron 을 직접 제거하지 않는다(SRP — remove 는
    +9a remover 가, 호출 결선은 seam 이).
  * live 조회는 dependency-injected ``cron_lister``. 실 subprocess
    (`cokacdir --cron-list`) 는 운영 collector 만, 본 task 구현/테스트는
    fake/격리 only (§6 9-R.4 — 실 운영 cron 절대 비접촉).
  * fail-safe: live 상태를 5조건으로 **적극(positive) 확정**하지 못하면
    무조건 SKIP. "모르면 제거하지 않는다."
    )annotationsN)	dataclassfield)datetimetimezone)Enum)Path)CallableOptionall   L5: c119085addb0f8b7fallbackc                 f    t        j                  t        j                        j	                  d      S )Nz%Y-%m-%dT%H:%M:%SZ)r   nowr   utcstrftime     5/home/jay/workspace/utils/live_cron_state_verifier.py_now_utcr   0   s!    <<%../CDDr   c                  (    e Zd ZdZdZdZdZdZdZdZ	y)	LiveVerifyClassificationVERIFIEDSKIP_QUERY_FAILEDSKIP_NOT_FOUNDSKIP_ALREADY_REMOVEDSKIP_ALREADY_FIREDSKIP_MISMATCHSKIP_MARKER_ABSENTN)
__name__
__module____qualname__r   r   r   r   r   r   r   r   r   r   r   r   4   s)    H+%N1-#M-r   r   c                6   t        | t              r| j                  d      dk7  rdg | dS g }| j                  dg       xs g D ]N  }t        |t              st        |j                  dd            }d}d}|j	                  d	d
      j                         D ]  }|j                  d      s|j                  d      r3|j                  dd      d   j                         j                  d      }X|j                  d      sj|j                  dd      d   j                         j                  d      } |j                  |j                  d      |t        |t        |j                  dd            t        |j                  dd            d       Q d|| dS )u  실 `cokacdir --cron-list` 출력 → 정규화 entries.

    cokacdir cron-list 는 [{id,prompt,schedule,created_at},...] 를 돌려준다.
    task_id/role 은 prompt 내 embedded callback-policy 토큰에서 best-effort
    파싱한다. 확정 불가 필드는 None 으로 두어 verifier 가 보수적으로 SKIP
    하도록 한다(fail-safe). chat_id 는 chat-scoped 조회이므로 ANU_CHAT_ID.
    statusokerrorr#   entriesraw	schedulesprompt N
 ztask_id=ztask-id==   z,;zrole=idfiredFremoved)r0   task_idchat_idroler1   r2   )
isinstancedictgetstrreplacesplit
startswithstripappendANU_CHAT_IDbool)r(   r'   schr*   r3   r5   toks          r   normalize_cokacdir_schedulesrC   C   sq    c4 CGGH$5$=!b==Gww{B'-2 
#t$SWWXr*+>>$,224 	@C~~j)S^^J-G))C+A.446<<TB(yya(+11399$?		@
 	ggdm"&cgggu56	5 9:		

* ws;;r   c                      e Zd ZdZdZddZy)RealCokacdirCronListeru\  실 `cokacdir --cron-list` wrapper (운영 collector 전용 기본값).

    본 task 의 regression 은 fake lister 를 주입하므로 이 클래스의 subprocess
    경로는 테스트에서 절대 실행되지 않는다(§6 9-R.4). 조회 실패/파싱불가는
    status=error 로 반환되어 verifier 가 SKIP(보존)하도록 한다.
    z/usr/local/bin/cokacdirc                h   	 t        j                  | j                  ddt        t              dt
        gddd      }t        j                  |j                  j                         xs d      }t        |      S # t        t        t         j                  f$ r}dg dt        |      id	cY d }~S d }~ww xY w)
Nz--cron-listz--chatz--keyT<   )capture_outputtexttimeoutz{}r%   r&   )
subprocessrunbinaryr9   r?   ANU_KEYjsonloadsstdoutr=   OSError
ValueErrorSubprocessErrorrC   )selfprocpayloadexcs       r   __call__zRealCokacdirCronLister.__call__p   s    	R>>KK!$  $D jj!2!2!4!<=G ,G44 Z%?%?@ 	R%"gs3x=PQQ	Rs   A+A8 8B1B,&B1,B1Nreturnr7   )r   r    r!   __doc__rM   rY   r   r   r   rE   rE   f   s     'F5r   rE   c                      e Zd ZU ded<   ded<   ded<   ded<    ee      Zd	ed
<   dZded<   dZded<   dZ	ded<    ee
      Zded<   dZded<   ddZy)LiveCronStateVerificationr   classificationr9   r3   target_cron_idr@   remove_allowed)default_factoryr7   checksNOptional[dict]
live_entryOptional[str]marker_fallback_cron_idr+   skip_reasonlistnotests_utcc                    d| j                   | j                  | j                  j                  | j                  | j
                  | j                  | j                  | j                  | j                  | j                  dS )Nlive_cron_state_verification_v1)schemar3   r`   r_   ra   rc   re   rg   rh   rj   rk   )r3   r`   r_   valuera   rc   re   rg   rh   rj   rk   )rU   s    r   to_dictz!LiveCronStateVerification.to_dict   se    7||"11"1177"11kk//'+'C'C++ZZkk
 	
r   rZ   )r   r    r!   __annotations__r   r7   rc   re   rg   rh   ri   rj   rk   rp   r   r   r   r^   r^      se    ,,L.FD.!%J%-1]1K-E4-FC
r   r^   c                    	 t        j                  | j                  d            S # t        t         j                  f$ r Y y w xY w)Nzutf-8)encoding)rO   rP   	read_textrR   JSONDecodeError)paths    r   
_read_jsonrw      s>    zz$..'.:;;T))* s   $' AAc                    | j                  d      }t        |t              sy |j                  d      }t        |t              r|r|S d S )Ncallback_policy_afallback_callback_cron_id)r8   r6   r7   r9   )markerpolicycids      r   _marker_fallback_cron_idr~      sC    ZZ+,Ffd#
**0
1CS#&338D8r   )cron_listernow_fnc                    |       }ddddddd}t        |      }| t        t        j                  | d|d|      S t	        |      }|xs
 t               }		  |	       }
t        |
t              r|
j                  d      d	k7  r!t        t        j                  | d||d
|      S |
j                  d      xs g }t        fd|D        d      }|$t        t        j                  | d|d|ddg|
      S |j                  d      r$t        t        j                  | d|||ddg|
      S |j                  d      r$t        t        j                  | d|||ddg|
      S |j                  d      | k(  |d<   |j                  d      t        k(  |d<   |j                  d      t        k(  |d<   t!        |xr- t        |t"              xr |k(  xr |j                  d      k(        |d<   |j                  d       xr |j                  d       |d<   t%        |d   |d   |d   |d   |d   f      |d<   |d   r$t        t        j&                  | d |||d!d"g|
      S d#D cg c]	  }||   s| }}t        t        j(                  | d|||d$d%j+                  |      z   d&g|
      S # t        $ r.}t        t        j                  | d||d| |      cY d}~S d}~ww xY wc c}w )'u  실 cron-remove 직전 live cron-state 5조건 AND 교차검증.

    live 조회 결과가 권위. marker 의 fallback_cron_id 는 교차입력(④)일 뿐
    단독 권위가 아니다. 5조건 전부 positive 확정 시에만 remove_allowed=True.
    그 외 모든 경로(조회실패·부재·발화·제거·불일치·marker부재)는 SKIP
    (remove_allowed=False) → fallback 보존, 실 remove 0.
    F)c1_task_id_matchc2_chat_id_ownedc3_role_fallbackc4_marker_id_crosscheck c5_pending_not_fired_not_removedall_satisfiedNuG   dispatch-fired marker 부재/파싱불가 → 교차입력 없음, SKIP)r_   r3   r`   ra   rc   rh   rk   u*   live cron 조회 예외 → SKIP(보존): )r_   r3   r`   ra   rc   rg   rh   rk   r#   r$   u9   live cron 조회 실패/비정상 응답 → SKIP(보존)r'   c              3  n   K   | ],  }t        |t              s|j                  d       k(  s)| . yw)r0   N)r6   r7   r8   ).0er`   s     r   	<genexpr>z)verify_live_cron_state.<locals>.<genexpr>   s)     Uqz!T2quuT{n7TUs   555u^   live 목록에 대상 cron 부재 → 이미 제거/만료, idempotent no-op (이중 remove 0)u3   idempotent: already-removed 분기 — 실 remove 0)
r_   r3   r`   ra   rc   re   rg   rh   rj   rk   r1   ud   live cron 이미 발화(fired) → idempotent no-op, 기존 DUPLICATE_CALLBACK_IGNORED 경로 유지u1   idempotent: already-fired 분기 — 실 remove 0r2   uC   live cron entry.removed=true → idempotent no-op (이중 remove 0)u=   idempotent: already-removed 플래그 분기 — 실 remove 0r3   r   r4   r   r5   r   r0   r   r   r   Tr+   uF   5조건 AND 전부 충족 — live 조회 권위 확정, remove 허용)r   r   r   r   r   uB   live cron-state 5조건 미충족 → SKIP(보존), 실 remove 0: ,u4   mismatch — 추정 remove 0, wrong cron id 제거 0)rw   r^   r   r   r~   rE   	Exceptionr   r6   r7   r8   nextr   r   r?   FALLBACK_ROLEr@   r9   allr   r   join)r3   r`   dispatch_fired_marker_pathr   r   tsrc   r{   marker_cron_idlisterlistedrX   r'   entrykfaileds    `              r   verify_live_cron_stater      s    
B!!!#(,1F 23F~(3FF) a
 	
 .f5N 424F
 fd#vzz(';t'C(3EE) $2S	
 		
 jj#)rGUGUE }(3HH) $2$ II
 	
" yy(3FF) $2; GG
 	
" yy(3HH) $2]RS
 	
 "'9!5!@F!&9!5!DF!&6!2m!CF(, 	.~s+	.n,	. IIdO~-	)F$% IIg;uyy';#; -. "%&%&%&,-56	
F? o(3<<)$2X 
 	
"

 ay 	

F 
 %/==% .Phhv FF E  

(3EE) $2DSEJ	
 		


n
s$   J' !K!'	K0#KKK)r[   r9   )r(   r7   r[   r7   )rv   r	   r[   rd   )r{   r7   r[   rf   )r3   r9   r`   r9   r   r	   r   zOptional[CronLister]r   zCallable[[], str]r[   r^   )r\   
__future__r   rO   rK   dataclassesr   r   r   r   enumr   pathlibr	   typingr
   r   r?   rN   r   r   r9   r   r7   
CronListerrC   rE   r^   rw   r~   r   r   r   r   <module>r      s   > #   ( '   % 
E.sD . b$h
 <F5 5> 
 
 
89 )- ({{ { !%	{
 &{ { {r   