
    G,>ja                       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mZmZmZ ddlmZmZmZ ddlmZmZ d	Zd
ZdZdZdZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%dZ&ee fZ'e G d d             Z(efdd	 	 	 	 	 d*dZ)d+dZ*d,dZ+d-d Z,d.d!Z-d/d"Z.d#d#d#d$edddd e/e      ddd$d$d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d0d&Z0efddddddd$d$d'	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d1d(Z1g d)Z2y)2u  dispatch.anu_callback_launch_audit — task-2731 finish-callback 강제 결선.

ANCHOR (task-2731 / finish_callback_enforcement_followup_task_packet 축5):
  - executor 가 callback 안 쏘고 죽어도 OS runner/collector 의 deterministic
    closeout 경로가 **ANU-owned callback launch 를 코드로 강제**한다(executor
    세션 생존 무관 · dev bot prompt 의존 0 · finish-task 끝단 단독 의존 0).
  - callback = **ANU key 강제, self-key launch 거부**. launch audit jsonl 기록.
  - result.json 1건당 callback 1회 idempotent(audit jsonl dedupe + runner ledger
    dedupe + fresh-closeout-only 의 3중 dedupe 중 본 모듈은 audit dedupe 담당).

★ raw key 0: owner_key / ANU key / argv literal 을 audit jsonl 에 **절대 기록하지
  않는다**. owner_key_class(ANU/SELF/FOREIGN) 분류 라벨만 담는다.

★ 자가발사 0: 본 모듈은 ANU-owned callback launch **request(argv 데이터)** 를
  코드로 강제 빌드하고 audit 를 남길 뿐, 실 cron subprocess 를 직접 발사하지
  않는다(``anu_runner_pickup_and_fire`` 재사용 — 그 함수도 surface-only). 실 발사는
  권한 있는 ANU 세션이 ACTIVE flag 활성 + ANU key 로 수행한다(activation 별도 승인).

ACTIVE=false 기본: ``memory/state/p0b_callback_launch_enabled`` flag 가 "enabled"
  가 아니면 **전면 no-op**(audit 미기록 · launch 미빌드).

Layer A / NO-CRON: cron register/remove 0, subprocess 직접 0, merge 0, git/gh 0.
    )annotationsN)	dataclassfield)datetimetimezone)CallableListOptionalSequence)ANU_KEYCANONICAL_ROOTanu_runner_pickup_and_fire)DEFAULT_ANU_KEYS
is_anu_keyz%dispatch.anu_callback_launch_audit.v1z(memory/state/p0b_callback_launch_enabledenabledz,memory/p0b_state/callback_launch_audit.jsonlLAUNCH_ENFORCEDSELF_KEY_REFUSEDSKIP_DEDUPENOOP_DISABLEDNO_RESULT_JSONFAIL
REAL_FIREDREAL_FIRE_BLOCKEDREAL_FIRE_FAILEDz<ANU_SEALED_KEY>ANUSELFFOREIGNc                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   ded
<   dZded<   d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Zded<   edd       ZddZy)CallbackLaunchResultuQ   launch_callback_for_closeout 반환값. argv/raw key 미포함(분류 라벨만).strschemaverdicttask_idsha256owner_key_classboolsuccessexecutor_session_independent schedule_id	launch_tsNOptional[str]
audit_pathFaudit_writtenterminal_intent)default_factory	List[str]reasonsOptional[List[str]]argvreal_fire_attemptedc                <    | j                   t        t        t        fv S N)r"   r   LAUNCH_SKIP_DEDUPELAUNCH_REAL_FIREDselfs    9/home/jay/workspace/dispatch/anu_callback_launch_audit.pyokzCallbackLaunchResult.okf   s     ||1CEVWWW    c                0   | j                   | j                  | j                  | j                  | j                  | j
                  | j                  t        | j                        t        | j                        | j                  t        | j                        dS )uI   audit jsonl 한 줄 payload. raw key/argv literal 0 — 분류 라벨만.)r!   r"   r#   r$   r%   r+   r*   r'   r(   r/   r2   )r!   r"   r#   r$   r%   r+   r*   r&   r'   r(   r/   listr2   r:   s    r<   to_audit_jsonz"CallbackLaunchResult.to_audit_jsonk   sr     kk||||kk#33++DLL),01R1R,S#33DLL)
 	
