
     j"V                    2   d Z ddlmZ ddlZddlmc mZ ddl	Z
ddlZddlZddlZddlZ ej                  e      j#                         j$                  d   Z ee      Zeej,                  v r*ej,                  j/                  e       eej,                  v r*ej,                  j1                  de        eej4                        D ]e  Zedv s#ej9                  d      sej9                  d      s*ej4                  e   Z eedd      xs d	Zej9                  e      rYej4                  e= g ed
z  dz  Z e
jB                  jE                  de       Z#g Z$dZ%e#e%uZ&e&Z'e&re#jP                  Z)dZ*e)e*uZ+e+Z'e'sB ejX                  de&fde#e%f      d ejZ                         v s ej\                  e#      r ej^                  e#      nd ej^                  e%      dz  Z0dde0iz  Z1e$je                  e1       e&r ejX                  de+fde)e*f      d ejZ                         v s ej\                  e#      r ej^                  e#      nd ej^                  e)       ej^                  e*      dz  Z3dde3iz  Z4e$je                  e4        ejj                  e$d      i z  Z6dde6iz  Z7 e8 ejr                  e7            dxZ'xZ$xZ&xZ%xZ)xZ+Z*e
jB                  ju                  e#      Z;e;ej4                  d<   e#jP                  jy                  e;       ddl=m>Z> ddl?m@Z@mAZAmBZBmCZCmDZD ddlEmFZF ddlGmHZHmIZImJZJ e;j                  ZKe;j                  ZLe;j                  ZMd4dZNd4dZOd4dZPd  ZQd! ZRd" ZSd#ZTd$ ZUd% ZVd& ZWd' ZXd( ZYd) ZZd* Z[d+ Z\d, Z]d- Z^d. Z_d/ Z`d0 Zad1 Zbd2 Zcd3 Zdy)5u  tests/regression/test_pr_convergence_pipeline_2729.py — task-2729 Phase 2 회귀.

PR convergence pipeline 결함4·5·6·7 회귀 보호:
  - 결함7: live_prune (collect_and_prune success gating + summarize_live_prune)
  - 결함6: owner_gemini_trigger (fire_owner_gemini_review + _validate_head_lock + dedupe)
  - 결함5: base_sync_before_merge (merge 전 base sync 파이프라인)
  - 결함4: authoritative_completion_marker (scope evidence 3개 기반 authoritative completion)

