
    jL                    V   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ddlmZ ddlZ e ee      j)                         j*                  j*                  j*                        Zeej.                  v rej.                  j1                  e       ej.                  j3                  de       ddlmZmZmZmZmZ ddl m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+m,Z,m-Z-m.Z. d Z/dd	Z0ddd
Z1	 	 	 d	 	 	 	 	 	 	 	 	 ddZ2d Z3d Z4d Z5d Z6d Z7d Z8d Z9d Z:d Z;d Z<d Z=d Z>d Z?d Z@y)u   
task-2700 regression test: local main ↔ origin/main divergence prevention.

벨레스 (QA/테스터 역할) — 8 검증 시나리오 + task-2699 fixture.
    )annotationsN)Path)DivergenceResultHoldDecisionmeasure_divergenceshould_holdDIVERGENCE_GATE_TASK_KINDS)EXTERNAL_DIRTY_BLOCKEROWN_DIRTY_FAILCLEANclassify_blockercollect_dirtyseparate_dirtysnapshot_main_dirty
glob_match)NORMAL_CALLBACK_MISSINGFINISH_TASK_GIT_GATE_BLOCKEDFINISH_PROFILESclassify_callback_missingresolve_finish_profilec                    t         j                  j                  t        dd      } t        j
                  j                  d|       }t        j
                  j                  |      }|j                  j                  |       |S )Nscriptszworktree_manager.pyworktree_manager)
ospathjoinREPO	importlibutilspec_from_file_locationmodule_from_specloaderexec_module)	spec_pathspecmods      R/home/jay/workspace/tests/regression/test_local_main_divergence_prevention_2700.py_load_worktree_managerr(   8   s[    T9.CDI>>112DiPD
..
)
)$
/CKKC J    c                 ~    t         j                  j                         } d| d<   d| d<   d| d<   d| d<   d| d<   d	| d
<   | S )Nz	/dev/nullGIT_CONFIG_GLOBALtesterGIT_AUTHOR_NAMEzt@t.testGIT_AUTHOR_EMAILGIT_COMMITTER_NAMEGIT_COMMITTER_EMAILz/tmpHOME)r   environcopy)envs    r'   _git_envr5   E   sW    
**//
C*C%C(C (C!+CCKJr)   c                    |xs
 t               }t        j                  dg| z   |dd|d      }|j                  dk7  r)t	        d|  d|j
                  j                                |j                  j                         S )u!   git 명령 실행, stdout 반환.gitT   )cwdcapture_outputtextr4   timeoutr   zgit z	 failed: )r5   
subprocessrun
returncodeRuntimeErrorstderrstripstdout)argsr9   r4   ers        r'   _gitrG   Q   sw    xzA	$	A 	||qT$y1A0BCDD88>>r)   c                F   t               }t        | dz        }t        j                  |d       t	        dddd|gt        |       |       t        | d	z        }t	        d
||gt        |       |       t	        g d||       t        j
                  j                  |d      }t        |d      5 }|j                  d       ddd       t	        ddg||       t	        g d||       t	        g d||       t        | dz        }	t	        d
||	gt        |       |       t	        g d|	|       t        |      D ]t  }
d|
 d}t        t        j
                  j                  ||      d      5 }|j                  d|
 d       ddd       t	        d|g||       t	        ddd|
 g||       v |dkD  rt	        g d||       t        |      D ]t  }
d|
 d}t        t        j
                  j                  |	|      d      5 }|j                  d|
 d       ddd       t	        d|g|	|       t	        ddd|
 g|	|       v t	        ddg|	|       |r|j                         D ]  \  }}t        j
                  j                  |	|      }t        j                  t        j
                  j                  |      d       t        |d      5 }|j                  |       ddd        |	S # 1 sw Y   1xY w# 1 sw Y   zxY w# 1 sw Y   xY w# 1 sw Y   xY w)u   origin(bare) + local clone 을 구성하여 local 이 origin/main 대비
    ahead/behind 가 되도록 만든다.

    extra_files: {relpath: content} — local 작업트리에 uncommitted dirty로 생성.
    반환: local repo 경로(str).
    z