r>   )returnr&   )rB   dict)__name__
__module____qualname____doc____annotations__r*   r+   r-   r.   r/   r   r@   r2   r4   r5   propertyr=   rA    r>   r<   r   r   Q   s    [KLLKM"&&KIs $J$M4OSt4GY4 $D
$ %%X X
r>   r   flag_readerc                  |P	  |       }|yt        |      }|j                         r(|j                         d   j                         t        k(  S dS t
        j                  j                  | t              }	 t        |dd      5 }|j                         }ddd       j                         t        k(  S # t         $ r Y yw xY w# 1 sw Y   /xY w# t        t        f$ r Y yw xY w)u   callback launch ACTIVE flag 첫 줄 trim == "enabled" 일 때만 True.
    부재/읽기실패/그 외 값 → False (fail-closed · 전면 no-op). flag_reader: 테스트 주입.NFr   rutf-8encoding)	Exceptionr    strip
splitlinesCALLBACK_LAUNCH_ENABLEDospathjoinCALLBACK_LAUNCH_FLAG_RELopenreadlineOSError
ValueError)rootrL   valval_str	flag_pathfhfirsts          r<   read_callback_launch_enabledrd   }   s    
 	-C ;c(MT]]_w!!#A&,,.2IIgbggT#;<I)S73 	"rKKME	" ;;=333  			" 	"Z  s;   B6 8C CC 6	CCC
C C#"C#c                   | rt         j                  j                  |       sy	 t        | dd      5 }|D ]  }|j	                         }|s	 t        j                  |      }t        |t              s=|j                  d      t        v sU|j                  d      |k(  sj|j                  d      |k(  s ddd       y	 	 ddd       y# t        t        f$ r Y w xY w# 1 sw Y   yxY w# t        $ r Y yw xY w)
u   audit jsonl 에 동일 (task_id, sha256) 의 성공 launch 기록 존재 여부.
    1 result(=task_id+sha256) → callback 1회 idempotency 의 audit-layer dedupe.FrN   rO   rP   r"   r#   r$   NT)rV   rW   isfilerZ   rS   jsonloadsr]   	TypeError
isinstancerC   get_AUDIT_DEDUPE_VERDICTSr\   )r-   r#   r$   rb   lineentrys         r<   audit_dedupe_hitro      s     RWW^^J7*cG4 	   zz| JJt,E ud+		),0FF		),7		(+v5	  	  	 $  #I. 	 $   sv   C* CC	!C2C
CC4C5C* >C C* 	CCCCC'#C* 'C* *	C65C6c                   	 t        j                  t         j                  j                  |      xs dd       t	        |dd      5 }|j                  t        j                  | j                         d      d	z          |j                          t        j                  |j                                d
d
d
       y# 1 sw Y   yxY w# t        $ r Y yw xY w)u   CallbackLaunchResult.to_audit_json() 한 줄 append(fsync). 성공 True.
    실패는 비치명(fail-safe) — False 반환(launch 자체는 이미 코드강제됨)..T)exist_okarO   rP   F)ensure_ascii
