
    4j`B                       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mZ ddlZ ee      j!                         j"                  j"                  j"                  Z ee      e
j(                  v r!e
j(                  j+                   ee             e
j(                  j-                  d ee             ddlmZmZmZmZmZ edz  dz  Zd#dZd$d	Z G d
 d      Z  ejB                  d      d        Z"d%dZ#d Z$d Z%d Z&d Z'd Z(d Z)d Z*d Z+d Z,d Z-d Z.d Z/d Z0d Z1d Z2d Z3d  Z4e5d!k(  r e6 ejn                  ed"g            y)&ue  tests/regression/test_completion_callback_fallback_cancel_2553plus9a.py

task-2553+9a — CALLBACK_FALLBACK_CANCEL_ON_SUCCESS regression.

§6 필수 테스트 10 + §9-R.3 adversarial 추가 6 = 총 16.

100% offline. 실 callback cron / cokacdir subprocess / network / git 0.
모든 cron-remove 는 **주입된 fake remover**(§9-R.5). 실 subprocess `--cron-remove`
호출 시 테스트 즉시 FAIL(§9-R.3-13: subprocess.run 전면 차단 spy).

callback orchestrator(utils/anu_delegation_completion_callback.py) 는 본 테스트
어디에서도 import/호출하지 않는다 — §9-R.4 분리 seam 검증.
    )annotationsN)Path)CancelClassificationRealCokacdirCronRemoverRemoverResultcancel_fallback_on_successevaluate_durable_evidencememoryfixturesc                `    t        j                  t        |  dz  j                  d            S )Nz.jsonutf-8encoding)jsonloadsFIXDIR	read_text)names    [/home/jay/workspace/tests/regression/test_completion_callback_fallback_cancel_2553plus9a.py_fxr   *   s*    ::v4&.9979KLL    c                *   i }|| d    dz  }|j                  t        j                  | d         d       ||d<   || d    dz  }|j                  t        j                  | d         d       ||d	<   || d    d
z  }|j                  | j                  dd      d       ||d<   || d    dz  }| j                  dd      r-d| v r)|j                  t        j                  | d         d       ||d<   || d    dz  |d<   || d    dz  |d<   |S )uJ   fixture 의 marker/result/report/collector-result 를 tmp_path 에 기록.task_idz.dispatch-fired.jsondispatch_fired_markerr   r   dispatch_fired_marker_pathz.result.jsonresult_jsonresult_json_pathz
.report.mdreport_text report_pathz.collector-result.jsoncollector_result_marker_presentTcollector_result_markercollector_result_marker_path.fallback-cancelled.jsonfallback_cancelled_marker_pathz.cancel.lockcancel_lock_path)
write_textr   dumpsget)fxtmppathsdfmrjrepcrms          r   _materializer1   .   s[   E
2i=/!56
6CNN4::b!89:WNM*-E
&'	"Y--	-BMM$**R./'MB "E

2i=/,
,CNN266-,wN?E-
2i=/!78
8C	vv/6;TXZ;ZJJr345 	 	
 -0E
().1r)}oE]4^.^E
*+ #I|&D DE
Lr   c                  (    e Zd ZdZdddZddd	dZy)

SpyRemoveruF   주입 fake remover — 호출 인자/횟수 기록. 실 subprocess 0.c                     || _         g | _        y )N)statuscalls)selfr5   s     r   __init__zSpyRemover.__init__M   s    
r   Tdry_runc                   | j                   j                  ||d       t        | j                  d| j                         S )Ncron_idr:   zfake:)r5   detail)r6   appendr   r5   )r7   r=   r:   s      r   __call__zSpyRemover.__call__Q   s5    

