
    >jÞ                    Z   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dZdZ eeeeeeeh      Zded<   dZdZdZ eh d      Zded<    eh d      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.dId*Z/dJd+Z0dKd,Z1dKd-Z2dLd.Z3d/Z4d!ed0<    ejj                  d1jm                  d2 e4D              ejn                        Z8dMd3Z9 G d4 d5      Z: ed$%       G d6 d7             Z;dNd8Z<	 	 	 	 dOd9Z=d:Z>d;Z?d!ed<<   d=ddd>	 	 	 	 	 	 	 	 	 dPd?Z@ ed$%       G d@ dA             ZA	 	 	 	 	 	 	 	 dQdBZBdCZCdDZDdEZEdFZFdGZG	 	 	 	 	 	 	 	 	 	 dRdHZHy)Su  anu_v2.auto_gemini_triage — ANU v2 Gemini review evidence 자동 분류기 (task-2538 + task-2558).

회장 §명시 (2026-05-10) 4 분류 + task-2558 (2026-05-12) §명시 1 분류 확장:
  1. false_positive            → dismiss (회귀 fixture 매칭)
  2. style_only                → dismiss (코드 동작 무관)
  3. minor_fix_in_scope        → expected_files 내부 자동 적용
  4. scope_expansion           → Critical 7종 #N 보고
  5. minor_in_expected_files   → single_follow_up_commit_allowed (max 1 hard cap, cascade reply+resolve)
                                 — task-2558 PR #110 실전 패턴 박제.

설계 원칙:
  - 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 토큰 미노출.
  - minor_in_expected_files (§task-2558): 9조건 game-tree 평가 → 17필드 decision schema.
    follow-up 1회 hard cap. cascade 신규 finding 은 non-functional 이면 reply+resolve.
    follow-up commit 후 기존 Gemini evidence stale → owner_trigger_only 자동 재트리거.
    회장 수동 `/gemini review` fallback 금지.
    )annotationsN)	dataclassfield)datetimetimezone)AnyCallableIterableMappingSequencedismiss_false_positivedismiss_style_onlyauto_apply_minor_fixescalate_scope_expansionsingle_follow_up_commit_allowedreply_and_resolvezfrozenset[str]ACTIONSminor_in_expected_files
escalation   >   	dead-codesmall-guardclarityloggingmessage	dead_codesmall_guardMINOR_FIX_NATURE_BUCKETS>   docsstyler   r   r   consistencydocumentationCASCADE_NON_FUNCTIONAL_BUCKETSGEMINI_REAL_BUG_SCOPE_EXPANSIONhighmediumlowsecurityr    bugperformancer   STYLE_ONLY_CATEGORIES)github_tokenbot_github_tokengh_token	owner_patghp_ghs_github_pat_z	x-api-keyauthorizationsecretpassword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_idN
str | None	signature reason)__name__
__module____qualname____doc____annotations__r?   rA        0/home/jay/workspace/anu_v2/auto_gemini_triage.pyr;   r;   o   s"     L Iz FCrH   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)rN   rO   rP   )listrN   rO   rP   selfs    rI   	serializezTriageResult.serialize   s0    DLL)dnn-dnn-
 	
rH   c                ,    t        | j                        S N)boolrP   rS   s    rI   has_escalationzTriageResult.has_escalation   s    DNN##rH   N)returndict[str, list[dict[str, Any]]])rZ   rX   )rB   rC   rD   rE   r   rR   rN   rF   rO   rP   rU   propertyrY   rG   rH   rI   rK   rK   |   sR     %*$$?G!?&+D&AI#A&+D&AI#A
 $ $rH   rK   c                    | |f}y)u  기본 fix_applier — 본 v0 는 분류만 보장하고 실제 patch 적용 자체는 호출부가 담당.

    분류 결과가 minor_fix_in_scope 인 finding 은 호출부가 별도 패치 적용 후 결과를
    fix_applier 로 리포트한다. default 는 항상 성공으로 가정 (테스트에서 실패 mock 주입).
    인자는 시그니처 유지를 위해 받기만 하고, 본 default 는 부수효과 없이 True 반환.
    TrG   )findingtarget_file_s      rI   _default_fix_applierra      s     