구현 파일(utils/*, scripts/*)은 절대 수정하지 않는다.
fake remover / mock 주입 / 임시 registry 사용. 실 gh/subprocess/cokacdir 호출 0.
    )annotationsN   )dispatchutilsz	dispatch.zutils.__file__ scriptszci_watch_handoff_runner.pyci_watch_handoff_runneris not)z%(py2)s is not %(py5)s_spec)py2py5%(py7)spy7)z5%(py11)s
{%(py11)s = %(py9)s.loader
} is not %(py14)s)py9py11py14z%(py16)spy16zassert %(py19)spy19)register_fallback)RemoverResultCancelClassificationsummarize_live_prunePruneOutcome
PruneCause)collect_and_prune)fire_owner_gemini_review_validate_head_lockOWNER_GEMINI_REVIEW_BODYc               "    t        dd|        S )Nremovedzfake removed statusdetailr   cron_iddry_runs     f/home/jay/workspace/.worktrees/task-2729-p2-dev6/tests/regression/test_pr_convergence_pipeline_2729.py_remover_removedr+   O   s    	M'2KLL    c               "    t        dd|        S )Nalready_gonezfake already_gone r#   r&   r'   s     r*   _remover_already_goner/   S   s    9KG97UVVr,   c               "    t        dd|        S )Nfailedzfake failed r#   r&   r'   s     r*   _remover_failedr2   W   s    <y1IJJr,   c                    t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        dddd|
      }|d   }|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}|d   }	t        |	      }d}
||
k(  }|st        j                  d|fd||
f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |
      dz  }t        j                  d t        |	             d!z   d"|iz  }t        t        j                  |            dx}x}}
|	d#   d$   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d%      dz   d|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)&u   임시 registry에 pending fallback 1건 → collect_and_prune(success trigger, fake remover,
    dry_run=False) → live_prune.fired=True, real_tombstone=True, prune_outcomes에 pruned=True.
    .jsonlFsuffixdeleteNztask-2729-lp   (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaCRON_LP_REALanutask_idroundhead_shar(   	owner_keyregistry_pathnormal_callback_collectedT)
r=   r>   r?   triggerremoverr)   rB   dispatch_firedrequire_successrA   
live_prunewiredisz%(py1)s is %(py4)spy1py4#live_prune.wired must be True, got 
>assert %(py6)spy6firedz#live_prune.fired must be True, got real_tombstonez,live_prune.real_tombstone must be True, got prune_outcomes==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenoutcomespy0rM   py3rQ   zExpected 1 prune_outcome, got 
>assert %(py8)spy8r   prunedz%prune_outcomes[0].pruned must be True)tempfileNamedTemporaryFilenamer   r   r+   
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanationrX   @py_builtinslocals_should_repr_global_name)fregresultlp@py_assert0@py_assert3@py_assert2@py_format5@py_format7rY   @py_assert5@py_assert4@py_format9s                r*   0test_live_prune_collector_success_real_tombstonerx   `   s    
	$	$HU	C qff  + "&F 
	Bg;J$J;$JJJ;$JJJ;JJJ$JJJ"EbT JJJJJJJJg;J$J;$JJJ;$JJJ;JJJ$JJJ"EbT JJJJJJJJ\4\4'\\\4\\\\\\4\\\+WXZW[)\\\\\\\\ &'Hx=OAO=AOOO=AOOOOOO3OOO3OOOOOOxOOOxOOO=OOOAOOO!?HOOOOOOOOA;x RDR D(RRR DRRR RRRDRRR,QRRRRRRRC s   O55O?c                 "   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       d}ddl}|j
                  j                  |      xr |j                  |       t        ddddt        dddd||      }|d   }|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}x}}|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}x}}|j                   }d} ||      }d}||k(  }|st        j                  d|fd||f      dt#        j$                         v st        j&                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d |j!                  d            d!z   d"|iz  }t        t        j                  |            dx}x}x}x}}|d#   }g }||k(  }|st        j                  d|fd$||f      t        j                  |      t        j                  |      dz  }	t        j                  d%|d#          dz   d|	iz  }
t        t        j                  |
            dx}x}}y# 1 sw Y   axY w)&u   normal_callback_collected=False + result 없음 + trigger 비success + require_success=True
    → live_prune.fired=False, reason=NO_SUCCESS_SIGNAL, prune 미수행.
    r4   Fr5   Nztask-2729-gater8   (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb	CRON_GATEr;   r<   z9/tmp/does_not_exist_task_2729_gate_regression.result.jsonr   some_unknown_triggerT)r=   r>   r?   rC   rD   r)   rB   rE   rF   result_json_pathrA   rG   rH   rI   rK   rL   rO   rP   rQ   rR   z,live_prune.fired must be False (gated), got reasonNO_SUCCESS_SIGNALrU   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sro   r[   r   rN   rQ   r   z1live_prune.reason must be NO_SUCCESS_SIGNAL, got z
>assert %(py11)sr   rT   %(py1)s == %(py4)sz*prune_outcomes must be [] when gated, got )r`   ra   rb   r   ospathexistsunlinkr   r+   rc   rd   re   rf   rg   rh   getri   rj   rk   )rl   rm   
missing_rjr   rn   ro   rp   rq   rr   rs   rt   @py_assert1ru   @py_assert8@py_assert7@py_format10@py_format12s                    r*   'test_live_prune_no_success_signal_gatedr      s    
	$	$HU	C qff   MJrww~~j)Cbii
.C & "'#F 
	Bg;J$J;$JJJ;$JJJ;JJJ$JJJ"EbT JJJJJJJJg;T%T;%TTT;%TTT;TTT%TTT#OPRt!TTTTTTTT66 ( 6( 2 22  2                           3    <BFF8<L;OP      "# r #r)  #r    $    (*    5V<L5M4NO     M s   NNc                 b   ddl m}  t        ddddt        j                  t
        j                  j                  ddd |        	
      t        dddd
t        j                  t
        j                  j                  ddd |        	
      g}t        |      }|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                   |            dx}x}}|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d          dz   d|iz  }t        t        j                   |            dx}x}}|d   }t#        |      }d}	||	k(  }
|
st        j                  d|
fd||	f      dt%        j&                         v st        j(                  t"              rt        j                  t"              ndt        j                  |      t        j                  |      t        j                  |	      dz  }t        j                  d|d          dz   d |iz  }t        t        j                   |            dx}x}x}
}	t        d!dd"d#t        j                  t
        j                  j                  ddd |        	
      g}t        |      }|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d$|       dz   d|iz  }t        t        j                   |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }d%d|iz  }t        t        j                   |            dx}x}}y)&u   summarize_live_prune 직접 단위테스트: pruned=True+cron_remove_invoked=True 건이 있으면
    real_tombstone=True, pruned_count 집계, causes 리스트.
    r   )_now_utct1r8   (ccccccccccccccccccccccccccccccccccccccccC1Tr"   )
r=   r>   r?   r(   classificationcausecron_remove_invokedr_   r%   ts_utcC2Fr1   rS   rI   rK   rL   zAreal_tombstone must be True when pruned+cron_remove_invoked, got rP   rQ   Npruned_countrU   r   zpruned_count must be 1, got causesr   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)srX   )r[   r   rN   r   z causes must have 2 entries, got z
>assert %(py9)sr   t2(ddddddddddddddddddddddddddddddddddddddddC3z9real_tombstone must be False when no pruned+invoked, got assert %(py6)s))utils.completion_callback_fallback_cancelr   r   r   	CANCELLEDr   !NORMAL_CALLBACK_ALREADY_COLLECTEDvalueREMOVE_FAILED_WARNINGCANCEL_FAILEDr   rc   rd   re   rf   rg   rh   rX   ri   rj   rk   )r   rY   summaryrp   rq   rr   rs   rt   r   @py_assert6ru   @py_format8r   outcomes_none_prunedsummary2s                  r*   $test_summarize_live_prune_aggregatesr      s    C 	/99>>DD $:	
 	/EE**00 $:	
H6 #8,G#$  $,  $    %    )-    LG9U     >" a "a'  "a    #    '(    'w~'>&?@     x  3 ! Q !Q&  !Q              !    "    &'    +78+<*=>      	/EE**00 $:	
 $$89H$%  %.  %    &    */    DH:N     N#(q(#q((((#q(((#(((q(((((((r,   (a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c                 Z   t        dt        ddd      } | d   }|t        k(  }d}t        |k(  }|r|st        j                  d||fd	|t        |f      t        j
                  |      d
t        j                         v st        j                  t              rt        j
                  t              nd
t        j
                  |      dz  }t        j                  d| d         dz   d|iz  }t        t        j                  |            dx}x}x}}| d   }d}||u }|st        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }t        j                  d| d         dz   d|iz  }t        t        j                  |            dx}x}}y)u.   반환 body == '/gemini review', active False.*   	testownertestrepo	task-2729)	pr_numbercurrent_head_shaownerrepor=   bodyz/gemini review)rV   rV   )r   z%(py4)s == %(py6)sr    )rM   rN   rQ   z#body must be '/gemini review', got r]   r^   NactiveFrI   rK   rL   z!active must always be False, got rP   rQ   )r   