N)rV   makedirsrW   dirnamerZ   writerg   dumpsrA   flushfsyncfilenor\   )resultr-   rb   s      r<   _append_auditr~      s    
BGGOOJ/63F*cG4 	"HHTZZ 4 4 6UKdRSHHJHHRYY[!	" 		"  s1   AC A+B<3C <CC C 	CCc                2    t        | |      rt        S t        S )u   executor_key 의 owner_key_class 라벨(raw key 0).
    - executor_key 가 ANU key 면 SELF(self-key launch 시도 → 거부 대상).
    - 비어있거나 ANU key 가 아니면 FOREIGN(executor 자체 key).)r   OKC_SELFOKC_FOREIGN)executor_keyanu_keyss     r<   _classify_owner_keyr      s     ,)r>   c                   | yg }d}| D ]{  }t        |      }|r|j                  t               d}(|dk(  r|j                  |       d}A|j                  d      r|j                  dt        z          k|j                  |       } |S )u  callback argv 에서 raw ANU key 를 placeholder 로 redact(두 형식 모두).
      - ``--key VALUE`` (공백 구분): 다음 토큰을 placeholder 로 치환(기존 동작 유지).
      - ``--key=VALUE`` (등호 결합): ``--key=<ANU_SEALED_KEY>`` 로 치환(신규). 값이
        비어있거나(``--key=``) 비정상이어도 raw key 노출 없이 안전 처리.
    persist/return/log 용 — raw ANU key 0. argv None 이면 None.NFz--keyTz--key=)r    appendCALLBACK_ARGV_KEY_PLACEHOLDER
startswith)r4   outredact_nexttokss        r<   _redact_callback_argvr      s     |CK HJJ45K<JJqMK<<!JJx"??@

1 Jr>   c                      yd fd} |d      }|t        |      j                         nd} |d      }|t         |d            xs t        |      }t        |      |fS )u  real_launch_fn 반환값(dict 또는 attr 객체) → (cron_registered: bool, schedule_id: str).
    fail-closed: 필드 부재/None 보수적 기본값. ``build_oneshot_cokacdir_fire_fn`` 의 _fire
    dict({spawned,cron_registered,callback_evidence,schedule_id}) 형태와 호환.)Fr)   c                `    t        t              rj                  |       S t        | d       S r7   )rj   rC   rk   getattr)keyraws    r<   _gz#_read_real_fire_outcome.<locals>._g   s*    c4 773<sC&&r>   r*   r)   cron_registeredspawned)r   r    )r    rS   r&   )r   r   schedule_rawr*   regs   `    r<   _read_real_fire_outcomer      so     {'
 m$L/;/G#l#))+RK

C
{2i=!6T+%69k!!r>   r)   F)r   r*   r/   relay_requiredr^   r-   rL   clockanu_key_loaderr   runner_fire_fnreal_launch_fnactivation_intentcanary_onlyc                  |
xs d }|xs$ t         j                  j                  |t               |       j	                         dddd	 	 	 	 	 	 	 	 	 	 	 d"fd}t        ||	      s |t        t        ||      ddgd	      S | rt         j                  j                  |       s |t        t        dd
gd	      S t        ||      r |t        t        ddgd	      S t              r |t        t         ddgd	      S |xs t"        }t$        }|	  |       }|rt)        |      }t        ||      r||k(  r |t        t        ddgd	      S 	  || ||t+        |            }t/        |dd      rt/        |dd       |t,        t         ddgd	      S t1        t/        |dd      xs g       }t3        |      }|rdnd}| |t4        t         dd| dgd|      S |r|s0 |t6        t         ddt9        |       dt9        |       dgd|      S 	  ||      }tA        |      \  }}|r! |tB        t         dd| dgd|d|xs        S  |t:        t         dd!gd|d      S # t&        $ r d}Y Vw xY w# t&        $ r  |t,        t         ddgd	      cY S w xY w# t&        $ r9} |t:        t         ddt=        |      j>                   dgd|d      cY d}~S d}~ww xY w)#u  collector deterministic closeout 직후, ANU-owned callback launch 를 **코드로
    강제**하고 audit jsonl 을 남긴다(executor 세션 생존 무관 · finish-task 끝단 무관).

    처리 순서 (fail-closed):
      0. ACTIVE flag gate (OFF → 전면 no-op, audit 미기록).
      1. result.json 존재 확인 (부재 → NO_RESULT_JSON).
      2. self-key 거부: executor_key 가 ANU key 면 SELF → 거부(audit 기록).
      3. audit dedupe: (task_id, sha256) 이미 성공 launch → SKIP_DEDUPE(재발사 0).
      4. ANU-owned callback launch request 빌드(anu_runner_pickup_and_fire 재사용,
         self-key 자가발사 금지 내장) → ANU key 강제.
      5. audit jsonl append (owner_key_class·launch_ts·schedule_id·success·
         executor_session_independent — raw key/argv 0).

    executor_session_independent 는 항상 True: 본 경로는 OS runner/collector 가
    호출하며 executor 세션 생명주기와 분리되어 있다(ANCHOR-1).
    c                 H    t        j                  t        j                        S r7   )r   nowr   utcrJ   r>   r<   <lambda>z.launch_callback_for_closeout.<locals>.<lambda>  s    x||HLL9 r>   NF)r4   r5   schedule_id_overrider4   c                   t        t        | ||dt        ||nxs d      
	t        xs d      |||      }|rt        |	      |_        |S )NTr)   )r!   r"   r#   r$   r%   r'   r(   r*   r+   r-   r/   r2   r4   r5   )r   CALLBACK_LAUNCH_AUDIT_SCHEMAr    r~   r.   )r"   r%   r'   r2   write_auditr4   r5   r   res_auditr+   r*   r$   r#   r/   s            r<   _resultz-launch_callback_for_closeout.<locals>._result"  sr    
 #/+)-4H4T0"-"36 526 3
