
    +j5                       d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	m
Z
 dZdZ ej                  d      Z ej                  d	      Z ej                  d
      Ze G d d             ZddZe G d d             Zdd dZddd	 	 	 	 	 	 	 	 	 d!dZe G d d             Zddd	 	 	 	 	 	 	 d"dZe G d d             Zddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d#dZg dZy)$u  dispatch.anu_finish_preflight — task-2731 finish/closeout preflight (dev1/dev2 마찰 분리).

dev1/dev2 closeout 미완 실증 결함 중 **finish-task 끝단 마찰 3종**을 기존 guard
직접 수정 없이 별개 모듈로 분리 판정한다(expected_files 내부 유지 · canonical 0 touch):

  1. **branch regex `-rN`/`+N` 안전 추출**(dev2): `task-2730-r2` → base `task-2730`
     로만 추출하면 dev1 의 `task-2730` state/lock 과 충돌. revision suffix 를
     인식하여 base + revision + full ref 를 분리, 정체성 충돌 0.
  2. **worktree path mismatch 탐지**(dev2): `.worktrees/<task>` 기대 vs CODE_ROOT
     직접 체크아웃 불일치를 preflight 에서 탐지(경고/차단 판정).
  3. **task-lock 마찰 우회 판정**(dev2): OS runner 의 result.json 수거는 task-lock /
     worktree / tracked 여부와 **독립**(lock-free)임을 명문화 — lock 마찰로 callback
     누락 0.

순수 함수(git/subprocess 0 · 파일시스템 mutation 0 · 입력만으로 판정) → isolated
unit test 가능. 기존 guard(scripts/start_task_guard.py 등) **직접 수정 0**.

Layer A / NO-CRON: cron 0, subprocess 0, merge 0.
    )annotationsN)	dataclassfield)ListOptionalz dispatch.anu_finish_preflight.v1z
.worktreesz
task-(\d+)z^(?:-r(\d+)|\+(\d+))z^-(dev\d+|[a-z][a-z0-9]*)$c                      e Zd ZU dZdZded<   dZded<   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y)BranchParseu  branch/ref 에서 안전 추출한 task 정체성.

    base_task_id  : revision/executor suffix 제거한 base (예: task-2731)
    revision      : replacement revision 라벨 (예: r2 / +1) — 없으면 ""
    executor      : executor 라벨 (예: dev6) — 없으면 ""
    full_task_ref : base + revision (lock/state 정체성 — 충돌 회피용. 예: task-2730-r2)
    is_revision   : revision suffix 존재 여부 (True → base 단독 추출 시 dev1 충돌 위험)
     strbase_task_idrevisionexecutorfull_task_refFboolis_revisiondefault_factory	List[str]reasonsc                ,    t        | j                        S N)r   r   selfs    4/home/jay/workspace/dispatch/anu_finish_preflight.pyokzBranchParse.ok>   s    D%%&&    Nreturnr   )__name__
__module____qualname____doc__r   __annotations__r   r   r   r   r   listr   propertyr    r   r   r	   r	   ,   s\     L#HcHcM3Kt4GY4' 'r   r	   c           	     H   t        | xs d      j                         }|st        dg      S |j                  dd      d   }t        j                  |      }|st        d|dg      S d	|j                  d       }||j                         d
 }d}d}g }t        j                  |      }	|	rj|	j                  d      xs |	j                  d      }
|j                  d      rd|
 nd|
 }||	j                         d
 }|j                  d| d| d       |r=t        j                  |      }|r|j                  d      }n|j                  d|       |j                  d      r| d| }n|j                  d      r| | }n|}t        ||||t        |      |      S )u  branch/ref 문자열에서 task 정체성을 **안전 추출**한다(`-rN`/`+N` 인식).

    예시:
      - ``task/task-2731-dev6``  → base=task-2731, executor=dev6, full=task-2731
      - ``task-2731-r2``         → base=task-2731, revision=r2, full=task-2731-r2 (is_revision)
      - ``task-2730-r2``         → base=task-2730, revision=r2, full=task-2730-r2
                                    (★ base 단독이면 dev1 task-2730 충돌 → full_task_ref 사용)
      - ``task/task-2730+1``     → base=task-2730, revision=+1, full=task-2730+1
      - ``main`` / ""            → ok=False (task ref 아님)

    lock/state 정체성은 **full_task_ref** 를 사용해야 revision 이 base 와 충돌하지
    않는다(dev2 → dev1 state 충돌 방지).r
   u   빈 branch — task ref 아님.)r   /   u   task-<N> 패턴 부재: u    — task ref 아님.ztask-N   z-rr+u   revision suffix 인식: u    — base 단독(uN   )은 원본 task state/lock 과 충돌 가능 → full_task_ref 사용 권장.u!   잔여 suffix 미인식(무시): -)r   r   r   r   r   r   )r   stripr	   rsplit_TASK_BASE_REsearchgroupend_REVISION_REmatch