g'BCDKK%}8MNNr   N)removed)r5   str)r=   rB   r:   boolreturnr   )__name__
__module____qualname____doc__r8   r@    r   r   r3   r3   J   s    P 9= Or   r3   T)autousec                8    ddl }d }| j                  |d|       y)uV   §9-R.3-13 — 어떤 테스트에서도 실 subprocess --cron-remove 호출 0 강제.r   Nc                     t        d      )NuI   실 subprocess 호출 금지 (§9-R.5) — 주입 fake remover 만 허용)AssertionError)aks     r   _boomz%_block_real_subprocess.<locals>._boom[   s    W
 	
r   run)
subprocesssetattr)monkeypatch_sprP   s      r   _block_real_subprocessrV   V   s      

 UE*r   c           
         t        |       }t        ||      }t        d|d   |d   |j                  dd      |j                  d      |dd|}|j	                  |       |t        di |fS )	Nr   target_cron_idnormal_collector_success_auxFcallback_contractTr   rX   normal_collector_successrZ   removerr:   rI   )r   r1   dictr)   updater   )fx_nametmp_pathr]   	overridesr*   r,   kwargss          r   _runrd   c   s    	WBX&E 9*+!#(F!N&&!45 F MM))3F333r   c                   t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nd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}}	|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   ddg}	||	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}}	t!        j"                  | |d    dz  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}}y
)uT   1. normal success + fallback pending → cron remove 호출, fallback_cancelled=truerA    callback_fallback_cancel_success==zU%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.CANCELLED
}dr   py0py2py4py6assert %(py8)spy8NTisz:%(py2)s
{%(py2)s = %(py0)s.fallback_cancelled
} is %(py5)srl   rm   py5assert %(py7)spy7z;%(py2)s
{%(py2)s = %(py0)s.cron_remove_invoked
} is %(py5)srX   r<   z-%(py2)s
{%(py2)s = %(py0)s.calls
} == %(py5)sspyr   r$   fallback_cancelledz%(py1)s is %(py4)spy1rn   assert %(py6)sro   )r3   rd   classificationr   	CANCELLED
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprrM   _format_explanationr|   cron_remove_invokedr6   r   r   r   )ra   r{   r*   rj   @py_assert1@py_assert5@py_assert3@py_format7@py_format9@py_assert4@py_format6@py_format8marker@py_assert0@py_assert2@py_format5s                   r   !test_01_success_pending_cancelledr   v   s   
Y
C3XsCEB=3===============1===1=========3===3==========='4'4''''4''''''1'''1''''''4'''''''  (D( D(((( D((((((1(((1((( (((D(((((((99LR(8%9dKLL9LLLLL9LLLLLLL3LLL3LLL9LLLLLLLLLLLZZ	r)}o%=>	>IIKF &'/4/'4////'4///'///4///////r   c                $   t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nd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}}	|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                  }g }	||	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
)uG   2. normal success + fallback already deleted → no failure, idempotentalready_gonerf   rg   )zX%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.ALREADY_GONE
}rj   r   rk   rp   rq   NFrr   rt   ru   rw   rx   Try   )z4%(py2)s
{%(py2)s = %(py0)s.hold_reasons
} == %(py5)s)r3   rd   r   r   ALREADY_GONEr   r   r   r   r   r   rM   r   r|   r   hold_reasonsra   r{   _rj   r   r   r   r   r   r   r   r   s               r   "test_02_already_deleted_idempotentr      s   
^
$C2HcBDAq@3@@@@@@@@@@@@@@@1@@@1@@@@@@@@@3@@@3@@@@@@@@@@@(5(5((((5((((((1(((1((((((5(((((((  (D( D(((( D((((((1(((1((( (((D(((((((>>R>R>R11>Rr   c                   t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nd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}}	d}|j                  }	||	v }|st        j                  d|fd||	f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }
dd|
iz  }t        t        j                  |            d
x}x}}	y
)uP   3. normal success + fallback already fired → no failure, duplicate path 유지already_fired&callback_fallback_cancel_already_firedrg   )zY%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.ALREADY_FIRED
}rj   r   rk   rp   rq   NFrr   rt   ru   rw   rx   DUPLICATE_CALLBACK_IGNOREDinz=%(py1)s in %(py5)s
{%(py5)s = %(py3)s.cancel_skipped_reason
}r   py3rv   )r3   rd   r   r   ALREADY_FIREDr   r   r   r   r   r   rM   r   r|   cancel_skipped_reasonra   r{   r   rj   r   r   r   r   r   r   r   r   r   r   s                 r   $test_03_already_fired_duplicate_pathr      s   
_
%C8(CHDAqA3AAAAAAAAAAAAAAA1AAA1AAAAAAAAA3AAA3AAAAAAAAAAA(5(5((((5((((((1(((1((((((5((((((('B1+B+BB'+BBBBB'+BBBB'BBBBBB1BBB1BBB+BBBBBBBBr   c                $   t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nd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}}	|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                  }g }	||	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=   4. normal collector failure → fallback remove 호출 금지rA   'callback_normal_failed_fallback_remainsrg   za%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.SKIPPED_NORMAL_FAILED
}rj   r   rk   rp   rq   NFrr   ry   ru   rw   rx   rt   rz   r{   )r3   rd   r   r   SKIPPED_NORMAL_FAILEDr   r   r   r   r   r   rM   r   r   r|   r6   r   s               r    test_04_normal_failure_no_remover      s   
Y
C98SIDAqI3IIIIIIIIIIIIIII1III1IIIIIIIII3III3IIIIIIIIIII  )E) E)))) E))))))1)))1))) )))E)))))))(5(5((((5((((((1(((1((((((5(((((((999?9339r   c           	        t        d      }t        d      }t        ||       }|d   j                          t	        d|d   |d   d|dd|}|j
                  }t        j                  }||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                   }|sydd
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }
t        t        j                  |
            d}|j"                  }g }||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}}d |j$                  D        }t'        |      }|sd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}}y)uQ   5. fallback_cron_id missing → cancel_skipped_reason 기록 (HOLD reason 동반)rA   rf   r   r   rX   Tr   rX   r\   r]   r:   rg   z]%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.SKIPPED_UNTRUSTED
}rj   r   rk   rp   rq   Nz9assert %(py2)s
{%(py2)s = %(py0)s.cancel_skipped_reason
}rl   rm   rz   r{   ru   rw   rx   c              3  $   K   | ]  }d |v  
 yw)u   marker 부재NrI   .0rs     r   	<genexpr>z>test_05_fallback_id_missing_reason_recorded.<locals>.<genexpr>   s     <!#<s   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyrl   rm   rn   rI   )r3   r   r1   unlinkr   r   r   SKIPPED_UNTRUSTEDr   r   r   r   r   r   rM   r   r   r6   r   r   )ra   r{   r*   r,   rj   r   r   r   r   r   @py_format3r   r   r   r   s                  r   +test_05_fallback_id_missing_reason_recordedr      s   
Y
C	/	0BX&E	
&'..0" 	9*+!%	 	A E3EEEEEEEEEEEEEEE1EEE1EEEEEEEEE3EEE3EEEEEEEEEEE"""""""""1"""1""""""""""999?9339<Q^^<<3<<<<<<<<<3<<<3<<<<<<<<<<<<<<r   c           	     l   t        d      }t        d      }d|d   d   d<   t        ||       }t        d|d   |d   d	|d	d
|}|j                  }t
        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t
              rt        j                  t
              ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|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}}|j                   }g }||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+   6. wrong chat_id fallback → remove 금지rA   rf   l   c(	 r   callback_policy_achat_idr   rX   Tr   rg   r   rj   r   rk   rp   rq   Nc3_ownershipFrr   r}   r~   r   ro   rz   r{   ru   rw   rx   rI   )r3   r   r1   r   r   r   r   r   r   r   r   r   r   rM   r   safe_remove_checksr6   ra   r{   r*   r,   rj   r   r   r   r   r   r   r   r   r   r   r   s                   r   test_06_wrong_chat_id_no_remover      s   
Y
C	/	0BBLB 34Y?X&E" 	9*+!%	 	A E3EEEEEEEEEEEEEEE1EEE1EEEEEEEEE3EEE3EEEEEEEEEEE/858/58888/5888/88858888888999?9339r   c                   t        d      }t        d| |d      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|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}
}|j                  }g }||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)   7. fallback id mismatch → remove 금지rA   rf   zWRONG-ID-XYZ)rX   rg   r   rj   r   rk   rp   rq   Nc1_marker_id_matchesFrr   r}   r~   r   ro   rz   r{   ru   rw   rx   )r3   rd   r   r   r   r   r   r   r   r   r   rM   r   r   r6   )ra   r{   r*   rj   r   r   r   r   r   r   r   r   r   r   r   s                  r   test_07_id_mismatch_no_remover      s   
Y
C*%	EB E3EEEEEEEEEEEEEEE1EEE1EEEEEEEEE3EEE3EEEEEEEEEEE 67@5@75@@@@75@@@7@@@5@@@@@@@999?9339r   c                "   t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nd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}}	|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}}	d}|j                  }	||	v }|st        j                  d|fd||	f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }
dd|
iz  }t        t        j                  |            d
x}x}}	y
)uB   8. cron remove 실패 → warning marker, collector success 유지failedrf   rg   )za%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.REMOVE_FAILED_WARNING
}rj   r   rk   rp   rq   NTrr   ry   ru   rw   rx   Frt   u   실패로 바꾸지 않r   r   r   )r3   rd   r   r   REMOVE_FAILED_WARNINGr   r   r   r   r   r   rM   r   r   r|   r   r   s                 r   9test_08_remove_failed_warning_collector_success_preservedr      s   
X
C2HcBDAqI3IIIIIIIIIIIIIII1III1IIIIIIIII3III3IIIIIIIIIII  (D( D(((( D((((((1(((1((( (((D((((((((5(5((((5((((((1(((1((((((5((((((($?(?(??$(?????$(????$????????????(????????r   c                
   ddl }t        j                  j                  dt	        t
        dz  dz               |j                  d      }|j                         }| dz  }|j                  |j                  |      ||j                        }|j                  |j                  |      ||j                        }|j                  }|j                  }|j                  }	||	k(  }
|
st        j                   d|
fd	||	f      d
t#        j$                         v st        j&                  |      rt        j(                  |      nd
t        j(                  |      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}
x}}	|j                  }|j                  }|j.                  }	||	k(  }
|
st        j                   d|
fd||	f      dt#        j$                         v st        j&                  |      rt        j(                  |      ndt        j(                  |      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}
x}}	|j0                  }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}
}ddl}t
        dz  dz  j5                  d      }|j7                  |      }g }|j9                  |      D ]v  }t;        ||j<                        r(||j>                  D cg c]  }|j@                   c}z  }At;        ||jB                        sX|jE                  |jF                  xs d       x d |D        }tI        |      }
|
 }|st        jJ                  d|       dz   dt#        j$                         v st        j&                  tH              rt        j(                  tH              ndt        j(                  |      t        j(                  |
      d z  }t+        t        j,                  |            dx}x}
}yc c}w )!u   9. duplicate callback path 기존 regression 무회귀.

    본 메커니즘은 orchestrator 와 분리(§9-R.4) — dedup 경로 코드/테스트 무회귀를
    실제 기존 dedup regression 모듈을 재실행하여 입증한다.
    r   Ntests
regression8test_completion_callback_dup_ignored_realworld_2553plus1zdup.callback-ack.json)post_result_review_fnrg   )zt%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.Classification
}.PASS
}firstdup)rl   rm   rn   ro   rq   assert %(py10)spy10)z%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.Classification
}.DUPLICATE_CALLBACK_IGNORED
}secondFrr   )z:%(py2)s
{%(py2)s = %(py0)s.closeout_candidate
} is %(py5)sru   rw   rx   utils&completion_callback_fallback_cancel.pyr   r   r   c              3  ,   K   | ]  }d |xs dv   yw)"anu_delegation_completion_callbackr   NrI   )r   ms     r   r   z@test_09_duplicate_callback_path_no_regression.<locals>.<genexpr>  s      >?,b9s   u#   orchestrator import 결합 발견: z2
>assert not %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}r   r   )&	importlibsyspathinsertrB   	WORKSPACEimport_module_fixture!run_completion_callback_collector_normal_input_mock_review_advisory_fallback_inputr   ClassificationPASSr   r   r   r   r   r   rM   r   r   closeout_candidateastr   parsewalk
isinstanceImportnamesr   
ImportFromr?   moduler   _format_assertmsg)ra   r   r   fxdackpr   r   r   r   @py_assert7r   r   @py_format11r   r   r   r   srctreeimportednoderN   s                         r   -test_09_duplicate_callback_path_no_regressionr      s    HHOOAs9w.=>?

!
!BC ,,.C--D11#C<U<U 2 E 22C $c>W>W 3 F :3#5#5:#5#:#::#:::::#:::::::5:::5:::::::::3:::3:::#5:::#::::::::  QC$6$6Q$6$Q$QQ $QQQQQ $QQQQQQQ6QQQ6QQQ QQQQQQCQQQCQQQ$6QQQ$QQQQQQQQ$$--$----$------6---6---$---------- 	GFFii!  99S>DH /dCJJ'4A44Hcnn-OODKK-2.	/
CK 8s   8   8  8&7&7	,XJ78 8178 877  8 8.7i  8 8.7i 8 8.7i 8 8 8$7$78 8 8 5s   U6c                   t         dz  dz  j                  d      }d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  }dd|iz  }t        t        j                  |            d} t        d      }t        d| |      \  }}|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                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}	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}}
y)u   10. callback policy(a) collector-only 권한 유지 — 본 모듈은 fallback cron
    제거 메커니즘만 제공, write/closeout/재가동 코드 경로 0.r   r   r   r   )zgit pushzgh prz--mergeload_owner_patcloseout)not in)z%(py0)s not in %(py2)s	forbiddenr   r   zassert %(py4)srn   NrA   rf      rg   )zK%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.calls
})
} == %(py8)slenr{   )rl   r   r   rv   rq   r   r   Trr   ry   rj   ru   rw   rx   )r   r   r   r   r   r   r   r   rM   r   r3   rd   r6   r  r   )ra   r   r  r   r   r   r{   r   rj   r   r   r   @py_assert6r   r   r   r   r   s                     r   2test_10_callback_policy_a_collector_only_authorityr  
  s    	GFFii!  T $	####y######y###y################$ Y
C2HcBDAqyy3y>Q>Q>Q33ssy>Q  (D( D(((( D((((((1(((1((( (((D(((((((r   c           
        t        d      t        |       g fd}t        d      D cg c]  }t        j                  |       }}|D ]  }|j                           |D ]  }|j                           D cg c]%  }|d   j                  t        j                  k(  s$|' }}D cg c]%  }|d   j                  t        j                  k(  s$|' }}t        |      }d}	||	k(  }
|
st        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  }dd|iz  }t%        t        j&                  |            dx}x}
}	t        |      }t              }d}||z
  }||k(  }
|
st        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"                  |      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  }dd|iz  }t%        t        j&                  |            dx}x}
x}x}}t)        d D              }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      dz  }dd|iz  }t%        t        j&                  |            dx}}yc c}w c c}w c c}w )uN   11. concurrent: normal-success-cancel vs fallback-fire race → 단일 결과.rf   c            
         t        d      } t        dd   d   dj                  d      | dd}j                  || f       y )NrA   r   rX   TrZ   r[   rI   )r3   r   r)   r?   )r{   rj   r*   r,   resultss     r   workerz?test_11_concurrent_cancel_vs_fire_single_result.<locals>.worker"  s^    #& 
yM./%) ff%89
 
 	3x r      )targetr   r  rg   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr  	cancelled)rl   r   r   ro   rp   rq   N)z[%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == (%(py8)s
{%(py8)s = %(py5)s(%(py6)s)
} - %(py10)s)losersr  )rl   r   r   rv   ro   rq   r   zassert %(py13)spy13c              3  L   K   | ]  }t        |d    j                          yw)r  N)r  r6   r   s     r   r   zBtest_11_concurrent_cancel_vs_fire_single_result.<locals>.<genexpr><  s     >S1_>s   "$)z%(py0)s == %(py3)stotal_remove_calls)rl   r   zassert %(py5)srv   )r   r1   range	threadingThreadstartjoinr   r   r   r   r  r   r   r   r   r   r   rM   r   sum)ra   r	  r   threadstr   r  r  r   r   r   r   r   r   @py_assert9@py_assert11@py_format12@py_format14r  r   @py_format4r   r*   r,   r  s                         @@@r   /test_11_concurrent_cancel_vs_fire_single_resultr    s   	/	0BX&EG! 9>aA1yv.AGA 		 	 $]qqt':':>R>\>\'\]I]ad115I5W5WWF  y>Q>Q>Q33yy>Qv;*#g,**,**;*****;*******3***3******v***v***;******#***#******g***g***,**********>g>>!""""""""""""""""""""""""" B ^s   Q*%Q/(Q/2%Q4Q4c           
        t        d      }t        d      }d|d   d<   t        ||       }t        d|d   |d   d|j	                  d      |dd	|}|j
                  }t        j                  }||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|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}}|j"                  }g }||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]   12. same-owner 이나 다른 live task 의 cron id → task_id binding 불일치 → remove 0rA   rf   ztask-OTHER-liver   r   rX   TrZ   r[   rg   r   rj   r   rk   rp   rq   Nc2_task_bindingFrr   r}   r~   r   ro   rz   r{   ru   rw   rx   rI   )r3   r   r1   r   r)   r   r   r   r   r   r   r   r   r   rM   r   r   r6   r   s                   r   *test_12_same_owner_other_live_task_blockedr!  @  s   