_VALID_SHAr    rc   rd   re   ri   rj   rk   rf   rg   rh   )rn   rp   rr   ru   rq   rt   rw   rs   s           r*    test_owner_gemini_body_hardcodedr     s   %#F &> >5I 9I 59II   >59I          6    6    :J    .fVn-?@      ( u u$  u         %    ,F8,<+?@     r,   c                 x   ddl } | j                  t              5  t        dddd       ddd       | j                  t              5  t        dddd       ddd       | j                  t              5  t	        d	       ddd       t	        t
               y# 1 sw Y   oxY w# 1 sw Y   NxY w# 1 sw Y   1xY w)
u!   비40hex head_sha → ValueError.r   Nr   tooshortr   r   )r   r   r   r   (zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz	not-a-sha)pytestraises
ValueErrorr   r   r   )r   s    r*   *test_owner_gemini_head_lock_invalid_raisesr     s    	z	" 
 '		

 
z	" 
 %		

 
z	" )K() 
#-
 

 
) )s#   BB$4B0B!$B-0B9c                    t        dt        dddt        h      } | d   }d}||u }|st        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }t        j
                  d|        dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|st        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }t        j
                  d|        dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      d
z  }t        j
                  d| d         dz   d|iz  }t        t        j                  |            dx}x}}y)uP   already_fired_heads에 head 포함 → dedupe True, fired False, status DEDUPED.r   r   r   r   )r   r   r   r   r=   already_fired_headsdedupeTrI   rK   rL   z=dedupe must be True when already in already_fired_heads, got rP   rQ   NrR   Fz#fired must be False on dedupe, got r$   DEDUPEDrU   r   zstatus must be DEDUPED, got )r   r   rc   rd   re   rf   rg   rh   rn   rp   rq   rr   rs   rt   s         r*   test_owner_gemini_dedupe_skipsr   .  s   %#'LF ( t t#  t         $    HxP     '? e ?e#  ?e        $    .fX6     ( y y(  y         )    'vh'7&:;     r,   c            	        dt        dt        dddfd      } t        |       }|v}|st        j                  d|fd	|f      d