origin.gitTexist_okinitz--barez-bmain)r9   r4   seedclone)checkoutz-BrL   zbase.txtwzbase commit
Nadd)commit-mzbase commit)pushoriginrL   localbehind_z.txtzbehind commit 
rR   rS   r   ahead_zahead commit fetchrU   )r5   strr   makedirsrG   r   r   openwriterangeitemsdirname)tmp_pathaheadbehindextra_filesr4   
origin_dirseed_dir	seed_filef	local_dirifnrelpathcontentabs_paths                  r'   make_divergent_reporp   a   s    *C X,-JKK
T*&(D&*	53x=cR 8f$%H':x	(c(mE	#s; Xz2I	i	 !	 !%	(4	(hC@	#s; Hw&'I':y	)s8}#F	#< 6] Lqc"'',,x,c2 	,aGGnQCr*+	,eR[hC0hqc23sKL z'X3? 5\ LaS"'',,y"-s3 	+qGGmA3b)*	+eR[iS1haS12	sKL 	'8	)5  + 1 1 3 	!GWww||Iw7HKK1DAh$ ! ! !	! Q! !	, 	,	+ 	+! !s0   0K10K>LL1K;>L	L	L 	c                
   t        | dd      }t        |dd      }|j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d|fd||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t!        d|      }|j"                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j$                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u@   ahead=2, behind=1 → diverged=True, hold=True, DIVERGENCE_HOLD.      rc   rd   origin/mainF
remote_refdo_fetchTisz0%(py2)s
{%(py2)s = %(py0)s.measured
} is %(py5)sresultpy0py2py5$measured should be True, got error: 
>assert %(py7)spy7N==z-%(py2)s
{%(py2)s = %(py0)s.ahead
} == %(py5)szahead expected 2, got z.%(py2)s
{%(py2)s = %(py0)s.behind
} == %(py5)szbehind expected 1, got z0%(py2)s
{%(py2)s = %(py0)s.diverged
} is %(py5)sassert %(py7)scodingz,%(py2)s
{%(py2)s = %(py0)s.hold
} is %(py5)sdecisionDIVERGENCE_HOLDz6%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py5)srp   r   measured
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgerrorAssertionError_format_explanationrc   rd   divergedr   holdclassification	rb   repor|   @py_assert1@py_assert4@py_assert3@py_format6@py_format8r   s	            r'   test_divergence_holdr      s   xq;DOF??YdY?d"YYY?dYYYYYY6YYY6YYY?YYYdYYY&J6<<.$YYYYYYYY<<E1E<1EEE<1EEEEEE6EEE6EEE<EEE1EEE 6v||nEEEEEEEE==HAH=AHHH=AHHHHHH6HHH6HHH=HHHAHHH!8HHHHHHHH??"d"?d""""?d""""""6"""6"""?"""d"""""""8V,H== D =D    =D      8   8   =   D       ""7&77"&77777"&777777787778777"777&77777777r)   c                	   t        | dd      }t        |dd      }|j                  }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}t!        d|      }|j"                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|j$                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}y)u=   ahead=0, behind=0 → diverged=False, hold=False, CLEAN_PASS.r   rt   ru   Frv   Try   r{   r|   r}   r   r   r   Nr   r   r   r   r   r   r   r   
CLEAN_PASSr   r   r   s	            r'   test_clean_passr      s   xq;DOF??YdY?d"YYY?dYYYYYY6YYY6YYY?YYYdYYY&J6<<.$YYYYYYYY<<1<1<166<1==A=A=A66=A??#e#?e####?e######6###6###?###e#######8V,H==!E!=E!!!!=E!!!!!!8!!!8!!!=!!!E!!!!!!!""2l2"l2222"l22222282228222"222l2222222r)   c                   t               }t        | dd      }t               }t        j                  g d|dd|      }|j
                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }t        j                  d
|j                         dz   d|iz  }	t        t        j                  |	            dx}x}}|j                   j#                         }
t%        | dz        }t'        j(                  |d       d}t&        j*                  j-                  |d      }t/        |d      5 }t1        j2                  d|i|       ddd       |j5                  |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}}y# 1 sw Y   QxY w)"u<   worktree-base 마커가 다른 SHA → ok=False, stale=True.r   rt   r7   z	rev-parseru   Tr9   r:   r;   r4   r   z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)srF   r}   rev-parse failed: r   r   NeventsrI   (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatask-2700.worktree-base.jsonrP   base_sha	task-2700dev6ru   base_ref
events_dirokFry   z%(py1)s is %(py4)spy1py4zExpected ok=False, got 
>assert %(py6)spy6stalezExpected stale=True, got r(   rp   r5   r=   r>   r?   r   r   r   r   r   r   r   rA   r   r   rC   rB   r[   r   r\   r   r   r]   jsondumpverify_spawn_base)rb   wmr   r4   rF   r   r   r   r   r   
actual_shar   fake_shamarker_pathri   r|   @py_assert0@py_assert2@py_format5@py_format7s                       r'   test_spawn_base_stale_failr      s)   		!Bxq;D *C+	A <<=1=<1===<1======1===1===<===1=== 2188*========!J X()JKK
T*H'',,z+IJK	k3	 -1		:x(!,- !!k6 " F $<D5D<5 DDD<5DDD<DDD5DDD$;F8"DDDDDDDD'?HdH?d"HHH?dHHH?HHHdHHH&?x$HHHHHHHH- -s   K??L	c                   t               }t        | dd      }t               }t        j                  g d|dd|      }|j
                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }t        j                  d