Y
C	/	0B->B	*X&E" 	9*+!%&&!45	 	A E3EEEEEEEEEEEEEEE1EEE1EEEEEEEEE3EEE3EEEEEEEEEEE 12;e;2e;;;;2e;;;2;;;e;;;;;;;999?9339r   c                    t               }  | 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  }d	d
|iz  }t        t        j                  |            dx}x}}g }|j                  }|}|r|j                  d   }d}	||	u }
|
}|sddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }|j                  |       |r_t        j                  d
fd	f      t        j                  |      t        j                  |	      dz  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}
}	t        j                  t              5   | dd       ddd       y# 1 sw Y   yxY w)u   13. enforced mock transport — 실 subprocess --cron-remove 호출 시 FAIL.

    autouse fixture 가 subprocess.run 을 차단. 기본 RealCokacdirCronRemover 의
    dry_run=True 경로는 subprocess 를 호출하지 않음을 동적 증명.
    ANYIDTr9   rA   rg   )z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)srrru   rw   rx   Nr:   z %(py4)s
{%(py4)s = %(py2)s.raw
})rm   rn   rr   )z%(py7)s is %(py10)s)rx   r   z%(py12)spy12r   zassert %(py15)spy15F)r   r5   r   r   r   r   r   r   rM   r   rawr?   _format_booloppytestraises)realr$  r   r   r   r   r   r   r  r  @py_assert8r   r   @py_format13r  @py_format16s                   r   2test_13_enforced_mock_transport_no_real_subprocessr/  U  se    #$D	gt	$B99!	!9	!!!!9	!!!!!!2!!!2!!!9!!!	!!!!!!!/266/6/6bffY'/4/'4///////2///2///6////6///'4///'///4//////////////	~	& %We$% % %s   9IIc                   t        d      }t        d| |ddi      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|j                  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}
}|j                  }g }||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}}t        d      }t        d| |      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}|j                  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)uH   14. fallback_cron_id 2-source(marker AND contract) 양쪽 경로 검증.rA   rf   fallback_callback_cron_idzCONTRACT-DIFFERENT)rZ   rg   r   rj   r   rk   rp   rq   Ncontract_cross_checkMISMATCH)z%(py1)s == %(py4)sr~   r   ro   rz   r{   ru   rw   rx   ri   d2match)r3   rd   r   r   r   r   r   r   r   r   r   rM   r   r   r6   r   )ra   r{   r   rj   r   r   r   r   r   r   r   r   r   r   r   spy2r4  s                    r   )test_14_two_source_extraction_cross_checkr7  d  s   
Y
C*68LM	DAq E3EEEEEEEEEEEEEEE1EEE1EEEEEEEEE3EEE3EEEEEEEEEEE 67E:E7:EEEE7:EEE7EEE:EEEEEEE999?9339i D3XtDEAr> 4 > >> >>>>> >>>>>>>2>>>2>>>>>>>>> 4>>> 4>>> >>>>>>>>  !78CGC8GCCCC8GCCC8CCCGCCCCCCCr   c           
        t        d      }t        d      }t        ||       }|d   j                          t	        d|d   |d   d|j                  d      |dd|}|j                  }t        j                  }||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}|j"                  }g }