t        j                         v st        j                        rt        j                        nd
dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d|iz  }t        t        j                  |            d}| j                  d      }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}}|k7  }|st        j                  d|fd|f      dt        j                         v st        j                  |      rt        j                  |      ndd
t        j                         v st        j                        rt        j                        nd
dz  }t        j                  d|      dz   d|iz  }t        t        j                  |            d}ddl}	|	j                  j                  d            j!                         dd }
||
k(  }|st        j                  d|fd ||
f      dt        j                         v st        j                  |      rt        j                  |      ndd!t        j                         v st        j                  |
      rt        j                  |
      nd!dz  }t        j                  d"|d#|
      dz   d|iz  }t        t        j                  |            d}y)$u\   token_provider 주입 → 반환에 raw token 미포함, token_hash_prefix는 raw와 다름.ghp_SECRETTOKENr   r   r   r   c                      S N )	raw_tokens   r*   <lambda>z:test_owner_gemini_token_hash_only_no_raw.<locals>.<lambda>M  s    y r,   )r   r   r   r   r=   token_providernot in)z%(py0)s not in %(py2)sr   
result_str)r[   r   z)Raw token must NOT appear in result, got z
>assert %(py4)srN   Ntoken_hash_prefixr   )z%(py0)s is not %(py3)sprefixr[   r\   z?token_hash_prefix must not be None when token_provider is given
>assert %(py5)sr   )!=)z%(py0)s != %(py2)sz2token_hash_prefix must differ from raw token, got r   zutf-8   rU   )z%(py0)s == %(py2)sexpected_prefixz+token_hash_prefix must be sha256[:12], got z, expected )r   r   strrc   rd   ri   rj   rk   re   rf   rg   rh   r   hashlibsha256encode	hexdigest)rn   r   r   @py_format3rs   r   rr   @py_format4@py_format6r   r   r   s              @r*   (test_owner_gemini_token_hash_only_no_rawr   C  s   !I%#(F VJJ&  9J                '    '    4J<@    
 ZZ+,F`6```6``````6```6`````````````Y  6Y                        =VJG     nnY%5%5g%>?IIKCRPO_$  6_                %    %    6fZ{?J]^    r,   c            	        t         j                         } t        ddddt        d|       }|d   }d}||k(  }|st	        j
                  d	|fd
||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d         dz   d|iz  }t        t	        j                  |            dx}x}}|d   }d}||u }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d         dz   d|iz  }t        t	        j                  |            dx}x}}y)u#   head_changed=False → NO_NEW_HEAD.r   r   r   r   F)r=   r   r   r   r   head_changedstater$   NO_NEW_HEADrU   r   rL   z8status must be NO_NEW_HEAD when head_changed=False, got rP   rQ   NrR   rI   rK   z1fired must be False when head_changed=False, got )