|j                         dz   d|iz  }	t        t        j                  |	            dx}x}}|j                   j#                         }
t%        | dz        }t'        j(                  |d       t&        j*                  j-                  |d      }t/        |d      5 }t1        j2                  d|
i|       ddd       |j5                  |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 }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }d d|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   8xY w)!u/   worktree-base 마커가 동일 SHA → ok=True.r   rt   r   Tr   r   r   rF   r}   r   r   r   Nr   rI   r   rP   r   r   r   ru   r   r   ry   r   r   zExpected ok=True, got r   r   r   Fassert %(py6)sr   )rb   r   r   r4   rF   r   r   r   r   r   r   r   r   ri   r|   r   r   r   r   s                      r'   test_spawn_base_match_passr      s   		!Bxq;D *C+	A <<=1=<1===<1======1===1===<===1=== 2188*========!J X()JKK
T*'',,z+IJK	k3	 /1		:z*A./ !!k6 " F $<B4B<4BBB<4BBB<BBB4BBB#9&!BBBBBBBB'?#e#?e####?e###?###e#######/ /s   K$$K.c                 l   dg} ddg}t        | |      }|d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }d	d
|iz  }t        t        j                  |            dx}}|d   }g }||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }d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  }dd|iz  }t        t        j                  |            dx}
x}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)uN   dirty paths 가 expected_files와 겹치지 않음 → EXTERNAL_DIRTY_BLOCKER.utils/divergence_guard.pyscripts/other.pyzmemory/x.jsonr   r   z%(py1)s == %(py3)sr
   r   py3assert %(py5)sr   N	own_dirtyz%(py1)s == %(py4)sr   r   r   unrelated_dirtyrr   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slen)r~   r   r   r   zassert %(py9)spy9inz%(py1)s in %(py4)s)r   r
   r   r   r   r   r   r   r   r   r   )expected_filesdirty_pathsr|   r   r   @py_format4r   r   r   r   r   @py_assert6@py_assert5r   @py_format10s                  r'   test_external_dirty_blockerr   "  s
   12N%7Knk:F"#=#'=====#'====#======'===='========+$"$"$$$$"$$$$$$"$$$$$$$'(.3().Q.)Q....)Q......3...3...(...)...Q.......:(9!::!:::::!:::::::!::::::::7f%677?77777?7777?77777777777r)   c                 n   dg} ddg}t        | |      }|d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }d	d
