
    j7+                    2   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	 ddl
mZmZmZ ddlmZmZmZmZmZ eg ee   f   Zdd	Zdd
ZddZddZ	 	 	 	 	 	 	 	 ddZ ed       G d d             Zeee e	d      ddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy)u*  8 source 동시 조회 collector.

회장 verbatim 필수 구현 9 의 1~8 source 를 한 번의 호출로 수집한다.
9 번째(status enum)는 status_classifier 의 책임이다.

source 8 (회장 verbatim):
  1. legacy worktree path 확인
  2. cokacdir workspace path 확인
  3. schedule_id 기반 workspace dir 확인
  4. wt-<task_id>-<team> glob (두 위치 모두)
  5. executor process (ps + pattern match)
  6. schedule_history (<schedule_id>.log)
  7. result/report/done marker (memory/events + memory/reports)
  8. callback_inbox.acked (memory/.callback_inbox/*.acked)

self-attestation 단독 인정 금지 (ANCHOR-2). callback evidence 는
result/report/done marker 와 callback_inbox.acked 양쪽을 인정하되
최소 2 source 교차가 필요하다(분류는 status_classifier 가 수행).
    )annotationsN)	dataclassfield)Path)CallableOptionalSequence   )COKACDIR_WORKSPACE_ROOTLEGACY_WORKTREE_ROOTSCHEDULE_HISTORY_ROOTWorktreeCandidatesresolve_worktree_candidatesc                     	 t        j                  g ddddd      } | j                  dk7  ryt        | j                  j                               S # t        t         j                  f$ r Y yw xY w)uj  기본 ps lister.

    `ps -e -o pid,args` 형태로 한 줄씩 반환. 호출 실패 시 빈 리스트.
    실제 ANU 측정 환경(예: 컨테이너)에 따라 ps 가 없을 수 있으므로
    실패해도 silent drop 단정으로 직결되지 않도록 한다 — status_classifier
    가 process 단독 부재로 silent drop 을 단정하지 않음.
    )psz-ez-ozpid,argsT   F)capture_outputtexttimeoutcheck r   )
subprocessrunFileNotFoundErrorTimeoutExpired
returncodetuplestdout
splitlines)	completeds    4utils/anu_spawn_visibility_guard/source_collector.py_default_process_listerr"   )   sp    	NN*
	 q !!,,.//	 z889 s   A A-,A-c                   | j                         r| j                         syg }	 | j                  ddd      5 }|D ]M  }|j                         }|s	 t	        j
                  |      }t        |t              s=|j                  |       O 	 ddd       d}t        |      D ]'  }|j                  d      }t        |t              s%|} n dt        |      |fS # t        j                  $ r Y w xY w# 1 sw Y   gxY w# t        $ r Y yw xY w)	u   schedule_history JSONL 을 파싱한다.

    반환: (present, records_tuple, last_status)
    각 줄은 JSON object. 파싱 실패 줄은 무시.
    last_status 는 마지막 record 의 status 필드.
    )Fr   Nrzutf-8replace)encodingerrorsNstatusT)existsis_fileopenstripjsonloadsJSONDecodeError
isinstancedictappendOSErrorreversedgetstrr   )log_pathrecordsfhrawobjlast_statusreccands           r!   _read_schedule_historyr?   @   s    ??H$4$4$6G]]3]C 
	(r 	(iik**S/C c4(NN3'	(
	( "&K  wwx dC K	 w,, ++ 
	( 
	(  sR   C? C3C(C39C3C? C0-C3/C00C33C<8C? ?	D
Dc                   | dz  }| dz  }|| dz  }|| dz  }d}|j                         r9|j                         r)t        t        |j	                  | d      d             }|j                         xr |j                         t        |      |j                         xr |j                         t        |      t        d	 |D              t        |      d
S )Neventsreportsz.donez.mdr   z.*-result-*.jsonc                    t        |       S Nr6   ps    r!   <lambda>z'_collect_marker_paths.<locals>.<lambda>m   s    PSTUPV     keyc              3  2   K   | ]  }t        |        y wrD   rE   .0rG   s     r!   	<genexpr>z(_collect_marker_paths.<locals>.<genexpr>u   s     $DSV$D   )done_present	done_pathreport_presentreport_pathresult_marker_pathsresult_present)r)   is_dirr   sortedglobr*   r6   bool)
memory_dirtask_id
events_dirreports_dirrR   rT   result_matchess          r!   _collect_marker_pathsr`   c   s    h&Jy(Ky..I7)3/K')Nz002:??gY.>#?@FVW

 "((*By/@/@/B^%,,.H;3F3F3H;'$$D^$DD~. rI   c                   | dz  }|j                         r|j                         sdddS |j                  d      }g }t               }d| dd| dfD ][  }|j	                  |      D ]E  }|j                         st        |      }||vs$|j                  |       |j                  |       G ] |j                  d 	       t        |      t        d
 |D              dS )Nz.callback_inboxFr   )acked_presentacked_pathstask-*z*.ackedc                    t        |       S rD   rE   rF   s    r!   rH   z)_collect_callback_inbox.<locals>.<lambda>   s
    s1v rI   rJ   c              3  2   K   | ]  }t        |        y wrD   rE   rM   s     r!   rO   z*_collect_callback_inbox.<locals>.<genexpr>   s     5SV5rP   )r)   rW   removeprefixsetrY   r*   r6   addr2   sortrZ   r   )	r[   r\   inboxshortmatchesseenpatternrG   rK   s	            r!   _collect_callback_inboxrq   z   s    **E<<>!&r::  )EGUDy(AeWG*<= &G$ 	&Ayy{!fd?HHSMNN1%	&& LL%L&g5W55 rI   c           
     X   |j                  d      }d| d| d| d| d| d| d| dd| dg}|r.|j                  d| d	|        |j                  d| d	|        g }| D ](  t        fd
|D              s|j                         * t        |      t	        |      fS )uq   executor process pattern match.

    봇 spawn 의 별칭/분기를 모두 고려해 patterns 다수 사용.
    rd   zwt-ztask/ztask_id=z
--task-id z--task /.-c              3  &   K   | ]  }|v  
 y wrD   r   )rN   rG   lines     r!   rO   z)_process_pattern_match.<locals>.<genexpr>   s     +QqDy+s   )rh   r2   anyrZ   r   )	processesr\   
team_shortrm   patternsrn   rw   s         @r!   _process_pattern_matchr|      s       )E
eW
y
y
7)
WI
'
G9A
G9A	H #eWAj\23%y*67G !+(++NN4 ! =%.((rI   T)frozenc                  b   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ed<    ee      Z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<    ee      Zded<   ed#d       Zed#d       Zed#d       Zed$d       Zd$d Zd$d!Zy")%SourceSnapshotu   8 source 수집 결과.r6   r\   Optional[str]rz   schedule_idr   worktree_candidatesrZ   executor_process_presenttuple[str, ...]executor_process_matchesschedule_history_presentschedule_history_last_status)default_factoryztuple[dict, ...]schedule_history_recordsFrQ    rR   rS   rT   rV   rU   callback_inbox_acked_presentcallback_inbox_acked_pathsc                @    t        | j                  j                        S rD   )rZ   r   legacy_matchesselfs    r!   legacy_worktree_presentz&SourceSnapshot.legacy_worktree_present   s    D,,;;<<rI   c                @    t        | j                  j                        S rD   )rZ   r   cokacdir_matchesr   s    r!   cokacdir_worktree_presentz(SourceSnapshot.cokacdir_worktree_present   s    D,,==>>rI   c                2    | j                   j                  d uS rD   )r   schedule_workspace_dirr   s    r!   schedule_workspace_dir_presentz-SourceSnapshot.schedule_workspace_dir_present   s    ''>>dJJrI   c                N    t        d | j                  j                  D              S )Nc              3  2   K   | ]  }t        |        y wrD   rE   rM   s     r!   rO   z0SourceSnapshot.wt_dir_matches.<locals>.<genexpr>   s     JSVJrP   )r   r   all_matchesr   s    r!   wt_dir_matcheszSourceSnapshot.wt_dir_matches   s    JT%=%=%I%IJJJrI   c                &   g }| j                   r|j                  d       | j                  r|j                  d       | j                  r|j                  d       | j                  r|j                  d       | j
                  r|j                  d       | j                  r|j                  d       | j                  r|j                  d       | j                  r|j                  d       | j                  r|j                  d	       t        |      S )
uK   양성으로 관측된 source 이름 목록(2 source 교차 룰에 사용).legacy_worktreecokacdir_worktreer   executor_processschedule_historydone_markerresult_markerreport_markercallback_inbox_acked)r   r2   r   r   r   r   rQ   rV   rS   r   r   r   outs     r!   positive_sourceszSourceSnapshot.positive_sources   s    ''JJ()))JJ*+..JJ/0((JJ)*((JJ)*JJ}%JJ'JJ',,JJ-.SzrI   c                   g }| j                   r|j                  d       | j                  r|j                  d       | j                  r|j                  d       | j                  r|j                  d       t        |      S )u   callback recovery 증거로 채택 가능한 source.

        ANCHOR-2: result/report/done marker 와 callback_inbox.acked 둘 다
        callback evidence 로 인정한다.
        r   r   r   r   )rQ   r2   rV   rS   r   r   r   s     r!   callback_evidence_sourcesz(SourceSnapshot.callback_evidence_sources   sh     JJ}%JJ'JJ',,JJ-.SzrI   N)returnrZ   )r   r   )__name__
__module____qualname____doc____annotations__r   r   r   rQ   rR   rS   rT   rV   rU   r   r   propertyr   r   r   r   r   r   r   rI   r!   r   r      s    !L++""--"""//16u1M.ML$Is ND K ND +0+GG). $.272NN= = ? ? K K K K.rI   r   z/home/jay/workspace/memoryr   )legacy_rootcokacdir_rootschedule_history_dirr[   process_listerextra_cokacdir_schedule_idsc          	     &   t        | |||||      }	|xs t        }
t         |
             }t        || |      \  }}d}|r|| dz  }d}d}d}|t	        |      \  }}}t        ||       }t        ||       }t        di d| d|d|d	|	d
|d|d|d|d|dt        |d         dt        |d         dt        |d         dt        |d         dt        |d         dt        |d         dt        |d         dt        |d         S )u5  8 source 를 한 번에 수집한다.

    Parameters
    ----------
    task_id: 예) "task-2657"
    team_short: 예) "dev6" (없으면 None)
    schedule_id: 예) "426931FE" (모르면 None — 광역 glob 으로 fallback)
    process_lister: 테스트 의존성 주입. 기본은 `ps -e -o pid,args`.
    )r   r   r   Nz.logFr   r\   rz   r   r   r   r   r   r   r   rQ   rR   rS   rT   rV   rU   r   rb   r   rc   )
r   r"   r   r|   r?   r`   rq   r   rZ   r6   )r\   rz   r   r   r   r   r[   r   r   
candidateslisterprocess_linesprocess_presentprocess_matchesr7   history_presenthistory_recordshistory_last_statusmarkersrl   s                       r!   collect_sourcesr      s   * -#$?J 66F&(OM'=w
($O_  $H'[M*>>O(*O)-@VW_@`=*=#J8G#J8E    '	
 "1 "1 "1 &9 "1 '.12 gk*+ G$456 ./ G$456 "'*?"@A  &*%*@%A!" $)})=#># rI   )r   Sequence[str])r7   r   r   z,tuple[bool, tuple[dict, ...], Optional[str]])r[   r   r\   r6   r   r1   )ry   r   r\   r6   rz   r   r   ztuple[bool, tuple[str, ...]])r\   r6   rz   r   r   r   r   r   r   r   r   r   r[   r   r   zOptional[ProcessLister]r   r   r   r   )r   
__future__r   r-   r   dataclassesr   r   pathlibr   typingr   r   r	   path_resolverr   r   r   r   r   r6   ProcessListerr"   r?   r`   rq   r|   r   r   r   rI   r!   <module>r      s&  ( #   (  / /  Xc]*+0. -F.*))'*)8E)!)8 $P P Pp -1!689.213BBB B
 B B B B ,B "/B BrI   