
    li(y                    &
   U 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mZ ddl	m
Z
 ddlmZmZmZ ddlmZmZmZ ddlmZ g d	Zed
   Z ed       G d d             Z G d dee
      Z ed       G d d             Zdd	 	 	 	 	 d@dZdd	 	 	 	 	 dAdZdBdZ	 dCdd	 	 	 	 	 	 	 	 	 dDdZ	 	 	 	 	 	 dEdZ	 	 	 	 	 	 	 	 dFdZ dGdZ!	 	 	 	 	 	 	 	 dHd Z"dd	 	 	 dId!Z#dd	 	 	 	 	 dJd"Z$dKd#Z%dLd$Z&dMd%Z'e(d&k(  r e%       Z)e)jU                         Z+de,d<   de,d<   e+jZ                  r&e+j\                  re+jZ                  e+j\                  cZ-Z.n: e#       \  Z-Z.e+jZ                  re+jZ                  Z-e+j\                  re+j\                  Z.e+j<                  r	  ee-e.e+j^                        Z0e+jl                  r e&e0      Z7	 ddl8Z9d*Z:	  eg d+d      Z;e;jx                  dk(  re;jz                  j}                         Z: e9j~                  d,e:      Z@e@re@j                  d      nd-ZB eeBd./      ZCeCj                  eCj                  d0e7d1<    e3 ej                  e7d23             n+ e&e0      j                         D ]  \  ZHZI e3eH d4eI          ejj                  d       e+j                  	  ee-e.e+j^                        Z0 e$e+j                        ZK eeKe0      ZL e eKe0eL      ZMe+j                  r~e+j                  eLeLj                  ndd5ZPe+jl                  r e3 ej                  ePd23             n(eLeLj                  nd6ZQ e3d7e+j                   d8eQ         ejj                  d       e+j                   e&e0      eLeLj                  nd e'eM      d9ZRe+jl                  r e3 ej                  eRd23             n e3d:e+j                           e3d;eLreLj                  nd         e3d<eMj                           e3d=eMj                           e3d>eMj                           e3d?eMj                           ejj                  d       e)j                           ejj                  d       yy# e1$ r4Z2 e3d'e2 ejh                  (        ejj                  d)       Y dZ2[2dZ2[2ww xY w# e1$ r Y w xY w# e1$ r	 de7d1<   Y w xY w# e1$ r4Z2 e3d'e2 ejh                  (        ejj                  d)       Y dZ2[2!dZ2[2ww xY w)Nuv  utils/repository_policy_adapter.py — GitHub repository policy → runtime capability layer.

회장 §명시: GitHub repository ruleset / branch protection / merge capability를
runtime capability layer로 표준화. admin override 없이 deterministic merge path 선택.
"회장 직접 머지 fallback" 완전 제거.

회장 §명시 11개 금지 행위:
  1.  admin override 호출 (--admin flag 정적 차단)
  2.  branch protection 우회 코드
  3.  canonical_workspace_resolver 수정
  4.  automation_contracts 수정
  5.  merge_queue_executor 본체 수정
  6.  replacement_pr_runner 본체 수정
  7.  auto_gemini_triage 본체 수정
  8.  post_merge_smoke_runner 본체 수정
  9.  critical_escalation_reporter 본체 수정
  10. expected_files 외 파일 수정
  11. AUTOMATION_CAPABILITY_GAP을 CriticalEscalationType에 추가 (Critical 7종 외 보고 금지)
    )annotationsN)	dataclass)Enum)AnyCallableOptional)CriticalEscalationTypeEscalationPacket	RiskLevel)resolve_canonical_workspace)RepositoryCapabilityBlockedReasonMergePathPlanprobe_capabilityclassify_blocked_reasonselect_merge_pathassert_no_admin_overrideinvoke_triage_hookr	   r
   r   r   build_capability_gap_packet).z subprocess.CompletedProcess[str]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	)
r   u   GitHub repository의 머지 관련 capability snapshot.

    모든 필드는 probe 시점에 고정(frozen). admin override 없이 deterministic.
    boolcan_squash_mergerequires_approvalrequires_thread_resolutionauto_merge_enabledbot_can_mergeadmin_override_requiredN__name__
__module____qualname____doc____annotations__     P/home/jay/workspace/.worktrees/task-2520-dev4/utils/repository_policy_adapter.pyr   r   B   s-    
  $$!!r&   r   c                  ,    e Zd ZdZdZdZdZdZdZdZ	dZ