|iz  }t        t        j                  |            dx}}d |d   D        }t        |      }|st        j                  d|d          dz   dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }	t        t        j                  |	            dx}}d}|d   }||v }|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)uh   utils/** 패턴에 utils/divergence_guard.py 가 own, scripts/other.py 는 unrelated → OWN_DIRTY_FAIL.zutils/**r   r   r   r   r   r   r   r   r   Nc              3  $   K   | ]  }d |v  
 yw)zutils/N ).0ps     r'   	<genexpr>z&test_own_dirty_fail.<locals>.<genexpr>=  s     :x1}:s   r   z(own_dirty should contain utils/..., got z.
>assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}any)r~   r   r   r   r   r   r   z5unrelated_dirty should contain scripts/other.py, got r   r   )r   r   r   r   r   r   r   r   r   r   r   r   )r   r   r|   r   r   r   r   r   r   r   r   s              r'   test_own_dirty_failr   4  s    \N.0BCKnk:F"#5#~5555#~555#555555~555~5555555:f[&9: I3:: I: I7H7H
26+3F2GHI IBH&I I0H0H  I I?Hy  I I?Hy ; I I?Hy ; I I I5H5HI I  \(9!: \!:: \J[J[\!: \ \R[R[  \ \R[R[ "; \ \J[J[
?GX@Y?Z[\ \ \H[H[\ \ \r)   c                    t        ddd      } | d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndd	z  }d
d|iz  }t        t        j                  |            d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)ud   git_gate_blocked=True + done_exists=False + EXTERNAL_DIRTY_BLOCKER → FINISH_TASK_GIT_GATE_BLOCKED.FTr
   )done_existsgit_gate_blockedblocker_classificationcauser   r   r   r   r   r   N	sub_causer   r   r   r   )
r   r   r   r   r   r   r   r   r   r   )r|   r   r   r   r   r   r   r   s           r'   $test_callback_cause_git_gate_blockedr   H  s    &7F '?:?:::::?::::?::::::::::::::::::+:"::":::::":::::::"::::::::r)   c                    t        dd      } | d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }d	d
|iz  }t        t        j                  |            dx}}t        d      }|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}}t        d      }	|	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)uE   done_exists=True, git_gate_blocked=False → NORMAL_CALLBACK_MISSING.TF)r   r   r   r   r   r   r   r   r   Nread_only_watchercreate_donery   r   r   r   r   codegit_gate)r   r   r   r   r   r   r   r   r   r   r   )
r|   r   r   r   r   
rw_profiler   r   r   code_profiles
             r'   "test_callback_cause_normal_missingr   X  s0   &F
 '?5?55555?5555?555555555555555555 ((;<Jm$--$----$---$----------)&1L
#+t+#t++++#t+++#+++t+++++++r)   c           	     V   dddd}t        | dd|      }t        |dd	
      }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t!        d|      }	|	j"                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|	j$                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |      dz  }t        j                  d|	j$                         dz   d|iz  }t        t        j                  |            dx}x}}dg}
t'        |j)                               }t+        |
|      }|d    }|t,        k(  }|st        j                  d|fd!|t,        f      t        j                  |      d"t        j                         v st        j                  t,              rt        j                  t,              nd"d#z  }t        j                  d$|d     d%|d&    d'|d(          d)z   d*|iz  }t        t        j                  |            dx}}|d&   }g }||k(  }|slt        j                  d|fd+||f      t        j                  |      t        j                  |      d,z  }d-d.|iz  }t        t        j                  |            dx}x}}|d(   }t/        |      }t/        |      }||k(  }|sSt        j                  d|fd/||f      d0t        j                         v st        j                  t.              rt        j                  t.              nd0t        j                  |      t        j                  |      d0t        j                         v st        j                  t.              rt        j                  t.              nd0d1t        j                         v st        j                  |      rt        j                  |      nd1t        j                  |      d2z  }d3d4|iz  }t        t        j                  |            dx}x}x}}y)5u   task-2699 상황 재현: ahead=3, behind=2, 다수 dirty.

    - measure_divergence → diverged=True
    - should_hold("coding") → DIVERGENCE_HOLD
    - classify_blocker(task-2699 expected, dirty_paths) → EXTERNAL_DIRTY_BLOCKER
    zx
zy
zz
)zscripts/unrelated_a.pyzmemory/specs/unrelated_b.mdzutils/replacement_pr_runner.py   rr   rc   rd   re   ru   Frv   Try   r{   
div_resultr}   zmeasure failed: r   r   Nr   r   r   r   r   r   r   hold_decisionr   r   zExpected DIVERGENCE_HOLD, got zanu_v2/owner_trigger*.pyr   r   r
   r   z%Expected EXTERNAL_DIRTY_BLOCKER, got z. own_dirty=r   z, unrelated_dirty=r   z
>assert %(py5)sr   r   r   r   r   )zN%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py9)s
{%(py9)s = %(py6)s(%(py7)s)
}r   r   )r~   r   r   r   r   r   zassert %(py11)spy11)rp   r   r   r   r   r   r   r   r   r   r   r   r   rc   rd   r   r   r   r   listkeysr   r
   r   )rb   re   r   r   r   r   r   r   r   r   task_2699_expectedr   blocker_resultr   r   r   r   r   @py_assert8r   r   @py_format12s                         r'   test_task_2699_fixturer  m  s    #(',*/K
 xqTD $D]USJM$M$&MMM$MMMMMM:MMM:MMMMMM$MMM*::;K;K:L(MMMMMMMM q q    q      :   :      q       !!!!!!!!!!!!:!!!:!!!!!!!!!!!!!&$&$&&&&$&&&&&&:&&&:&&&&&&$&&&&&&&*5M%%%%%%%%%%%%=%%%=%%%%%%%%%%%%%'' H+< H'+<< H6G6GH'+< H HAGH H/G/G  H H>Gi  H H>Gi ( H H>Gi ,= H H6G6G
()E)E(FGH H H4G4GH H
 55{'')*K%&8+FN*+ A+/EE A 0A/@A+/E A A 8Ay , A A ;A&A A )A(@ 0F A A 8Ay 0F A A 0A/@ 1@P1Q0R S$[12 3*+<=>
@A A A .A-@A A +&,",&",,,,&",,,&,,,",,,,,,,/0E301ES5EE15EEEEE15EEEEEEE3EEE3EEE0EEE1EEEEEESEEESEEEEEEEEEEEE5EEEEEEEEr)   c           	     2   ddd}t        | dd|      }t        | dz        }t        ||dd	      }|d
   }d}||k\  }|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}}t        j                  }
