
    8#jX                       U 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mZ ddl	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+dZd,dZedededededediZded<   edededededed iZded!<   	 	 	 	 	 	 	 	 d-d"Z 	 	 	 	 	 	 	 	 d.d#Z!	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d/d$Z"d0d%Z#d1d&Z$	 	 d2	 	 	 	 	 	 	 	 	 	 	 	 	 d3d'Z%	 	 d2	 	 	 	 	 	 	 	 	 	 	 	 	 d4d(Z&d5d6d)Z'e(d*k(  r e) e'             y)7u  scripts/harness/v36/terminal_state_callback.py
task-2724 TERMINAL_STATE_CALLBACK_CONTRACT — EXIT trap terminal-state 판정 + ANU callback 등록.

CLI: python3 terminal_state_callback.py emit --task-id <id> --events-dir <dir>
     --workspace <ws> --done-file <path>

★ ANU owner key: DEFAULT_ANU_KEYS[0] (sealed import — 리터럴 금지).
★ TERMINAL_CALLBACK_ENABLED default off — 이 모듈은 호출될 때만 동작.
★ fail-open: 예외는 모두 내부 흡수, 항상 exit 0.
    )annotationsN)datetimetimezone)Path)CallableListOptional)DEFAULT_ANU_KEYSlaunch_callbackNORMAL_SUCCESSFINISH_BLOCKED_EXTERNAL_DIRTYFINISH_BLOCKED_SCOPE_VIOLATIONFINISH_BLOCKED_GIT_GATE	QC_FAILEDUNKNOWN_FINISH_FAILUREterminal_state_envelope_v1
6937032012z+5mc                    	 t        j                  dd| ddgddd      }|j                  j                         }|r|S dS # t        $ r Y yw xY w)	Ngitz-Cz	rev-parseHEADT
   capture_outputtexttimeoutunknown)
subprocessrunstdoutstrip	Exception)	workspaceresultshas      ^/home/jay/workspace/.worktrees/task-2729+7-dev1/scripts/harness/v36/terminal_state_callback.py_get_head_shar&   /   s]    D)[&9dB
 mm!!#s(y( s   ;A  A   	AAc                .   	 t        |       }| d}t        |j                  |            }|rMt        |d   d      5 }t	        j
                  |      }d d d        j                  dd      }|rt        |      S y# 1 sw Y   )xY w# t        $ r Y yw xY w)Nz.dispatched-*.jsonutf-8encodingschedule_id r   )	r   sortedglobopenjsonloadgetstrr!   )
events_dirtask_ideventspatternmatchesfdatasids           r%   _get_attempt_idr=   ?   s    j!I/0W-.gbkG4 $yy|$((="-C3x $ $
  s(   <B A<&B <BB 	BBztask completed normallyz#external dirty files blocked finishzscope violation blocked finishzgit gate blocker detectedzQC result was FAILz%finish failed with no specific markerdict_DEFAULT_CAUSEzno action requiredz$clean external dirty files and retryzfix scope violations and retryz!commit or stash changes and retryzfix QC failures and retryzinvestigate finish-task logs_DEFAULT_REMEDIATIONc           	     @   	 t        |       | dz  }|j                         rt        |d      5 }t        j                  |      }d d d        t        j                  d|j                  dd                  }t        |j                  d|j                  dd                  }|st        j                  |d	      }|st        j                  |d	      }||fS 	 t        j                  |d	      t        j                  |d	      fS # 1 sw Y   xY w# t        $ r Y Cw xY w)
N.callback-cause.jsonr)   r*   causereasonr-   remediationactionr   )
r   existsr0   r1   r2   r4   r3   r?   r@   r!   )r5   r6   terminal_state
cause_pathr:   r;   rC   rE   s           r%   _get_cause_remediationrJ   f   s   *%7)3G(HH
j73 $qyy|$$((8R*@ABEdhh}dhhx6LMNK&**>9E266~yQ+%%  	>95  ; $ $  s)   .D DBD D
D 	DDc                |   t        |      j                         r
t        d|gfS t        |       }|| dz  }|j                         rt        dt	        |      gfS || dz  }|j                         rt
        dt	        |      gfS || dz  }|j                         rt        dt	        |      gfS || dz  }|j                         rC	 t        |d      5 }|j                         }	d	d	d	       d
	v sd|	v rt        dt	        |      gfS 	 || dz  }
