
    f_j+              	         U d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m	Z	m
Z
 ddlmZ ddlmZmZmZmZmZ ddlmZmZmZmZ d	Zd
ZdZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+ e,e%e&e'e(e)e*e+h      Z-de.d<    e,h d      Z/de.d <   d!Z0d"Z1d#Z2d$Z3 ed%&       G d' d(             Z4 ed%&       G d) d*             Z5e G d+ d,             Z6eee7   ee7e7f   dz  gejp                  f   Z9eee7   gejp                  f   Z:eee7   ge;f   Z<eee7ef   gdf   Z=d6d-Z>d7d.Z?d8d/Z@d9d0ZAd:d1ZB G d2 d3      ZCd;d4ZDd<d5ZEy)=u  anu_v2.merge_queue_executor — ANU v2 자동 머지 큐 실행기 v0 (task-2531).

회장 §명시 (2026-05-10) 9 기능 박제:
  1. queue head 자동 검증
  2. expected_files diff gate
  3. forbidden path gate
  4. CI / Gemini / mergeStateStatus CLEAN / HEAD SHA lock 확인
  5. BOT GITHUB_TOKEN process-local injection으로 squash merge
  6. post-merge smoke (회귀 재실행)
  7. downstream stale revalidation
  8. Critical 7종 분류 (회장 보고 대상)
  9. 비critical 자동 처리 (escalation 없이 self-resolve)