runner_modWatcherState#maybe_fire_owner_gemini_on_new_headr   rc   rd   re   rf   rg   rh   )r   rn   rp   rq   rr   rs   rt   s          r*   test_maybe_fire_no_new_headr   d  sY   ##%E0#F ( } },  }         -    C6(CSBVW     '? e ?e#  ?e        $    <F7O;NO     r,   c                 Z   t        ddddd      } | d   }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }t        j                  d| d         dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u&   merge_approved=False → NOT_APPROVED.r   BEHIND/tmpNFr   merge_state_status
pr_workdirrunnermerge_approvedr$   NOT_APPROVEDrU   r   rL   zExpected NOT_APPROVED, got rP   rQ   merge_okrI   rK   r   base_sync_before_mergerc   rd   re   rf   rg   rh   r   s         r*   test_base_sync_not_approvedr   }  s   ##F ( ~ ~-  ~         .    &fX&6%9:     *&&&&&&&&&&&&&&&&&&&r,   c                 Z   t        ddddd      } | d   }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }t        j                  d| d         dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u=   merge_state_status 'DIRTY' → ABORT_ADMIN_OR_FORCE_REQUIRED.r   DIRTYr   NTr   r$   ABORT_ADMIN_OR_FORCE_REQUIREDrU   r   rL   z,Expected ABORT_ADMIN_OR_FORCE_REQUIRED, got rP   rQ   r   FrI   rK   r   r   r   s         r*   #test_base_sync_admin_force_requiredr     s   #"F ( > >>  >         ?    7vh7G6JK     *&&&&&&&&&&&&&&&&&&&r,   c            	     l   dD ].  } t        d| ddd      }|d   }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }t        j                  d| d|d         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d} ||      }d}	||	u }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}	1 y)uI   merge_state_status 'OPEN' 또는 'CLEAN' → UP_TO_DATE (sync 불필요).)OPENCLEANr   r   NTr   r$   
UP_TO_DATErU   r   rL   z$Expected UP_TO_DATE for merge_state=z, got rP   rQ   r   rI   rK   r   base_syncedFzI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)srn   r   assert %(py11)sr   r   rc   rd   re   rf   rg   rh   r   ri   rj   rk   )r$   rn   rp   rq   rr   rs   rt   r   ru   r   r   r   r   s                r*   test_base_sync_up_to_dater    s   # 2'%
 h 	
< 	
</ 	
 	
< 	
 	
 		   	
 	
 		 $0 	
 	
  36*F6(CSBVW	
 	
 	
 	
 	
 j!)T)!T))))!T)))!)))T)))))))zz1-1z-(1E1(E1111(E111111v111v111z111-111(111E111111112r,   c            	        g fd} d }t        ddddd| |      }|d	   }d
}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d	         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d} ||      }	d}
|	|
u }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}x}	x}}
dg}|k(  }|st        j                  d|fd|f      dt        j                         v st        j                        rt        j                        ndt        j                  |      dz  }t        j                  d       dz   d |iz  }t        t        j                  |            dx}}y)!uW   sync mock 수행(conflict False), fetch mock returns CLEAN → BASE_SYNCED_REVALIDATED.c                .    j                  |        ddiS NconflictFappend)r   r   sync_calleds     r*   fake_sync_fnz7test_base_sync_behind_revalidated.<locals>.fake_sync_fn  s    9%E""r,   c                
    ddiS )NmergeStateStatusr   r   r   r   s     r*   fake_fetch_fnz8test_base_sync_behind_revalidated.<locals>.fake_fetch_fn  s    "G,,r,   r   r   r   NTr   r   r   r   r   sync_fnfetch_state_fnr$   BASE_SYNCED_REVALIDATEDrU   r   rL   z&Expected BASE_SYNCED_REVALIDATED, got rP   rQ   r   rI   rK   r   r   r   rn   r   r   r   )z%(py0)s == %(py3)sr	  r   z0sync_fn must be called once with pr_number, got r   r   r  )r
  r  rn   rp   rq   rr   rs   rt   r   ru   r   r   r   r   r   r   r	  s                   @r*   !test_base_sync_behind_revalidatedr    s    K#- $#$F ( 8 88  8         9    11A0DE     *%%%%%%%%%%%%%%%%%%%::,m,:m$,,$,,,,$,,,,,,6,,,6,,,:,,,m,,,$,,,,,,,,,,$`;$```;$``````;```;```$```"RS^R_ ```````r,   c                 b   d } t        ddddd|       }|d   }d	}||k(  }|st        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u+   sync mock conflict True → ABORT_CONFLICT.c                
    ddiS )Nr  Tr   r  s     r*   r
  z3test_base_sync_conflict_abort.<locals>.fake_sync_fn  s    D!!r,   r   r   r   NT)r   r   r   r   r   r  r$   ABORT_CONFLICTrU   r   rL   zExpected ABORT_CONFLICT, got rP   rQ   r   FrI   rK   r   r   r
  rn   rp   rq   rr   rs   rt   s          r*   test_base_sync_conflict_abortr    s   " $#F ( / //  /         0    (x(8';<     *&&&&&&&&&&&&&&&&&&&r,   c            
     f   d } t        ddddd| dd	      }|d
   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d
         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u?   observed_diff_lines > max_diff_lines → ABORT_DIFF_OVER_LIMIT.c                
    ddiS r  r   r  s     r*   r
  z4test_base_sync_diff_over_limit.<locals>.fake_sync_fn      E""r,   r   r   r   NTd   e   )r   r   r   r   r   r  max_diff_linesobserved_diff_linesr$   ABORT_DIFF_OVER_LIMITrU   r   rL   z$Expected ABORT_DIFF_OVER_LIMIT, got rP   rQ   r   FrI   rK   r   r   r  s          r*   test_base_sync_diff_over_limitr!    s   # $#	F ( 6 66  6         7    /vh/?.BC     *&&&&&&&&&&&&&&&&&&&r,   c            	     j   d } d }t        ddddd| |      }|d	   }d
}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d	         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)uJ   sync 성공 후 fetch_state_fn이 여전히 BEHIND → ABORT_STILL_BEHIND.c                
    ddiS r  r   r  s     r*   r
  z1test_base_sync_still_behind.<locals>.fake_sync_fn  r  r,   c                
    ddiS )Nr  r   r   r  s     r*   r  z2test_base_sync_still_behind.<locals>.fake_fetch_fn  s    "H--r,   r   r   r   NTr  r$   ABORT_STILL_BEHINDrU   r   rL   z!Expected ABORT_STILL_BEHIND, got rP   rQ   r   FrI   rK   r   r   )r
  r  rn   rp   rq   rr   rs   rt   s           r*   test_base_sync_still_behindr&    s   #. $#$F ( 3 33  3         4    ,F8,<+?@     *&&&&&&&&&&&&&&&&&&&r,   c                 	   t        ddddd      } | d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d	| d         d
