
    nai4              	       X   d 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 ej                  j                  dd      Z
dedefdZd	d
d
dddZdedee   fdZd&defdZdedefdZdedefdZdedefdZdefdZd Zd'dedee   fdZdddddddd d!Zd"edefd#Zd$ Zed%k(  r e        yy)(u   
bot_status_resolver.py — 봇 상태 라벨링 시스템
task-2375 (dev3/루)

배경: 2026-05-02 사고 — ps를 보지 않고 commits만 보고 봇 상태를 잘못 판단한 사건으로 인해
객관적 라벨로 자동 판정하는 도구 필요.
    N)OptionalWORKSPACE_ROOTz/home/jay/workspaceps_alivereturnc                 <    |ry| r||dk  ry| ry| s
||dkD  r|yy)uT  5개 라벨 판정 순수 함수.

    순서가 중요:
    1. MERGED — pr_merged_at 있으면 다른 조건 무시
    2. ALIVE  — ps_alive AND last_commit_age_min < 5
    3. IDLE   — ps_alive (last_commit_age_min >= 5)
    4. STALE  — NOT ps_alive AND last_commit_age_min > 30 AND pr_number is None
    5. UNKNOWN — 기타
    MERGED   ALIVEIDLE   STALEUNKNOWN )r   last_commit_age_minpr_merged_at	pr_numbers       L/home/jay/workspace/.worktrees/task-2516-dev3/scripts/bot_status_resolver.pyclassify_statusr      sA     '38Ka8O#/#b(!    	completedin_progressstalledunknownr   r
   r   r   r   task_idc                    t         j                  j                  t        dd      }	 t	        |d      5 }t        j                  |      }ddd       j                  d|      }|j                  |       }|r>t        |t              r.|j                  dd      }|j                  d	      r|dd
 }|r|S dS y# 1 sw Y   mxY w# t        $ r Y yw xY w)uQ   task-timers.json을 읽어서 task_id에 해당하는 팀 반환. 없으면 None.memoryztask-timers.jsonzutf-8)encodingNtasksteam_id z-team)ospathjoinr   openjsonloadget
isinstancedictendswith	Exception)r   timers_pathfdatar   entryteams          r   _infer_team_from_timersr3   =   s    '',,~x9KLK+0 	 A99Q<D	  $'		'"Zt,99Y+D}}W%CRy4)T) 	  	   s/   C B6
A(C 3C 6B?;C 	CCtimeoutc                     	 t        j                  | dt         j                  t         j                  d|      }|j                  j                         |j                  fS # t        $ r Y yw xY w)u+  subprocess로 명령 실행, (stdout, returncode) 반환. 실패시 graceful fallback.

    cmd는 list[str] (인수 분리). shell=False 고정 — task_id/team_id/pattern 등
    외부 입력이 그대로 흘러들어와도 command injection 불가.
    stderr는 무시 (graceful fallback).
    FT)shellstdoutstderrtextr4   )r!      )
subprocessrunPIPEDEVNULLr7   strip
returncoder-   )cmdr4   results      r   _runrC   U   sc    ??%%
 }}""$f&7&777 s   AA 	A+*A+r    c                    g }| r|j                  |        |r|j                  d|        g }|D ]z  }ddd|g}t        |      \  }}|s|j                         D ]N  }|j                         }|s|j	                  dd      }	|	s+	 t        |	d         }
|
|vr|j                  |
       P | t        |      |fS # t        $ r Y kw xY w)u^   ps aux로 프로세스 확인.

    Returns:
        (ps_alive: bool, ps_pids: list[int])
    zclaude.*pgrepz-az-fNr:   r   )appendrC   
splitlinesr?   splitint
ValueErrorbool)r   r    patternspidspatternrA   r7   _linepartspids              r   	_check_psrS   j   s     H (7),-D  dG,I	))+ zz|

4+!%(md? KK,& :t & s   #B::	CCc           	         g }|r|j                  d|  d|        |j                  d       |j                  d       t        t        j                               }|D ]  }|dv rddt        d|d	|  d
dg}nddt        d|d
dg}t	        |      \  }}|dk7  s|s<|j                         j                         }t        |      dk\  si	 t        |d         }	|d   }
t        ||	z
  dz        }||
fc S  y# t        $ r Y w xY w)u   git log에서 마지막 커밋 정보 반환.

    브랜치 우선순위:
    1. task/task-{id}-{team} (team_id 있을 때)
    2. main에서 task_id 메시지 매칭

    Returns:
        (last_commit_age_min: int|None, last_commit_sha: str|None)
    task/-mainHEAD)rW   rX   gitz-Clogz--grep=z--format=%ct %hz-1r      r:   <   )NN)	rF   rI   timer   rC   r?   rH   lenrJ   )r   r    branch_candidatesnowbranchrA   r7   rcrQ   	commit_tsshaage_mins               r   _get_last_commitrf      s3      5	7)!<=V$V$
diik
C# %% t^UF'#%6C t^UF!4C
 #Y
