
    6Ij_                       U d Z ddlmZ ddlZddlmZmZ ddlmZmZ ddl	m
Z
mZmZmZmZ dZdZd	Zd
Z eeeeeh      Zded<   dZdZdZdZdZdZdZdZdZ eeeh      Zded<   dZ ded<   dZ! ed       G d d             Z"e G d d              Z#eee$e
f   gdf   Z%eee$e
f   e$ge&f   Z'd-d!Z(d.d"Z)d/d#Z*d/d$Z+d0d%Z,d&Z-ded'<    ej\                  d(j_                  d) e-D              ej`                        Z1d1d*Z2 G d+ d,      Z3y)2u  anu_v2.auto_gemini_triage — ANU v2 Gemini review evidence 자동 분류기 v0 (task-2538).

회장 §명시 (2026-05-10) 4 분류:
  1. false_positive            → dismiss (회귀 fixture 매칭)
  2. style_only                → dismiss (코드 동작 무관)
  3. minor_fix_in_scope        → expected_files 내부 자동 적용
  4. scope_expansion           → Critical 7종 #N 보고

설계 원칙:
  - one-way isolation: anu_v2/* 만 import. utils/dispatch/scripts/dashboard 의존성 0.
  - 외부 부수효과 (audit / file write) 는 주입 가능한 callable 로 추상화.
  - executor (task-2531) 인터페이스 contract: triage_batch → {"applied", "dismissed", "escalated"}.
  - chat_id tag + filter 로 다른 chat record 노출 차단 (default chat=6937032012).
  - token raw 0: classify/triage 결과/audit record 어디에도 BOT_GITHUB_TOKEN 등 raw 토큰 미노출.
    )annotationsN)	dataclassfield)datetimetimezone)AnyCallableIterableMappingSequencedismiss_false_positivedismiss_style_onlyauto_apply_minor_fixescalate_scope_expansionzfrozenset[str]ACTIONSGEMINI_REAL_BUG_SCOPE_EXPANSIONhighmediumlowsecuritystylebugperformancedocsSTYLE_ONLY_CATEGORIES)github_tokenbot_github_tokengh_token	owner_patghp_ghs_github_pat_z	x-api-keyauthorizationsecretpasswordztuple[str, ...]TOKEN_KEY_HINTS
6937032012T)frozenc                  8    e Zd ZU dZded<   dZded<   dZded<   y)	FalsePositiveFixtureu  false_positive 박제 fixture.

    - rule_id: Gemini rule code (예: `unused-import`, `redundant-cast`).
    - signature: optional finding 본문 signature 요약 (regex). None 이면 rule_id 만 매칭.
    - reason: dismiss 사유 기록 (audit 용).
    strrule_idNz
str | None	signature reason)__name__
__module____qualname____doc____annotations__r-   r/        O/home/jay/workspace/.worktrees/task-2554plus2-dev5/anu_v2/auto_gemini_triage.pyr*   r*   H   s"     L Iz FCr6   r*   c                      e Zd ZU dZ ee      Zded<    ee      Zded<    ee      Z	ded<   d
dZ
edd       Zy	)TriageResultu   triage_batch 결과 — executor 인터페이스 contract 의 raw container.

    serialize() 호출 시 회장 §명시 3 키 (applied / dismissed / escalated) 만 노출.
    )default_factorylist[dict[str, Any]]applied	dismissed	escalatedc                    t        | j                        t        | j                        t        | j                        dS )N)r<   r=   r>   )listr<   r=   r>   selfs    r7   	serializezTriageResult.serialize_   s0    DLL)dnn-dnn-
 	
r6   c                ,    t        | j                        S N)boolr>   rA   s    r7   has_escalationzTriageResult.has_escalationf   s    DNN##r6   N)returndict[str, list[dict[str, Any]]])rH   rF   )r0   r1   r2   r3   r   r@   r<   r4   r=   r>   rC   propertyrG   r5   r6   r7   r9   r9   U   sR     %*$$?G!?&+D&AI#A&+D&AI#A
 $ $r6   r9   c                    | |f}y)u  기본 fix_applier — 본 v0 는 분류만 보장하고 실제 patch 적용 자체는 호출부가 담당.

    분류 결과가 minor_fix_in_scope 인 finding 은 호출부가 별도 패치 적용 후 결과를
    fix_applier 로 리포트한다. default 는 항상 성공으로 가정 (테스트에서 실패 mock 주입).
    인자는 시그니처 유지를 위해 받기만 하고, 본 default 는 부수효과 없이 True 반환.
    Tr5   )findingtarget_file_s      r7   _default_fix_applierrO   p   s     
+Ar6   c                 h    t        j                  t        j                        j	                  d      S )Nseconds)timespec)r   nowr   utc	isoformatr5   r6   r7   _now_isorV   }   s#    <<%///CCr6   c                Z    | yt        |       j                         j                         }|S )uI   severity 값을 lowercase 로 정규화. 미지정 = "" (low 와 구분).r.   r+   striplower)valuetexts     r7   _normalize_severityr]      s*    }u:##%DKr6   c                V    | yt        |       j                         j                         S )Nr.   rX   )r[   s    r7   _normalize_categoryr_      s&    }u:##%%r6   c                   t        | t              rpi }| j                         D ]Y  \  }}t        |      }|t	        |      j                         ndt        fdt        D              rd||<   Lt        |      ||<   [ |S t        | t        t        t        f      r| D cg c]  }t        |       c}S t        | t              rt        d | D              S t        | t              rt        j                  |       ry| S | S c c}w )u   dict / list / str 안에 박혀있을 수 있는 raw 토큰 / secret 흔적 제거.

    - dict key 가 token hint 매치 → 값 `***REDACTED***`.
    - str 값에 `ghp_` / `ghs_` prefix 가 보이면 `***REDACTED***`.
    - 재귀 처리 (list/Mapping/tuple 중첩 허용).
    - dict 의 key 자체에도 토큰이 박혀 있을 수 있어 _redact_tokens 재귀 적용
      (Gemini 6차 Security-High 박제 — key 를 통한 token raw 누출 차단).
    - OrderedDict / TypedDict 등 dict 가 아닌 Mapping 도 동일하게 처리.
    r.   c              3  &   K   | ]  }|v  
 y wrE   r5   ).0hint	key_lowers     r7   	<genexpr>z!_redact_tokens.<locals>.<genexpr>   s     A49$As   z***REDACTED***c              3  2   K   | ]  }t        |        y wrE   )_redact_tokens)rb   vs     r7   re   z!_redact_tokens.<locals>.<genexpr>   s     61^A&6s   )
isinstancer   itemsrg   r+   rZ   anyr&   r@   set	frozensettuple_TOKEN_VALUE_REsearch)r[   outkrh   
k_redactedrd   s        @r7   rg   rg      s     %! KKM 	4DAq'*J*+-ARIAAA"2J"0"3J	4 
%$Y/0 ,11aq!11%6666% !!%(#L 2s   !D)r    r!   r"   _TOKEN_VALUE_PREFIXES|c              #  F   K   | ]  }t        j                  |        y wrE   )reescape)rb   ps     r7   re   re      s     9aRYYq\9s   !c                    t        j                  |       }d|j                  dd      j                  dd      j                  dd      j                  dd      z   d	z   }t        j                  ||      d
uS )u   forbidden_paths / expected_files glob 매칭 (task-2531 동일 규칙).

    `**/foo.py` 형태가 루트 파일 `foo.py` 도 매칭하도록 `**/` 를 우선 처리.
    ^z\*\*\/z(?:.*/)?z\*\*/z\*\*z.*z\*z[^/]*$N)rw   rx   replacematch)patternpathescapedregexs       r7   _glob_matchr      ss    
 ii G
//)[
1'(K0''4('%)		*
 	 
 88E4 ,,r6   c                      e Zd ZdZddedd	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 dd	Z	 	 	 	 dd
Z		 	 	 	 	 	 ddZ
	 	 	 	 ddZddZ	 	 	 	 	 	 ddZy)AutoGeminiTriageu  ANU v2 Gemini review evidence 자동 분류기 v0.

    회장 §명시 4 분류:
      - false_positive: 회귀 fixture 매칭 → dismiss
      - style_only: 코드 동작 무관 → dismiss
      - minor_fix_in_scope: expected_files 내부 → 자동 적용
      - scope_expansion: expected_files 밖 → Critical 7종 #N 보고

    executor (task-2531) 인터페이스 contract:
      input: `gemini_findings: list[dict]`, `expected_files: set`
      output: `{"applied": list, "dismissed": list, "escalated": list}`
    Nr5   unknown)fix_applierfalse_positive_fixtureschat_idtask_idc                   || _         ||nt        | _        t        |      | _        t        |      | _        t        |      | _        y rE   )_auditrO   _fix_applierrn   	_fixturesr+   _chat_id_task_id)rB   audit_writerr   r   r   r   s         r7   __init__zAutoGeminiTriage.__init__   s>     #7B7NTh;@AX;YGGr6   c                   t        |t              r|n| j                  |      }t        |j	                  dd            j                         }t        |j	                  d            }t        |j	                  d            }t        |j	                  dd            j                         }| j                  ||      }|t        ||j                  |dfS |t        k(  xr	 |t        k(  }	|	r.| j                  ||      rt        ||||ddfS t        ||||d	dfS |t         v r|t        k7  rt"        |||d
fS | j                  ||      rt        ||||ddfS t        ||||ddfS )uD  Gemini finding 1건 분류 → (action, details).

        action ∈ ACTIONS.

        분류 우선순위 (회장 §):
          1. false_positive fixture 매칭 → dismiss_false_positive (rule_id 일치 검사)
          2. severity=high & category=security → in-scope 면 minor_fix, 아니면 escalate
             (Security-High 는 style 분류와 무관하게 우선 적용, 단 scope 내부일 때만)
          3. style_only → dismiss_style_only
          4. in-scope minor_fix → auto_apply_minor_fix
          5. out-of-scope → escalate_scope_expansion
        r,   r.   severitycategoryr   )r,   fixture_reasonr   security_high_in_scope)r,   r   r   r   r/   security_high_out_of_scope)r,   r   r   minor_fix_in_scopescope_expansion_required)ri   rl   _normalize_expectedr+   getrY   r]   r_   _match_false_positiveACTION_DISMISS_FALSE_POSITIVEr/   SEVERITY_HIGHCATEGORY_SECURITYis_in_scopeACTION_AUTO_APPLY_MINOR_FIXACTION_ESCALATE_SCOPE_EXPANSIONr   ACTION_DISMISS_STYLE_ONLY)
rB   gemini_findingexpected_filesexpected_setr,   r   r   finding_pathfpis_security_highs
             r7   classify_evidencez"AutoGeminiTriage.classify_evidence   s   ( .#. )).9 	
 n((B78>>@&~'9'9*'EF&~'9'9*'EF>--fb9:@@B ''@>-&&(ii(  $}4VEV9Vl;/#*$,$, ,":	 	 0& ( ((:	 	 ,,]1J)& ((  L,7+& ( ((2	 	 ,"$$$4	
 		
r6   c                    |syt        |t              r|n| j                  |      }|sy||v ry|D ]  }t        ||      s y y)u  finding 의 path 가 expected_files 집합 내부인지 검증.

        - exact match 와 glob match 모두 허용 (`**` / `*`).
        - finding_path 가 비어 있으면 False (path 미지정 → 자동 적용 불가).
        - expected_files 가 이미 정규화된 set 인 경우 재정규화 생략 (triage_batch
          루프 내부 호출 핫패스 — Gemini medium #3 박제 최적화).
        FT)ri   rl   r   r   )rB   r   r   r   r   s        r7   r   zAutoGeminiTriage.is_in_scopeT  sb      .#. )).9 	
 <'# 	G7L1	 r6   c                   t               }	 t        | j                  t        |      |            }d}||rdnd| j                  | j                  t        |j                  dd            ||| |t        t        |            d
}| j                  |       |S # t        $ r&}d}t        |      j                   d| }Y d}~d}~ww xY w)	u8  minor fix 자동 적용 — expected_files 내부만 (호출부에서 scope 보장 가정).

        - fix_applier callable 호출. 성공 시 applied=True, 실패 시 escalate=True 로 전환.
        - 결과 record 는 token raw 0 (redact) 보장.
        - audit 기록: `kind=auto_apply_minor_fix`.
        r.   Fz: Nr   auto_apply_failed_escalatedr,   )
tskindr   r   r,   rM   r<   r>   errorrL   )rV   rF   r   dict	Exceptiontyper0   r   r   r+   r   rg   r   )rB   rL   rM   r   okerr_msgexcrecords           r7   apply_minor_fixz AutoGeminiTriage.apply_minor_fixq  s     Z	d''W{CDB
 G .0*6S}}}}7;;y"56&%d7m4
 	F'  	5Bc++,Bse4G	5s   $B 	CC  Cc                n   t               }|d| j                  | j                  t        t	        |j                  dd            t        |j                  d            t        |j                  d            t	        |j                  dd            t        t        |            d
}| j                  |       |S )u(  scope 확장 요구 → Critical 7종 #N 보고 record 생성.

        - critical_code = `GEMINI_REAL_BUG_SCOPE_EXPANSION` (task-2531 정의 재참조).
        - audit 기록: `kind=scope_expansion_critical`.
        - 회장 §명시: 본 record 가 곧 회장 보고 source-of-truth.
        scope_expansion_criticalr,   r.   r   r   r   )
r   r   r   r   critical_coder,   r   r   r   rL   )rV   r   r   CRITICAL_GEMINI_SCOPE_EXPANSIONr+   r   r]   r_   rg   r   r   )rB   rL   r   r   s       r7   r   z)AutoGeminiTriage.escalate_scope_expansion  s     Z.}}}}<7;;y"56+GKK
,CD+GKK
,CDFB/0%d7m4
 	Fr6   c           	     L   t               }| j                  |      }|D ]  }t        |      }| j                  ||      \  }}||j	                  dd      |j	                  dd      t        |      d}	|t        k(  rX|j                  j                  i |	ddi       | j                  t               d| j                  | j                  |	d   d       |t        k(  rY|j                  j                  i |	dd	i       | j                  t               d
| j                  | j                  |	d   d       |t        k(  r| j                  ||	d         }
|
j	                  d      r&|j                   j                  i |	d|	d   i       l| j#                  i |ddi      }|j$                  j                  i |	t&        d|d   d       | j#                  |      }|j$                  j                  i |	|d   |j	                  dd      d        |j)                         S )u+  task-2531 executor 인터페이스 contract.

        input  : `gemini_findings: list[dict]`, `expected_files: set`
        output : `{"applied": list, "dismissed": list, "escalated": list}`

        escalated 비어있으면 호출부 자동 진행, 비어있지 않으면 Critical 보고.
        r,   r.   r   )actionr,   r   detailsr/   false_positiver   )r   r   r   r   r,   
style_onlyr   r<   rM   auto_apply_failedTr   )r   r/   r   scope_expansion)r   r/   )r9   r   r   r   r   rg   r   r=   appendr   rV   r   r   r   r   r   r<   r   r>   r   rC   )rB   gemini_findingsr   resultr   raw_findingrL   r   r   baser<   
escalations               r7   triage_batchzAutoGeminiTriage.triage_batch  sA    //?* 2	K;'G"44WlKOFG ";;y"5FB/)'2	D 66  ''(L4(L;K(LM"*4#}}#}}#I  44  ''(H4(H<(HI"*0#}}#}}#I  66..wVE;;y)NN))*OT*O=$v,*OP "&!>!> @!@+T@ "J $$++ --"A"5)3O)D	-  "::7C
  '' ))%/%@%kk(4EF) ]2	h !!r6   c                    g }|D ]P  }t        |j                  dd            }|| j                  k7  r.|j                  t	        t        |                   R |S )uM  주어진 audit record stream 에서 본 인스턴스 chat_id 의 record 만 반환.

        회장 §명시 chat=6937032012 격리 — 다른 chat record 노출 0.
        - 동일 chat_id 만 통과
        - chat_id 누락 record 는 차단 (보수적)
        - 추가로 token redact 한 번 더 적용 (이중 안전)
        r   r.   )r+   r   r   r   rg   r   )rB   all_recordskeptreccids        r7   list_chat_auditz AutoGeminiTriage.list_chat_audit  sX     &( 	3Ccggi,-Cdmm#KKtCy12		3
 r6   c                    |D ch c]5  }t        |      j                         st        |      j                         7 c}S c c}w rE   )r+   rY   )rB   r   ry   s      r7   r   z$AutoGeminiTriage._normalize_expected
  s,    (6I1#a&,,.AIIIs
   AAc           	        |syd}| j                   D ]  }|j                  |k7  r|j                  |c S |j                  s0|:t        |j	                  dd            dz   t        |j	                  dd            z   }	 t        j                  |j                  |      r|c S  y# t
        j                  $ r+ | j                  t               d||j                  d       Y w xY w)uG   fixture 매칭 — rule_id 일치 + (signature 있으면) regex 매칭.Nbodyr.    titlefixture_regex_error)r   r   r,   fixture_signature)
r   r,   r-   r+   r   rw   rp   r   r   rV   )rB   r,   rL   r   r   s        r7   r   z&AutoGeminiTriage._match_false_positive  s     .. 	BzzW$||#	 <<|7;;vr23c9CGUW@X<YY99R\\40I 1	0  88 	 "*1&)+	  	s   ?!B&&;C$#C$)r   AuditWriterr   zFixApplier | Noner   zIterable[FalsePositiveFixture]r   r+   r   r+   rH   None)r   Mapping[str, Any]r   Iterable[str]rH   ztuple[str, dict[str, Any]])r   r+   r   r   rH   rF   )rL   r   rM   r+   rH   dict[str, Any])rL   r   rH   r   )r   zSequence[Mapping[str, Any]]r   r   rH   rI   )r   zIterable[Mapping[str, Any]]rH   r;   )r   r   rH   zset[str])r,   r+   rL   r   rH   zFalsePositiveFixture | None)r0   r1   r2   r3   DEFAULT_CHAT_IDr   r   r   r   r   r   r   r   r   r5   r6   r7   r   r      s;   " *.BD& % "% '	%
 "@% % % 
%"c
)c
 &c
 
$	c
L & 
	:!"! ! 
	!H" 
6C"4C" &C" 
)	C"L0 
(J"" #" 
%	"r6   r   )rL   r   rM   r+   rH   rF   )rH   r+   )r[   r   rH   r+   )r[   r   rH   r   )r   r+   r   r+   rH   rF   )4r3   
__future__r   rw   dataclassesr   r   r   r   typingr   r	   r
   r   r   r   r   r   r   rm   r   r4   r   r   SEVERITY_MEDIUMSEVERITY_LOWr   CATEGORY_STYLECATEGORY_BUGCATEGORY_PERFORMANCECATEGORY_DOCSr   r&   r   r*   r9   r+   r   rF   
FixApplierrO   rV   r]   r_   rg   rt   compilejoin
IGNORECASEro   r   r   r5   r6   r7   <module>r      s    # 	 ( ' = = !9 0 4 "< #!#	%   #D   $  )2>=2Q(R ~ R$   $	 	 	 $ $ $, S)*D01wsCx(#.45
	D&"N *I  H"**HH9#899MM-$` `r6   