z   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d| d         d
z   d|iz  }t        t        j                  |            dx}x}}| j                  di       }|j                  }d} ||      }d}	||	u }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}	|j                  }d} ||      }d}	||	u }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}	|j                  }d} ||      }d}	||	u }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}	y)uV   전부 True → AUTHORITATIVE_COMPLETION_BY_SCOPE_EVIDENCE, manual_done_forgery False.r   Tr=   external_dirtymerge_base_clean	ci_passedlocal_fix_verifiedr$   *AUTHORITATIVE_COMPLETION_BY_SCOPE_EVIDENCErU   r   rL   z9Expected AUTHORITATIVE_COMPLETION_BY_SCOPE_EVIDENCE, got rP   rQ   Nauthoritative_completionrI   rK   r   manual_done_forgeryFz.manual_done_forgery must always be False, got evidencer*  r   evr   r   r   cir,  )authoritative_completion_markerrc   rd   re   rf   rg   rh   r   ri   rj   rk   )rn   rp   rq   rr   rs   rt   r1  r   ru   r   r   r   r   s                r*   +test_authoritative_completion_full_evidencer4    ss   ,F ( K KK  K         L    DF8DTCWX     ,-55-5555-555-5555555555'( E (E1  (E    )    -2    9@U9V8YZ     
J	#B66-$-6$%--%----%------2---2---6---$---%----------66$6$<4<4<4226$<466/&/6&'/4/'4////'4//////2///2///6///&///'///4////////r,   c                 X   t        ddddd      } | d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }t        j                  d
| d         dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }dd|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }dd|iz  }t        t        j                  |            dx}x}}y)u4   ci_passed=False → EXTERNAL_DIRTY_BLOCK_UNRESOLVED.r   TFr(  r$   EXTERNAL_DIRTY_BLOCK_UNRESOLVEDrU   r   rL   z.Expected EXTERNAL_DIRTY_BLOCK_UNRESOLVED, got rP   rQ   Nr.  rI   rK   r   r/  r3  rc   rd   re   rf   rg   rh   r   s         r*   .test_authoritative_completion_missing_evidencer8  #  s[   ,F ( @ @@  @         A    99I8LM     ,-66-6666-666-6666666666'(1E1(E1111(E111(111E1111111r,   c                 X   t        ddddd      } | d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }t        j                  d
| d         dz   d|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }dd|iz  }t        t        j                  |            dx}x}}| d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }dd|iz  }t        t        j                  |            dx}x}}y)u1   external_dirty=False → NO_EXTERNAL_DIRTY_BLOCK.r   FTr(  r$   NO_EXTERNAL_DIRTY_BLOCKrU   r   rL   z&Expected NO_EXTERNAL_DIRTY_BLOCK, got rP   rQ   Nr.  rI   rK   r   r/  r7  r   s         r*   /test_authoritative_completion_no_external_dirtyr;  3  sZ   ,F ( 8 88  8         9    11A0DE     ,-66-6666-666-6666666666'(1E1(E1111(E111(111E1111111r,   c                    ddl } ddl}g fd}t        ddddd|      }t              }d}||k(  }|st	        j
                  d|fd	||f      d
t        j                         v st	        j                  t              rt	        j                  t              nd
dt        j                         v st	        j                        rt	        j                        ndt	        j                  |      t	        j                  |      dz  }t	        j                  dt                     dz   d|iz  }t        t	        j                  |            dx}x}}|d   }	d}
|	|
u }|slt	        j
                  d|fd|	|
