
    *iD              	         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Zddl	m
Z
mZ ddlmZmZ ddlmZ ddlmZ ddlmZmZmZ  ee      j/                         j0                  j0                  Z ee      ej6                  vr"ej6                  j9                  d ee             dd	lmZmZ dd
l m!Z!m"Z"m#Z#m$Z$  ejJ                  e&      Z'edejP                  f   Z) ed      Z* ed      Z+dZ,dZ-dZ.dZ/ G d dee      Z0 G d dee      Z1dZ2e
 G d d             Z3e
 G d d             Z4e
 G d d             Z5dd	 	 	 	 	 dSdZ6ddd	 	 	 	 	 	 	 dTd Z7dUd!Z8dVd"Z9dWd#Z:dXd$Z;dd%dYd&Z<dddd'	 	 	 	 	 	 	 	 	 dZd(Z=ddd)	 	 	 	 	 	 	 d[d*Z>ddd)	 	 	 	 	 	 	 d\d+Z?ddd,	 	 	 	 	 	 	 d]d-Z@dd.	 	 	 	 	 d^d/ZAd_d0ZBdd1	 	 	 	 	 d`d2ZCddd)	 	 	 	 	 	 	 dad3ZDdddddd4	 	 	 	 	 	 	 	 	 	 	 	 	 dbd5ZEdcd6ZFddd7ZGdd.	 	 	 	 	 	 	 ded8ZH	 	 	 	 	 	 	 	 dfd9ZIdgd:ZJdhd;ZKdd<ddd=	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 did>ZLdjd?ZM	 	 	 	 	 	 	 	 dkd@ZNdldAZOdldBZP	 	 	 	 	 	 	 	 	 	 dmdCZQdd<ddD	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dndEZRd<ddddddddF	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dodGZSddd<ddH	 	 	 	 	 	 	 	 	 dpdIZTdqdJZUdrdKZVddL	 	 	 	 	 dsdMZWdtdNZXdudvdOZYe&dPk(  r& ej                  ej                  dQR        eY        yy)wuu  utils/lifecycle_reconciliation_manager.py — task-2518 P0.

회장 명시 요구사항:
  - bot session ↔ task lifecycle 결합 제거
  - GitHub/CI/smoke evidence를 source-of-truth로 사용하는 idempotent reconcile state machine
  - manual .done 위장 차단 (evidence 없으면 RuntimeError)
  - dry-run 기본 (--apply 없이 side effect 0)

7 Lifecycle States:
  RUNNING                 — task-timer running, 작업 진행 중
  PR_OPEN                 — PR 생성됨, 머지 대기
  MERGED_PENDING_RECONCILE — PR merged, finalize 누락 (회장 §1 사례)
  RECONCILING             — reconcile 진행 중
  FINALIZED               — .done.acked + .merge-done + timer end 모두 정상
  STUCK_NEEDS_RECONCILE   — stuck 자동 감지
  ESCALATED               — Critical 7종 발동

8 Stuck Cases (회장 §1~7 + Telegram cut-off):
  TIMER_RUNNING_BUT_PR_MERGED          — 회장 §2
  PR_MERGED_BUT_DONE_MISSING           — 회장 §1
  MERGE_COMMIT_BUT_MERGE_DONE_MISSING  — 회장 §1
  CI_PASS_BUT_NOT_FINALIZED
  TELEGRAM_REPLY_CUT_OFF               — cron history truncation
  BOT_SESSION_ENDED_BUT_TASK_OK        — 회장 §7
  FINISH_TASK_INTERRUPTED              — 회장 §3
  STALE_ESCALATE_MARKER                — 회장 §4

Evidence priority (회장 명시):
  PR state > mergeCommit > origin/main 포함 > CI > smoke > timer > file marker
    )annotationsN)	dataclassfield)datetimetimezone)Enum)Path)AnyCallableOptional)CanonicalWorkspaceresolve_canonical_workspace)AutomationDecisionCriticalEscalationTypeEscalationPacketSmokeResult.z/home/jay/workspacez$/home/jay/.cokacdir/schedule_historyzmemory/eventszmemory/task-timers.jsonz memory/task-timers-archived.jsoni  c                  ,    e Zd ZdZdZdZdZdZdZdZ	dZ
y	)
LifecycleStateu1   7 lifecycle states (회장 명시 정확 매칭).RUNNINGPR_OPENMERGED_PENDING_RECONCILERECONCILING	FINALIZEDSTUCK_NEEDS_RECONCILE	ESCALATEDN)__name__
__module____qualname____doc__r   r   r   r   r   r   r        W/home/jay/workspace/.worktrees/task-2528-dev1/utils/lifecycle_reconciliation_manager.pyr   r   R   s*    ;GG9KI3Ir!   r   c                  @    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdZdZdZy)StuckReasonu@   기존 8 + task-2521 §3 신규 4 stuck cases (BOT_CANCELLED_*).TIMER_RUNNING_BUT_PR_MERGEDPR_MERGED_BUT_DONE_MISSING#MERGE_COMMIT_BUT_MERGE_DONE_MISSINGCI_PASS_BUT_NOT_FINALIZEDTELEGRAM_REPLY_CUT_OFFBOT_SESSION_ENDED_BUT_TASK_OKFINISH_TASK_INTERRUPTEDSTALE_ESCALATE_MARKER&BOT_CANCELLED_TIMER_RUNNING_PR_MISSING"BOT_CANCELLED_WITH_ACTIVE_WORKTREE$BOT_CANCELLED_AFTER_COMMIT_BEFORE_PR&BOT_CANCELLED_AFTER_PR_BEFORE_FINALIZEN)r   r   r   r   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r    r!   r"   r$   r$   ^   sP    J"?!=*O' ;5$C!73 .V*)M&+Q(-U*r!   r$   g     r@c                     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<   ded<   d	ed<   d	ed<   d	ed<   d	ed<   d	ed<   d	ed<   ded<   d	ed<   ded<   d	ed<   d	ed<   dZded<   dZd	ed<   dZd	ed<   dZd ed!<   y)"LifecycleEvidenceu9   모든 source-of-truth 증거를 모은 unified envelope.strtask_idzOptional[int]	pr_numberOptional[str]pr_statemerge_commitboolmerged_into_main	ci_statussmoke_statustimer_statustimer_end_timehas_donehas_done_ackedhas_merge_donehas_qc_resulthas_followuphas_escalate_markerzOptional[float]escalate_marker_age_minutestelegram_reply_truncatedbot_session_statusworktree_existsbranch_pushed_to_remoteNworktree_mtime_seconds_agoFhas_pushed_commitsreport_artifact_presentzOptional[bool]process_alive)	r   r   r   r   __annotations__rJ   rK   rL   rM   r    r!   r"   r2   r2   z   s    CL!!N!00""%%!!266$$$)T)$(M>(r!   r2   c                  "    e Zd ZU ded<   ded<   y)	StuckCaser$   reasonr3   detailN)r   r   r   rN   r    r!   r"   rP   rP      s    Kr!   rP   c                      e Zd ZU dZded<   ded<    ee      Zded<   d	Zd
ed<    ee      Z	ded<    ee      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dZddZddZy	)LifecycleReportu   reconcile 결과 envelope.r3   r4   r   state)default_factorylist[StuckCase]stuck_casesNzOptional[LifecycleEvidence]evidence	list[str]actions_takenactions_plannedTr9   dry_run 	timestampreconcile_run_idzdict[str, Any]backfill_metadatac                    | j                   i S | j                   j                  j                         D ci c]  \  }}||
 c}}S c c}}w )uI   evidence를 dict로 직렬화 (EscalationPacket 연동 등에서 사용).)rY   __dict__items)selfkvs      r"   evidence_to_dictz LifecycleReport.evidence_to_dict   s@    == I!%!7!7!=!=!?@A1@@@s   Ac                h   | j                   | j                  j                  | j                  D cg c]%  }|j                  j                  |j
                  d' c}| j                         | j                  | j                  | j                  | j                  | j                  | j                  d
