
    0j%                        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Z
dZdZd	Zd
ZdZdZdZdZdZe G d d             Ze G d d             Ze G d d             ZddZddZddZddZg dZy)u6  dispatch.anu_collector_result — task-2730 collector_result schema + atomic writer.

OS-level pickup deterministic closeout 의 산출물(``<task_id>.collector_result.json``)
스키마와 **원자적 writer**. ``pickup_once``(runner) 가 durability ordering
(**ledger → collector_result → done marker**) 안에서 이 helper 를 호출한다.

설계 anchor (수정 금지):
  - ANCHOR-A: closeout write OWNER = ``pickup_once`` (+ 이 helper). ``process_one``
    은 closeout 을 **소유하지 않는다** (verdict 분기만).
  - ANCHOR-B: durability order = ledger(dedupe·fsync) → collector_result(atomic
    os.replace) → done marker(terminal sentinel). 이 모듈의 writer 는 atomic
    write 만 책임지고, **순서 보장은 호출자(pickup_once)** 가 한다 (side-effect 최소).

★ raw key 0: ``owner_key`` / ANU key / argv literal 을 **절대 기록하지 않는다**.
  ``owner_proof`` 는 분류 라벨(outcome / verdict / schedule_id / query_ok)만 담는다.

Layer A / NO-CRON: cron register/remove 0, subprocess 0, merge 0, git/gh import 0.
    )annotationsN)	dataclassfield)Optionalz dispatch.anu_collector_result.v1ztask-2730.os-pickup-closeout.v1
done_ackedrelay_pendingquarantinedANUenvelope
provenancep0b_pickup_closeout)gemini_findingmerge_ready_ambiguous	critical7consolidated_reportc                  J    e Zd ZU dZdZded<   dZded<   dZded<   dZded	<   y
)
OwnerProofu   owner-proof 분류 라벨. resolve_authoritative_owner / verify_collector_authoritative
    결과에서 파생한 라벨만 담는다 (raw key/argv 0). str
l1_outcome
l2_verdictschedule_idFboolquery_okN)	__name__
__module____qualname____doc__r   __annotations__r   r   r        4/home/jay/workspace/dispatch/anu_collector_result.pyr   r   ;   s2    D JJKHdr!   r   c                  .    e Zd ZU dZdZded<   dZded<   y)	
AgentRelayu]   agent_relay 판정 결과. required + reason(relay_hints 필드명 또는 quarantine_reason).Fr   requiredr   r   reasonN)r   r   r   r   r%   r   r&   r    r!   r"   r$   r$   G   s    gHdFCr!   r$   c                      e Zd ZU dZded<   ded<   ded<   ded<    ee      Zded	<    ee      Z	d
ed<   dZ
ded<   eZded<   dZded<   dZded<   eZded<   eZded<   eZded<   eZded<   ddZy)CollectorResultuD   collector_result.json 스키마 (pure dataclass). raw key 미포함.r   task_idsource_result_sha256verdictcloseout_action)default_factoryr   owner_proofr$   agent_relayFr   dedupescopeNzOptional[str]contract_violationr   ts_kstrunner_versionschemacollector_roleadopted_viac                J   | j                   | j                  | j                  | j                  | j                  j
                  | j                  j                  | j                  j                  t        | j                  j                        dt        | j                        | j                  | j                  t        | j                  j                        | j                  j                  d| j                   | j"                  | j$                  | j&                  | j(                  dS )N)r   r   r   r   r%   r&   )r5   r)   r*   r+   r.   r0   r1   r,   r/   r2   r6   r7   r3   r4   )r5   r)   r*   r+   r.   r   r   r   r   r   r0   r1   r,   r/   r%   r&   r2   r6   r7   r3   r4   )selfs    r"   to_jsonzCollectorResult.to_jsone   s    kk||$($=$=||"..99"..99#//;; !1!1!:!:;	 4;;'ZZ#33 !1!1!:!:;**11 #'"9"9"11++kk"11-
 	
r!   )returndict)r   r   r   r   r   r   r   r.   r$   r/   r0   DEFAULT_SCOPEr1   r2   r3   RUNNER_VERSIONr4   COLLECTOR_RESULT_SCHEMAr5   COLLECTOR_ROLE_ANUr6   ADOPTED_VIA_ENVELOPEr7   r;   r    r!   r"   r(   r(   P   s    NLL#J?K?#J?K?FDE3(,,FC(NC()FC),NC, ,K+
r!   r(   c                   t        | t              r| j                  d      nd}t        |t              st        dd      S t        D ]+  }t        |j                  |            st        d|      c S  t        dd      S )uy  result.json 의 ``relay_hints`` producer-contract 필드만 읽어 agent_relay 판정.

    - ``relay_hints`` 가 dict 가 아니거나 부재 → required=false(green).
    - ``RELAY_HINT_FIELDS`` 4종 중 하나라도 truthy → required=true,
      reason=처음 truthy 인 필드명.
    - **임의 추론 0**: 정의된 4 필드만 본다. 다른 키는 무시한다.
    relay_hintsNFr   r9   T)