startswithappend_EXECUTOR_REr   )branchssegmbaserestr   r   r   rmatchrev_numematchfulls                r   parse_branchrD   C   s    	FLb!A$E#FGG
((3
2
CS!A&>sgEZ$[#\]]1771:,Dquuwx=DHHG %F,,q/4V\\!_$(OOD$9Qwi=7)}FJJLM"&xj0A$ HF G	
 ##D)||AHNN>thGH 3q
#			S	!z"N r   c                  ^    e Zd ZU dZded<   ded<   ded<    ee      Zded	<   edd
       Z	y)WorktreeChecku%   worktree path mismatch 판정 결과.r   mismatchr   expected_subpathactual_pathr   r   r   c                    | j                    S r   )rG   r   s    r   r   zWorktreeCheck.ok   s    ==  r   Nr   
r   r    r!   r"   r#   r   r$   r   r%   r   r&   r   r   rF   rF      s7    /Nt4GY4! !r   rF   r
   c                `    |s| n|  d| }t         j                  j                  t        |      S )u   기대 worktree 상대경로: ``.worktrees/<task_ref>[-<executor>]``.
    예: task_ref=task-2731, executor=dev6 → ``.worktrees/task-2731-dev6``.r.   )ospathjoinWORKTREES_DIR)task_refr   names      r   expected_worktree_subpathrS      s-     $8H:Qxj)AD77<<t,,r   )r   	code_rootc          	     D   t        ||      }t        j                  j                  t	        | xs d            }g }t        j                  j                  |      }t        j                  t        z   t        j                  z   |t        j                  z   v }|xrF |j                  t        j                  |z         xs" t        j                  j                  |      |k(  }	|xr |	 }
|
r|s |j                  dt         d| d| d       n7|	s5|j                  d| dt        j                  j                  |       d       |rP|t        j                  j                  |      k(  r.|j                  d| d       n|j                  d	| d
| d       t        |
|||      S )uj  실 작업 경로가 기대 worktree(`.worktrees/<task_ref>[-executor]`)인지 판정.

    dev2 마찰: executor 가 CODE_ROOT 에 직접 체크아웃(`.worktrees/...` 미사용) →
    start_task_guard 기대 경로 불일치로 finish 미완. 본 함수는 그 mismatch 를
    preflight 에서 탐지한다(경고/차단 판정). 파일시스템 mutation 0.r
   u!   worktree mismatch: 실 경로가 uE   / 하위가 아님 (CODE_ROOT 직접 체크아웃 의심) — 기대=u	   , 실제=.u@   worktree mismatch: worktree 이름 불일치 — 기대 segment=uM   ★ 실 경로 == CODE_ROOT — dev2 와 동일 마찰(직접 체크아웃). u    형태 worktree 분기 필요.zworktree OK: u    이(가) 기대 worktree(u   ) 와 일치.)rG   rH   rI   r   )rS   rM   rN   normpathr   basenameseprP   endswithr8   rF   )rI   rQ   r   rT   expectedactualr   expected_namein_worktrees
name_matchrG   s              r   detect_worktree_mismatchr`      s    )8<HWWc+"345FG GG$$X.MFF]*RVV3"&&IL ./\2773C3CF3K}3\  !/Z0HNN3M? CEEMJiX^W__`b NNRS`Ra b''**62316 277#3#3I#>>NN_*;=
 	vh.H