f      t	        j                  |	      t	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
d   }t        |      }g }d}||v}|}	|sd}||v }|}	|	snt	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }|j                  |       |st	        j
                  dfd|f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd z  }d!d"|iz  }|j                  |       t	        j                  |d      i z  }t	        j                  d#      d$z   d%|iz  }t        t	        j                  |            dx}	x}x}x}x}}|d&   }	d'}
|	|
k(  }|slt	        j
                  d|fd(|	|
f      t	        j                  |	      t	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
y))u0  marker_writer 주입해도 .done 파일 생성 안 함을 확인.

    marker_writer는 marker dict를 인자로 받아 호출되고, 본 함수는 .done 파일을
    직접 생성하지 않는다. marker_writer 호출 결과를 캡처하여 .done 경로가
    기록되지 않음을 확인한다.
    r   Nc                (    j                  |        y r   r  )marker_dictcaptured_markerss    r*   fake_marker_writerz7test_no_manual_done_forgery.<locals>.fake_marker_writerN  s    ,r,   r   T)r=   r)  r*  r+  r,  marker_writerr8   rU   rW   rX   r?  rZ   z)marker_writer should be called once, got r]   r^   r/  FrI   rK   rL   r   rQ   z.doner   )z%(py3)s not in %(py5)s
marker_str)r\   r   r   r   )in)z%(py10)s in %(py12)s)py10py12z%(py14)sr   zDmarker_writer dict must not encode direct .done file creation intentz
>assert %(py17)spy17r$   r-  r   )r`   r   r3  rX   rc   rd   ri   rj   rk   re   rf   rg   rh   r   r  _format_boolop)r`   r   r@  rn   rr   ru   rv   rt   rw   rp   rq   rs   r>  rB  r   @py_assert9@py_assert11r   r   @py_format13@py_format15@py_format16@py_format18r?  s                          @r*   test_no_manual_done_forgeryrN  C  sN    - -(F   A  A%    A                          !    %&    4C8H4I3JK    
 '(1E1(E1111(E111(111E1111111 #1%K[!J7 7*$ (= (=(K   7*          %    %     (=    )>      BL    BL        	O      (KKKKKKKKKKKKKKKKKKKKKKKr,   )r(   r   r)   boolreturnr   )e__doc__
__future__r   builtinsri   _pytest.assertion.rewrite	assertionrewriterc   importlib.util	importlibpathlibsysr`   typesPathr   resolveparents_ROOTr   	_ROOT_STRr   removeinsertlistmodules_name
startswith_modgetattr_f_RUNNER_PATHutilspec_from_file_locationr   r   rv   rq   rp   loader@py_assert10@py_assert13@py_assert12rd   rj   rk   re   r   r   r  rK  @py_format17rG  rM  @py_format20rg   rh   module_from_specr   exec_module utils.fallback_schedule_registryr   r   r   r   r   r   r   5utils.normal_completion_callback_collector_entrypointr   utils.owner_gemini_triggerr   r   r    r   r3  r   r+   r/   r2   rx   r   r   r   r   r   r   r   r   r   r   r  r  r  r!  r&  r4  r8  r;  rN  r   r,   r*   <module>rx     s  
 #      
  
 	X&&(003J	388HHOOI 388 9  #++ #E%%)9)9+)F%JZJZ[cJd{{5!T:t,2}}Y'E"# y #??../H,W 5D 5uD 5U\\ 5 5\5 5 5 5uD 5 5 5 5 5u 5 5 5u 5 5 5D 5 5 5 5 5 5 5\ 5 5 5 5 5U 5 5 5U 5 5 5\ 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5^^,,U3
)3% &    $
 ?   $:: ","L"L &0&T&T #MWK%SP,^@)N 8
"$8*B2''2"a8'&'*'80.2 2 )Lr,   