|
j                  } ||      }|s t        j                  d      dz   dt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |
      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |      dz  }t        t        j                  |            dx}
x}}t        |      j!                  d      j#                         j%                         }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 |d
          d!z   d"|	iz  }t        t        j                  |            dx}x}}h d#}d$}|D ]'  }t)        j*                  |      }|D ]  }||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+|       d,z   d-|iz  }t        t        j                  |            d}
 |d.   }d}||k(  }|slt        j                  d|fd/||f      t        j
                  |      t        j
                  |      dz  }d0d|iz  }	t        t        j                  |	            dx}x}}|d1   }d}||k(  }|slt        j                  d|fd/||f      t        j
                  |      t        j
                  |      dz  }d0d|iz  }	t        t        j                  |	            dx}x}}|d2   t-        |d2   t.              s&d3}* |s{t        j                  d4      d5z   d6d7t        j                         v st        j                  |      rt        j
                  |      nd7iz  }t        t        j                  |            y)8u^   dirty 파일 2개 → snapshot_main_dirty → JSONL 각 줄에 필수 키, mtime float 검증.za
zb
)zscripts/foo.pyzutils/bar.pyr   r   z	reg.jsonldispatchr   )phasetask_idcountrs   )>=)z%(py1)s >= %(py4)sr   z"At least one dirty record expectedr   r   Nz registry JSONL should be createdzd
>assert %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.exists
}(%(py5)s)
}r   registry_path)r~   r   r   r   r   zutf-8)encodingr   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   lines)r~   r   r   r   zLine count z != snap count z
>assert %(py8)spy8>   tsr   mtimer  status
owner_taskFr   )z%(py0)s in %(py2)skeyrecord)r~   r   zKey 'z' missing in registry record: z
>assert %(py4)sr   r  r   r   r  r  Tz.At least one record should have mtime as floatz
>assert %(py0)sr~   float_mtime_found)rp   r[   r   r   r   r   r   r   r   r   r   existsr   r   r   r   	read_textrB   
splitlinesr   r   loads
isinstancefloat)rb   re   r   r  snapr   r   r   r   r   r   r   r   r  r   r   @py_format9required_keysr  liner  r  @py_format3@py_format1s                           r'   test_registry_jsonlr&    s     K xqTD;./M	D =CAC=ACCC=ACCC=CCCACCCCCCCCCCC77L7>>L>-(L(LL*LLLLLLL2LLL2LLL7LLL>LLLLLL-LLL-LLL(LLLLLL ))7);AACNNPEu: Ag A:& A A/@/@A: A A:@&A A(@(@  A A7@y  A A:@&A A(@(@  A A7@y  A A7@y  A A7@y ' A A/@/@+c%j\g@A A A-@-@A A MM 	%D!  	VC&=UUU3&UUUUUU3UUU3UUUUUU&UUU&UUUUE#.LVH"UUUUUUU	V g,*,*,,,,*,,,,,,*,,,,,,,i /K/ K//// K/// ///K///////'?&:fWou+M $	%  9'8'889 928&9 9 8 8  9 9/8y  9 9 9%8%89 9r)   c                .   t        | dz        }t        j                  |       t        |dd      }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}t        d|d      }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}y)uY   빈 디렉토리에서 measure_divergence → measured=False → MEASUREMENT_FAILED_HOLD.
not_a_reporu   Frv   ry   r{   r|   r}   r   r   Nr   T)fail_closedr   r   MEASUREMENT_FAILED_HOLDr   r   )r[   r   r\   r   r   r   r   r   r   r   r   r   r   r   r   r   )	rb   	empty_dirr|   r   r   r   r   r   r   s	            r'   #test_measurement_failed_fail_closedr,    s    H|+,IKK		meTF??#e#?e####?e######6###6###?###e#######8V>H== D =D    =D      8   8   =   D       ""?&??"&?????"&???????8???8???"???&????????r)   c                    t        | dd      }t        |dd      }|j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t        d|      }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)uK   task_kind='docs' (게이트 비대상) → hold=False, NON_GATED_KIND_PASS.rr   rs   rt   ru   Frv   Try   r   r|   r}   r   r   Ndocsr   r   NON_GATED_KIND_PASSr   r   )rp   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s	            r'   test_non_gated_kind_passr0    sz   xq;DOF??"d"?d""""?d""""""6"""6"""?"""d"""""""66*H==!E!=E!!!!=E!!!!!!8!!!8!!!=!!!E!!!!!!!"";&;;"&;;;;;"&;;;;;;;8;;;8;;;";;;&;;;;;;;;r)   c                   t        | dd      }t        j                  j                  t        dd      }t        j                  t        j                  |d|dddd	d
g	ddt                     }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      dz  }t        j"                  d|j                         dz   d|iz  }t%        t        j&                  |            dx}x}}y)u   divergence_guard CLI 가 --fail-open 같은 bypass flag 를 거부해야 한다.
    (회장 verbatim: divergence HOLD bypass flag 금지 — fail-closed 불변)rs   rt   utilsdivergence_guard.py--repo-root	--task-idr   --task-kindr   z--fail-openTr:   r;   r4   rr   r   r   procr}   u4   --fail-open 이 수용됨(bypass 가능) returncode=r   r   N)rp   r   r   r   r   r=   r>   sys
