
    iA                       U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 dZ
dZdZdgZd	ed
<   de
d	 	 	 	 	 	 	 ddZddd	 	 	 	 	 	 	 	 	 	 	 ddZddd	 	 	 	 	 	 	 	 	 ddZddd	 	 	 	 	 	 	 	 	 ddZddddZddd dZdd	 	 	 	 	 	 	 d!dZ	 d"dd	 	 	 	 	 	 	 d#dZdddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d$dZdd	 	 	 	 	 d%dZdd	 	 	 	 	 d%dZy)&uU  Silent corruption guard for task done preconditions.

Three hardcoded fail-closed checks:

1. ``check_pr_merged_at`` — ``gh pr view <PR> --json mergedAt`` not null.
2. ``check_pr_merge_commit_oid`` — ``gh pr view <PR> --json mergeCommit`` oid not null.
3. ``check_origin_main_ancestry`` — race-safe fetch + 2회 교차 검증으로
   ``origin/<base>`` 가 merge commit 의 ancestor 인지 확인.

각 함수는 ``GuardResult-like`` dict 를 반환 한다::

    {"ok": bool, "reason": str, "detail": dict}

외부 호출 (subprocess, gh, git) 실패 시 항상 fail-closed (``ok=False``).
    )annotationsN)Path)Optional   g      ?gh	list[str]DEFAULT_GH_CMDcwdtimeoutc          	     ,   	 t        j                  | |rt        |      nddd|dd      }|j                  |j                  |j
                  fS # t         j                  t        t        f$ r'}ddt        |      j                   d| fcY d}~S d}~ww xY w)zRun subprocess with shell=False, capture_output, timeout.

    Returns ``(returncode, stdout, stderr)``. On any exception returns
    ``(-1, "", str(exc))`` so callers can fail-closed without try/except noise.
    NTF)r   capture_outputtextr   shellcheck z: )
subprocessrunstr
returncodestdoutstderrTimeoutExpiredFileNotFoundErrorOSErrortype__name__)cmdr   r   procexcs        P/home/jay/workspace/.worktrees/task-2471+1-dev2/utils/silent_corruption_guard.py_runr#   !   s    6~~CT
 T[[88%%'8'B 62$s),,-Ru5556s   AA B,BBBgh_cmdr   c               r   |rt        |      nt        t              }|ddt        |       d|ddgz   }t        ||t              \  }}}	|dk7  rdd	|	j                         xs d
| fS 	 t        j                  |      }
||
vrdd	d| fS d|
|   dfS # t        j                  $ r}dd	d| fcY d	}~S d	}~ww xY w)zRun ``gh pr view <PR> --repo <repo> --json mergedAt,mergeCommit -q '<expr>'``.

    Returns ``(ok, value, raw_stderr_or_msg)``. ``value`` is whatever JSON the
    ``-q`` selector emits (string for mergedAt, object/None for mergeCommit).
    prviewz--repoz--jsonzmergedAt,mergeCommitr
   r   FNzgh exited rc=zgh JSON decode failed: zmissing field Tr   )	listr	   r   r#   _GH_TIMEOUT_SECstripjsonloadsJSONDecodeError)	pr_numberrepofieldr%   r   baser   rcr   r   payloadr!   s               r"   _gh_view_fieldr5   ;   s     "4<tN';D
I C csODB	QwdFLLNBbT.BBB<**V$ GdnUG444##  <d5cU;;;<s   (B B6$B1+B61B6c               ~    t        | |d||      \  }}}|sdd| | |dddS ||dk(  r
dd| ||d	dS d
d| ||d	dS )u/   ``gh pr view --json mergedAt`` not null 검증.mergedAtr$   Fgh pr view failed: r/   r0   r1   okreasondetailr   z mergedAt is null (PR not merged))r/   r0   r7   TzmergedAt present)r5   )r/   r0   r%   r   r;   valuemsgs          r"   check_pr_merged_atr@   a   s     $4FNBs +C51$-tjQ
 	
 }8$-tO
 	
 $ )4UK     c                   t        | |d||      \  }}}|sdd| | |dddS t        |t              s
dd| ||ddS |j                  d	      }|rt        |t              s
dd
| ||ddS dd| ||ddS )u6   ``gh pr view --json mergeCommit`` oid not null 검증.mergeCommitr$   Fr8   r9   r:   zmergeCommit is null/not-object)r/   r0   rC   oidzmergeCommit.oid missingTzmergeCommit.oid present)r/   r0   merge_commit_sha)r5   
isinstancedictgetr   )r/   r0   r%   r   r;   r>   r?   rD   s           r"   check_pr_merge_commit_oidrI      s     $4v3NBs +C51$-tmT
 	
 eT"6$-tER
 	
 ))E