y	)
r   u(   PR 머지 차단 이유. 정확히 7종.UNRESOLVED_REVIEW_THREADREQUIRED_APPROVAL
STALE_BASEMISSING_CI_CHECKBRANCH_PROTECTIONPERMISSION_ISSUEAUTO_MERGE_UNSUPPORTEDN)r    r!   r"   r#   r)   r*   r+   r,   r-   r.   r/   r%   r&   r'   r   r   U   s.    29+J)+)5r&   r   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)r   u  Deterministic merge path 선택 결과.

    회장 §명시: admin override 없이 선택된 경로만 허용.
    capability_gap=True 시 CriticalEscalationType에 추가하지 않고
    ops 채널 보고 마커로만 사용 (Critical 7종 외 회장 보고 0건).
    stractionOptional[BlockedReason]reasonr   requires_chaircapability_gapOptional[str]triage_hookbase_sync_commanddescriptionNr   r%   r&   r'   r   r   e   s4     K $#$$r&   r   cwdc               6    t        j                  | |ddd      S )NT   )r<   capture_outputtexttimeout)
subprocessrun)argsr<   s     r'   _default_runnerrE   ~   s$    
 >> r&   runnerc                  t        |        ||nt        }	  || d      }|j                  d	k7  ry|j                  j                         sy	 t        j                  |j                        }t        |t        t         f      r|S dS # t        $ rQ}t        dt	        |      j
                   ddj                  | dd        dt        j                         Y d}~yd}~ww xY w# t        j"                  $ rG}t        d
dj                  | dd        d|j$                   t        j                         Y d}~yd}~ww xY w)u   gh api 호출 후 JSON dict 반환. 실패 시 None (safe fallback).

    예외 발생 시 stderr에 짧은 trace 기록 후 None 반환 — silent fallback이지만
    디버깅 가능하도록 원인을 남긴다 (Gemini high 권고 수용).
    Nr;   z,[repository_policy_adapter] gh call failed (z):     u   ... → fallback Nonefiler   z2[repository_policy_adapter] JSON parse failed for : )r   rE   	Exceptionprinttyper    joinsysstderr
returncodestdoutstripjsonloads
isinstancedictlistJSONDecodeErrormsg)rD   rG   fnresultexcparseds         r'   _run_ghrb      s'    T"%?B	Dd# A== 	FMM*#FT4L9vCtC#  :49;M;M:NcxxRa!""79	

 $  @xxRa!""SWWI/	

 s6   
B 6C# C# 	C ACC #D=6=D88D=Nonec                >    t        d | D              rt        d      y)u   gh 호출 args에 --admin 포함 시 RuntimeError. 어디서든 gh 호출 전에 호출.

    회장 §명시: admin override는 항상 금지.
    c              3  &   K   | ]	  }|d k(    yw)z--adminNr%   .0as     r'   	<genexpr>z+assert_no_admin_override.<locals>.<genexpr>   s     
(a1	>
(s   uh   admin override is forbidden by repository_policy_adapter (회장 §명시: admin override 항상 금지)N)anyRuntimeError)rD   s    r'   r   r      s)    
 
(4
((>
 	
 )r&   ownerr1   repoc                  d}d}d}t        ddd|  d| d| g|      }|g }t        |t              r|}n"t        |t              r|j	                  d	g       }|D ]  }	|	j	                  d
d      }
|	j	                  di       xs i }|
dk(  r]|j	                  dd      xs d}|dkD  rd}|j	                  dd      rd}|	j	                  dg       xs g }|rt        d |D              rd}|
dk(  sd} t        ddd|  d| d| dg|      }|t        |t              r|j	                  di       xs i }|rU|j	                  dd      xs d}|dkD  rd}|j	                  dd      rd}|j	                  di       j	                  dd      rd}|j	                  dd      }|d}d}d}t        ddd|  d| g|      }|Ft        |t              r6t        |j	                  dd            }t        |j	                  dd            }d}t        ddd|  d| dg|      }|,t        |t              r|j	                  dd      xs d}|dv rd}t        ||||||       S )!u   GitHub API 4회 호출로 RepositoryCapability 6 field를 probe.

    각 호출은 실패 허용 — fallback False 처리.
    runner: subprocess.run 주입 가능 (테스트 stubbing).
    Fghapizrepos//z/rules/branches/rF   NrulesrP    