S c c}w )NrQ   rR   
r4   rU   rX   rY   r[   r\   r]   r_   r`   ra   )r4   rU   valuerX   rQ   rR   rh   r[   r\   r]   r_   r`   ra   )re   scs     r"   to_dictzLifecycleReport.to_dict   s    ||ZZ%% ** 99??bii@ --/!//#33|| $ 5 5!%!7!7
 	
s   *B/c                N    t        j                  | j                         dd      S )N   Findentensure_ascii)jsondumpsrn   )re   s    r"   to_jsonzLifecycleReport.to_json   s    zz$,,.GGr!   )returndictrw   r3   )r   r   r   r   rN   r   listrX   rY   r[   r\   r]   r_   r`   rx   ra   rh   rn   rv   r    r!   r"   rT   rT      s    $L#(#>K>,0H)0$T:M9:!&t!<OY<GTIsc(-d(C~CA
"Hr!   rT   cwdc               6    t        j                  | |ddd      S )NT   )r|   capture_outputtexttimeout)
subprocessrun)argsr|   s     r"   _default_runnerr      s$    
 >> r!   r|   runnerc               N    ||nt         } || |t        |            S d       S )Nr{   )r   r3   )r   r|   r   fns       r"   _runr      s-     %?BdCOC>>>>r!   c                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformatr    r!   r"   _now_isor      s    <<%//11r!   c                    | t         z  S r   )_EVENTS_DIR_NAMEworkspace_roots    r"   _events_dirr      s    ,,,r!   c                (    t        |       | d| z  S N.)r   )r   r4   suffixs      r"   _marker_pathr      s    ~&G9AfX)>>>r!   c                     t         S r   )_DEFAULT_WORKSPACEr    r!   r"   _default_workspace_rootr      s    r!   r   c                   	 t        | d|      S # t        $ r, t        | t        t        dz  | z  d|  ddt        dd	      cY S w xY w)zUResolve canonical workspace for the task (delegates to canonical_workspace_resolver).F)fetchr   
.worktreestask/r^   T)	r4   r   worktree_pathbranch_namemain_head_shabase_shar|   is_mainis_clean)r   	Exceptionr   r   )r4   r   s     r"   _resolve_workspacer      s^    
*7%OO 
!-,|;gEy)"

 
	

s    2AAr   r   	pr_lookupc                   |		  ||       S t        |xs
 t	                     }	 t        ddddd|  dd	d
dddg||      }|j                  dk7  s|j                  j                         si S t        j                  |j                        }|si S |D ]  }|j                  d      dk(  s|c S  |d   S # t         $ r#}t        j                  d| |       i cY d}~S d}~ww xY w# t         $ r#}t        j                  d| |       i cY d}~S d}~ww xY w)z/Fetch PR info via gh CLI or injected pr_lookup.Nzpr_lookup failed for %s: %sghprrz   z--searchz
head:task/z--stateall--jsonznumber,state,mergeCommitz--limit5r   r   rU   MERGEDzgh pr list failed for %s: %s)r   loggerwarningr3   r   r   
returncodestdoutstriprt   loadsget)	r4   r   r   r   excr|   rrd   items	            r"   _gather_pr_infor     s'    	W%%
 n9 7 9
:CdFj	2543 

 <<1AHHNN$4I

188$I 	Dxx H,	 Qx5  	NN8'3GI	6  5wD	sS   B2 AC! +"C! C! (C! ,C! 2	C;CCC!	D*DDDr   r   c                   | syt        |xs
 t                     }	 t        ddd| dg||      }|j                  dk(  S # t        $ r!}t
        j                  d| |       Y d	}~yd	}~ww xY w)
z4Check if merge_commit is an ancestor of origin/main.Fgitz
merge-basez--is-ancestorzorigin/mainr   r   z"merge-base check failed for %s: %sN)r3   r   r   r   r   r   r   )r8   r   r   r|   r   r   s         r"   _check_merged_into_mainr   =  su     
n9 7 9
:C	L/<O

 ||q   ;\3Os   !> 	A(A##A(c               6   t        |xs
 t                     }	 t        dddt        |       ddg||      }|j                  dk7  s|j                  j                         syt        j                  |j                        }|j                  d      xs g }|sy|D ch c]*  }|j                  d	      xs |j                  d
      xs d, }}d|v sd|v ryt        d |D              ryyc c}w # t        $ r!}	t        j                  d| |	       Y d}	~	yd}	~	ww xY w)z?Fetch CI rollup status via gh pr view --json statusCheckRollup.r   r   viewr   statusCheckRollupr   r   NrU   
conclusionr^   FAILUREFAILEDc              3  *   K   | ]  }|s|d v   yw)>   SUCCESS	COMPLETEDNr    ).0ss     r"   	<genexpr>z$_gather_ci_status.<locals>.<genexpr>n  s     Bq,,Bs   	r   PENDINGz$CI status fetch failed for PR %s: %s)r3   r   r   r   r   r   rt   r   r   r   r   r   r   )
r5   r   r   r|   r   datarollupr   statesr   s
             r"   _gather_ci_statusr   S  s    n9 7 9
