
    'ii7                       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ZddlmZm	Z	 ddl
mZ  eej                  j                  d      xs ej                  j                  d      xs d      Zed	z  d
z  Zed	z  dz  Zedz  ZdZdZddZddZddZddZddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZedk(  r ej8                   e              yy) u  
escalation_marker.py — .done.escalated / .done.blocked 마커 발행 단일 출입구.

raw shell `: > foo.done.escalated` 을 차단하기 위해 모든 escalation/blocked
marker는 이 모듈을 통해 발행되어야 한다.

요구사항 (task-2472 추가 8~10):
- JSON payload validation (필수 6 필드)
- post-write stat (size > 0)
- post-write sha256
- post-write JSON re-parse
- 실패 시 fail-closed (마커 즉시 삭제 + audit reject 기록)
    )annotationsN)datetimetimezone)Path	WORKSPACEWORKSPACE_ROOTz/home/jay/workspacememoryeventszorchestration-auditzstate-recovery.jsonl)	escalatedblocked)reasontstask_idsourceblocking_conditionevidence_pathc                 f    t        j                  t        j                        j	                  d      S )u$   UTC ISO 8601 타임스탬프 반환.z%Y-%m-%dT%H:%M:%SZ)r   nowr   utcstrftime     L/home/jay/workspace/.worktrees/task-2472+1-dev2/scripts/escalation_marker.py_nowr   2   s!    <<%../CDDr   c                H    t        j                  |       j                         S )u0   바이트 데이터의 sha256 hex digest 반환.)hashlibsha256	hexdigest)datas    r   _sha256_hexr    7   s    >>$))++r   c                    t         D cg c]  }|| vs| |   dv r| }}|rd|fS 	 t        j                  | d       dg fS c c}w # t        t        f$ r}dd| gfcY d}~S d}~ww xY w)us   필수 6 필드 + JSON 직렬화 가능성 검사.

    Returns:
        (ok: bool, missing_fields: list[str])
    )N Fensure_asciizjson_serialization_error: NT)REQUIRED_PAYLOAD_FIELDSjsondumps	TypeError
ValueError)payloadfmissingexcs       r   validate_payloadr.   <   s     +GwqzZ7 	
G  g~;

7/ 8O z" ;3C59:::;s"   AA A'A"A'"A'c                l   	 t        j                  | dd      j                  d      }t        |       } t	        |      | d<   t        j                  dd       t        j                  | d      d	z   }t        j                  t        t              t        j                  t        j                  z  t        j                  z  d
      }	 t        j                  ||j                  d             t        j                   |       t        S # t
        $ r d| d<   Y w xY w# t        j                   |       w xY w)u   state-recovery.jsonl 에 append. evidence_hash 추가.

    Args:
        record: audit 기록 dict
    Returns:
        audit 파일 경로
    FT)r$   	sort_keysutf-8evidence_hasherrorparentsexist_okr#   
  )r&   r'   encodedictr    	Exception	AUDIT_DIRmkdirosopenstrESCALATION_AUDIT_PATHO_WRONLYO_APPENDO_CREATwriteclose)recordcontent_byteslinefds       r   _record_emit_auditrK   S   s    *

6NUUV]^f"-m"< OOD4O0 ::f51D8D	*+R[[2;;-F-SUZ	[B
T[[)*
    *")* 	s   A D %D DDD3r   )kindextrac                  t               }|t        vr+dd| dt         ddd| |d}t        d| ||d   ||d	       |S ||| ||||d
}	|r||	d<   t        |	      \  }
}|
s"dd| ddd}t        d| ||d   |||d       |S t        j                  dd       |  d| }t        |z  }	 t        j                  |	dd      }|j                  d      }	 t        j                  t        |      t        j                  t        j                  z  t        j                  z  d      }	 t        j                   ||       t        j"                  |       	 	 |j-                         j.                  }|dk(  r3t1        |       ddt        |      dd}t        d| ||d   |||d        |S 	 |j3                         }t5        |      }	 t        j6                  |      }t        |      \  }}|s7t1        |       dd&| t        |      |d}t        d'| ||d   ||||d(       |S dd)t        |      ||| ||d*}t        d+| |t        |      ||||||d,
       |S # t        $ r4}dd| t        |      dd}t        d| ||d   ||d	       |cY d}~S d}~ww xY w# t        j"                  |       w xY w# t$        $ r4}dd| t        |      dd}t        d| ||d   ||d	       |cY d}~S d}~wt&        $ re}	 |j)                         r|j+                          n# t&        $ r Y nw xY wdd| t        |      dd}t        d| ||d   ||d	       |cY d}~S d}~ww xY w# t&        $ r?}t1        |       dd| t        |      dd}t        d| ||d   ||d	       |cY d}~S d}~ww xY w# t&        $ r?}t1        |       dd!| t        |      dd}t        d"| ||d   ||d	       |cY d}~S d}~ww xY w# t        j8                  $ r@}t1        |       dd#| t        |      |d}t        d$| ||d   |||d%       |cY d}~S d}~ww xY w)-u  escalation/blocked 마커 발행.

    단계:
    1. kind 검증 (escalated / blocked만 허용)
    2. payload 구성 + validate_payload
    3. EVENTS_DIR/{task_id}.done.{kind} 에 JSON write
    4. post-write stat (size > 0) + sha256 + JSON re-parse
    5. audit 기록

    Returns:
        {"ok": bool, "reason": str, "marker_path": str, "sha256": str}

    fail-closed: validation 또는 post-write 실패 시 마커 삭제 + audit reject + return ok=False.
    Fu   허용되지 않는 kind='u   ' (허용: )r"   )okr   marker_pathr   r   r   emit_rejectr   )actionr   rL   r   r   r   )r   r   r   r   r   r   rL   rM   u)   payload 검증 실패 — 누락 필드: )rP   r   rQ   r   )rS   r   rL   r   r   missing_fieldsr   Tr4   z.done.   r$   indentr1   u   JSON 직렬화 실패: Nr8   u-   마커 파일 이미 존재 (TOCTOU 차단): emit_reject_existsu   마커 파일 쓰기 실패: u   post-write stat 실패: emit_reject_postwrite_statr   u.   post-write stat: 0-byte 마커 — fail-closedemit_reject_zero_byte)rS   r   rL   r   r   	file_sizer   u!   post-write sha256 계산 실패: emit_reject_postwrite_sha256u:   post-write JSON re-parse 실패 (non-JSON 마커 차단): emit_reject_non_json)rS   r   rL   r   r   r   r   u/   post-write JSON re-parse 필드 검증 실패: emit_reject_reparse_invalid)rS   r   rL   r   r   rT   r   r   u   escalation marker 발행 성공)rP   r   rQ   r   r[   r   rL   r   emit_ok)