|
j                         r\	 t        |
d      5 }|j                         j                         j                         }d	d	d	       dk(  rt        dt	        |
      gfS 	 t        dg fS # 1 sw Y   xY w# t        $ r Y w xY w# 1 sw Y   FxY w# t        $ r Y ;w xY w)z2Returns (terminal_state, success, evidence_paths).Tz.external-dirty-blocker.jsonFz.scope-violation.jsonz.git-gate-blocker.jsonrB   r)   r*   NGIT_GATEuncommittedz
.qc-resultFAIL)r   rG   r   r   r4   r   r   r0   readr!   r    upperr   r   )r5   r6   	done_filer7   	ext_dirty
scope_violgit_gatecallback_causer:   content	qc_resultqc_texts               r%   _determine_terminal_staterY      s    Iti[00*F G9$@AAI,ec)n5EEE WI%:;;J-us:6GGG 7)#9::H&H>>	)=>>N	nw7 #1&&(#W$(@.N8K7LLL )A G9J//I	i'2 3a&&(..*0023&  %#i.)999 ! "5",,'# #  		3 3  		sT   	F F'"F $F/ 1-F#F/ FF 	F F #F,(F/ /	F;:F;c                    i dt         d| d|d|d|dt        |      j                         d|d|d	|d
d d|ddddd|	d|
dddt        j                  t
        j                        j                         S )Nschemar6   
attempt_idrH   successdone_createdrC   rE   head_sha	pr_numberevidence_pathscollector_roleANUowner_key_sealedTcallback_registered
dedupe_key
emitted_byz3finish-task EXIT trap -> terminal_state_callback.pyts)ENVELOPE_SCHEMAr   rG   r   nowr   utc	isoformatr6   r\   rH   r]   rQ   rC   rE   r_   ra   re   rf   s              r%   _build_envelopern      s    /7 	j 	.	
 	7 	Y..0 	 	{ 	H 	T 	. 	% 	D 	2 	j  	K!" 	hll8<<(224#     c                .   d| d    d| d    ddt          dt         d| d	d
| d    d| d    d| d    g}dj                  |      }t        |j	                  d            dkD  r-|dd }|j                  d
| d           dj                  |      }|S )u9   envelope-only prompt (key=value lines, §5.6 compliance).ztask_id=r6   zcollector_role=rb   zcallback_kind=normalzchat_id=zat=zcanonical_root=z-source_attribution=terminal_state_envelope_v1zsummary=terminal_state=rH   z	 attempt=r\   z	 success=r]   
r)   i<  N   )_ANU_CHAT_ID_CALLBACK_ATjoinlenencodeappend)envelopecanonical_rootlinesprompts       r%   _build_promptr}      s     8I&'(
(#3456

<.!
l^
.)*
7
!(+;"<!=YxP\G]F^^ghpqzh{g|}	E YYuF
6==!"T)bq	.x8H/I.JKL5!Mro   c                4    t        j                  | ddd      S )u   운영 경로 전용 실제 cokacdir cron 등록 backend.

    테스트/스모크는 injectable ``runner`` 또는 ``dry_run=True`` 로 이 backend 를
    우회하여 실제 cron 등록이 발생하지 않도록 한다 (회장 9-8).
    T   r   )r   r   )argvs    r%   _default_callback_runnerr      s     >>$t$KKro   c           
        t        |       | dz  }|j                         ryt        j                  j	                  dd      }t        t        t              d      }|hddd|t        j                  t        j                        j                         d	}		 t        |d