"  -c6 :C
r>   rK   uT   callback launch ACTIVE flag OFF — 전면 no-op(audit 미기록·launch 미빌드).)r   u;   result.json 부재 — callback launch 불가(fail-closed).Tu   executor self-key 로 callback launch 시도 → 거부(ANU key 강제). self-collector 패턴 차단(SELF_COLLECTOR_FORBIDDEN).uN   (task_id, sha256) 이미 성공 launch — audit dedupe, callback 재발사 0.uj   launch owner_key 가 독립 ANU key 아님 또는 executor self key 와 동일 → 거부(ANU key 강제).)result_json_pathr   anu_keyr   ub   callback launch request 빌드 예외(fail-closed) — 상세는 audit 미기록(raw-key hygiene).r=   u   ANU-owned callback launch request 빌드 실패(owner enforcement 미통과) — 상세 reason 은 audit 미기록(raw-key hygiene).z
relay-pathz
green-pathu.   ANU-owned callback launch 코드강제 성공(um   ·executor 세션 독립). 실 cron 발사는 권한 있는 ANU 세션이 ANU key 로 수행(자가발사 0).)r   r4   u   real_launch_fn 주입됐으나 activation_intent/canary_only 미충족 — surface-only, real fire 0(fail-closed). activation_intent=z canary_only=rq   u&   real_launch_fn 호출 중 예외(type=u|   ) — REAL_FIRE_FAILED (type-only·raw key 0·message 원문 미기록). 재발사는 audit dedupe single-flight 로 차단.)r   r4   r5   uA   ANU-owned callback real-fire 1회 성공(cokacdir --cron --once, u[   ) — schedule 등록 확인. single-flight 보존(audit dedupe). raw key 0(redacted argv).)r   r4   r5   r   u   real_launch_fn 호출됨 — cron 등록 미확인(REAL_FIRE_FAILED). real fire 시도·등록 실패. 고정 라벨(raw key 0). 재시도는 audit dedupe single-flight 적용.)r"   r    r%   r    r'   r&   r2   r1   r   r&   r4   r3   r5   r&   r   r,   rB   r   )"rV   rW   rX   DEFAULT_AUDIT_JSONL_REL	isoformatrd   LAUNCH_NOOP_DISABLEDr   rf   LAUNCH_NO_RESULT_JSONr   r   LAUNCH_SELF_KEY_REFUSEDr   ro   r8   OKC_ANUr   r   rR   r    tupleLAUNCH_FAILr   r@   r   r   LAUNCH_REAL_FIRE_BLOCKEDr&   LAUNCH_REAL_FIRE_FAILEDtyperD   r   r9   ) r   r#   r$   r   r*   r/   r   r^   r-   rL   r   r   r   r   r   r   r   _clockr   _fire_anu_keyloadedrrraw_argvredacted_argv
path_labeloutcomeexcr   schedule_id_outr   r+   s     `` ``                        @@r<   launch_callback_for_closeoutr      s   J :9FF277<<.EFF""$I -1,16:	"48) &* '4	 @T	 6 (+F "5lH"Mucd
 	
 277>>2B#C!;JK
 	
 ,)#XuG H	
 	
 0]^
 	
 88EH!	#%F 6{Hh)h,.F#Xu' (	
 	