||
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   15. crash window: marker 생성 ~ cancel 호출 사이 중단 → fallback 보존.

    crash 를 'collector-result marker 미생성(durable evidence 미완)'으로 모사 →
    SKIPPED_NORMAL_FAILED, remove 0.rA   rf   r#   r   rX   TrZ   r[   rg   r   rj   r   rk   rp   rq   Nrz   r{   ru   rw   rx   rI   )r3   r   r1   r   r   r)   r   r   r   r   r   r   r   r   r   rM   r   r6   )ra   r{   r*   r,   rj   r   r   r   r   r   r   r   r   s                r   'test_15_crash_window_fallback_preservedr9  x  s   
 Y
C	/	0BX&E	
()002" 	9*+!%&&!45	 	A I3IIIIIIIIIIIIIII1III1IIIIIIIII3III3IIIIIIIIIII999?9339r   c                0   t        d      }t        d| |d      \  }}|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|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}
}|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}
}|j                  }g }||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)uZ   16. durable evidence 부재인데 boolean=true 주입 → SKIPPED_NORMAL_FAILED (§9-R.2).rA   r   T)r\   rg   r   rj   r   rk   rp   rq   Ncaller_boolean_auxrr   r}   r~   r   ro   	satisfiedFrz   r{   ru   rw   rx   )r3   rd   r   r   r   r   r   r   r   r   r   rM   r   durable_evidencer6   )ra   r{   r   rj   r   r   r   r   r   r   r   r   r   r   r   s                  r   4test_16_boolean_true_but_no_durable_evidence_skippedr>    s   
Y
C1!%	DAq I3IIIIIIIIIIIIIII1III1IIIIIIIII3III3IIIIIIIIIII23;t;3t;;;;3t;;;3;;;t;;;;;;;k*3e3*e3333*e333*333e3333333999?9339r   c                B   | dz  }| dz  }| dz  }|j                  t        j                  ddi      d       |j                  dd       |j                  d	d       t        |||
      }|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                  t        j                  ddi      d       t        |||
      }