+ArH   c                 h    t        j                  t        j                        j	                  d      S )Nseconds)timespec)r   nowr   utc	isoformatrG   rH   rI   _now_isorh      s#    <<%///CCrH   c                Z    | yt        |       j                         j                         }|S )uI   severity 값을 lowercase 로 정규화. 미지정 = "" (low 와 구분).r@   r<   striplower)valuetexts     rI   _normalize_severityro      s*    }u:##%DKrH   c                V    | yt        |       j                         j                         S )Nr@   rj   )rm   s    rI   _normalize_categoryrq      s&    }u:##%%rH   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rW   rG   ).0hint	key_lowers     rI   	<genexpr>z!_redact_tokens.<locals>.<genexpr>   s     A49$As   z***REDACTED***c              3  2   K   | ]  }t        |        y wrW   )_redact_tokens)rt   vs     rI   rw   z!_redact_tokens.<locals>.<genexpr>   s     61^A&6s   )
isinstancer   itemsry   r<   rl   anyr7   rR   set	frozensettuple_TOKEN_VALUE_REsearch)rm   outkrz   
k_redactedrv   s        @rI   ry   ry      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)r0   r1   r2   _TOKEN_VALUE_PREFIXES|c              #  F   K   | ]  }t        j                  |        y wrW   )reescape)rt   ps     rI   rw   rw      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)r   r   replacematch)patternpathescapedregexs       rI   _glob_matchr      ss    
 ii G