:CdFC	N- 
 <<1AHHNN$4zz!((#-.4"PVW$((7#Ctxx'=CCWW(f"4BfBB X  =y#Ns<   AC.  6C. C. /C)

C. C. )C. .	D7DDr   timer_loaderc                  |		  ||       S |xs
 t               t        z  }	 |j                         si S |j                  d      }t        j                  |      }|j                  di       }|j                  | i       S # t         $ r#}t        j                  d| |       i cY d}~S d}~ww xY w# t         $ r#}t        j                  d| |       i cY d}~S d}~ww xY w)z?Load timer info from task-timers.json or injected timer_loader.Nztimer_loader failed for %s: %sutf-8encodingtasksz'task-timers.json read failed for %s: %s)
r   r   r   r   _TIMERS_FILE_NAMEexists	read_textrt   r   r   )r4   r   r   r   timers_filerawr   r   s           r"   _gather_timer_infor   v  s     	((
 ">%<%>BSSK	!!#I##W#5zz#"%yy"%%  	NN;WcJI	  @'3O	s@   A? B. A
B. ?	B+B& B+&B+.	C7CCCr   c                   |xs
 t               }t        |      d fd} |d      }d}|rZ	   dz  j                         j                  }t	        j
                  t        j                        j                         |z
  }|dz  } |d       |d       |d       |d	       |d
      ||dS # t        $ r d}Y 5w xY w)z%Check file markers in memory/events/.c                4     d|  z  j                         S r   )r   )r   evr4   s    r"   r   z$_gather_file_markers.<locals>.exists  s"    y&**2244r!   zdone.escalatedNz.done.escalatedg      N@done
done.acked
merge-donez	qc-resultzfollowup.txt)r?   r@   rA   rB   rC   rD   rE   )r   r3   rw   r9   )
r   r   statst_mtimer   r   r   r   r_   r   )	r4   r   wdr   has_escalateescalate_agemtimeage_secsr   s	   `       @r"   _gather_file_markersr     s     
	424B	RB5 *+L$(L	 WI_55;;=FFE||HLL1;;=EH#d?L
 6N . .,~.+'3   	 L	 s   AB1 1B?>B?c                    | sy| j                         }t        | j                  d            t        k\  ry|rd|d   cxk  rdk  ry d|v r|j	                  d      }|dz  d	k7  ryy)
z<Heuristic: response is likely truncated if it ends abruptly.Fr   Tu   가u   힣z```rp   r   )rstriplenencode$_TELEGRAM_TRUNCATION_THRESHOLD_BYTEScount)r   strippedr   s      r"   _is_truncated_responser     sq    {{}H
4;;w $HHEXb\2U2 3 u%19>r!   cron_history_dirc                  |xs t         }ddd}	 |j                         s|S d}t        |j                  d      d d      D ]  }	 |j	                  dd	
      j                         }t        |      D ]I  }|j                         }|s	 t        j                  |      }|j                  dd      xs d}	| |	v sG|} n | n ||S |j                  dd      xs d}
t        |
      |d<   |j                  dd      }|t        |      |d<   |S # t        j                  $ r Y w xY w# t        $ r Y w xY w# t        $ r"}t        j!                  d| |       Y d}~|S d}~ww xY w)zIScan cron history logs to detect Telegram cut-off and bot session status.FN)rF   rG   z*.logc                6    | j                         j                  S r   )r   r   )ps    r"   <lambda>z&_gather_cron_history.<locals>.<lambda>  s    HYHY r!   T)keyreverser   replace)r   errorspromptr^   responserF   statusrG   z#cron history scan failed for %s: %s)_DEFAULT_CRON_HISTORY_DIRr   sortedglobr   
splitlinesreversedr   rt   r   JSONDecodeErrorr   r   r   r3   r   r   )r4   r   history_dirresultmatching_last_recordlog_filelineslinerecordr  r  r  r   s                r"   _gather_cron_historyr    s    #?&?K$)"F
(L!!#M 04{//8>Ycgh 	H **GI*NYY[$UO D::<D !!%D!1 $ZZ"5;F&(/5, (3 4!	*  'M (++J;Ar-CH-M)* &))(D9+.v;F'(
 M5  // ! !    L<gsKKMLsr   D9 #D9 AD*
DD*:D*D9 	AD9 D'$D*&D''D**	D63D9 5D66D9 9	E$EE$c          	        |xs
 t               }|dz  }dddddd}	 ddl}t        ||  dz        }|j                  |      }t        |      |d<   |rL	 t	        d |D              }	t        j                  t        j                        j                         |	z
  }
|
|d	<   	 t        d
dddd|  dgt        |      |      }|j                  dk(  r$|j                  j                         r
d|d<   d|d<   	 |dz  dz  }|j#                         r$t%        |j                  |  d            }|rd|d<   |S # t        $ r Y w xY w# t        $ r Y w xY w# t        $ r!}t        j!                  d| |       Y d}~d}~ww xY w# t        $ r Y |S w xY w)uL   Check worktree + branch + (task-2521 §3) mtime / pushed commits / artefact.r   FN)rH   rI   rJ   rK   rL   r   z-*rH   c              3     K   | ]A  }t        |      j                         st        |      j                         j                   C y wr   )r	   r   r   r   )r   cs     r"   r   z(_gather_worktree_info.<locals>.<genexpr>  s2      #0147>>CSDGLLN++#s
   A	(A	rJ   r   z	ls-remotez--headsoriginr   *r   TrI   rK   zls-remote failed for %s: %smemoryreportsrL   )r   r  r3   r9   maxr   r   r   r   r_   r   r   r   r   r   r   r   r   rz   )r4   r   r   r   worktrees_rootr  _globpattern
candidateslatest_mtimer   r   r   reports_dirmatchess                  r"   _gather_worktree_infor"    s    
	424B,&N #(&*##(Fn'"~56ZZ(