d      5 }
t        j                  |	|
d       ddd       yt#        ||      }t%        d|||t&        |t(        |      }|j*                  rdd|j,                  |t        j                  t        j                        j                         d}	 t        |d
d      5 }
t        j                  ||
d       ddd       y|r|dddt/        |j*                        |t        j                  t        j                        j                         d}	 t        |d
d      5 }
t        j                  ||
d       ddd       y||nt0        }	  ||j*                        }ddt/        |j*                        |j2                  t        j                  t        j                        j                         d}t        |d
d      5 }
t        j                  ||
d       ddd       y# 1 sw Y   yxY w# t         $ r Y yw xY w# 1 sw Y   yxY w# t         $ r Y yw xY w# 1 sw Y   yxY w# t         $ r Y yw xY w# 1 sw Y   yxY w# t         $ r}dd| |t        j                  t        j                        j                         d}	 t        |d
d      5 }
t        j                  ||
d       ddd       n# 1 sw Y   nxY wn# t         $ r Y nw xY wY d}~yd}~ww xY w)us  Returns True if successfully registered. Updates envelope in-place.

    canonical_root: 상위(emit)에서 전달받은 workspace — 하드코딩 금지(회장 9-1/9-3).
    runner: 실제 등록 backend 주입점. None 이면 _default_callback_runner(실제 cokacdir).
    dry_run: True 면 실제 등록 backend 호출 0 (테스트/스모크 — 회장 9-7/9-8).
    z".terminal-callback-registered.jsonTCOKACDIR_KEY_SELFzexecutor-self-unknownNNOT_REGISTEREDNO_OWNER_KEYu<   DEFAULT_ANU_KEYS empty — fail-closed, no cron registration)statusrD   detailr6   rh   wr)   r*   F)ensure_asciinormal)kindr6   executor_key	owner_keychat_idr|   atrz   zfail-closed)r   rD   verdictr6   rh   DRY_RUNu'   dry_run — no actual cron registration)r   
registeredrD   argv_lenr6   rh   
REGISTERED)r   rd   r   
returncoderh   zcron subprocess error: )r   rD   r6   rh   )r   rG   osenvironr3   nextiterr
   r   rj   r   rk   rl   r0   r1   dumpr!   r}   r   rs   rt   r   r   rv   r   r   )r5   r6   ry   rz   runnerdry_runregistered_markerr   r   no_ownerr:   r|   decisionnot_reg
dry_resultrun_backendr#   
reg_resultexcs                      r%   _register_callbackr     sS    Z(gY6X+YY !::>>"57NOLT*+T2I &$T,,x||,668
	'w? ;1		(AE:; 8^4F!%	H }} '#'',,x||,668
	'w? :1		'159:  ?HMM*,,x||,668

	'w? =1		*ae<=  #.&4LKX]]+" $HMM* ++,,x||,668

 #S7; 	9qIIj!%8	9E;   		8:   		=   			9  '/u5,,x||,668	
	'w? :1		'159: : : 		s  J $J
=J ;J1 	J%"J1 9K K  K 4A4K' (KK' 
JJ J 	J"!J"%J.*J1 .J1 1	J=<J= K	K 	K 	KKK$ K' $K' '	N 09M;*M'8M	M'M#	M'&M;'	M30M;2M33M;;N c                R   t        |      }|j                  dd       ||  dz  }||  dz  }	 t        j                  t	        |      t        j
                  t        j                  z  t        j                  z  d      }	t        j                  |	       |j                         ryt        || |      \  }
}}|
t        k(  rxt        ||       }t        |      }t        || |
      \  }}|  d| d|
 }t!        | ||
d|||||d|	      }t        |d
d      5 }t#        j$                  ||dd       ddd       yt        ||       }t        |      }t        || |
      \  }}|  d| d|
 }t!        | ||