R_`a!	 r   c                  ^    e Zd ZU dZded<   ded<   ded<    ee      Zded<   edd	       Z	y
)TaskLockBypassuF   task-lock 마찰 우회 판정 결과(result.json 수거 lock 독립).r   	bypass_oklock_contendedresult_json_presentr   r   r   c                    | j                   S r   )rc   r   s    r   r   zTaskLockBypass.ok   s    ~~r   Nr   rK   r&   r   r   rb   rb      s7    POt4GY4 r   rb   F)rd   	lock_pathc                    g }| s |j                  d       t        d|d|      S |r|j                  d|xs d d       n|j                  d       t        d|d|      S )	u  OS runner 의 result.json 수거가 task-lock 마찰과 **독립**임을 판정한다.

    dev2 마찰: task-lock 생성/요구가 worktree 경로·tracked 여부에 묶여 finish 끝단이
    막힘 → callback 미발사. 본 모듈은 result.json 수거 경로가 lock 과 **무관**(lock-free)
    임을 명문화한다:

      - result.json 존재 → lock 마찰 여부와 무관하게 수거 가능(bypass_ok=True).
      - lock 이 contended 여도 result.json 수거/closeout/callback launch 는 진행
        (OS runner 는 lock 을 잡지 않는다 — ANCHOR-1).
      - result.json 부재 → 수거 대상 없음(bypass_ok=False, 단 lock 과 무관한 사유).

    파일시스템 mutation 0 · lock acquire 0 (판정만).uf   result.json 부재 — 수거 대상 없음(lock 과 무관). executor 미작성 또는 경로 오류.F)rc   rd   re   r   u   task-lock 마찰 감지(lock=unknownuu   ) — 그러나 OS runner 의 result.json 수거는 lock-free(lock 독립). 수거·closeout·callback launch 진행.uJ   task-lock 마찰 없음 — result.json 수거 진행(lock 독립 경로).T)r8   rb   )re   rd   rg   r   s       r   assess_task_lock_bypassrj      s    $ Gt	
 ) %	
 	
 +I,B+C Df f	

 	X	
 % 	 r   c                  h    e Zd ZU dZded<   ded<   ded<   ded	<    ee
      Zded<   edd       Z	y)FinishPreflightuB   finish/closeout preflight 통합 결과(판정만 — mutation 0).r   schemar	   r:   zOptional[WorktreeCheck]worktreezOptional[TaskLockBypass]lock_bypassr   r   r   c                \    t        | j                  xr | j                  j                        S )u   OS runner 가 result.json 을 수거 가능한가.
        worktree mismatch / branch revision / lock 마찰이 있어도 result.json 존재 시
        수거 가능(ANCHOR-1) — lock_bypass.bypass_ok 가 단일 게이트.)r   ro   rc   r   s    r   collectiblezFinishPreflight.collectible  s&    
 D$$C)9)9)C)CDDr   Nr   )
r   r    r!   r"   r#   r   r$   r   r%   rq   r&   r   r   rl   rl     s?    LK%%))t4GY4E Er   rl   )rI   r   rT   re   rd   rg   c           	        t        |       }|xs |j                  }|j                  xs |j                  }	d}
|r|	rt	        ||	||      }
t        |||      }g }|j                  r9|j                  d|j                   d|j                   d|j                   d       |
r|
j                  r|j                  d       t        t        ||
||	      S )
u  branch 안전 추출 + worktree mismatch + task-lock 우회 통합 판정.

    ★ 핵심 불변: worktree mismatch/branch revision/lock 마찰은 **수거를 막지 않는다**.
    이들은 경고(reasons)로 surface 하되, result.json 이 존재하면 OS runner 가 수거하여
    callback launch 를 강제한다(finish-task 끝단 마찰과 callback 발사의 분리 — ANCHOR-1).N)rQ   r   rT   )re   rd   rg   zbranch revision(u#   ) — lock/state 는 full_task_ref=u    사용(base u    단독 시 충돌).uT   worktree mismatch 경고 — 단 result.json 수거는 lock/worktree 독립(진행).)rm   r:   rn   ro   r   )rD   r   r   r   r`   rj   r   r8   r   rG   rl   PREFLIGHT_SCHEMA)r:   rI   r   rT   re   rd   rg   bp
exec_labelrQ   wtlbr   s                r   	preflightrx     s     
f	B(R[[J22??H"&Bx%(Z9
 
!/%
B G	~~r{{m+N boo->>RT	
 
bkkmn r   )rs   rP   r	   rD   rF   rS   r`   rb   rj   rl   rx   )r:   r   r   r	   )r
   )rQ   r   r   r   r   r   )
rI   r   rQ   r   r   r   rT   r   r   rF   )re   r   rd   r   rg   r   r   rb   )r:   r   rI   r   r   r   rT   r   re   r   rd   r   rg   r   r   rl   )r"   
__future__r   rM   redataclassesr   r   typingr   r   rs   rP   compiler1   r5   r9   r	   rD   rF   rS   r`   rb   rj   rl   rx   __all__r&   r   r   <module>r      s  & # 	 	 ( ! 6   

=)rzz12rzz78 ' ' ',?F 
! 
! 
!- 00 0 	0
 0 0h 
 
 
  !	++ + 	+
 +^ E E E(  % .. . 	.
 . . . . .br   