$($4 !" #5?#   $<<5??ALP7?34
DKHgYa6HIB

 <<1!104F,-+/F'(
8mi/;++wiqM:;G4801 M9     D4gsCCD  Ms[   6E AD2  AE 4<E= 2	D>;E =D>>E 	EE	E:E55E:=	F
	F
r   r   r   r   r   c                  |	 t        | |      }|j                  }n|}t	        | |||      }|j                  d      }	|j                  d      }
|
rt        |
      j                         nd}|j                  d      }t        |t              r%|j                  d      xs |j                  d      }nt        |t              r|r|}nd}d	}|rt        |||
      }d}|	t        |	||
      }d}t        |      }||  dz  }|j                         r1	 |j                  d      j                         }d|v rd}n	d|v rd}nd}t        | ||      }|j                  d      }|j                  d      }t!        | |      }t#        | |      }t%        | ||
      }t'        d.i d| d|	d|d|d|d|d|d|d|d|d   d |d    d!|d!   d"|d"   d#|d#   d$|d$   d%|d%   d&|d&   d'|d'   d(|d(   d)|d)   d*|j                  d*      d+t)        |j                  d+d	            d,t)        |j                  d,d	            d-dS # t        $ r
 t        }Y Bw xY w# t        $ r d}Y /w xY w)/u  모든 evidence source 수집 — gh/git/file/timer/cron history.

    workspace_root가 None이면 resolve_canonical_workspace(task_id, fetch=False)로
    자동 결정 (§6 CanonicalWorkspace 연동). 실패 시 fallback Path('/home/jay/workspace').
    Nr   r   numberrU   mergeCommitoidshaFr   z
.qc-resultr   r   PASSFAILSKIPPEDr   r  end_timer   r   r4   r5   r7   r8   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   rD   rE   rF   rG   rH   rI   rJ   rK   rL   rM   r    )r   r   r   r   r   r   r3   upper
isinstancerx   r   r   r   r   r   r   r   r  r"  r2   r9   )r4   r   r   r   r   r   cwsr   pr_infor5   	raw_stater7   raw_mcr8   r:   r;   r<   ev_dirqc_filecontent
timer_infor=   r>   markerscronwt_infos                             r"   gather_evidencer:  ?  sT    	$&8&PC##B  	G  '{{84IG$I8Ac)n224tH [['F&$&,jj&7&L6::e;L	FC	 V 2
  $I%irR	 #'L_F'*--G~~		 '''9??AG %7"%(
 $GB\ZJ",..":L$.NN:$>N #72>G  :JKD $GF2NG   "	
 *  " " & $ /0 /0 o. ^, $$9:  %,,I$J!" "&&@!A#$   45%&   12'( !((A B), $+;;/K#L-.  ,@% HI/0 !%W[[1JE%R S12 3 K  	$#B	$h  	 L	 s#   I 0I& I#"I#&I54I5c                X	   g }| j                   dk(  rT| j                  dk(  rE|j                  t        t        j
                  d| j                   d| j                   d             | j                  dk(  rD| j                  s8|j                  t        t        j                  d| j                   d             | j                  rD| j                  s8|j                  t        t        j                  d| j                   d	             | j                  d
k(  rz| j                  dk(  rk| j                  dk(  r\| j                  r| j                  sD|j                  t        t        j                  d| j                   d| j                                | j                   r8|j                  t        t        j"                  d| j$                   d             | j$                  dv rD| j&                  r8|j                  t        t        j(                  d| j$                   d             | j*                  rB| j,                  r6| j                  s*|j                  t        t        j.                  d             | j$                  dk(  r| j0                  duxr | j0                  t2        k  }| j                  dk(  }| j                  du }| j                   dk(  }|rk| j                  r| j                  sS|j                  t        t        j4                  d| j                   d| j                   d| j                   d             n|rE| j6                  r9|j                  t        t        j8                  d| j,                   d             n|r;|r9|j                  t        t        j:                  d| j*                   d             n\|rZ|j                  t        t        j<                  dt2        dd| j0                  dd| j                    d| j                                | j>                  r|| j@                  xs d d!kD  ri| j                  d"v xs | j                  dk(  xr | j&                   }|s9|j                  t        t        jB                  d#| j@                  dd$             |S )%u   8 케이스 자동 감지.runningr   z4timer_status=running but pr_state=MERGED (pr_number=, merge_commit=)rj   z4pr_state=MERGED but .done marker missing (pr_number=merge_commit=z  present but .merge-done missingr   r)  z5ci=SUCCESS, smoke=PASS, pr=MERGED but has_done_acked=z, has_merge_done=zAcron history last response appears truncated (bot_session_status=>   error	cancelledzbot_session_status=z0 but merged_into_main=True (task evidence is OK)uo   worktree exists + branch pushed to remote but no PR found — finish-task likely interrupted before PR creationrA  NOPENz*bot_session=cancelled + pr_state=OPEN (pr=u$   ) + finalize 미완 (has_done_acked=zMbot_session=cancelled + pr_missing + commits_pushed (branch_pushed_to_remote=zKbot_session=cancelled + timer_status=running + pr_missing (worktree_exists=u1   bot_session=cancelled + worktree active (mtime≤z.0fzs ago: z.1fzs); timer_status=z, pr_state=r   r~   >   r   zescalate marker exists for z# min but no Critical evidence found)"r=   r7   appendrP   r$   r%   r5   r8   r?   r&   rA   r'   r;   r<   r@   r(   rF   r)   rG   r:   r*   rH   rI   r+   rJ   ._BOT_CANCELLED_ACTIVE_WORKTREE_MAX_AGE_SECONDSr0   rK   r/   r-   r.   rD   rE   r,   )rY   cases	wt_activepr_open
pr_missingtimer_runninghas_active_criticals          r"   detect_stuck_casesrK    s   E 	)h.?.?8.KY::&001AVAV@WWXZ
 	 H$X->->Y99&0014
 	 X%<%<YBB 5 566VW
 	 	i'!!V+)((0G0GY88""*"9"9!::KHLcLcKdf
 	 ((Y55''/'B'B&C1F
 	 ""&<<AZAZY<<%h&A&A%B C> ?
 	 	  ,,""Y66D
 	 ""k1 //t; >33=> 	
 ##v-''4/
 --: ''x/F/FLL"II@ASAS@T U77?7N7N6O P&&.&=&=%>aA  H77LL"GG0080P0P/QQRT  MLL"II((0(@(@'AD  LL"EE  NsSSZ::3? @$$,$9$9#:+hFWFWEXZ  ##)M)M)RQRVX(X +- N(*L83L3L/L 	 #LL"881;;C@@ce  Lr!   c                0   t        |       }| j                  dk(  rR| j                  rF| j                  dv r8| j                  r,| j
                  r | j                  dv rt        j                  g fS | j                  dk(  r|| j                  rpg }| j                  s|j                  d       | j
                  s|j                  d       | j                  dk(  r|j                  d       |rt        j                  |fS | j                  dk(  r | j                  s|rt        j                  |fS | j                  dk(  r5| j                  dk(  r&|rt        j                  |fS t        j                  g fS | j                  dk(  r5| j                  dk7  r&|rt        j                  |fS t        j                  g fS | j                  r'| j                  xs d	}|d
k  rt        j                   |fS |rt        j                  |fS | j                  | j                  t        j                  g fS | j                  dk(  r| j                  t        j                  g fS | j                  dk(  r*| j                  r| j
                  rt        j                  g fS t        j                  g fS )u   evidence priority 규칙으로 state + stuck cases 결정.

    우선순위 (회장 명시):
    PR state > mergeCommit > origin/main 포함 > CI > smoke > timer > file marker
    r   >   Nr   >   N	completedz.done.ackedz.merge-doner<  z	timer-endrB  r   r~   rM  )rK  r7   r:   r;   r@   rA   r=   r   r   rC  r   r   r   r   rD   rE   r   )rY   rX   missingages       r"   determine_staterP  _  sp    %X.K 	X%%%"33####!!%88''++ H$)B)B&&NN=)&&NN=)  I-NN;'!::KGG H$X-F-F!77DD F"x'<'<	'I!77DD%%r)) F"x'<'<	'I!77DD%%r)) ##227a"9!++[88 33[@@  X%:%:%B%%r)) 	)h.?.?.G%%r)) +0G0GHLcLc''++ !!2%%r!   c                  |xs
 t               }t        || d      }|j                  dk(  xr |j                  }t	        |j
                        xr. |j                  xr  |j                  dk(  xs |j                  dk(  }|xs |}|sRt        d|  d| d|j                   d|j                   d	|j
                   d
|j                   d|j                         y)u  evidence가 부족한데 강제로 .done을 만들려고 하면 RuntimeError.

    충분한 evidence:
    - pr_state == MERGED and merged_into_main, OR
    - merge_commit not None and merged_into_main and (ci_status == SUCCESS or smoke_status == PASS)
    r   r   r   r)  z"MANUAL_DONE_FORGERY_BLOCKED: task=uB    — insufficient evidence to create .done. Target path would be: zt. Requires (pr_state=MERGED + merged_into_main) OR (merge_commit + merged_into_main + ci/smoke PASS). Got: pr_state=z, merged_into_main=r=  z, ci_status=z, smoke_status=N)	r   r   r7   r:   r9   r8   r;   r<   RuntimeError)r4   rY   r   r   	done_pathcond_acond_bhas_sufficients           r"   assert_no_manual_done_forgeryrW    s
    
	424BR&1I(*Hx/H/HFX""# 	Q%%	Q9,O0E0E0O 
 %vN0	 :%%.K 0 &..//B8C\C\B] ^$112,x?Q?Q>R S$1124
 	
 r!   c                   g }|j                   r|j                  d|j                           |j                  r|j                  d|j                          |j                  r|j                  d|j                          |j                  r|j                  d|j                          |j
                  r|j                  d       | d|t               ||j                  ddS )	z,Build JSON body for backfilled marker files.z	pr_state=r?  z
ci_status=zsmoke_status=zmerged_into_main=True lifecycle_reconciliation_managerz,evidence-based backfill (not manual forgery))r4   reconciled_byr`   r_   evidence_sourcer8   note)r7   rC  r8   r;   r<   r:   r   )r4   r`   rY   r[  s       r"   _build_backfill_metadatar]    s     "$O8+<+<*=>?x/D/D.EFGH,>,>+?@Ax/D/D.EFG  67 ;,Z* --> r!   c                d    | j                   j                  dd       | j                  |d       y )NTparentsexist_okr   r   )parentmkdir
write_text)pathr5  s     r"   _default_file_writerrf    s)    KKdT2OOGgO.r!   c                <   t               t        z  }	 |j                         r|j                  d      nd}t	        j
                  |      }|j                  di       }||| <   |j                  t	        j                  |dd      d       y# t        $ r i }Y Nw xY w)	z2Write updated timer data back to task-timers.json.r   r   z{}r   rp   Frq   N)