|
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;   보조 단위 — durable evidence 평가기 직접 검증.zr.jsonzr.mdzc.jsonr5   	completedr   r   okz{})r   r    r#   r<  Trr   r}   r~   r   ro   NHOLD_FOR_CHAIRF)	r'   r   r(   r	   r   r   r   rM   r   )ra   r.   r/   r0   evr   r   r   r   r   ev2s              r   test_durable_evidence_unitrE    sC   	H	B
V
C
X
CMM$**h45MHNN4'N*NN4'N*	"3
B k?"d"?d""""?d"""?"""d"""""""MM$**h(89:WMM
#3C {$u$u$$$$u$$$$$$u$$$$$$$r   __main__z-q)r   rB   rD   r^   )r*   r^   r+   r   rD   r^   )r`   rB   ra   r   )8rH   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   r   r  pathlibr   r)  __file__resolveparentr   rB   r   remover   )utils.completion_callback_fallback_cancelr   r   r   r   r	   r   r   r1   r3   fixturerV   rd   r   r   r   r   r   r   r   r   r   r  r  r!  r/  r7  r9  r>  rE  rE   
SystemExitmainrI   r   r   <module>rU     sZ   #    
   N""$++2299	y>SXXHHOOC	N# 3y> "  
X	
	*M8	O 	O 	+ 	+4&0 C=*&@&8R)$!#H*%D(,%& z
[V[[(D!12
33 r   