executabler5   r?   r   r   r   r   r   r   r   r   r   )	rb   r   clir8  r   r   r   r   r   s	            r'   &test_cli_rejects_fail_open_bypass_flagr<    s     xq;D
'',,tW&;
<C>>	mT;	-	1$HJD ??iai?aiii?aiiiiii4iii4iii?iiiaiii#WX\XgXgWh!iiiiiiiir)   c           
        t        | dd      }t        j                  j                  t        dd      }t        j                  t        j                  |d|ddd	d
gddt                     }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      dz  }t        j"                  d|j                   d|j$                         dz   d|iz  }t'        t        j(                  |            dx}x}}t+        j,                  |j.                        }	|	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}|
|k(  }|slt        j                  d|fd|
|f      t        j                   |
      t        j                   |      dz  }dd|iz  }t'        t        j(                  |            dx}
x}}y)uG   divergence_guard CLI 는 diverged repo 에서 fail-closed HOLD(exit 3).rr   r   rt   r2  r3  r4  r5  r   r6  r   Tr7  r   r   r8  r}   u)   diverged repo HOLD(exit3) 기대, 실제 z: r   r   Nr   ry   r   r   r   r   r   r   r   )rp   r   r   r   r   r=   r>   r9  r:  r5   r?   r   r   r   r   r   r   r   rA   r   r   r   r  rC   )rb   r   r;  r8  r   r   r   r   r   payloadr   r   r   r   s                 r'   test_cli_holds_on_diverged_repor?    s   xq;D
'',,tW&;
<C>>	mT;		"$HJD
 ??mam?ammm?ammmmmm4mmm4mmm?mmmammm#LT__L]]_`d`k`k_l!mmmmmmmmjj%G6?"d"?d""""?d"""?"""d"""""""#$9(99$(99999$(9999$999(99999999r)   )returndict)N)rD   z	list[str]r9   r[   r4   zdict | Noner@  r[   )r   r   N)
rb   r   rc   intrd   rB  re   zdict[str, str] | Noner@  r[   )A__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   importlib.utilr   r   r   r=   r9  pathlibr   pytestr[   __file__resolveparentr   r   removeinsertutils.divergence_guardr   r   r   r   r	   utils.dirty_registryr
   r   r   r   r   r   r   r   utils.callback_cause_classifierr   r   r   r   r   r(   r5   rG   rp   r   r   r   r   r   r   r   r   r  r&  r,  r0  r<  r?  r   r)   r'   <module>rT     sH  
 #      	  
  
 4>!!#**11889388HHOOD 4 
 	 	 	 	$ )-	CCC C '	C
 	CT8(3(!IP $N8$\(	; ,*'F\(9^@&	< j:r)   