r   r   r   r   rt   r   r   
setdefaultrd  ru   )r4   
timer_datar   r   r   r   s         r"   _default_timer_writerrj    s    )+.??K9D9K9K9Mk##W#5SWzz# OOGR(EE'N4::d15IT[\	  s   9B BBF)r   applyfile_writertimer_writerc                  	 |xs
 t               }
||nt        ||nt        t         ||      }t	        |t
              r|j                  n
t        |      |d<   t        j                  |dd      d	fd}	 t         ||
       |j                  st!        |
 d      } |d|ffd	       |j"                  st!        |
 d      } |d|ffd	       |j$                  st!        |
 d      } |d|ffd	       |j&                  dk(  r t)               }d|d|d} |d|f fd	       |S # t        $ rJ}t        j                  d|       rj                  d	|        n	j                  d
       |cY d}~S d}~ww xY w)zBackfill missing markers based on evidence. Returns backfill_metadata.

    `state` is recorded in metadata so reconcile audit trails preserve which
    LifecycleState authorised the backfill.
    Nauthorising_staterp   Frq   c                    r	  |        j                  |        y j                  |        y # t        $ r!}t        j                  d| |       Y d }~y d }~ww xY w)Nzbackfill action %s failed: %s)rC  r   r   r@  )action_namer   r   r\   r[   rk  s      r"   
