
    j#                    2   U d Z ddlmZ ddlmZ ddlmZmZmZ dZ	de
d<   dZde
d	<   d
Zde
d<    ee	eeh      Zde
d<   dZde
d<   dZde
d<    G d de      Z ed       G d d             Zd#dZd$dZd%dZdddd	 	 	 	 	 	 	 	 	 	 	 	 	 d&dZd'd Zg d!Zy")(u  anu_v2.gemini_evidence_freshness_checker — task-2641 신규 helper.

회장 verbatim 12 (2026-05-23) §10 1:1 박제: Gemini fresh evidence 판정 =
``PR current HEAD SHA == Gemini review commit_id`` (spec §3.1).

task-2640 사고 박제 (spec §3.1):
  - 회장 review id=4350214991 state=COMMENTED · body 빈 문자열 → ``gemini-code-assist[bot]``
    author 가 아니므로 Gemini review 로 인정 0
  - PR HEAD 변경 후 fresh Gemini review commit_id 미도착 → STALE
  - STALE 자동 감지 → 1차 OWNER nudge 발사 → fail 시 CHAIR_UI_FALLBACK_REQUIRED

본 모듈 책임 (spec §3.1, task md §회장 verbatim 12 #10):
  - 입력: pr_number / current_head_sha / github_api callable
  - 출력: FreshnessResult enum (FRESH / STALE / NO_REVIEW)
  - PR HEAD 와 Gemini review commit_id 정합 검증

one-way isolation: anu_v2/ 외부 import 금지. live cokacdir / gh CLI 호출 0
(github_api 는 호출자가 inject; regression mock 가능).
    )annotations)	dataclass)AnyCallableFinalFRESHz
Final[str]RESULT_FRESHSTALERESULT_STALE	NO_REVIEWRESULT_NO_REVIEWzFinal[frozenset[str]]ALL_RESULTSzgemini-code-assist[bot]GEMINI_BOT_LOGINl   O z
Final[int]CHAIR_REVIEW_ID_BLOCKED_FIXTUREc                      e Zd ZdZy)FreshnessCheckerErroruB   입력 schema 위반 (head SHA 형식 / github_api 호출 결과).N)__name__
__module____qualname____doc__     J/home/jay/workspace/scripts/../anu_v2/gemini_evidence_freshness_checker.pyr   r   -   s    Lr   r   T)frozenc                  N    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
<   y)FreshnessResultu  check_gemini_evidence_fresh 결과 객체.

    fields:
      - status: ``FRESH`` / ``STALE`` / ``NO_REVIEW``
      - pr_number: 평가된 PR 번호
      - current_head_sha: PR 의 실측 HEAD (입력 그대로, lower 정규화)
      - gemini_commit_id_observed: 관측된 가장 최근 Gemini review commit_id
        (``NO_REVIEW`` 시 None)
      - reviews_inspected: 검사한 review 항목 수 (진단용)
      - reason: 분류 사유 (진단용)
    strstatusint	pr_numbercurrent_head_sha
str | Nonegemini_commit_id_observedreviews_inspectedreasonN)r   r   r   r   __annotations__r   r   r   r   r   1   s*    
 KN))Kr   r   c                    t        | t              rt        |       dk7  rt        d|       | j	                         }t        d |D              rt        d|       |S )N(   z)head must be 40-char hex SHA string, got c              3  $   K   | ]  }|d v 
 yw0123456789abcdefNr   .0cs     r   	<genexpr>z"_normalise_head.<locals>.<genexpr>M   s     
811&&
8   z"head must be 40-char hex SHA, got )
isinstancer   lenr   lowerany)headlowereds     r   _normalise_headr7   G   sf    dC CIO#7x@
 	
 jjlG

8
88#09
 	
 Nr   c                n   t        | t              sy| j                  d      xs | j                  d      xs i }t        |t              sy|j                  d      xs |j                  d      xs d}t        |t              sy|j	                         j                         t        j                         k(  S )u   회장 verbatim §1/§2: PR Review comment 는 Gemini trigger 가 아니지만,
    *Gemini bot 이 직접 post 한 review* 는 commit_id 추출용으로 검사한다.

    빈 body / 회장 본인 review 등은 author login 으로 자동 배제.
    Fuserauthorloginname )r1   dictgetr   stripr3   r   )reviewr9   r;   s      r   _is_gemini_reviewrB   T   s     fd#::f;H!5;DdD!HHW7&!17REeS!;;= $4$:$:$<<<r   c                    t        | t              sy| j                  d      xs | j                  d      }t        |t              r|sy|j	                         }t        |      dk7  st        d |D              ry|S )u6   review dict 에서 commit_id 회수 (lower 정규화).N	commit_id