parameterspull_requestrequired_approving_review_countr   T!required_review_thread_resolutionbypass_actorsc              3     K   | ]@  }t        |t              r.|j                  d d      dk(  xr |j                  dd      dk(   B yw)
actor_typers   RepositoryRole	role_nameadminNrY   rZ   getrf   s     r'   ri   z#probe_capability.<locals>.<genexpr>   sR      ) !!T* UU<,0@@ ;{B/7:;)s   AArestrictionsz
/branches/z/protectionrequired_pull_request_reviewsrequire_code_owner_reviews required_conversation_resolutionenabledallow_squash_mergeallow_auto_mergez-/collaborators/github-actions[bot]/permission
permission)writer}   r   r   r   r   r   r   )rb   rY   r[   rZ   r   allr   r   )rl   rm   branchrG   r   r   r   ruleset_datarr   rulertypeparamscountrx   protection_dataprcr   r   r   	repo_datar   	perm_dataperms                          r'   r   r      s     "'#	uugQtf,<VHEFL lD) Ed+ $$Wb1E 	/DHHVR(EXXlB/52F&

#DaHMA19(,%::A5I15. $" = C S ) +) & /3+&*.'+	/4 	uugQtfJvhkJKO "z/4'H!!"A2FL"GG=qAFQEqy$(!ww3U;$(!""#ErJNNyZ_`-1*&**>4@#&*#
 	uugQtf-.I It!<	.BE JK!)--0BE"JK
 M	u%$L	M	OI
 It!<}}\2.4"%% M)+#=-# 7 r&   c                   d}| j                  dd      }t        |t              rt        d |D              }n]t        |t              r)|j                  dg       xs g }t        d |D              }n$| j                  dd      }t        |t
              r|}| j                  dd	      xs d	}| j                  d
d	      xs d	}| j                  dd      }d	}	t        |t              r|j                  dd	      xs d	}	nt        |t              r|}	|j                  r|dkD  rt        j                  S |dk(  rt        j                  S |j                  r|dk7  rt        j                  S |	dv rt        j                  S |j                  st        j                  S |j                   st        j"                  S |dk(  rt        j$                  S y)u>  PR dict + RepositoryCapability → BlockedReason | None.

    우선순위 순차 체크 (회장 §명시 순서 고정):
      1. UNRESOLVED_REVIEW_THREAD
      2. STALE_BASE
      3. REQUIRED_APPROVAL
      4. MISSING_CI_CHECK
      5. PERMISSION_ISSUE
      6. AUTO_MERGE_UNSUPPORTED
      7. BRANCH_PROTECTION
    r   reviewThreadsNc              3  d   K   | ](  }t        |t              r|j                  d d      sd * yw
isResolvedT   Nr~   rg   ts     r'   ri   z*classify_blocked_reason.<locals>.<genexpr>V  /      &
!T"155t+D &
   .0nodesc              3  d   K   | ](  }t        |t              r|j                  d d      sd * ywr   r~   r   s     r'   ri   z*classify_blocked_reason.<locals>.<genexpr>]  r   r   unresolved_thread_countmergeStateStatusrs   reviewDecisionstatusCheckRollupstateBEHINDAPPROVED)PENDINGEXPECTEDFAILUREBLOCKED)r   rY   r[   sumrZ   intr1   r   r   r)   r+   r   r*   r,   r   r.   r   r/   r-   )
pr
capabilityr   raw_threadsr   direct_countmerge_statereview_decisionrollupci_states
             r'   r   r   @  s   $ $%&&$/K+t$"% &
"&
 #
 