rS   r   rL   rQ   r   r[   r   r   r   r   )r   ALLOWED_KINDSrK   r.   
EVENTS_DIRr=   r&   r'   r9   r;   r@   r>   r?   rB   rD   O_EXCLrE   rF   FileExistsErrorOSErrorexistsunlinkstatst_size_safe_delete
read_bytesr    loadsJSONDecodeError)r   rL   r   r   r   r   rM   ts_nowresultr*   rP   r,   marker_filenamerQ   payload_jsonpayload_bytesr-   rJ   r[   written_bytesr   reparsed
reparse_okreparse_missings                           r   emit_escalationrv   q   s   0 VF = 24&M?RST
 	#X&
 	  0&G   #7+KBA'K	
 	#X&%
 	  TD1 	v.O.Kzz'aH$++G4(+WWS%r{{RZZ'?"))'KUS	HHR'HHRLV$$&..	& A~[!F{+	
 	-X&"
 	 #..0]+(::m,* #38"<J[!GGXY{+	
 	3X&-	
 		  3;'	F ;'0&  MK  /u5{+	
 	#X&
 	 0 HHRL EcUK{+	
 	*X&
 	  	!!#""$ 		 5cU;{+	
 	#X&
 	 +8  [!06{+	
 	2X&
 	 !R  [!9#?{+	
 	4X&
 	 !*  [!RSVRWX{+	
 	,X&
 	 #s   ))I AJ+ $J :J+ M $N"  O- 	J)J	J	JJ((J+ +	M4)K#M#M0 LM	LML,M	MM	N 4NNN"	O*+4O%O*%O*-Q  5P;5Q ;Q c                H    	 | j                  d       y# t        $ r Y yw xY w)u4   마커 파일 안전 삭제 (fail-closed 정리용).T)
missing_okN)rf   rd   )paths    r   ri   ri     s'    t$ s    	!!c            	        t        j                  dd      } | j                  d      }|j                  dd      }|j	                  d	d
d       |j	                  dt        t              dd       |j	                  dd
d       |j	                  dd
d       |j	                  dd
dd       |j	                  dd
dd       |j	                  ddd       | j                         }|j                  dk(  rd}|j                  r 	 t        j                  |j                        }t!        |j"                  |j$                  |j&                  |j(                  |j*                  |j,                  |#      }t        t        j.                  |d$d%&             |d'   ry(t        d)|d*    t        j                  !       y"| j1                          y"# t        j                  $ r(}t        d | t        j                  !       Y d}~y"d}~ww xY w)+z{CLI: python3 escalation_marker.py emit --task-id X --kind escalated --reason ... --source ... --blocking ... --evidence ...escalation_markeruI   escalation/blocked 마커 발행 (JSON payload, post-write 검증 포함))progdescriptioncommand)destemitu   마커 발행)helpz	--task-idTztask ID)requiredr   z--kindr   u#   마커 종류 (escalated / blocked))choicesdefaultr   z--reasonu   에스컬레이션 사유z--sourceuC   발행 출처 (finish-task.sh / done-watcher.sh / taskctl / manual)z
--blockingr   u   차단 조건)r   r   r   z
--evidencer   u   evidence 경로z--extraNu   추가 JSON 데이터 (선택))r   r   u$   [ERROR] --extra JSON 파싱 실패: )file   )r   rL   r   r   r   r   rM   FrU   rV   rP   r   u.   [ERROR] 마커 발행 실패 — fail-closed: r   )argparseArgumentParseradd_subparsers
add_parseradd_argumentlistr`   
parse_argsr~   rM   r&   rk   rl   printsysstderrrv   r   rL   r   r   r   r   r'   
print_help)parser
subparsersemit_parserargsrM   r-   rn   s          r   mainr     s   $$ _F &&I&6J ''_'EK[4iH]#2	   Z$=XYR  
 \D?SZij\DUfgY;[\D||v::

4::.
 !LL;;;;#66,,
 	djjeA>?$<B6(CSBTU\_\f\fg/ '' <SEBTs   G H	!HH	__main__)returnr@   )r   bytesr   r@   )r*   r:   r   ztuple[bool, list[str]])rG   r:   r   r   )r   r@   rL   r@   r   r@   r   r@   r   r@   r   r@   rM   zdict | Noner   r:   )ry   r   r   None)r   int)__doc__
__future__r   r   r   r&   r>   r   r   r   pathlibr   environgetr   ra   r<   rA   r`   r%   r   r    r.   rK   rv   ri   r   __name__exitr   r   r   <module>r      sA   #    	 
 '  JJNN; 	zz~~&'	
 !H,
 #88	!$::  ) E
,
.!B 
 ]] ] 	]
 ] ] ] ] 
]@	9x zCHHTV r   