//)[
1'(K0''4('%)		*
 	 
 88E4 ,,rH   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}`
    NrG   unknown)fix_applierfalse_positive_fixtureschat_idtask_idc                   || _         ||nt        | _        t        |      | _        t        |      | _        t        |      | _        y rW   )_auditra   _fix_applierr   	_fixturesr<   _chat_id_task_id)rT   audit_writerr   r   r   r   s         rI   __init__zAutoGeminiTriage.__init__  s>     #7B7NTh;@AX;YGGrH   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   rA   security_high_out_of_scope)r=   r   r   minor_fix_in_scopescope_expansion_required)r{   r~   _normalize_expectedr<   getrk   ro   rq   _match_false_positiveACTION_DISMISS_FALSE_POSITIVErA   SEVERITY_HIGHCATEGORY_SECURITYis_in_scopeACTION_AUTO_APPLY_MINOR_FIXACTION_ESCALATE_SCOPE_EXPANSIONr+   ACTION_DISMISS_STYLE_ONLY)
rT   gemini_findingexpected_filesexpected_setr=   r   r   finding_pathfpis_security_highs
             rI   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	
 		
rH   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)r{   r~   r   r   )rT   r   r   r   r   s        rI   r   zAutoGeminiTriage.is_in_scope{  sb      .#. )).9 	
 <'# 	G7L1	 rH   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=   r_   rN   rP   errorr^   )rh   rX   r   dict	ExceptiontyperB   r   r   r<   r   ry   r   )rT   r^   r_   r   okerr_msgexcrecords           rI   apply_minor_fixz AutoGeminiTriage.apply_minor_fix  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   r^   )rh   r   r   CRITICAL_GEMINI_SCOPE_EXPANSIONr<   r   ro   rq   ry   r   r   )rT   r^   r   r   s       rI   r   z)AutoGeminiTriage.escalate_scope_expansion  s     Z.}}}}<7;;y"56+GKK
,CD+GKK
,CDFB/0%d7m4
 	FrH   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   detailsrA   false_positiver   )r   r   r   r   r=   
style_onlyr   rN   r_   auto_apply_failedTr   )r   rA   r   scope_expansion)r   rA   )rK   r   r   r   r   ry   r   rO   appendr   rh   r   r   r   r   r   rN   r   rP   r   rU   )rT   gemini_findingsr   resultr   raw_findingr^   r   r   baserN   r   s               rI   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 !!rH   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   ry   r   )rT   all_recordskeptreccids        rI   list_chat_auditz AutoGeminiTriage.list_chat_audit  sX     &( 	3Ccggi,-Cdmm#KKtCy12		3
 rH   c                    |D ch c]5  }t        |      j                         st        |      j                         7 c}S c c}w rW   )r<   rk   )rT   r   r   s      rI   r   z$AutoGeminiTriage._normalize_expected1  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   r   r   r   r   rh   )rT   r=   r^   r   r   s        rI   r   z&AutoGeminiTriage._match_false_positive4  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<   rZ   None)r   Mapping[str, Any]r   Iterable[str]rZ   tuple[str, dict[str, Any]])r   r<   r   r   rZ   rX   )r^   r   r_   r<   rZ   dict[str, Any])r^   r   rZ   r   )r   Sequence[Mapping[str, Any]]r   r   rZ   r[   )r   zIterable[Mapping[str, Any]]rZ   rM   )r   r   rZ   zset[str])r=   r<   r^   r   rZ   zFalsePositiveFixture | None)rB   rC   rD   rE   DEFAULT_CHAT_IDr   r   r   r   r   r   r   r   r   rG   rH   rI   r   r      s;   " *.BD& % "% '	%
 "@% % % 
%"c
)c
 &c
 
$	c
L & 
	:!"! ! 
	!H" 
6C"4C" &C" 
)	C"L0 
(J"" #" 
%	"rH   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Zded<   y)MinorInExpectedFilesInputu   9조건 입력 1:1 (회장 §명시 task-2558).

    호출부가 PR head 기준 raw evidence 를 1회 채집 후 본 dataclass 로 정규화.
    classify_minor_in_expected_files 가 evaluate → action 결정.
    r<   r   int	pr_numbersource_thread_idr   r   r6   r   rX   forbidden_path_requiredr   functionality_impacttest_guarantee
fix_naturefollow_up_commits_usedeffective_diff_pathsgemini_fresh_on_new_headci_clean_on_new_headmerge_state_clean_on_new_headFbaseline_carry_overN)rB   rC   rD   rE   rF   r   rG   rH   rI   r   r   Z  so    
 LNM
I##!!""O))""#'' %%rH   r   c                    | syt        |t              r|n@|D ch c]5  }t        |      j                         st        |      j                         7 c}}|sy| |v ry|D ]  }t	        ||       s y yc c}w )u  단일 path 가 expected_files 집합 내부 (정확 또는 glob 매칭).

    Gemini PR #111 medium #1 박제: 호출부가 이미 정규화된 ``set`` 을 넘기면
    재정규화 생략 (핫패스 최적화). 다른 Iterable 일 때만 set 생성.
    FT)r{   r~   r<   rk   r   )r   expectedr   r   pats        rI   _path_is_in_expectedr  t  s~      h$ 	&.A#a&,,.c!fllnA 
 | sD!  Bs
   A5A5c           
        t        | j                        }| j                  D ch c]5  }t        |      j	                         st        |      j	                         7 c}|t
        t        dhv }t        | j                        }| j                   }| j                   }| j                  dk(  xs t        | j                        }| j                  j	                         j                         j!                  dd      t"        v }| j$                  t&        k  }	| j(                  rt+        fd| j(                  D              nd}
t        | j,                        xr, t        | j.                        xr t        | j0                        }|||||||	|
|d	}|j3                         D cg c]
  \  }}|r	| }}}|rt4        ||dfS t6        |g dfS c c}w c c}}w )	u  9조건 game-tree 평가 → (classification, condition_report).

    9 조건 (회장 §명시 1:1):
      C1. severity <= medium
      C2. path 가 expected_files 내부
      C3. forbidden path 요구 0
      C4. scope expansion 요구 0
      C5. 기능 영향 0 또는 테스트로 보장 가능
      C6. fix 성격이 clarity / dead-code / message / logging / small guard
      C7. follow_up_commits_used <= MAX_FOLLOW_UP_COMMITS_HARD_CAP (1)
      C8. effective diff 가 expected_files 유지 (외부 경로 0)
      C9. 새 head 기준 Gemini fresh + CI + CLEAN 재검증 통과

    모든 조건 통과 → classification = `minor_in_expected_files`
    하나라도 fail → `escalation` + report.failed_conditions 에 사유 기록.
    r@   r   r   r`   c              3  6   K   | ]  }t        |        y wrW   )r  )rt   r   r   s     rI   rw   z3classify_minor_in_expected_files.<locals>.<genexpr>  s      %23Q-%s   T)	C1_severity_allowedC2_path_in_expected_filesC3_no_forbidden_pathC4_no_scope_expansion-C5_no_functionality_impact_or_test_guaranteedC6_fix_nature_minorC7_follow_up_within_cap#C8_effective_diff_in_expected_filesC9_revalidated_fresh_ci_clean)