isinstancer=   getr$   RELAY_HINT_FIELDSr   )resulthintsnames      r"   determine_agent_relayrK      so     *4FD)AFJJ}%tEeT"544! :		$ tD99: uR00r!   c                    t         j                  j                  |       }t         j                  j                  || d      S )uk   collector_result.json 경로: result.json 과 같은 디렉토리,
    ``<task_id>.collector_result.json``.z.collector_result.json)ospathdirnamejoin)result_json_pathr)   
result_dirs      r"   collector_result_pathrS      s4     !12J77<<
wi/E$FGGr!   c                   	 t        j                  | t         j                        }	 t        j                  |       t        j
                  |       y# t        $ r Y yw xY w# t        $ r Y 0w xY w# t        j
                  |       w xY w)uh   디렉토리 엔트리(rename) 를 crash-durable 화. fsync 미지원 fs/플랫폼은 무시(fail-safe).N)rM   openO_RDONLYOSErrorfsyncclose)dir_pathfds     r"   
_fsync_dirr\      sm    WWXr{{+
 	     	s4   $A A! 	AA!	A-*A0 ,A--A0 0Bc                p   t        j                  | j                         dd      }t        j                  j                  |      xs d}t        j                  |d       | dt        j                          }	 t        |dd	
      5 }|j                  |       |j                          t        j                  |j                                ddd       t        j                  ||       t        |       |S # 1 sw Y   ,xY w# t        $ r' 	 t        j                   |        # t        $ r Y  w xY ww xY w)u  ``CollectorResult`` 를 **atomic(os.replace)** 으로 ``path`` 에 write. 경로 반환.

    side-effect 최소:
      - 부모 디렉토리 생성(makedirs exist_ok).
      - tmp 파일 write + flush + fsync.
      - ``os.replace(tmp, path)`` (같은 fs atomic rename).
    durability 순서(ledger→collector_result→marker)는 호출자(pickup_once)가 보장한다.
    실패 시 tmp 정리 후 OSError 를 **그대로 raise** — 호출자가 marker 미작성으로
    fail-closed 하여 다음 cycle 재시도하게 한다(collector_result before marker 불변 보존).

    raw key 미기록: ``result.to_json()`` 에 owner_key/ANU key/argv literal 0.
    F   )ensure_asciiindent.T)exist_okz.tmp-wzutf-8)encodingN)jsondumpsr;   rM   rN   rO   makedirsgetpidrU   writeflushrX   filenoreplacer\   rW   unlink)rH   rN   payloadparenttmp_pathfhs         r"   write_collector_resultrr      s    jj)aHGWW__T")cFKK&uRYY[M*H(C'2 	"bHHWHHJHHRYY[!	" 	

8T"6 K	" 	"  	IIh 	  		sI   ;D 	AC9)D 9D>D 	D5D%$D5%	D1.D50D11D5)r@   r?   CLOSEOUT_DONE_ACKEDCLOSEOUT_RELAY_PENDINGCLOSEOUT_QUARANTINEDrA   rB   ADOPTED_VIA_PROVENANCEr>   rG   r   r$   r(   rK   rS   rr   )rH   objectr<   r$   )rQ   r   r)   r   r<   r   )rZ   r   r<   None)rH   r(   rN   r   r<   r   )r   
__future__r   re   rM   dataclassesr   r   typingr   r@   r?   rs   rt   ru   rA   rB   rv   r>   rG   r   r$   r(   rK   rS   r\   rr   __all__r    r!   r"   <module>r}      s   $ #  	 (  = 2 # ( $   
 " %  &        ,
 ,
 ,
`1$HBr!   