7&$$&u:?aM	AhsY"45|#/6   s   'C33	C?>C?c           	      d   |rd|  d| nd|  }dddd|ddd	d
g	}t        |d      \  }}|dk7  s|sy	 t        j                  |      }|rA|d   }|j                  d      }|j                  d      }	|j                  d      xs d}
||	|
fS 	 y# t        j                  t
        t        f$ r Y yw xY w)u|   gh pr list로 PR 정보 조회.

    Returns:
        (pr_number: int|None, pr_state: str|None, pr_merged_at: str|None)
    rU   rV   ghprlistz--headz--jsonznumber,state,mergedAtz--stateall   )r4   r   )NNNnumberstatemergedAtN)rC   r'   loadsr)   JSONDecodeError
IndexErrorKeyError)r   r    head_branchrA   r7   rb   r0   ri   rm   rn   	merged_ats              r   _get_pr_inforv      s     29E'!G9-gY>OK 	dFHk)9eC c2&JFB	Qwf	zz&!aBVVH%FFF7OEz*2dI5)++     *h7 s   AB B/.B/c                 *   t         j                  j                  t        dd      }ddddd}ddddd}|j	                         D ]L  \  }}t         j                  j                  ||  d	|       }t         j                  j                  |      ||<   N |S )
u   마커 파일 존재 여부 확인.

    Returns:
        dict with keys: done, work_done, merged, merge_failed (all bool)
    r   eventsF)done	work_donemergedmerge_failedry   z	work-doner{   zmerge-failed.)r#   r$   r%   r   itemsexists)r   
events_dirmarkerssuffixeskeysuffixr$   s          r   _check_markersr      s     nhAJ	G  &	H  ~~' ,Vww||J7)1VH(=>ww~~d+, Nr   c                    g }|r|D ]  }	|j                  d|	 d        n| s|j                  d       |r||j                  d| d| d       n(|r|j                  d|        n|j                  d       |r|j                  d|        n,||j                  d	| d| d
       n|j                  d       |j                         D ]  \  }
}|s	|j                  d|
         |S )Nzps:z (alive)zps:none (dead)zlast_commit: (zm ago)no_commit_foundz
pr_merged:zpr:)	no_pr_yetzmarker:)rF   r~   )r   ps_pidsr   last_commit_shar   pr_stater   r   evidencerR   r   vals               r   _build_evidencer      s	   H  	1COOc#h/0	1() .:,&7r:M9NfUV	,&789)* *\N34		#i[8*A67$ MMO -SOOgcUO,- Or   c                    |st        |       }t        | |xs d      \  }}t        | |xs d      \  }}t        | |xs d      \  }}}t	        |       }	t        ||||      }
t        |
   }t        ||||||||	      }| ||
||||||||	||dS )u,   전체 조사 수행 후 결과 dict 반환.r!   )r   r    
bot_statusr   r   r   r   r   r   r   r   verdictr   )r3   rS   rf   rv   r   r   VERDICT_MAPr   )r   r    r   r   r   r   r   r   r   r   r   r   r   s                r   resolver   "  s     )'2 "'7=b9Hg+;GW]PR+S((4Wgm(L%IxW%G !+>iXJ*%G '.8\7H  2*$ r   u	   에리카u	   마르스u   루u   오시리스u	   이시스u   페룬u   아누u   헤르메스)dev1dev2dev3dev4dev5dev6dev7dev8rB   c                 r   | j                  d      xs d}t        j                  ||      }| d   }| d   }| j                  d      }| j                  d      }| d   }|| dnd	}|rd
| nd}	| d| d| d| d| d|	 d| d| d| d| dd}
|
j                  || d|       }| d| d| d| S )Nr    r   r   r   r   r   r   u   분 전 commitu   commit 시간 불명zPR #u   PR 생성 중u    완료 (머지됨)u    활발히 진행 중 (r   u    진행 중 (z, u	    멈춤 (u   , PR 없음)u    상태 불명 (r    r   z): u    — )r)   _TEAM_NAMES)rB   r    	team_namestatusr   ager   r   age_strpr_strstatus_descdescs               r   _format_humanr   U  s   jj#0yG1IL!FYG
***
+C

;'IYG(+^$=SG#,tI;/F I0193G9A>)=	F81=9IgYl;Y.wiq9K ??6gYay#9:DYb3vheD6::r   c                     t        j                  d      } | j                  ddd       | j                  ddd d	
       | j                  dddgdd       | j                         }t	        |j
                  |j                        }|j                  dk(  rt        t        |             y t        t        j                  |dd             y )NuK   봇 상태 라벨링 시스템 — ps/git/gh/marker 기반 객관적 판정)descriptionz	--task-idTu"   조사할 task ID (예: task-2375))requiredhelpz--teamr    u?   팀 ID (예: dev3). 미지정 시 task-timers.json에서 추론)destdefaultr   z--formatr'   humanu   출력 형식 (기본: json))choicesr   r   Fr[   )ensure_asciiindent)argparseArgumentParseradd_argument
parse_argsr   r   r    formatprintr   r'   dumps)parserargsrB   s      r   rW   rW   q  s    $$aF d9]^
y$^  `

VW,=v;  =DT\\4<<0F{{gmF#$djjeA>?r   __main__)
   )N)__doc__r   r'   r#   r;   r]   typingr   environr)   r   rK   strr   r   r3   rI   rC   rS   rf   rv   r   r   r   r   r+   r   rW   __name__r   r   r   <module>r      s1     	    02GHd UX 2 S Xc] 0s *"s "S "J-c -C -`#  >C : N$S $8C= $Z eKn;$ ;3 ;8@& zF r   