K	&,2"% &
&
 #
 vv7;lC(&2#vv0"5;K66"2B7=2O VV'.FH&$::gr*0b	FC	 
 ,,1H11L555
 h'''
 ##:(E...
 55---
 ##---
 ((333
 i... r&   c                b   | j                  dd      }|j                  }|j                  }|t        ddddddd| d| d	      S |t        j
                  k(  rt        d
|ddddd| d	      S |t        j                  k(  rt        d|ddddd| d	      S |t        j                  t        j                  t        j                  fv r)t        d|ddddd| d|j                   d| d| d		      S |t        j                  k(  rt        d|ddddd| d	      S t        d|ddddd| d	      S )u   BlockedReason → deterministic MergePathPlan 선택.

    회장 §명시: admin override 없이 deterministic. "회장 직접 머지 fallback" 없음.
    AUTOMATION_CAPABILITY_GAP은 ops 채널만 (requires_chair=False).
    number?Nsquash_mergeFzPR #uE   : 정상 squash merge path. 모든 capability 충족. (bot_can_merge=)r2   r4   r5   r6   r8   r9   r:   auto_gemini_triageauto_gemini_triage.triage_pruW   : Unresolved review thread 존재. auto_gemini_triage hook으로 자동 분류/resolve.	base_synczgit merge origin/mainuR   : Base branch가 stale. git merge origin/main으로 동기화. (force push 금지)escalate_capability_gapTu   : Capability gap 감지: z. requires_approval=z, bot_can_merge=uS   . 자동화 불가 — ops 채널로 보고. (회장 직접 머지 fallback 없음)wait_ciu/   : CI 체크 미완료/실패. CI 통과 대기.manual_merge_requireduG   : Repo auto_merge 비활성화. manual merge 필요. ops 채널 보고.)r   r   r   r   r   r)   r+   r*   r-   r.   valuer,   )r   r   blocked_reasonpr_numbot_mergeableneeds_approvals         r'   r   r     s    x-F$22M%77N!  "vh ""/3
 	
 ???'!  6"vh G G
 	
 111!  5vh M M
 	
 ''''&& 
 ,! "vh78L8L7M N%%3$44D]O Tdd
 	
 777!  "vh&UV
 	
 *! "vh : :
 	
r&   c                    ddl m} d| |dS )u  auto_gemini_triage 연동 hook. 실제 wiring은 후속 task.

    본 task에서는 인터페이스만 정의. 호출 시 lazy import로 circular 방지.
    실제로 triage_pr을 호출하지 않고 callable만 반환 (테스트에서 인터페이스 검증).
    r   )	triage_prr   )hook	pr_numbercallable)utils.auto_gemini_triager   )r   r   s     r'   r   r     s     3. r&   c                `   | j                   sy| j                  rt        j                  nt        j                  }t        ||t        j                  | j                  d| j                   d| j                  r| j                  j                  nd ddgd|j                  | j                  d      S )	u  capability_gap=True인 MergePathPlan을 EscalationPacket으로 변환.

    회장 §명시: AUTOMATION_CAPABILITY_GAP은 CriticalEscalationType 7종 외이므로
    Critical 보고 대상이 아님. 가장 가까운 기존 타입 사용 (ops 채널 marker만).
    RiskLevel은 plan.requires_chair 기반으로 결정 (HIGH vs MEDIUM).

    Returns:
        EscalationPacket (ops 채널용) or None (capability_gap=False인 경우)
    Nzcapability_gap=True: action=z, blocked_reason=rc   ops_channel_reportmanual_merge_review)
risk_levelr2   )task_idr   escalation_typer4   why_auto_cannot_continuesafe_optionsrecommended_optionevidence)r6   r5   r   HIGHMEDIUMr
   r	   .BLOCK_OVERRIDE_REQUIRED_OR_REASON_INSUFFICIENTr:   r2   r4   r   )planr   r   risks       r'   r   r      s     !009>>i6F6FD.]]*4;;- 837;;dkk//FKM +,AB/ $

dkkB r&   c                   | | nt         }	  |g dd      }|j                  dk(  rT|j                  j                         }ddl}|j                  d|      }|r"|j                  d      |j                  d      fS y# t        $ r Y yw xY w)	uO   git remote get-url origin에서 owner/repo 추출. 실패 시 기본값 반환.N)gitremotezget-urloriginr;   r   z[:/]([^/]+)/([^/]+?)(?:\.git)?$r      )zJeon-Jonghyukdev_workspace)rE   rT   rU   rV   researchgrouprN   )rG   r^   r_   urlr   ms         r'   _extract_owner_repo_from_remoter   E  s    
 %?B:E!--%%'C 		<cBAwwqz1771:-- ,  +s   A.A< <	BBc                   ||nt         }	  |dddt        |       ddgd      }|j                  dk(  r9|j                  j	                         rt        j                  |j                        S i S # t        $ r Y i S w xY w)	u   gh pr view로 PR 정보 조회.Nro   r   view--jsonz?mergeStateStatus,reviewThreads,reviewDecision,statusCheckRollupr;   r   )rE   r1   rT   rU   rV   rW   rX   rN   )r   rG   r^   r_   s       r'   _fetch_pr_infor   Z  s     %?BdFC	NQ
 
 !fmm&9&9&;::fmm,, I  Is   A A/ /	A<;A<c                 x   t        j                  dt         j                  d      } | j                  ddd       | j                  dd d	
       | j                  dd d
       | j                  ddd
       | j                  dt        dd d       | j                  ddd       | j                  dddd       | S )Nu   repository_policy_adapter — GitHub repository capability probe & deterministic merge path selector. (회장 §명시: admin override 항상 금지)u  
Examples:
  # capability probe (JSON 출력)
  python3 utils/repository_policy_adapter.py --probe-capability --owner Jeon-Jonghyuk --repo dev_workspace --branch main --json

  # PR 분석 (probe + classify + select_merge_path)
  python3 utils/repository_policy_adapter.py --pr 42 --json

  # PR 차단 이유만 분류
  python3 utils/repository_policy_adapter.py --pr 42 --classify-blocked