d|||||d|	      }t        |d
d      5 }t#        j$                  ||dd       ddd       t'        || ||||      }||d<   t        |d
d      5 }t#        j$                  ||dd       ddd       y# t        $ r Y yw xY w# 1 sw Y   yxY w# 1 sw Y   kxY w# 1 sw Y   yxY w)u   Core emit: 판정 → envelope → callback 등록.

    workspace: canonical_root 로 하류(_register_callback/_build_prompt) passthrough.
    runner/dry_run: 실제 cron 등록 backend 주입/우회 (테스트·스모크 경로).
    T)parentsexist_okz.terminal-state.jsonz.terminal-emit-locki  N:Frm   r   r)   r*      )r   indent)rz   r   r   re   )r   mkdirr   r0   r4   O_CREATO_EXCLO_WRONLYcloseOSErrorrG   rY   r   r=   r&   rJ   rn   r1   r   r   )r6   r5   r"   rQ   r   r   r7   envelope_path	lock_pathlock_fdrH   _successra   r\   r_   rC   rE   rf   ry   r:   r   s                        r%   emitr     sx    *F
LLL-y(<==MG9$788I''#i."**ryy*@2;;*NPUV
  0IGY0,NHn
 '$Z9
 +3JX{y*Q~.>?
"!)#) %!
 -w7 	A1IIha@	A !W5JY'H/
G^TE;9Aj\>*:;J %%!H 
mS7	3 =q		(AE!<= $JR[djt{|J '1H"#	mS7	3 =q		(AE!<= =M  B	A6= == =s6   A%G6 HHH6	HHHHH&c                   	 t        j                  dd      }|j                  dd      }|j                  d      }|j	                  dd	       |j	                  d
d	       |j	                  dd	       |j	                  dd	       |j	                  ddd       |j                  |       }|j                  dk(  rBt        |j                  |j                  |j                  |j                  |j                         y# t        $ r Y yw xY w)Nzterminal_state_callback.pyz)task-2724 terminal state callback emitter)progdescriptioncmdT)destrequiredr   z	--task-id)r   z--events-dirz--workspacez--done-filez	--dry-run
store_trueF)rF   default)r6   r5   r"   rQ   r   r   )argparseArgumentParseradd_subparsers
add_parseradd_argument
parse_argsr   r   r6   r5   r"   rQ   r   r!   )r   parsersubepargss        r%   mainr     s    ((-C
 ###>^^F#
d3
6
5
5
L%H  &88v??....   s   C=D   	DD__main__)r"   r4   returnr4   )r5   r4   r6   r4   r   r4   )r5   r4   r6   r4   rH   r4   r   ztuple[str, str])r5   r4   r6   r4   rQ   r4   r   ztuple[str, bool, list])r6   r4   r\   r4   rH   r4   r]   boolrQ   r4   rC   r4   rE   r4   r_   r4   ra   listre   r   rf   r4   r   r>   )ry   r>   rz   r4   r   r4   )r   r   )NF)r5   r4   r6   r4   ry   r>   rz   r4   r   Optional[Callable]r   r   r   r   )r6   r4   r5   r4   r"   r4   rQ   r4   r   r   r   r   r   None)N)r   zOptional[List[str]]r   int)*__doc__
__future__r   r   r1   r   r   r   r   pathlibr   typingr   r   r	   (dispatch.normal_fallback_callback_helperr
   r   r   r   r   r   r   r   ri   rs   rt   r&   r=   r?   __annotations__r@   rJ   rY   rn   r}   r   r   r   r   __name__
SystemExit ro   r%   <module>r      s{  	 #   	  '  + + " ? !A 3 	1  / 	 * -!#H"$D8#C  (!#I"$D@*: d !3661-1-1- 1- 	1-p  	
        
L<L "&rrr r 	r
 r r 
r| "&^=^=^= ^= 	^=
 ^= ^= 
^=J: z
TV
 ro   