Cjc*/$-tER
 	
 + )4SQ rA   r   c                   d|  d|  }t        dddd|g|t              \  }}}|dk7  rd	|j                         xs d
| fS y)zM``git fetch origin --no-tags +refs/heads/<base>:refs/remotes/origin/<base>``.z+refs/heads/z:refs/remotes/origin/gitfetchoriginz	--no-tagsr
   r   Fzgit fetch rc=)Tr   r#   _GIT_TIMEOUT_SECr+   )base_branchr   refspecr3   _r   s         r"   _git_fetch_baserT      sb    [M)>{mLG	;8 MB6
 
Qwflln<-t(<<<rA   c               t    t        dddd|  g|t              \  }}}|dk7  ry|j                         }|xs dS )z)``git rev-parse --verify origin/<base>``.rL   z	rev-parsez--verifyrefs/remotes/origin/r
   r   NrO   )rQ   r   r3   r   rS   shas         r"   _git_rev_parse_remoterX      sM    	Z+?})MN MB
 
Qw
,,.C;$rA   c               H    t        ddd| d| g|t              \  }}}|dk(  S )NrL   z
merge-basez--is-ancestorrV   r
   r   )r#   rP   )rE   rQ   r   r3   rS   s        r"   _git_is_ancestorrZ      sB     ";-0	
  
HB1 7NrA   mainc          	         t        | t              r| j                         s	dd| |ddS t        ||      \  }}|sdd| | |ddS t	        ||      }|s	dd| |ddS t        j                  t               t	        ||      }|s	dd| |ddS ||k7  rXt        ||       t	        ||      }t        j                  t               t	        ||      }|r|r||k7  rdd	| |||||d
dS |}t        | ||      s
dd| ||ddS dd| ||ddS )u  Race-safe ancestry verification (fetch + 2회 교차 검증).

    Sequence
    --------
    1. ``git fetch origin --no-tags +refs/heads/<base>:refs/remotes/origin/<base>``
    2. 1차 ``git rev-parse origin/<base>`` -> ``sha_a``
    3. ``time.sleep(0.5)``
    4. 2차 ``git rev-parse origin/<base>`` -> ``sha_b``
    5. ``sha_a == sha_b`` -> ``git merge-base --is-ancestor`` 통과 검증
       그렇지 않으면 1회 fetch+재조회 후 그래도 불일치면 fail-closed.
    Fzmerge_commit_sha empty)rE   rQ   r:   rJ   zgit fetch failed: z$rev-parse origin/<base> failed (1st)z$rev-parse origin/<base> failed (2nd)z;origin/<base> SHA unstable across 2 fetches (race detected))rE   rQ   sha_asha_bsha_csha_dz*merge_commit not ancestor of origin/<base>)rE   rQ   