commit_shar(   c              3  $   K   | ]  }|d v 
 ywr*   r   r,   s     r   r/   z%_extract_commit_id.<locals>.<genexpr>m   s     !P!!+="=!Pr0   )r1   r>   r?   r   r3   r2   r4   )rA   rD   cid_norms      r   _extract_commit_idrH   e   sg    fd#

;'C6::l+CIi%Y H
8}c!Px!PPOr   z(/repos/{owner}/{repo}/pulls/{pr}/reviewsr=   )reviews_path_templateownerrepoc                   t        | t              rt        | t              s| dk  rt        d      t	        |      }|j                  |xs d|xs d|       }	  |d|      }|t        t        | |d	dd
      S t        |t              s!t        dt        |      j                         g }
|D ]-  }t        |      st        |      }||
j                  |       / |
s&t        t        | |d	t        |      dt          d      S |
d   }||k(  r%t        t"        | ||t        |      d|d	d  d      S t        t$        | ||t        |      d|d	d  d|d	d  d      S # t        $ r}	t        d|d|	      |	d	}	~	ww xY w)u  Gemini fresh evidence 판정. spec §3.1 1:1.

    Args:
      pr_number: 평가 대상 PR 번호 (positive int).
      current_head_sha: 실측 PR HEAD SHA (40-char hex).
      github_api: ``Callable[(method, path), payload]`` — REST 호출 추상화.
        regression mock 가능.
      reviews_path_template: reviews endpoint template. {owner}/{repo}/{pr}
        치환. spec §2.6 1:1 — gh api 또는 동등 안전 경로.
      owner: GitHub owner (없을 시 빈 문자열 — github_api 가 처리).
      repo: GitHub repo (없을 시 빈 문자열).

    Returns:
      FreshnessResult — status (FRESH/STALE/NO_REVIEW) + commit_id observed.

    Raises:
      FreshnessCheckerError: 입력 schema 위반.
    r   z pr_number must be a positive intOWNERREPO)rJ   rK   prGETz!github_api invocation failed for z: Nu+   github_api returned None — no review data)r   r    r!   r#   r$   r%   z$reviews payload must be a list, got zno Gemini bot review (author=z) with valid commit_id foundz0Gemini latest review commit_id matches PR HEAD (   z...)z	PR HEAD (z(...) != latest Gemini review commit_id (u   ...) — STALE)r1   r   boolr   r7   format	Exceptionr   r   listtyper   rB   rH   appendr2   r   r	   r   )r    r!   
github_apirI   rJ   rK   	head_normpathpayloadexcgemini_commit_idsrA   cidlatest_commit_ids                 r   check_gemini_evidence_freshra   r   s   6 i%It)D	UV#$FGG 01I ''w^V ( DUD) #&&*@
 	
 gt$#24=3I3I2JK
 	
 $& & ( (;  %& #&&*!'l/0@/A B( (

 
	
 ),9$&&6!'lB9Ra=/QUV	
 		
 ""2g,	"1 '*2A./~?
 
q  #/xr#A
	s   	E   	F )E;;F c                D   t        | t              sy| j                  d      xs | j                  d      xs d}t        |t              sy|j	                         j                         dk7  ry| j                  d      }t        |t              sy|j	                         dk(  S )u  회장 verbatim §1/§2/§3 — PR-backed issue comment body="/gemini review"
    만 trigger 로 인정.

    spec §2.1 / §2.2 1:1 박제:
      - comment.kind == "issue_comment" (NOT "review_comment" / "review")
      - comment.body.strip() == "/gemini review"
      - author 가 owner allowlist 안 (호출자 enforce)

    task-2640 사고 박제: 회장 review id=4350214991 state=COMMENTED · empty body
    → kind="review" + body="" → False.
    Fkindcomment_typer=   issue_commentbodyz/gemini review)r1   r>   r?   r   r@   r3   )commentrc   rf   s      r   is_gemini_trigger_commentrh      s     gt$;;vC'++n"=CDdC zz|.;;vDdC ::<+++r   )
r	   r   r   r   r   r   r   r   ra   rh   N)r5   r   returnr   )rA   r   ri   rS   )rA   r   ri   r"   )r    r   r!   r   rY   zCallable[[str, str], Any]rI   r   rJ   r   rK   r   ri   r   )rg   r   ri   rS   )r   
__future__r   dataclassesr   typingr   r   r   r	   r&   r   r   	frozensetr   r   r   RuntimeErrorr   r   r7   rB   rH   ra   rh   __all__r   r   r   <module>rp      s  ( # ! ' ' #j ""j "* * *%.<!12&"   9 * 8 /9  8ML M $  *
="
$ "Lii i *	i
 i i i iX,2r   