conditionsfailed_conditions)ro   r   r   r<   rk   SEVERITY_LOWSEVERITY_MEDIUMr  r   r   r   r   rX   r   r   rl   r   r   r   MAX_FOLLOW_UP_COMMITS_HARD_CAPr   allr   r   r   r|   CLASSIFICATION_ESCALATION&CLASSIFICATION_MINOR_IN_EXPECTED_FILES)evsevr   c1_severity_allowedc2_in_scopec3_no_forbiddenc4_no_scope_expansionc5_no_func_impactc6_fix_nature_okc7_followup_capc8_effective_diff_in_scopec9_revalidatedr  r   rz   failedr   s                   @rI    classify_minor_in_expected_filesr$    s   & bkk
*C 796G6GZ3q6<<>c!fllnZL,!DD&rww=K444O " ; ;;00A5Q$r?P?P:Q}}**,224<<S#FJbb//3QQO 
	 	  "% %797N7N% "&*  	R(() 	3(()	3112   3%0 /!69J/#2/I)7
J ',,.8DAqaa8F8%%FC
 	

 	/!; E [8 9s   GG&
G1Gz anu_v2.gemini_triage_decision.v1)r   r   r   r   r   classificationexpected_files_internalr   r   r   r   allowed_actionmax_follow_up_commitsr   cascade_findingsfinal_actioncritical_escalationGEMINI_TRIAGE_DECISION_FIELDSrG   )r)  r*  r+  c          
        t        |       \  }}|t        k(  rt        nt        }|xs |}||n|t        k7  }t	        |d   d         }	i dt
        dt        | j                        dt        | j                        dt        | j                        dt        | j                        dt        | j                        d	|d
|	dt	        | j                        dt	        | j                        dt        | j                         dt	        | j"                        d|dt$        dt        | j&                        d|D 
cg c]  }
t)        t+        |
             c}
d|dt	        |      iS c c}
w )u  17 필드 1:1 decision JSON (회장 §명시 schema v1).

    - classification 은 9조건 평가 결과 사용 (외부에서 강제 override 0).
    - allowed_action: classification 이 minor_in_expected_files 면 single_follow_up_commit_allowed,
      그 외 escalation.
    - max_follow_up_commits 는 hard cap 1.
    - cascade_findings 는 fresh review 신규 finding 들의 1:1 list (각 finding dict 정규화).
    - final_action / critical_escalation 미지정 시 base classification 기반 derive.
    r  r  schemar   r   r   r   r   r%  r&  r   r   r   r   r'  r(  r   r)  r*  r+  )r$  r  &ACTION_SINGLE_FOLLOW_UP_COMMIT_ALLOWEDr   rX    GEMINI_TRIAGE_DECISION_SCHEMA_V1r<   r   r   r   r   ro   r   r   r   r   r   r   r  r   ry   r   )r  r)  r*  r+  r%  reportbase_allowed_actionderived_finalderived_criticalr&  cfs              rI   build_gemini_triage_decisionr6    s   & >bANF CC 	/, 
 !7$7M * 	EE 
 #6,#78S#TU23rzz? 	S& 	C 3 34	
 	'4 	BGG 	. 	"#: 	"4(B(B#C 	#D)D)D$E 	B$;$; < 	tB$:$:; 	- 	 !? 	!#b&?&?"@  	/?
)+N48$
!& 	'( 	t$45)  
s   E0c                  X    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
<   y)CascadeFindinguM   follow-up commit 후 새 Gemini fresh review 에서 발견된 신규 finding.r<   r   r   rX   is_real_bugbehavior_changingr   r   natureN)rB   rC   rD   rE   rF   rG   rH   rI   r8  r8    s,    WM
I!!""KrH   r8  c                  t        | j                        }g }| j                  r|j                  d       | j                  r|j                  d       | j
                  r|j                  d       | j                  r|j                  d       |t        t        dhvr|j                  d       | j                  j                         j                         j                  dd      }t        | j                  |      }|rt        d	|| j                  |d