origin_shaTzancestry verified)	rF   r   r+   rT   rX   timesleep_FETCH_RETRY_SLEEP_SECrZ   )	rE   rQ   r   fetchedr?   r]   r^   r_   r`   s	            r"   check_origin_main_ancestryrf      s   " &,4D4J4J4L.+;KX
 	
 #;C8LGS*3%0+;KX
 	
 "+37E<+;KX
 	
 	JJ%&!+37E<+;KX
 	
 ~-%ks;

)*%ks;EUe^W(8#.""""  ,ksCB$4*#
 	
 % 0&
 rA   )rQ   r%   r   task_id
events_dirc          
        i }t        | |||      }||d<   |d   sdd|d    d| ||ddS t        | |||      }	|	|d	<   |	d   sdd
|	d    d	| ||ddS |	d   d   }
|d   d   }t        |
||      }||d<   |d   sdd|d    d| ||
|||ddS |rVt        ||      }||d<   |d   sdd|d    d| ||
|||ddS t	        ||      }||d<   |d   sdd|d    d| ||
|||ddS dd|
|| |||ddS )u  3 check 통합. 하나라도 FAIL 시 ``ok=False``.

    Returns
    -------
    dict
        ``{"ok": bool, "reason": str, "detail": {...}}``.

        성공 시 ``detail`` 은 ``merge_commit_sha``, ``merged_at``, ``checks`` 키를 포함.
        실패 시 어느 check 가 실패 했는지 ``detail["failed_check"]`` 에 명시.

    Notes
    -----
    task_id / events_dir 가 None 이면 task-2471+1 F2 신규 검사 스킵 (후방 호환).
    r$   	merged_atr;   Fzmerged_at check failed: r<   )failed_checkr/   r0   checksr:   merge_commit_oidzmerge_commit_oid check failed: r=   rE   r7   )rQ   r   ancestryzancestry check failed: )rk   r/   r0   rE   rj   rQ   rl   rh   done_escalated_conflictz done_escalated_conflict failed: escalation_marker_payloadz"escalation_marker_payload failed: Tz%all 3 silent_corruption checks passed)rE   rj   r/   r0   rQ   rl   )r@   rI   rf   check_done_escalated_conflictcheck_escalation_marker_payload)r/   r0   rQ   r%   r   rg   rh   rl   merged_at_result
oid_resultrE   rj   ancestry_resultconflict_resultmarker_results                  r"   verify_done_preconditionsry   1  s   0 !F))T&cR*F;D!01A(1K0LM +& 		
 		
 +9d6sSJ!+Fd7
88L7MN 2& 		
 		
 "(+,>? *:6I0ksO )F:4 /0I/JK *&$4&* 
 	
  7JW,;()t$<_X=V<WX$=!* (8!*#.$  8JW.;*+T">}X?V>WX$?!* (8!*#.$  9 0""&
 rA   ro   c                   |sd}t        |      |  dz  }t        |      |  dz  }|j                         }|j                         }| t        |      t        |      ||d}|r|r
dd|  d|dS d	d
|dS )u   `.done` + `.done.escalated` 동시 존재 reject (silent corruption pattern).

    Returns {"ok": bool, "reason": str, "detail": dict}.
    !/home/jay/workspace/memory/eventsz.done.done.escalated)rg   	done_pathescalated_pathdone_existsescalated_existsFz&.done and .done.escalated coexist for u^    — silent corruption: either rollback escalation (resolve + archive) or block .done emissionr:   Tzno done/escalated conflict)r   existsr   )rg   rh   r}   esc_pathr   
esc_existsr=   s          r"   rr   rr     s     8
Z gYe#44IJWI_"==H""$K"J^h-"&F z8	 BY Y 
 	
 ">&QQrA   c               b   |sd}t        |      |  dz  }| t        |      d}|j                         sdd|dS 	 |j                         j                  }||d
<   |dk(  r
dd|  d|dS 	 |j                  d      }t        j                  |      }t        |t              rt        |j                               ng |d<   t        |t              rd|vsd|vrdd|dS dd|dS # t
        $ r}dd| |dcY d	}~S d	}~ww xY w# t
        t        j                  f$ r}dd| |dcY d	}~S d	}~ww xY w)u   `.done.escalated` 가 존재한다면 0 byte 또는 JSON 미파싱 시 reject.

    빈 marker(`os.close(fd)` 직후 발행) 결함 차단. 사유 박제 강제.
    r{   r|   )rg   r~   Tzno escalation markerr:   Fzstat failed: Nsizer   z&empty escalation marker (0 bytes) for u$    — reason payload missing (defect)zutf-8)encodingz*escalation marker payload not valid JSON: payload_keystriggerr<   z8escalation marker missing required keys: trigger, reasonzescalation marker payload OK)r   r   r   statst_sizer   	read_textr,   r-   r.   rF   rG   sortedkeys)rg   rh   r   r=   r   r!   r   r4   s           r"   rs   rs     sr    8
JWI_"==H&#h-HF??&<OOP}}&& F6Nqy>wiGkl
 	


!!7!3**T" 8B'47PVGLLN3VXF>gt$	(@HT[D[P
 	

 "@FSS3  Pse'<OOP T))* 
B3%H
 	

s;   C# *'D #	C?,C:4C?:C?D.D)#D.)D.)r   r   r   Optional[Path]r   intreturnztuple[int, str, str])r/   r   r0   r   r1   r   r%   Optional[list[str]]r   r   r   ztuple[bool, object, str])
r/   r   r0   r   r%   r   r   r   r   rG   )rQ   r   r   r   r   ztuple[bool, str])rQ   r   r   r   r   Optional[str])rE   r   rQ   r   r   r   r   bool)r[   )rE   r   rQ   r   r   r   r   rG   )r/   r   r0   r   rQ   r   r%   r   r   r   rg   r   rh   r   r   rG   )rg   r   rh   r   r   rG   )__doc__
__future__r   r,   r   rb   pathlibr   typingr   r*   rP   rd   r	   __annotations__r#   r5   r@   rI   rT   rX   rZ   rf   ry   rr   rs    rA   r"   <module>r      sr    #        !F	 " "	6	6 
6 	6
 6> #'#$#$
#$ #$
  #$ 
#$ #$T #'
  	
 
 
H #'%%
%  	%
 
% 
%P @D 
 FJ 
 GK(+5C	& Z 	ZZZ 
	Z
 
ZB "&! ${{
{ 	{
  { 
{ { { 
{L !%RR R 
	RJ !%*T*T *T 
	*TrA   