-%8_	
 2tU#wr64'@'H %C D	
 	
 GB-34H)(3M!/\J Wd=j\ Jb b c=	
 	
 +$gt<<@AR<S;T U -.a1 2 =
 	

 * (?w'G$O_wPQ[P\ ]e e f=d!0!?K
 	
 %
U 	V}$	 u  	F	&  
 %qr
 	

h  	
 #We5d3i6H6H5I JC C D =d
 	
	
sB   I- I? J$ -I<;I<?J! J!$	K&-.K!K&!K&)r-   rL   r   r   r   r   r   r   c          
     >     ddddd f	d
}	|	S )uH  pickup_once/driver 에 주입할 callback launch fn 반환(partial).

    반환 fn(result_json_path, *, task_id, sha256, executor_key, schedule_id,
    terminal_intent, relay_required) → CallbackLaunchResult. ACTIVE flag gate 는
    fn 내부에서 평가 — flag OFF 면 호출돼도 전면 no-op(audit 0).

    task-2774+13: real_launch_fn/activation_intent/canary_only 미주입(기본) → surface-only
    (real fire 0). 주입 시에만 gated real-fire(ACTIVE flag ON + activation_intent +
    canary_only + real_launch_fn). default OFF 보존(production driver 무주입).r)   F)r   r*   r/   r   c               8   	 t        | f||||||	
dS )N)r#   r$   r   r*   r/   r   r^   r-   rL   r   r   r   r   r   r   )r   )r   r#   r$   r   r*   r/   r   r   r   r-   r   r   rL   r   r^   r   s          r<   _fnz%build_callback_launch_fn.<locals>._fn  sE    +
%#+)!#)))/#!
 	
r>   rJ   )
r^   r-   rL   r   r   r   r   r   r   r   s
   ````````` r<   build_callback_launch_fnr     s#    , @BBu
 
* Jr>   )r   rY   rU   r   r   r   r8   r   r   r   r9   r   r   r   r   r   r   r   rd   ro   r   r   )r^   r    rL   %Optional[Callable[[], Optional[str]]]rB   r&   )r-   r    r#   r    r$   r    rB   r&   )r}   r   r-   r    rB   r&   )r   r    r   Sequence[str]rB   r    )r4   zOptional[Sequence[str]]rB   r3   )r   objectrB   r   )$r   r    r#   r    r$   r    r   r    r*   r    r/   r    r   r&   r^   r    r-   r,   rL   r   r    Optional[Callable[[], datetime]]r   r   r   r   r   Optional[Callable[..., object]]r   +Optional[Callable[[Sequence[str]], object]]r   r&   r   r&   rB   r   )r^   r    r-   r,   rL   r   r   r   r   r   r   r   r   r   r   r&   r   r&   )3rG   
__future__r   rg   rV   dataclassesr   r   r   r   typingr   r	   r
   r   'dispatch.anu_owned_callback_enforcementr   r   r    dispatch.callback_owner_enforcerr   r   r   rY   rU   r   r   r   r8   r   r   r   r9   r   r   r   r   r   r   rl   r   rd   ro   r~   r   r   r   r   r   r   __all__rJ   r>   r<   <module>r      s  . #  	 ( ' 5 5 
  G  F #  I  $, " & (  ! . ,  !3   *+<=  '
 '
 '
X 4X\4
40U4	4068"6   $9=.2<@#$456:BF#'II I 	I
 I I I I I I 7I ,I :I I 4I" @#I$ %I& 'I( )I\ + !%9=<@.26:BF#+
+ + 7	+
 :+ ,+ 4+ @+ + +\r>   