do_or_planz%_backfill_markers.<locals>.do_or_plan  sW    P$$[1 "";/  P<k3OOPs   0 	AAAr   zBackfill blocked: %szBLOCKED:zBLOCKED (insufficient evidence)r   created_donec                     |       S r   r    r   fw	meta_jsons    r"   r   z#_backfill_markers.<locals>.<lambda>6  s    "Q	2B r!   r   created_done_ackedc                     |       S r   r    ru  s    r"   r   z#_backfill_markers.<locals>.<lambda>;  s    1i8H r!   r   wrote_merge_donec                     |       S r   r    ru  s    r"   r   z#_backfill_markers.<locals>.<lambda>@  s    bI6F r!   r<  rM  rY  )r  r,  ended_byr`   ended_timerc                     |       S r   r    )tpr4   tws    r"   r   z#_backfill_markers.<locals>.<lambda>K  s    2gr? r!   )rq  r3   r   zCallable[[], None]rw   None)r   rf  rj  r]  r.  r   rl   r3   rt   ru   rW  rR  r   r   rC  r?   r   r@   rA   r=   r   )r4   r`   rY   rU   r   rk  rl  rm  r[   r\   r   metarr  r   re  timer_end_isotimer_payloadrv  rw  r  s   `    `  ``       @@@r"   _backfill_markersr    s   & 
	424B#/5IB%17LB#G-=xHD/9%/PVYZ_V`D	

4>I0%gxK B0>D#BC ""B6'$)HI ""B6%'FG 	) 
!%: 0	
 	=M"JKKE  -s3  8C5!12""%DFs   D9 9	F?FFFc                    	 | j                         si S | j                  d      }|j                         si S t        j                  |      }t        |t              si S |S # t        $ r#}t        j                  d| |       i cY d}~S d}~ww xY w)z7task-timers.json / task-timers-archived.json safe read.r   r   z timers file read failed (%s): %sN)
r   r   r   rt   r   r.  rx   r   r   r   )re  r   r   r   s       r"   _read_timers_filer  c  sz    {{}Inngn.yy{Izz#$%I 94E	s-   A! #A! &A! A! !	B*BBBc                   t        |      }t        |j                  d      t              r| |d   v ryt        |      }t        |j                  d      t              r| |d   v ryy)uB   Return (present, source) where source ∈ {"active","archive",""}.r   )Tactive)Tarchive)Fr^   )r  r.  r   rx   )r4   active_patharchive_pathr  r  s        r"   _timer_entry_presentr  t  s^     {+F&**W%t,F7O1K-G'++g&-'WW=M2Mr!   c                :   t        | |d      }|j                         si S 	 |j                  d      j                         }|si S t	        j
                  |      }t        |t              si S |S # t        $ r#}t        j                  d||       i cY d}~S d}~ww xY w)z<Read memory/events/<task_id>.done JSON if present (else {}).r   r   r   z done JSON read failed for %s: %sNr   r   r   r   rt   r   r.  rx   r   r   r   r   r4   r   r   r   r   s         r"   _read_done_jsonr    s    ^Wf5A88:	
kk7k+113Izz#$%I 97CH	)   #A. &A. ,A. .	B7BBBc                :   t        | |d      }|j                         si S 	 |j                  d      j                         }|si S t	        j
                  |      }t        |t              si S |S # t        $ r#}t        j                  d||       i cY d}~S d}~ww xY w)zBRead memory/events/<task_id>.merge-done JSON if present (else {}).r   r   r   z&merge-done JSON read failed for %s: %sNr  r  s         r"   _read_merge_done_jsonr    s    ^Wl;A88:	
kk7k+113Izz#$%I ?#N	r  c               v   t        ||       }t        ||       }|j                  d      xs7 |j                  d      xs$ |j                  d      xs |j                  d      }|j                  d      xs |j                  d      }|j                  d      xs |j                  d      }|j                  d      xs |j                  d      }	|rdn|rdnd}
|sm|j                  ra	 t	        || d	      }t        j                  |j                         j                  t        j                  
      }|j                         }|
xs d}
|sm|j                  ra	 t	        || d      }t        j                  |j                         j                  t        j                  
      }|j                         }|
xs d}
|syd}|rQ	 ddlm} t        j                  |j!                  dd            }| |t#        |            z
  }|j                         }||}| |||dd||
xs dt%               dd
}|	 t#        |      |d<   |	r|	|d<   |j&                  r|j&                  |d<   |j(                  |j(                  |d<   |S # t        $ r Y @w xY w# t        $ r Y w xY w# t        $ r d}Y w xY w# t        $ r Y zw xY w)uA  Construct timer entry from .done / .merge-done evidence.

    Priority:
      1. .done JSON (task-2528 primary source — has end_time/duration/team_id)
      2. .merge-done JSON
      3. .done file mtime (last resort; flagged in metadata)
    Returns None if no completion evidence available (caller decides no-op).
    r,  completed_atteam_idduration_seconds	qc_result	done_jsonmerge_done_jsonNr   )tz
done_mtimer   merge_done_mtimer   )	timedeltaZz+00:00)secondsrM  rY  rY   u<   task-2528: worktree completion → task-timers.json backfill)
r4   r  
start_timer,  r  r|  r`   reconciled_fromreconciled_atr\  r8   r5   )r  r  r   r?   r   r   fromtimestampr   r   r   r   r   r   rA   r  fromisoformatr   floatr   r8   r5   )r4   r`   rY   r   	done_datamerge_done_datar,  r  r  r  sourcerS  r   md_pathr  r  end_dtstart_dtentrys                      r"   _build_reconciled_timer_entryr    s     8I+NGDO 	j! 	/==(	/z*	/ ~.	  	i  	*y) 
 	() 	312 
 	k" 	,{+ 
 &[,=VZF ))	$^WfEI**9>>+;+D+DVE(H+|F //	">7LIG**7<<>+B+Bx||TE(H11F  !%J	*++H,<,<S(,KLF	%8H2I JJH!++-J 
  6,!/Z!NE #	(-.>(?E$% &k ( 5 5n%%//kLk  		  		  	J	(  		sK   (A I< A J ?AJ /J, <	J	J		JJJ)(J),	J87J8)r   rk  active_timer_inserterc               l   |xs
 t               }|t        z  }	|t        z  }
|j                  xs |j                  xs |j
                  dk(  }| |ddddd}|sd|d<   d|d<   |S t        | |	|
	      \  }}|rd|d<   d
| |d<   |S t        | |||      }|d|d<   d|d<   |S |j                  d      |d<   ||d<   |}||fdd}|}d}|r"	  || |       d|d<   |j                  |       |S |j                  |       |S # t        $ r*}t        j                  d| |       d| |d<   Y d}~|S d}~ww xY w)uB  Bridge worktree-completion evidence into task-timers.json (task-2528 fix).

    Trigger conditions (ALL required):
      - Has completion evidence: has_done OR has_merge_done OR pr_state == MERGED
      - Active task-timers.json has no entry for task_id
      - Archived task-timers-archived.json has no entry for task_id

    On match: build entry from .done JSON / .merge-done JSON / mtime fallback,
    insert into active timers (idempotent — no-op if any subsequent run finds entry).

    Returns metadata dict (caller stores it in LifecycleReport.backfill_metadata).
    r   FN)r4   r`   skippedrQ   entry_insertedr  Tr  no_completion_evidencerQ   )r  r  timer_entry_present_in_r   %no_usable_evidence_for_reconstructionr  r  proposed_entryc                B   |t         z  }t        |      }t        |t              si }|j	                  di       }t        |t              si }||d<   | |v ry ||| <   |j
                  j                  dd       |j                  t        j                  |dd      d       y )	Nr   Tr_  rp   Frq   r   r   )
r   r  r.  rx   rh  rb  rc  rd  rt   ru   )tident_wdr   r   r   s         r"   _scoped_inserterzB_reconcile_worktree_completion_to_timers.<locals>._scoped_inserterX  s     11K$[1DdD)OOGR0EeT* %We|E#J$$TD$A""

4>  # r!   reconciled_worktree_timerr  z1worktree timer reconcile insert failed for %s: %szinsert_failed: )r  r3   r  rx   r  r	   rw   r  )r   r   _TIMERS_ARCHIVE_FILE_NAMEr?   rA   r7   r  r  r   rC  r   r   r@  )r4   r`   rY   r   rk  r  r[   r\   r   r  r  has_completion_evidencer  presentr  r  inserterr  action_labelr   s                       r"   (_reconcile_worktree_completion_to_timersr    s   . 
	424B((K11L 	 	)""	)(  ,D #Y1X*!OGV
 Y26(;X)	E }Y@XYY01DN"D	$H>@ 	$ $.L	5We$%)D!"  . K 	|,K  	5LLLgWZ[.se4DN K	5s   D   	D3	D..D3)rk  r   r   r   r   r   rm  rl  c                  t        j                         j                  }	t               }