fS |t         k\  r8|t"        v r|rt$        d|| j                  ddfS t        d|| j                  |dfS |t"        v r|rt$        d|| j                  ddfS t        d|| j                  |dfS )u  cascade 신규 finding 처리 결정.

    회장 §명시 (task-2558 §3):
      - real bug / behavior_changing / forbidden_path / scope_expansion → escalation
      - follow_up_commits_used >= MAX_FOLLOW_UP_COMMITS_HARD_CAP 이면 추가 code change 금지
      - severity <= medium 이고 non-functional/consistency/style 성격 + path 가 expected_files 내부
        → reply_and_resolve 허용 (2번째 code change 없이 thread 해결)
      - 그 외 → escalation (보수적)
    real_bugr:  forbidden_pathr   r@   severity_high_or_unknownr   r`   escalation_required)rA   factorsr   in_scopecap_reached_non_functionalT)rA   r;  r   rB  cap_reached_blockingnon_functional_in_scopedefault_escalate_conservative)ro   r   r9  r   r:  r   r   r  r  r;  rk   rl   r   r  r   r   r  r#   ACTION_REPLY_AND_RESOLVE)r5  r   r   r  reason_partsnature_normrB  s          rI   handle_cascade_findingrJ  &  s    bkk
*C L	~~J'	/0	!!,-	""-.
<"5567))//#))+33C=K#BGG^<H+/'$	
 	
 !??88X(:)GG $	  ,0%$	
 	
 44$3% 	
 	
 	(5!GG 		
 rH   FIRST_GEMINI_TRIGGER_MISSINGGEMINI_STALE_ON_HEADowner_trigger_onlyexecutor_scheduler_automanual_review_forbiddenc                r    |st         t        ||ddfS | r||k7  rt         t        ||ddfS t        d||ddfS )u  follow-up commit 후 Gemini fresh review 재트리거 판정.

    회장 §명시 (task-2558 §4):
      - follow-up commit pushed 이면 기존 Gemini evidence stale (head 변경)
      - FIRST_GEMINI_TRIGGER_MISSING (first_trigger_observed=False) 또는
        GEMINI_STALE_ON_HEAD (prior_head != current_head) 발생 시
        owner_trigger_only runner 또는 ExecutorScheduler 자동 처리
      - 회장 수동 `/gemini review` fallback 절대 금지 → MANUAL_REVIEW_FORBIDDEN

    return (result_code, detail). result_code ∈ {
        OWNER_TRIGGER_RESULT_OK, OWNER_TRIGGER_RESULT_EXECUTOR_SCHEDULER,
        OWNER_TRIGGER_RESULT_MANUAL_REVIEW_FORBIDDEN,
    }
    detail.reason ∈ {FIRST_MISSING, STALE_ON_HEAD, NO_ACTION_REQUIRED, FORBIDDEN}.
    T)rA   current_head
prior_head manual_review_fallback_forbiddenNO_ACTION_REQUIRED)OWNER_TRIGGER_RESULT_OK"OWNER_TRIGGER_REASON_FIRST_MISSING"OWNER_TRIGGER_REASON_STALE_ON_HEAD'OWNER_TRIGGER_RESULT_EXECUTOR_SCHEDULER)follow_up_commit_pushedprior_gemini_evidence_headrQ  first_trigger_observeds       rI   owner_trigger_decisionr\    sp    , "#< ,848	
 	
 #=#M#< ,848	
 	
 	0*(404		
 rH   )r^   r   r_   r<   rZ   rX   )rZ   r<   )rm   r   rZ   r<   )rm   r   rZ   r   )r   r<   r   r<   rZ   rX   )r   r<   r  r   rZ   rX   )r  r   rZ   r   )
r  r   r)  r   r*  r>   r+  zbool | NonerZ   r   )r5  r8  r   r   r   r   rZ   r   )
rY  rX   rZ  r<   rQ  r<   r[  rX   rZ   r   )IrE   
__future__r   r   dataclassesr   r   r   r   typingr   r	   r
   r   r   r   r   r   r   r/  rG  r   r   rF   r  r  r  r   r#   r   r   r  r  r   CATEGORY_STYLECATEGORY_BUGCATEGORY_PERFORMANCECATEGORY_DOCSr+   r7   r   r;   rK   r<   r   rX   
FixApplierra   rh   ro   rq   ry   r   compilejoin
IGNORECASEr   r   r   r   r  r$  r0  r,  r6  r8  rJ  rV  rW  rU  rX  ,OWNER_TRIGGER_RESULT_MANUAL_REVIEW_FORBIDDENr\  rG   rH   rI   <module>ri     s#  , # 	 ( ' = = !9 0 4 "< )J &. #!#*%   *C &(  "#  ,5 6 , .  2; < 2   #D   $  )2>=2Q(R ~ R$   $	 	 	 $ $ $, S)*D01wsCx(#.45
	D&"N *I  H"**HH9#899MM-$` `H $& & &2.;!;;~ $F  2  0 57#'+5!5 25 	5
 %5 5r $  XX  X "	X
  Xx &D "%; ". *C '/H ,3!3 !$3 	3
 !3  3rH   