설계 원칙:
  - one-way isolation: anu_v2/* 만 import. utils/dispatch/scripts/dashboard 의존성 0.
  - 외부 부수효과는 모두 주입 가능한 callable 로 추상화 (gh_runner, git_runner,
    pytest_runner, audit_writer) — 테스트 시 mock 으로 대체 가능.
  - admin override / force / rebase / owner_pat fallback / manual .done 일체 사용 금지.
    )annotationsN)	dataclassfield)datetimetimezone)Path)AnyCallableIterableMappingSequence)!EVIDENCE_MISSING_FOR_CURRENT_HEADOUTCOME_FAILED
OUTCOME_OKOwnerTriggerPatAUTO_MERGE_ALLOWEDAUTO_MERGE_SUCCESSWAITING_FOR_PREDECESSORBLOCKED_WITH_REASONHEAD_SHA_LOCK_BROKENCI_FAILURE_BLOCKGEMINI_UNRESOLVED_BLOCKMERGE_STATE_NOT_CLEANDIFF_CONTAMINATIONFORBIDDEN_PATH_HITNON_CRITICAL_AUTO_RESOLVEDOWNER_TRIGGER_REQUESTEDSTALE_EVIDENCE_BLOCKFORBIDDEN_PATH_INVASION/EFFECTIVE_DIFF_CONTAMINATION_REPLACEMENT_FAILEDGEMINI_REAL_BUG_SCOPE_EXPANSION.BLOCK_OVERRIDE_REQUIRED_OR_INSUFFICIENT_REASON(DEPENDENCY_CYCLE_OR_SERIAL_ONLY_CONFLICTREPLACEMENT_PR_ALSO_FAILEDPOST_MERGE_SMOKE_FAILUREzfrozenset[str]CRITICAL_CODES>   -f--admin--force--rebase--force-with-leaseFORBIDDEN_GIT_FLAGSGEMINI_COMPLETEDGEMINI_UNRESOLVEDGEMINI_REAL_BUGGEMINI_SCOPE_EXPANSIONT)frozenc                  B    e Zd ZU dZded<   ded<   dZded<   dZd	ed
<   y)TaskSpecu:   task md 에서 추출한 머지 게이트 메타데이터.strtask_idtuple[str, ...]expected_files forbidden_pathsFboolcherry_pick_allowedN)__name__
__module____qualname____doc____annotations__r9   r;   r8       L/home/jay/workspace/.worktrees/task-2553-dev5/anu_v2/merge_queue_executor.pyr3   r3   Y   s%    DL##')O_) %%rA   r3   c                  z    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Zded<   y)PRMetau  PR 메타 (gh pr view 결과 정규화).

    `gemini_commit_id` 는 회장 §명시 11번 — Gemini evidence 가 어느 commit 에 대한
    리뷰인지 박제하는 필드. 기본값 `""` 은 "정보 미제공" 의미 — STALE 검증을 skip 하여
    기존 호출부/테스트와 하위 호환을 유지한다. 명시적으로 채워진 경우에만
    `pr.head_sha` 와 비교하여 stale evidence 를 탐지한다.
    intnumberr4   head_shahead_refbase_refr6   changed_filesr:   ci_required_all_successgemini_statusmerge_state_statusqueue_predecessors_open gemini_commit_idN)r<   r=   r>   r?   r@   rP   r8   rA   rB   rD   rD   b   sF     KMMM""!!  crA   rD   c                  x    e Zd ZU dZded<   dZded<   dZded<    ee      Z	ded	<   e
dd
       Ze
dd       Zy)GateOutcomeu   gate 평가 결과.r4   decisionrO   reasoncritical_code)default_factoryzdict[str, Any]extrac                2    | j                   t        t        fv S N)rS   r   r   selfs    rB   passedzGateOutcome.passed   s    }}!35G HHHrA   c                T    t        | j                        xr | j                  t        v S rY   )r:   rU   r&   rZ   s    rB   is_criticalzGateOutcome.is_critical   s#    D&&'PD,>,>.,PPrA   N)returnr:   )r<   r=   r>   r?   r@   rT   rU   r   dictrW   propertyr\   r^   r8   rA   rB   rR   rR   w   sW    MFCM3!$7E>7I I Q QrA   rR   c                 h    t        j                  t        j                        j	                  d      S )Nseconds)timespec)r   nowr   utc	isoformatr8   rA   rB   _now_isorh      s#    <<%///CCrA   c                d    | yt        | t              r| j                  dd      S t        |       S )u   subprocess stdout/stderr 정규화: None → "", bytes → utf-8 decode, str → 그대로.

    `text=True` 미설정 호출이 섞여 들어와도 직렬화 시점에 `bytes` 가 dict 에 박히지
    않게 한다.
    rO   utf-8replace)errors)
isinstancebytesdecoder4   )values    rB   _coerce_streamrq      s3     }%||GI|66u:rA   c                h   t         D ch c](  }t        |      dk(  s|j                  d      s$|dd * c}g }| D ]  }t        |t              s|t         v r|j                  |       .d|v r/|j                  dd      d   }|t         v r|j                  |       asdt        |      dkD  ss|j                  d      s|j                  d      rt        fd|dd D              s|j                  |        |r,t        t        j                  |            }t        d	|       yc c}w )
uN  gh / git 호출 인자에 admin/force/rebase 금지 플래그가 섞이면 즉시 RuntimeError.

    회장 §6 정적 차단 — 호출부에서 우회를 막는 단방향 hard-stop.

    검사 규칙:
      - exact: 인자 토큰이 FORBIDDEN 셋과 정확히 일치 (`--admin`, `--force`, ...).
      - prefix: 값 형태 (`--admin=true`, `--force-with-lease=...`) 도 차단.
      - 단축 결합 (`-af`): `-`/`--` 접두어가 1개일 때 각 문자가 단축 금지에 포함되는지.
        본 모듈은 `-f` 만 단축 형태로 사용 가정 (`-af`, `-fa` 모두 매칭).
       -   N=r   z--c              3  &   K   | ]  }|v  
 y wrY   r8   ).0chshort_charss     rB   	<genexpr>z0assert_no_forbidden_git_flags.<locals>.<genexpr>   s     92$9s   zFORBIDDEN_GIT_FLAG_BLOCKED: )r,   len
startswithrm   r4   appendsplitanysortedr`   fromkeysRuntimeError)argsflagbadtokenheaduniquerz   s         @rB   assert_no_forbidden_git_flagsr      s&    )<gs4yA~RVRaRabeRf48gKC %%''JJu%<;;sA&q)D**

5! E
Q  %$$T*9uQRy99

5!+, c*+9&BCC 1 hs   D/D/D/c                    g }| j                         D ][  }t        j                  d|      }|st        d |j	                         D        d      }|j                  |j                                ] t        |      S )u   간단한 inline YAML list 파서 (`expected_files:` 블록의 `- "..."`).

    외부 yaml 모듈 의존성을 피하기 위해 직접 구현. 본 모듈이 다루는
    task md format 만 지원하면 되므로 minimal grammar.
    z7^\s*-\s*(?:\"([^\"]+)\"|'([^']+)'|([^#]+?))\s*(?:#.*)?$c              3  &   K   | ]	  }||  y wrY   r8   )rx   gs     rB   r{   z#_parse_yaml_list.<locals>.<genexpr>   s     A1=!As   rO   )
splitlinesrematchnextgroupsr~   striptuple)blockitemslinemrp   s        rB   _parse_yaml_listr      sr     E  " 	( HHF
 AQXXZA2FELL'	( <rA   c                t   | j                  d      | j                  }dfd}t         |d            }t         |d            }g }t        j                  dt        j
                        D ]  }|j                  |        |j                   |d             |j                   |d             d}t        j                  dt        j                  t        j
                  z        }|D ]<  }	|	s|j                  |	      }
|
s|
j                  d	      j                         d
k(  } n t        ||||      S )uS   task md 파일에서 expected_files / forbidden_paths / cherry_pick_allowed 추출.rj   )encodingc                J   t        j                  dt        j                  |        dt         j                        }|j	                  	      }|syt        |j                  d            }	|j                         d  j                         }g }|D ]x  }|j                         }|s|j                  |       '|j                  d      r|j                  |       Jt        |      t        |      z
  }||k  r n|j                  |       z dj                  |      |rdz   S dz   S )Nz	^([ \t]*)z\s*:\s*$rO   ru   #
)r   compileescape	MULTILINEsearchr|   groupendr   lstripr~   r}   join)
keyhead_rer   
key_indentlinesr   lnstrippedcurrent_indenttexts
            rB   _extract_blockz.load_task_spec_from_md.<locals>._extract_block   s    **	"))C.)9BBLLQ~~d#A'
TXXZ[!,,. 	Byy{HR ""3'R  Ws8}4N+LL	 yy5499b99rA   r7   r9   z```ya?ml\s*\n([\s\S]*?)```Fz9^[ \t]*cherry_pick_allowed\s*:\s*(true|false)\s*(?:#.*)?$ru   true)r5   r7   r9   r;   )r   r4   r_   r4   )	read_textstemr   r   findall
IGNORECASEr~   r   r   r   r   lowerr3   )task_md_pathr5   r   expected	forbiddencherry_search_targetsfencecherry	cherry_rechunkr   r   s              @rB   load_task_spec_from_mdr      s0   !!7!3DG:8  /? @AH 0A!BCI
 (*94O ,$$U+,  0@!AB  0A!BCF

D
r}}$I
 ' U#WWQZ%%'61F !"	 rA   c                      e Zd ZdZ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ddZ	 	 	 	 	 	 ddZdd	 	 	 	 	 	 	 	 	 	 	 ddZy)MergeQueueExecutoru   ANU v2 자동 머지 큐 실행기 v0.

    9 기능을 method 단위로 분리. 부수효과는 생성자 주입 callable 로 분리되어
    테스트는 mock callable 만 교체하면 충분하다.
    BOT_GITHUB_TOKEN)bot_token_envc               j    || _         || _        || _        || _        t	        |      | _        || _        y rY   )_gh_git_pytest_auditr   _task_md_root_bot_token_env)r[   	gh_runner
git_runnerpytest_runneraudit_writertask_md_rootr   s          rB   __init__zMergeQueueExecutor.__init__(  s5     	$"!,/+rA   c                ~    |j                   dkD  rt        t        |j                    d      S t        t        d      S )uL   선행 PR 모두 머지 완료 (queue_predecessors_open == 0) 인지 검증.r   z predecessor(s) still openrS   rT   queue_head_confirmed)rN   rR   r   r   )r[   prs     rB   evaluate_queue_headz&MergeQueueExecutor.evaluate_queue_head:  sC    %%)04455OP  $6?UVVrA   c                    t        |j                        }t        |j                        }||k(  rt        t        d      S ||z
  }||z
  }t        t
        dt        |      t        |      d      S )u   PR diff 가 task md expected_files 와 정확히 일치하는지 검증.

        불일치는 DIFF_CONTAMINATION (비critical) — 정정 트랙은 본 v0 범위 외.
        expected_files_matchr   expected_files_mismatch)missingrW   rS   rT   rW   )setr7   rJ   rR   r   r   r   )r[   specr   r   actualr   rW   s          rB   check_expected_files_diffz,MergeQueueExecutor.check_expected_files_diffD  sr     t**+R%%&v(:CYZZV#!',$WouF
 	
rA   c                F   |j                   st        t        d      S g }|j                  D ]2  }|j                   D ]!  }t	        ||      s|j                  |        2 4 |r/t        t        t        t        dt        t        |            i      S t        t        d      S )uM   PR diff 에 task md forbidden_paths 매칭 파일이 있으면 Critical 7종.no_forbidden_pathsr   hitsrS   rT   rU   rW   forbidden_paths_clean)r9   rR   r   rJ   _glob_matchr~   r   r   CRITICAL_FORBIDDEN_PATHr   r   )r[   r   r   r   changedpatterns         rB   check_forbidden_pathsz(MergeQueueExecutor.check_forbidden_pathsZ  s    ##(:CWXX'' 	G// w0KK(	
 ,)5vc$i01	  $6?VWWrA   c                  |j                   st        t        d      S |j                  t        k(  rt        t
        dt              S |j                  t        fvrt        t        d|j                         S |j                  rB|j                  |j                  k7  r)t        t        d|j                  |j                  d      S |j                  d	k7  r>t        }d
}|j                  dk(  rt
        }t        }t        |d|j                   |      S |j                  |k7  rt        t        d||j                  d      S t        t         d      S )uH   CI required all SUCCESS + Gemini unresolved 0 + CLEAN + HEAD SHA 일치.ci_required_not_all_successr   gemini_scope_expansionrS   rT   rU   gemini_status=(gemini_evidence_stale_commit_id_mismatch)evidence_commit_idcurrent_headr   CLEANrO   BLOCKEDzmergeStateStatus="head_sha_changed_during_evaluation)lockedcurrentall_gates_clean)rK   rR   r   rL   r0   r   CRITICAL_GEMINI_SCOPE_EXPANSIONr-   r   rP   rG   r   rM   r   CRITICAL_BLOCK_OVERRIDEr   r   )r[   r   head_sha_at_lockrS   criticals        rB   check_ci_gemini_clean_sha_lockz1MergeQueueExecutor.check_ci_gemini_clean_sha_locko  sU    )))4  55,/= 
 $4#660'(8(8'9:  2#6#6"++#E-A*,*=*=$&KK    G+,HH$$	1.2!*2+@+@*AB& 
 ;;**-;!1bkkJ 
 $6?PQQrA   c               6   t         j                  j                  | j                  d      j	                         }|st        t        dt              S ddt        |j                        dd|g}t        |       t         j                  j                         }||d<   ||d	<   | j                  ||      }|j                  d
k7  r0t        t        dt        dt        t        |dd            dd i      S t        t         ddt        t        |dd            dd i      S )u  `GH_TOKEN=$BOT_GITHUB_TOKEN` process-local injection 으로 squash merge.

        - 호스트 환경의 GH_TOKEN / GITHUB_TOKEN 은 절대 그대로 사용하지 않는다.
        - admin / force / rebase 플래그는 정적 차단 (assert_no_forbidden_git_flags).
        rO   bot_token_unavailabler   r   mergez--squashz--match-head-commitGH_TOKENGITHUB_TOKENr   gh_pr_merge_failedstderrNi   r   bot_squash_merge_completedstdoutr   )osenvirongetr   r   rR   r   r   r4   rF   r   copyr   
returncoderq   getattrr   )r[   r   r   r   r   envcps          rB   execute_bot_squash_mergez+MergeQueueExecutor.execute_bot_squash_merge  s    

t22B7==?,.5  '3ryy>!#3

 	&d+
 jjooJ#NXXdC ==A,+5  Hd0K!LTc!RS  '/^GB$,GH#NO
 	
rA   c                   |st        t        d      S | j                  t        |            }|dk7  rt        t        dt
        d|i      S t        t        d      S )u$   머지 후 회귀 (smoke) 재실행.smoke_skipped_no_targetsr   r   post_merge_smoke_failedpytest_exit_coder   post_merge_smoke_passed)rR   r   r   listr   CRITICAL_POST_MERGE_SMOKE)r[   smoke_test_pathsrcs      rB   run_post_merge_smokez'MergeQueueExecutor.run_post_merge_smoke  s`      (:C]^^\\$/017,07)2.	  $6?XYYrA   c                  t         j                  j                  | j                  d      j	                         }|s|D cg c]  }|ddt               d c}S t         j                  j                         }||d<   ||d<   g }|D ]]  }ddt        |      d	d
| dg}t        |       | j                  ||      }|j                  ||j                  dk(  t               d       _ |S c c}w )u   머지로 stale 이 된 다른 OPEN PR 의 evidence 재검증 트리거.

        모든 gh 호출은 BOT 토큰 process-local injection 으로 격리한다 (회장 §6 — 봇
        identity 강제). 외부 owner_pat 누출 차단.
        rO   Fr   )	pr_numberokrT   tsr   r   r   commentz-bz+[anu_v2.merge_queue_executor] base merged: uC    → evidence revalidation requested (CI rerun + Gemini retrigger).r   )r  r  r  )r  r  r  r   r   rh   r  r4   r   r   r~   r  )	r[   merged_task_iddownstream_pr_numbersr   pr_nor
  resultsr   r  s	            rB   revalidate_downstreamz(MergeQueueExecutor.revalidate_downstream  s    

t22B7==? 3  "'5"*	  jjooJ#N(** 
	E)SZA.AQ RU UVD *$/$$BNN"mmq(j 
	 3s   C&c                >    |j                   r|j                  S t        S )uP   Critical 7종 코드면 그대로 반환, 그 외는 NON_CRITICAL_AUTO_RESOLVED.)r^   rU   r   r[   outcomes     rB   classify_failurez#MergeQueueExecutor.classify_failure  s    ((())rA   c           	        |j                   t        k7  rF| j                  t               d|j                   |j                  t        |j                        d       t        t        |j                  t        |j                              S )uJ  비critical 결과는 audit 에만 기록하고 self-resolve. 회장 보고 X.

        WAITING_FOR_PREDECESSOR 는 머지 큐의 정상적인 대기 상태라 짧은 주기로 반복
        실행 시 audit 로그가 과도하게 누적된다. 대기 상태는 로깅을 생략하고 그대로
        반환만 한다.
        non_critical_auto_resolved)r  kindrS   rT   rW   r   )	rS   r   r   rh   rT   r`   rW   rR   r   r"  s     rB   auto_handle_non_criticalz+MergeQueueExecutor.auto_handle_non_critical  sm     66KKj4#,,!..gmm,  />>w}}%
 	
rA   c          	     
   t        |j                        }t        |      j                  }| j                  | dz  }|j                         s"t        t        dt        dt        |      i      S t        |      }| j                  ||      | j                  ||      | j                  |      | j                  ||      fD ]1  }|j                  r|j                   s| j#                  |      c S |c S  t        t$        d      S )u   9 기능 중 게이트 1~4 를 순차 평가하고 최종 결과를 반환.

        실제 머지 (5) / 스모크 (6) / 다운스트림 재검증 (7) 은 호출부에서 명시 호출.
        z.mdtask_md_missingpathr   )r   all_4_gates_passr   )_extract_task_id_from_branchrH   r   namer   existsrR   r   r   r4   r   r   r   r   r   r\   r^   r(  r   )r[   r   r   raw_task_idsafe_task_id	spec_pathr   gates           rB   evaluatezMergeQueueExecutor.evaluate1  s    32;;?K(--&&L>)==	!,(5s9~.	  &i0 &&tR0//EU/V$$R(**44	
 		D ;;''88>>		 $6?QRRrA   N)owner_triggerc          
        | j                  ||      }|j                  t        k(  r|S |j                  |k7  r|S ||S |j                  dkD  r|S |j
                  t        k(  xsL |j
                  t        k(  xr7 t        |j                  t              xr |j                  j                  d      }|s|S |j                  |j                  |j                  |j                  t        ||      }|j                  t         k(  r4t#        t$        d|j                  |j&                  |j
                  d      S |j                  t(        k(  s5t        |j                  t              ry|j                  j                  d      r^t#        t*        d	|j                   d
|j                   t,        |j                  |j                  |j&                  |j
                  d      S |S )us  `evaluate()` 결과가 GEMINI evidence 미도착 (`GEMINI_UNRESOLVED_BLOCK`) 이고
        queue-head 인 경우에만 주입된 `owner_trigger` 로 `/gemini review` 댓글 1회
        작성 → 다음 사이클에서 evidence 도착 후 재검증되도록 `OWNER_TRIGGER_REQUESTED`
        반환.

        설계 원칙 (회장 §명시 12 + 11/12번):
          - 기존 `evaluate()` 시그니처/동작 변경 0 — read-only 호출.
          - `owner_trigger=None` → trigger 호출 0, 기존 outcome 그대로 반환.
          - queue-head 가 아니면 (predecessors > 0) trigger 호출 0.
          - Gemini 가 이미 COMPLETED → trigger 호출 0, evaluate 결과 그대로.
          - OWNER PAT 은 owner_trigger 인스턴스 내부에만 존재. 본 executor 는 OWNER
            PAT env 를 직접 읽지 않고 env 이름 상수를 import 하지 않는다 (격리).
          - BOT_GITHUB_TOKEN squash merge 경로 (`execute_bot_squash_merge`) 는 그대로
            유지 — owner_trigger 호출이 머지 토큰을 대체하지 않는다.

        흐름:
          1. `evaluate()` 호출 → 4 gate 결과.
          2. `pr.gemini_status == GEMINI_COMPLETED` 면 evidence 도착 완료 → 그대로
             반환 (4 gate PASS 면 AUTO_MERGE_ALLOWED 로 다음 단계 머지).
          3. evidence 미도착 + queue-head + owner_trigger 주입됨 일 때만
             `owner_trigger.trigger_gemini_review(...)` 호출.
          4. trigger outcome == "ok" → `OWNER_TRIGGER_REQUESTED` 반환.
             그 외 (rejected / failed) → 기존 evaluate 결과 그대로 (다음 사이클 재시도).
        )r   r   r   r   )r  rG   queue_positiongemini_evidence_stateaudit_log_pathdecision_json_path owner_trigger_pat_comment_posted)trigger_reasondecision_pathprior_decisionr   token_missingzowner_trigger_escalated::)trigger_outcomer<  r=  r>  r   )r4  rL   r-   rG   rN   rS   r   r   rm   rT   r4   r}   trigger_gemini_reviewrF   r   r#  r   rR   r   r=  r   r   r   )	r[   r   r   r5  r9  r:  r#  _gemini_unresolved_allowedrA  s	            rB   evaluate_with_owner_triggerz.MergeQueueExecutor.evaluate_with_owner_triggerZ  s   B --28H-I //N ;;**N  N %%)N  77   $>> @w~~s3@NN--.>? 	# *N'==ii[[55"C)1 > 
 ""j009&5&<&<%4%B%B&-&6&6  ""n4--s3&&11/B,1/2I2I1J!OLbLbKcd5'6'>'>&5&<&<%4%B%B&-&6&6		
 
 rA   )r   GhRunnerr   	GitRunnerr   PytestRunnerr   AuditWriterr   r   r   r4   r_   None)r   rD   r_   rR   )r   r3   r   rD   r_   rR   )r   rD   r   r4   r_   rR   )r  Sequence[str]r_   rR   )r  r4   r  zIterable[int]r_   zlist[dict[str, Any]])r#  rR   r_   r4   )r#  rR   r_   rR   )r   rD   r   r4   r5  zOwnerTriggerPat | Noner9  r   r:  r   r_   rR   )r<   r=   r>   r?   r   r   r   r   r   r  r  r   r$  r(  r4  rD  r8   rA   rB   r   r   !  s    0, , 	,
 $, ", , , 
,$W

 
 
	
,X*5R5R 	5R
 
5Rp.
.
 	.

 
.
bZ (Z 
	Z&' '  -	'
 
'T*
,&S &S 	&S
 
&S\ 15q q 	q
 .q q !q 
qrA   r   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  간단한 glob 매칭.

    지원 규칙:
      - `**/`  → `(?:.*/)?` (0개 이상의 디렉토리. 루트 파일도 매칭).
      - `**`   → `.*`       (`/` 포함 임의 문자열).
      - `*`    → `[^/]*`    (디렉토리 경계를 넘지 않는 임의 문자열).

    `**/` 처리를 `**` 보다 먼저 두어 `**/foo.py` 패턴이 `foo.py` (루트 파일) 도
    매칭할 수 있게 한다 (re.escape 가 `/` 를 이스케이프하지 않는 환경 가정 외에도
    이스케이프된 `\/` 형태도 함께 처리).
    ^z\*\*\/z(?:.*/)?z\*\*/z\*\*z.*z\*z[^/]*$N)r   r   rk   r   )r   r+  escapedregexs       rB   r   r     ss     ii G
//)[
1'(K0''4('%)		*
 	 
 88E4 ,,rA   c                X    t        j                  d|       }|s| S |j                  d      S )u&   `task/task-2531-dev4` → `task-2531`.z ^task/(?P<id>task-\d+(?:\+\d+)?)id)r   r   r   )branchr   s     rB   r-  r-    s)    
4f=A774=rA   )r_   r4   )rp   r	   r_   r4   )r   rJ  r_   rI  )r   r4   r_   r6   )r   r   r_   r3   )r   r4   r+  r4   r_   r:   )rR  r4   r_   r4   )Fr?   
__future__r   r  r   
subprocessdataclassesr   r   r   r   pathlibr   typingr	   r
   r   r   r   anu_v2.owner_trigger_patr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    CRITICAL_DIFF_REPLACEMENT_FAILEDr   r   CRITICAL_DEPENDENCY_CYCLECRITICAL_REPLACEMENT_FAILEDr  	frozensetr&   r@   r,   r-   r.   r/   r0   r3   rD   rR   r4   CompletedProcessrE  rF  rE   rG  rH  rh   rq   r   r   r   r   r   r-  r8   rA   rB   <module>r^     s  ( # 	 	  ( '  = =  * ) 3 + - % 3 / ) ) 9 3  .  4 #T  "C J F : 6 !*$#, "  '0 1 ' ^ 
 & ' #1  $& & & $  ( Q Q Q" Xc]GCH$5$<=z?Z?ZZ[hsm_j&A&AAB	#,-S)*D01D
&DR(?Fj j\-0rA   