t	        | |||||      }t        |      \  }}g }g }i }|t        j                  k(  rt        j                  d|        nY|t        j                  t        j                  hv rt        | |	||||||||
      }n!t        j                  d| |j                         t        | |	|||||      }|j!                  d      s|j#                  d|       n7|j!                  d      r&|d   j%                  d	      r|j#                  d|       t'        | |||||| |
|	|

      S )uo  idempotent reconcile.

    - state == FINALIZED → no-op
    - state == MERGED_PENDING_RECONCILE + apply=True → backfill with evidence metadata
    - state == STUCK_NEEDS_RECONCILE + apply=True → reason별 backfill
    - apply=False → actions_planned에만 기록, 실제 변경 없음
    - 동일 reconcile 반복 호출 → 동일 state, no-op (멱등)
    r#  z task=%s already FINALIZED, no-op)r   rk  rl  rm  r[   r\   z$task=%s state=%s, no backfill needed)r   rk  r[   r\   r  worktree_timer_reconcilerQ   r  rk   )uuiduuid4hexr   r:  rP  r   r   r   infor   r   r  debugrl   r  r   rh  
startswithrT   )r4   rk  r   r   r   r   r   rm  rl  r`   r_   rY   rU   rX   r[   r\   ra   timer_reconcile_metas                     r"   	reconciler    s~   ( zz|''
I%)!H )2E;!M!#O(*(((6@	//,, 
 .)#%'+
 	;WekkR
 D%#'  ##I.$$%?AUV		!	!(	+0DX0N0Y0YZs0t$$%?AUV#'	)+ r!   )r   r   rk  r   c                   | xs
 t               }|t        z  }g }	 |j                         rP|j                  d      }t	        j
                  |      }t        |j                  di       j                               }|st        j                  d       g S g }
|D ]o  }d}|d
d} ||      }	 t        |||||      }|j                  t        j                   t        j"                  t        j$                  hv r|
j'                  |       q |
S # t        $ r }	t        j                  d|	       Y d}	~	d}	~	ww xY w# t        $ r!}	t        j)                  d	||	       Y d}	~	d}	~	ww xY w)u:   task-timers.json 전체 순회 → STUCK인 것만 보고.r   r   r   z/scan_stuck: failed to load task-timers.json: %sNz.scan_stuck: no tasks found in task-timers.jsonc                     d fd}|S )Nc                V            }|j                  di       j                  | i       S )Nr   )r   )r4   
all_timerstls     r"   _adapterz3scan_stuck.<locals>._make_adapter.<locals>._adapter  s(    !#J%>>'26::7BGGr!   )r4   r3   rw   rx   r    )r  r  s   ` r"   _make_adapterz!scan_stuck.<locals>._make_adapter  s    H  r!   )rk  r   r   r   z'scan_stuck: reconcile failed for %s: %s)r  zCallable[[], dict]rw   zCallable[[str], dict])r   r   r   r   rt   r   rz   r   keysr   r   r   r  r  rU   r   r   r   r   rC  r@  )r   r   rk  r   r   r   task_idsr   r   r   stuck_reportsr  adapted_loaderr  reports                  r"   
scan_stuckr    sp    
	424B((KHO'''9C::c?DDHHWb16689H DE	+-M N:># 
 +<8N	N!+F ||4477((  
 $$V,1N8 K  OH#NNOD  	NLLBCMM	Ns1   A D ,AD8	D5D00D58	E"EE"c                "    | j                   rdS dS )u   automation_contracts.SmokeResult → lifecycle smoke_status string.

    expects post_merge_smoke_runner.SmokeResult.passed →
    smoke_status='PASS' if True else 'FAIL'
    r)  r*  )passed)srs    r"   smoke_result_to_statusr    s     YY6*F*r!   c                   | j                   t        j                  k(  rd}d}d}n\| j                   t        j                  k(  rd}d}t        j
                  }n*| j                   t        j                  k(  rd}d}d}nd}d}d}t        || j                  D cg c]  }|j                  j                   c}|| || j                  | j                  | j                  d	      S c c}w )
u   LifecycleReport를 AutomationDecision으로 매핑 (자동화 의사결정 contract 연동).

    회장 §6: CanonicalWorkspace + automation_contracts 연동 필수.
    NO_OPFNESCALATETBACKFILLMONITOR)r4   r`   r_   )decisionreason_codescritical_escalation_typeauto_handledrequires_chairaudit)rU   r   r   r   r   POST_MERGE_SMOKE_FAILEDr   r   rX   rQ   rl   r4   r`   r_   )r  decision_strr  criticalr  s        r"   build_automation_decisionr  %  s    
 ||~///59	11	1!)AA	@@	@! .4.@.@AahhnnA!)((%~~ & 7 7))
 As   C!
)r5   c                   | j                   t        j                  k7  rydj                  d | j                  D              xs d}t        | j                  |t        j                  |dg dd| j                               S )	u   ESCALATED state에 대해 EscalationPacket 생성. 그 외 None.

    ⚠️ lifecycle은 일반적으로 ESCALATED를 직접 발동하지 않으므로,
    이 헬퍼는 explicit caller가 호출해야 함.
    Nz; c              3  f   K   | ])  }|j                   j                   d |j                    + yw)z: N)rQ   rl   rR   )r   rm   s     r"   r   z*build_escalation_packet.<locals>.<genexpr>T  s-      .0299??
2bii[)s   /1z"lifecycle ESCALATED state detecteduH   lifecycle state is ESCALATED — human review required before proceeding)'Review stuck_cases and resolve manuallyz0Run reconcile --apply after resolving root causez!Escalate to chair if unresolvabler  )r4   r5   escalation_typerQ   why_auto_cannot_continuesafe_optionsrecommended_optionrY   )
rU   r   r   joinrX   r   r4   r   r  rh   )r  r5   stuck_detailss      r"   build_escalation_packetr  I  s     ||~///II 4:4F4F  .	-  .FFV

 E((* r!   c                 J   t        j                  d      } | j                  d      }|j                  ddd       |j                  d	dd
       | j                  dd       | j                  dddd       | j                  ddddd       | j                  dd d       | S )NuW   lifecycle_reconciliation_manager — idempotent task lifecycle reconcile (task-2518 P0))descriptionT)requiredz--reconcile
store_truez,Reconcile a single task (requires --task-id))actionhelpz--scan-stuckz8Scan all tasks in task-timers.json and report stuck onesz	--task-idz/task-NNNN identifier (required for --reconcile))r  z--applyFz*Actually apply backfill (default: dry-run))r  defaultr  r   output_jsonzOutput as JSON (default: True))r  destr  r  z--workspace-rootzOverride workspace root path)r  r  )argparseArgumentParseradd_mutually_exclusive_groupadd_argument)r   groups     r"   _build_cli_parserr  n  s    A	A **D*9E	;  
 
G  
 NN;%VNWNN9	   NN-   NN+  
 Hr!   c           	        t               }|j                  |       }d }|j                  rt        |j                        }|j                  rZ|j
                  s|j                  d       	 t	        |j
                  |j                  |      }t        |j                                y |j"                  rW	 t#        ||j                        }|D cg c]  }|j%                          }}t        t        j                  |dd	             y y # t        $ rc}t        t        j                  t        |      |j
                  d      t        j                         t        j                   d       Y d }~y d }~ww xY wc c}w # t        $ rX}t        t        j                  d
t        |      i      t        j                         t        j                   d       Y d }~y d }~ww xY w)Nz--reconcile requires --task-id)rk  r   )r@  r4   )file   )r   rk  rp   Frq   r@  )r  
parse_argsr   r	   r  r4   r@  rk  printrv   r   rt   ru   r3   sysstderrexitr  rn   )	argvparserr   r   r  r   r  r   outputs	            r"   	_cli_mainr    sb    FT"D%)Nd112~~||LL9:		jj-F
 &.."#
 
		 -jjG ,33aaiik3F3$**VAEBC 
	  	$**s3xDLLIJQTQ[Q[\HHQKK	 4 	$**gs3x01

CHHQKK	sD   );D	 2E= E8$#E= 		E5AE00E58E= =	GAGG__main__z#%(levelname)s %(name)s: %(message)s)levelformat)r   rZ   r|   r6   rw   subprocess.CompletedProcess)r   rZ   r|   zOptional[str | Path]r   Optional[RunnerType]rw   r  ry   )r   r	   rw   r	   )r   r	   r4   r3   r   r3   rw   r	   )rw   r	   )r4   r3   r   r  rw   r   )
r4   r3   r   r  r   Optional[Path]r   Optional[Callable[[str], dict]]rw   rx   )r8   r3   r   r  r   r  rw   r9   )r5   intr   r  r   r  rw   r6   )r4   r3   r   r  r   r  rw   rx   )r4   r3   r   r  rw   rx   )r   r3   rw   r9   )r4   r3   r   r  rw   rx   )r4   r3   r   r  r   r  rw   rx   )r4   r3   r   r  r   r  r   r  r   r  r   r  rw   r2   )rY   r2   rw   rW   )rY   r2   rw   z&tuple[LifecycleState, list[StuckCase]])r4   r3   rY   r2   r   r  rw   r  )r4   r3   r`   r3   rY   r2   rw   rx   )re  r	   r5  r3   rw   r  )r4   r3   ri  rx   rw   r  )r4   r3   r`   r3   rY   r2   rU   r   r   r  rk  r9   rl  %Optional[Callable[[Path, str], None]]rm  %Optional[Callable[[str, dict], None]]r[   rZ   r\   rZ   rw   rx   )re  r	   rw   rx   )r4   r3   r  r	   r  r	   rw   ztuple[bool, str])r   r	   r4   r3   rw   rx   )
r4   r3   r`   r3   rY   r2   r   r	   rw   zOptional[dict])r4   r3   r`   r3   rY   r2   r   r  rk  r9   r  r  r[   rZ   r\   rZ   rw   rx   )r4   r3   rk  r9   r   r  r   r  r   r  r   r  r   r  rm  r  rl  r  rw   rT   )
r   r  r   r  rk  r9   r   zOptional[Callable[[], dict]]rw   zlist[LifecycleReport])r  r   rw   r3   )r  rT   rw   r   )r  rT   r5   r  rw   zOptional[EscalationPacket])rw   zargparse.ArgumentParserr   )r  zOptional[list[str]]rw   r  )\r   
__future__r   r  rt   loggingr   r  r  dataclassesr   r   r   r   enumr   pathlibr	   typingr
   r   r   __file__resolverb  _HEREr3   re  insert"utils.canonical_workspace_resolverr   r   utils.automation_contractsr   r   r   r   	getLoggerr   r   CompletedProcess
RunnerTyper   r  r   r   r  r   r   r$   rD  r2   rP   rT   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r"  r:  rK  rP  rW  r]  rf  rj  r  r  r  r  r  r  r  r  r  r  r  r  r  r  basicConfigINFOr    r!   r"   <module>r.     s  < #     
  ( '   * * 	X ''..u:SXXHHOOAs5z"  
		8	$
 c:6667
/0  !GH " - >  (, $	S$ 	V#t V* 27 . !) !) !)H   
 &H &H &H` 
 
 !	" !%#'	?
? 
? !	?
 !?2-? HL 
4 $(%)15(( !( #	(
 /( 
(\ $(%)	 ! #	
 
2 $(%)	   !  #	 
  L &*48	 # 2	
 
< &* # 
	B, (,66 %6 
	6x $(%)	:: !: #	:
 
:H &*#''+1548rr #r !	r
 %r /r 2r rr`NG&d &*	 
 
 
 #	 

 
 
N   
	:/

]& &*9=:>JJJ  J 	J #J J 7J 8J J J 
J@"  	
  $$ccc  c
 c cV &*CGjjj  j
 #j j Aj j j 
jh %)#''+1548:>9=YY Y #	Y
 !Y %Y /Y 2Y 8Y 7Y YD &*#'156"6 !6 	6
 /6 6z+!J 23+.J%P F zGgll3XYK r!   