금지 사항 (회장 §명시):
  - admin override (--admin flag 정적 차단)
  - branch protection 우회 코드
  - canonical_workspace_resolver / automation_contracts 수정
  - 5 모듈 본체 수정
  - expected_files 외 파일 수정
  - AUTOMATION_CAPABILITY_GAP을 CriticalEscalationType에 추가
        )r:   formatter_classepilogz--probe-capability
store_trueu+   Repository capability를 probe하여 출력)r2   helpz--owneru0   GitHub owner (미지정 시 remote에서 추출))defaultr   z--repou/   GitHub repo (미지정 시 remote에서 추출)z--branchmainu   대상 branch (기본: main)z--prNu6   PR 번호. probe + classify + select_merge_path 실행)rP   metavarr   r   z--classify-blockedu0   --pr N과 함께 사용: 차단 이유만 출력r   output_jsonu   JSON 출력)r2   destr   )argparseArgumentParserRawDescriptionHelpFormatteradd_argumentr   )ps    r'   _build_parserr   q  s    a !<<	A4 NN:  
 NN9d1cNdNN8T0aNbNN:v4RNSNNE   NN?  
 NN8L}=NYHr&   c                    | j                   | j                  | j                  | j                  | j                  | j
                  dS )Nr   r   )caps    r'   _capability_to_dictr     sB    00 22&)&D&D!44**#&#>#> r&   c                    | j                   | j                  | j                  j                  nd | j                  | j                  | j
                  | j                  | j                  dS )Nr   )r2   r4   r   r5   r6   r8   r9   r:   )r   s    r'   _plan_to_dictr    sX    ++'+{{'>$++##D----''!33'' r&   __main__zERROR: rK   r   rs   )r   z	rev-parsez--abbrev-refHEADztask-\d+(?:[+\-]\w+)?probeF)fetch)r   branch_nameworkspace_metar   )indentrM   )r   r   u   None (merge 가능)zpr #z blocked_reason: )r   r   r   merge_path_planzpr              : #zblocked_reason  : zaction          : zcapability_gap  : zrequires_chair  : zdescription     : )rD   	list[str]r<   r7   returnz"'subprocess.CompletedProcess[str]')rD   r  rG   Optional[RunnerType]r  zOptional[Any])rD   r  r  rc   )r   )
rl   r1   rm   r1   r   r1   rG   r  r  r   )r   rZ   r   r   r  r3   )r   rZ   r   r   r   r3   r  r   )r   r   r  rZ   )r   r   r   r   r   r1   r  z'EscalationPacket | None')rG   r  r  ztuple[str, str])r   r   rG   r  r  rZ   )r  zargparse.ArgumentParser)r   r   r  rZ   )r   r   r  rZ   )Xr#   
__future__r   r   rW   rB   rR   dataclassesr   enumr   typingr   r   r   utils.automation_contractsr	   r
   r   "utils.canonical_workspace_resolverr   __all__
RunnerTyper   r1   r   r   rE   rb   r   r   r   r   r   r   r   r   r   r   r  r    parser
parse_argsrD   r$   rl   rm   r   r   rN   r`   rO   rS   exitr   cap_dictr   _re_branch_guess_brrT   rU   rV   r   _task_matchr   _inferred_task_id	workspacer   r  dumpsitemskvr   pr_infoblockedr   classify_blockedr   r_   valcombinedr2   r6   r5   r:   
print_helpr%   r&   r'   <module>r*     s  & #    
 !  * * 
( =>
 $" " "$	6C 	6  $  6 
 
 (	" $(&
& !& 	&Z	
& p
 $(pp
p p
 !p pn]]$] ]Hc
c
$c
 ,c
 	c
T$
  	N $(, , ,0 $( ! 
	.0f	  z_FD J
Izzdiijj$))t57t::JJE9999D
 	"5$<C
 *3/H2  ")D C ~~*(+

(8(8(: )cjj)A=Q<GK$5$5a$8W!7%	
  )00#,#8#8.)* *$**Xa01+C0668 #12aSk"#
 ww	"5$<C$TWW-G-gs;G$Wc7;D
    "WW3:3F'--DF jdjj23'.':gmm@UTWWI%6se<=CHHQK -c2/6/Bgmm,T2	
 *$**Xa01'y12&w}}V&LMN&t{{m45&t':':&;<=&t':':&;<=&t'7'7&89: CHHQKY ,  	GC5/

3CHHQKK	& !   2-1)*2"  	GC5/

3CHHQKK	sg   Q> S 5R: =AS 19S >R7)R22R7:S?S SS 
SST)TT