
     jK                    F   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mZ ddlZddl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mZmZ ddlmZmZm Z m!Z! d dZ"d d	Z#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 Z0d Z1d Z2d Z3d Z4d Z5d Z6d Z7d Z8d Z9y)!uo  tests/regression/test_callback_fallback_prune_2728.py
task-2728 Phase 2 regression: idempotent fallback prune, 6 PruneCause,
self-check, cancel_not_wired, canonical_root injection, result.json contract,
and §9-R legacy regression guard.

All fixtures are fully isolated via tempfile registry_path injection.
No subprocess / cokacdir / cron calls. Fake remover only.
    )annotationsN)Path)register_fallbackpending_forresolve_registry_path)

PruneCausePruneOutcomeSelfCheckDecisionCancelClassificationRemoverResultprune_fallbacks_for_keydetect_unwired_fallbackfallback_self_checkevaluate_safe_removeevaluate_durable_evidence)collect_and_pruneresolve_canonical_rootresolve_result_json_pathRESULT_JSON_MISSING_RECOVEREDc               "    t        dd|        S )Nremovedzfake removed statusdetailr   cron_iddry_runs     c/home/jay/workspace/.worktrees/task-2729-dev6/tests/regression/test_callback_fallback_prune_2728.py_remover_removedr    -   s    	M'2KLL    c               "    t        dd|        S )Nalready_gonezfake already_gone r   r   r   s     r   _remover_already_goner$   1   s    9KG97UVVr!   c               "    t        dd|        S )Nfailedzfake failed r   r   r   s     r   _remover_failedr'   5   s    <y1IJJr!   c               "    t        dd|        S )Nalready_firedzfake already_fired r   r   r   s     r   _remover_already_firedr*   9   s    :MgY8WXXr!   c            	     ,
   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        d|      }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}}|d   }|j                  }	t         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                  t               rt        j                  t               nd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}
}|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d|"      }g }||k(  }	|	st        j                  d|	fd#||f      d$t        j                         v st        j                  |      rt        j                  |      nd$t        j                  |      d%z  }t        j$                  d&|       d'z   d(|iz  }t        t        j                  |            dx}	}y# 1 sw Y   xY w))u   A: +1 16:46 normal callback → prune_fallbacks_for_key CANCELLED,
    pending_for afterwards → [] (stale fire blocked).
    .jsonlFsuffixdeleteNztask-2726+1   head1FALLBACK_PLUS1anutask_idroundhead_shar   	owner_keyregistry_pathnormal_callback_collectedTr5   r6   r7   triggerremoverr   r9   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenoutcomespy0py1py3py6assert %(py8)spy8r   )zU%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.CANCELLED
}or   rD   py2py4rG   zExpected CANCELLED, got 
>assert %(py8)sisz.%(py2)s
{%(py2)s = %(py0)s.pruned
} is %(py5)srD   rL   py5assert %(py7)spy7z;%(py2)s
{%(py2)s = %(py0)s.cron_remove_invoked
} is %(py5)sr6   r7   r9   z%(py0)s == %(py3)s	remainingrD   rF   zExpected [], got 
>assert %(py5)srS   )tempfileNamedTemporaryFilenamer   r   r    rA   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationclassificationr   	CANCELLED_format_assertmsgprunedcron_remove_invokedr   )fregrB   @py_assert2@py_assert5@py_assert4@py_format7@py_format9rJ   @py_assert1@py_assert3@py_format6@py_format8rY   @py_format4s                  r   5test_fixture_a_normal_callback_prune_cancels_fallbackrx   @   so    
	$	$HU	C qff   '+ H x=A=A=A33xx=AA 3== ==   =                     4     4     >    #1#3#3"45     88t8t8t118t  (D( D(((( D((((((1(((1((( (((D((((((( MWTWXI;9?;;;9;;;;;;9;;;9;;;;;;/	{;;;;;;;A s   T		Tc            	        t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        d|      }|d   }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  dt        |             dz   d|iz  }t        t        j                  |            dx}x}}|d   d   }	t         j"                  }|j$                  }
|	|
k(  }|st        j                  d|fd|	|
f      t        j                  |	      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}x}}
t	        ddddt
        d|      }|d   }	g }|	|k(  }|st        j                  d|fd|	|f      t        j                  |	      t        j                  |      d z  }t        j                  d!|d          d"z   d#|iz  }t        t        j                  |            dx}	x}}y# 1 sw Y   xY w)$uV   B: +2 A86DB611 → collect_and_prune CANCELLED, 2nd call idempotent (no error, empty).r,   Fr-   Nztask-2726+2   80416faaA86DB611r3   r4   r:   Tr;   prune_outcomesr0   r>   r@   rA   	outcomes1rC   zExpected 1 outcome, got rN   rI   r   rg   )zL%(py1)s == %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.CANCELLED
}.value
}r   rE   rF   rS   rU   assert %(py9)spy9z%(py1)s == %(py4)srE   rM   z)2nd idempotent call should yield [], got 
>assert %(py6)srG   )r\   r]   r^   r   r   r    rA   r_   r`   ra   rb   rc   rd   ri   re   rf   r   rh   value)rl   rm   result1r~   rn   ro   rp   rq   rr   @py_assert0@py_assert6rv   @py_format10result2rt   @py_format5s                   r   1test_fixture_b_idempotent_prune_collect_and_pruner   j   sw   		$	$HU	C qff   + G ()Iy>KQK>QKKK>QKKKKKK3KKK3KKKKKKyKKKyKKK>KKKQKKK":3y>:J KKKKKKKKQ<()Q-A-K-KQ-K-Q-QQ)-QQQQQ)-QQQQ)QQQQQQ-AQQQ-AQQQ-KQQQ-QQQQQQQQ  + G #$  $*  $    %    )+    4G<L4M3NO     I s   L;;Mc                 z   t        dd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                  }t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        j                  d| j                        dz   d|iz  }	t        t        j                  |	            dx}x}x}}y)ua   C-1: normal_callback_collected=True → proceed==False, cause==normal_callback_already_collected.task-xr0   h1T)r5   r6   r7   r:   FrO   z/%(py2)s
{%(py2)s = %(py0)s.proceed
} is %(py5)sdecrR   rT   rU   Nr>   z%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.NORMAL_CALLBACK_ALREADY_COLLECTED
}.value
}r   rD   rL   rM   rG   rI   0Expected normal_callback_already_collected, got 
>assert %(py10)spy10)r   proceedr_   r`   ra   rb   rc   rd   re   rf   causer   !NORMAL_CALLBACK_ALREADY_COLLECTEDr   ri   
r   rs   rp   rt   ru   rv   ro   @py_assert7rr   @py_format11s
             r   ;test_fixture_c_self_check_normal_callback_already_collectedr      s   
"&	C ;;%;%;%33;%99 
DD DJJ 9JJ   9J                    #    #    E    K    ;399-H     r!   c                 |   t        ddd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                  }t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        j                  d| j                        dz   d|iz  }	t        t        j                  |	            dx}x}x}}y)uE   C-2: round=1, current_round=2 → proceed==False, cause==stale_round.r   r0   r   rz   F)r5   r6   r7   current_roundr:   rO   r   r   rR   rT   rU   Nr>   )zi%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.STALE_ROUND
}.value
}r   r   zExpected stale_round, got r   r   )r   r   r_   r`   ra   rb   rc   rd   re   rf   r   r   STALE_ROUNDr   ri   r   s
             r   %test_fixture_c_self_check_stale_roundr      s   
"'C ;;%;%;%33;%99 
.. .44 944   94                    #    #    /    5    %SYYM2     r!   c                 |   t        ddd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                  }t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        j                  d| j                        dz   d|iz  }	t        t        j                  |	            dx}x}x}}y)uH   C-3: head_sha != current_head_sha → proceed==False, cause==stale_head.r   r0   oldnewF)r5   r6   r7   current_head_shar:   rO   r   r   rR   rT   rU   Nr>   )zh%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.STALE_HEAD
}.value
}r   r   zExpected stale_head, got r   r   )r   r   r_   r`   ra   rb   rc   rd   re   rf   r   r   
STALE_HEADr   ri   r   s
             r   $test_fixture_c_self_check_stale_headr      s   
"'C ;;%;%;%33;%99 
-- -33 933   93                    #    #    .    4    $CII=1     r!   c                    t        dddd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}||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}}y)u:   C-4: no stale conditions → proceed==True, cause is None.r   r0   r   F)r5   r6   r7   r   r   r:   TrO   r   r   rR   rT   rU   N)z-%(py2)s
{%(py2)s = %(py0)s.cause
} is %(py5)szExpected None cause, got 
>assert %(py7)s)r   r   r_   r`   ra   rb   rc   rd   re   rf   r   ri   )r   rs   rp   rt   ru   rv   s         r   /test_fixture_c_self_check_no_stale_proceed_truer      s   
"'C ;;$;$;$33;$99GG9GGG9GGGGGG3GGG3GGG9GGGGGG 9#))GGGGGGGGr!   c            	     
   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        d|      }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}}|d   }|j                  }	t         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                  t               rt        j                  t               ndt        j                  |      dz  }t        j$                  d|j                         dz   d|iz  }t        t        j                  |            dx}	x}
}|j&                  }	t(        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                  t(              rt        j                  t(              ndt        j                  |      t        j                  |      dz  }t        j$                  d|j&                        d z   d!|iz  }t        t        j                  |            dx}	x}
x}}|j&                  }	t(        j.                  }|j,                  }|	|k7  }
|
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                  t(              rt        j                  t(              ndt        j                  |      t        j                  |      dz  }t        j$                  d$      d z   d!|iz  }t        t        j                  |            dx}	x}
x}}y# 1 sw Y   GxY w)%uR   D-1: already_gone → ALREADY_GONE, cause==schedule_not_found (NOT cancel_failed).r,   Fr-   Nztask-dr0   hd1	CRON_GONEr3   r4   r:   Tr;   r>   r@   rA   rB   rC   rH   rI   r   )zX%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.ALREADY_GONE
}rJ   r   rK   zExpected ALREADY_GONE, got rN   )zp%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.SCHEDULE_NOT_FOUND
}.value
}r   r   z!Expected schedule_not_found, got r   r   )!=)zk%(py2)s
{%(py2)s = %(py0)s.cause
} != %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.CANCEL_FAILED
}.value
}z0cause must NOT be cancel_failed for already_gone)r\   r]   r^   r   r   r$   rA   r_   r`   ra   rb   rc   rd   re   rf   rg   r   ALREADY_GONEri   r   r   SCHEDULE_NOT_FOUNDr   CANCEL_FAILED)rl   rm   rB   rn   ro   rp   rq   rr   rJ   rs   rt   r   r   s                r   9test_fixture_d_already_gone_classified_schedule_not_foundr      sZ   		$	$HU	C qff  '+%H x=A=A=A33xx=AA 3@@ @@   @                     4     4     A    &a&6&6%78     77 j33 399 799   79                    !    !    4    :    ,AGG;7      77 j.. .44 744   74                    !    !    /    5    	;     = s   U**U4c            	     	   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        d|      }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}}|d   }|j                  }	t         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                  t               rt        j                  t               ndt        j                  |      t        j                  |
      dz  }t        j&                  d|j                        dz   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  }t        j&                  d       d!z   d"|iz  }t        t        j                  |            dx}	x}}t+        ddd|#      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndd$t        j                         v st        j                  |      rt        j                  |      nd$t        j                  |      t        j                  |      dz  }t        j&                  d%|       d&z   d|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)'uK   D-2: remover failed → cause==cancel_failed, pruned==False, still PENDING.r,   Fr-   Nztask-d2r0   hd2	CRON_FAILr3   r4   r:   Tr;   r>   r@   rA   rB   rC   rH   rI   r   )zk%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.CANCEL_FAILED
}.value
}rJ   r   r   zExpected cancel_failed, got r   r   rO   rQ   rR   z'pruned must be False when remover failsr   rU   rW   rY   z3Expected 1 PENDING record after cancel_failed, got rN   )r\   r]   r^   r   r   r'   rA   r_   r`   ra   rb   rc   rd   re   rf   r   r   r   r   ri   rj   r   )rl   rm   rB   rn   ro   rp   rq   rr   rJ   rs   r   rt   r   ru   rv   rY   s                   r   >test_fixture_d_failed_classified_cancel_failed_pending_remainsr      s   		$	$HU	C qff  '+H x=A=A=A33xx=AA77 j.. .44 744   74                    !    !    /    5    'qwwk2      88GuG8uGGG8uGGGGGG1GGG1GGG8GGGuGGGGGGGGGGG IQcRIy> Q >Q   >Q                                >i[I     ? s   S%%S/c            	     `	   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       d}t	        t
        |      }|st        j                  d      dz   dt        j                         v st        j                  t              rt        j                  t              nd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}}t        ddddt        d|      }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}}|d   }	|	j$                  }
t
        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                  t
              rt        j                  t
              ndt        j                  |      dz  }t        j                  d|	j$                         d z   d|iz  }t        t        j                  |            dx}
x}}|	j$                  }
|
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                  |      t        j                  |      d"z  }d#d$|iz  }t        t        j                  |            dx}
x}x}}y# 1 sw Y   xY w)%u   AF-1: remover status='already_fired' → CancelClassification.ALREADY_FIRED 매핑,
    AttributeError 0 (Gemini HIGH false positive 종결 — enum line 82 실재 증명).
    r,   Fr-   Nztask-afr0   haf1
CRON_FIREDr3   r4   ALREADY_FIREDuX   CancelClassification.ALREADY_FIRED enum이 실재해야 함 (Gemini HIGH false positive)z7
>assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr   )rD   rE   rF   rS   r:   Tr;   r>   r@   rA   rB   rC   rH   rI   r   )zY%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.ALREADY_FIRED
}rJ   rK   zExpected ALREADY_FIRED, got rN   )zQ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.classification
}.value
} == %(py7)s)rD   rL   rM   rU   r   r   )r\   r]   r^   r   r   r   r_   ri   ra   rb   rc   rd   re   rf   r   r*   rA   r`   rg   r   r   )rl   rm   rn   rp   ru   rB   ro   rq   rr   rJ   rs   rt   r   rv   r   s                  r   ;test_already_fired_maps_to_classification_no_attributeerrorr   #  sv    
	$	$HU	C qff  *9 7'9 9   	c               (    (    *9    :      '+&H x=A=A=A33xx=AA 3AA AA   A                     4     4     B    'q'7'7&89     4!!4_4!_4444!_44444414441444444!444_4444444A s   R##R-c            	     
   t        j                  dd      5 } | j                  }ddd       t        ddddd	
       t	        ddddt
        d|      }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}}|d   }|j                  }	t         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                  t               rt        j                  t               ndt        j                  |      t        j                  |
      dz  }t        j&                  d|j                        dz   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  }t        j&                  d       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dd|%      }g }||k(  }	|	st        j                  d|	fd&||f      d't        j                         v st        j                  |      rt        j                  |      nd't        j                  |      d(z  }t        j&                  d)|       d*z   d+|iz  }t        t        j                  |            dx}	}y# 1 sw Y   xY w),u   AF-2: already_fired → cause==normal_callback_already_collected, pruned==True,
    재조회 시 PENDING 없음(tombstone 기록됨).
    r,   Fr-   Nztask-af2r0   haf2CRON_FIRED2r3   r4   r:   Tr;   r>   r@   rA   rB   rC   rH   rI   r   r   rJ   r   r   r   r   r   rO   rQ   rR   u/   already_fired는 pruned=True (tombstone 기록)r   rU   rV   rT   rW   rX   rY   rZ   z+Expected [] after already_fired prune, got r[   rS   )r\   r]   r^   r   r   r*   rA   r_   r`   ra   rb   rc   rd   re   rf   r   r   r   r   ri   rj   rk   r   )rl   rm   rB   rn   ro   rp   rq   rr   rJ   rs   r   rt   r   ru   rv   rY   rw   s                    r   (test_already_fired_cause_and_pruned_truer   J  s    
	$	$HU	C qff  '+&H x=A=A=A33xx=AA77 jBB BHH 7HH   7H                    !    !    C    I    ;177+F      88NtN8tNNN8tNNNNNN1NNN1NNN8NNNtNNNNNNNNNNN  (D( D(((( D((((((1(((1((( (((D(((((((Ja&PSTIU9?UUU9UUUUUU9UUU9UUUUUUI)UUUUUUU? s   UUc                    t        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}}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	)uV   E-1: resolve_canonical_root(canonical_root=...) → (root, 'canonical_root_injected')./tmp/injected_rootcanonical_rootr>   rX   rootrZ   assert %(py5)srS   Ncanonical_root_injectedsource	r   r_   r`   ra   rb   rc   rd   re   rf   r   r   rn   rs   rw   ru   s         r   .test_fixture_e_resolve_canonical_root_injectedr   s  s    )9MNLD&''4'''''4'''''''4'''4'''''''''''..6.....6.......6...6...........r!   c                    t        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}}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	)uK   E-2: resolve_canonical_root(worktree_path=...) → (root, 'worktree_path').z/tmp/wt)worktree_pathr>   rX   r   rZ   r   rS   Nr   r   r   r   s         r   .test_fixture_e_resolve_canonical_root_worktreer   z  s    )	BLD&4949449$$6_$$$$6_$$$$$$6$$$6$$$_$$$$$$$r!   c            
        t        j                  dd      5 } | j                  }ddd       t        ddddd	t        d
      }|d   }d	}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d         dz   d|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d         dz   d|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   ZxY w)uH   E-3: collect_and_prune with canonical_root → result dict preserves it.r,   Fr-   Nztask-er0   he1r:   r   T)r5   r6   r7   r<   r   r=   r   r9   r   r>   r   r   z!Expected /tmp/injected_root, got r   rG   canonical_root_sourcer   z&Expected canonical_root_injected, got )r\   r]   r^   r   r    r_   r`   rd   ri   re   rf   )rl   rm   resultr   rt   rn   r   rq   s           r   9test_fixture_e_collect_and_prune_canonical_root_in_resultr     s   		$	$HU	C qff ++ 	F "# '; #';;  #';    $    (<    ,F3C,D+GH     )* .G *.GG  *.G    +    /H    18O1P0ST     # s   E==Fc            	     (   t        d      } t        |       }|j                  }d} ||      }|st        j                  d|        dz   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                  |      t        j                  |      dz  }t        t        j                  |            d	x}x}x}}t        |       }|j                  }d} ||      }|st        j                  d
|        dz   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                  |      t        j                  |      dz  }t        t        j                  |            d	x}x}x}}y	)zRE-4: resolve_registry_path uses canonical_root and ends with proper relative path.z/tmp/xyzr   z-memory/state/fallback_schedule_registry.jsonlzUnexpected registry path: zj
>assert %(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py0)s(%(py1)s)
}.endswith
}(%(py7)s)
}strp)rD   rE   rF   rS   rU   r   Nz)Path does not start with canonical_root: zl
>assert %(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py0)s(%(py1)s)
}.startswith
}(%(py7)s)
})r   r   endswithr_   ri   ra   rb   rc   rd   re   rf   
startswith)r   rn   rp   r   @py_assert8r   s         r   -test_fixture_e_resolve_registry_path_rel_pathr     s   Z8Aq6 6?? J ?JK K   %QC(                               K    L       q6Y6YZYZ(Y(YY,UVWUX*YYYYYYY3YYY3YYYYYYqYYYqYYY6YYYYYYZYYY(YYYYYYr!   c            
        t        j                  dd      5 } | j                  }ddd       d}t        |      j	                  d       t        dd	d
d|t        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  }t        j                  d|d         dz   d|iz  }t        t        j                   |            d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   xY w)uU   F-1: result_json_path missing → RESULT_JSON_MISSING_RECOVERED + follow_up_required.r,   Fr-   Nz4/tmp/does_not_exist_task_2728_regression.result.jsonT)
missing_okztask-fr0   hf1r:   r5   r6   r7   r<   result_json_pathr=   r   r9   result_json_statusr>   )z%(py1)s == %(py3)sr   )rE   rF   z9Expected RESULT_JSON_MISSING_RECOVERED_BY_COLLECTOR, got r[   rS   follow_up_requiredrO   z%(py1)s is %(py4)sr   z;follow_up_required must be True when result.json is missingr   rG   )r\   r]   r^   r   unlinkr   r    r   r_   r`   rd   ra   rb   rc   ri   re   rf   )rl   rm   missing_pathr   r   rn   rw   ru   rt   r   rq   s              r   ,test_fixture_f_result_json_missing_recoveredr     s   		$	$HU	C qff JL.+% 	F &' '+HH  '+H    (      ,I    ,I    DFK_D`Ccd     &' 4 '4/  '4    (    ,0    	F     + s   GGc            
        t        j                  dd      5 } | j                  }ddd       t        j                  dddd      5 }t        j                  d	d
i|       |j                  }ddd       t        ddddt        d      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d         dz   d|iz  }	t        t        j                  |	            dx}x}}|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   xY w# 1 sw Y   `xY w)uP   F-2: result.json exists → result_json_status=='OK', follow_up_required==False.r,   Fr-   Nz.result.jsonwzutf-8)r.   moder/   encodingr   	completedztask-f2r0   hf2r:   Tr   r   OKr>   r   r   zExpected OK, got r   rG   r   rO   r   z8follow_up_required must be False when result.json exists)r\   r]   r^   jsondumpr   r    r_   r`   rd   ri   re   rf   )
rl   rm   rjfrj_pathr   r   rt   rn   r   rq   s
             r   %test_fixture_f_result_json_present_okr     s   		$	$HU	C qff 
	$	$C
 			8[)3/((	 +  	F &' 4 '4/  '4    (    ,0    F#78;<     &' 5 '50  '5    (    ,1    	C     /  s   F<%G	<G	Gc                 \   t        j                  dd      5 } | j                  }ddd       t        dddd	      }d}||u}|st	        j
                  d
|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d      dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        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                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      dz  }
t	        j                  d|j                        dz   d|
iz  }t        t	        j                  |            dx}x}	x}}|j"                  }t$        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                  t$              rt	        j                  t$              ndt	        j                  |      dz  }t	        j                  d|j"                         dz   d|iz  }
t        t	        j                  |
            dx}x}	}y# 1 sw Y   ~xY w)u   G-1: empty registry + dispatch_fired=True → detect_unwired_fallback returns
    cause==cancel_not_wired, classification==SKIPPED_UNTRUSTED.
    r,   Fr-   Nr   r0   hT)r5   r6   r7   dispatch_firedr9   is not)z%(py0)s is not %(py3)sr   rZ   z<detect_unwired_fallback must return a PruneOutcome, not Noner[   rS   r>   )zn%(py2)s
{%(py2)s = %(py0)s.cause
} == %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.CANCEL_NOT_WIRED
}.value
}r   r   Expected cancel_not_wired, got r   r   )z]%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.SKIPPED_UNTRUSTED
}r   rK   z Expected SKIPPED_UNTRUSTED, got rN   rI   )r\   r]   r^   r   r_   r`   ra   rb   rc   rd   ri   re   rf   r   r   CANCEL_NOT_WIREDr   rg   r   SKIPPED_UNTRUSTED)rl   rm   r   rn   rs   rw   ru   ro   r   rt   rr   r   rq   s                r   5test_fixture_g_detect_unwired_fallback_empty_registryr     s    
	$	$HU	C qff %F ]6]]]6]]]]]]6]]]6]]]]]]]]]]]]]<< :66 6<< <<<   <<                    &    &    7    =    *&,,)9:         $8$J$J  $JJ    $J              !      %9    %9    %K    +6+@+@*AB      s   N!!N+c            
        t        j                  dd      5 } | j                  }ddd       t        ddddd	t        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   }t        j                  }|j                  }	||	k(  }|st        j                  d|fd||	f      t        j                  |      dt        j                         v st        j                   t              rt        j                  t              ndt        j                  |      t        j                  |	      dz  }
t        j                  d|d   d         dz   d|
iz  }t        t        j                  |            dx}x}x}}	y# 1 sw Y   xY w)u\   G-2: empty registry → collect_and_prune returns unwired dict with cause==cancel_not_wired.r,   Fr-   Nztask-g2r0   hg2r:   T)r5   r6   r7   r<   r   r=   r   r9   unwiredr   )z%(py1)s is not %(py4)sr   z?collect_and_prune must populate 'unwired' for an empty registryr   rG   r   r>   )zS%(py1)s == %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.CANCEL_NOT_WIRED
}.value
}r   r   r   z
>assert %(py9)sr   )r\   r]   r^   r   r    r_   r`   rd   ri   re   rf   r   r   r   ra   rb   rc   )rl   rm   r   r   rt   rn   r   rq   rp   r   rv   r   s               r   2test_fixture_g_collect_and_prune_unwired_in_resultr     s   		$	$HU	C qff + 	F ) D D(  D        %)    	J     )W% )D)D )D)J)J %)JJ  %)J    &      *4    *4    *E    *K    *&*;G*D)GH     # s   G55G?c                    t        j                         5 } t        |       dz  }t        dd|      }ddd       d   }d}||u }|st	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }t	        j                  d|d          dz   d|iz  }t        t	        j                  |            dx}x}}|d   }d}||u }|st	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }t	        j                  d|d          dz   d|iz  }t        t	        j                  |            dx}x}}y# 1 sw Y   AxY w)u   9R-1: evaluate_safe_remove without dispatch-fired marker →
    all_satisfied==False, marker_present==False (legacy 기존 동작 회귀 없음).
    zdispatch_fired.jsonztask-9rCRON_9R)r5   target_cron_iddispatch_fired_marker_pathNall_satisfiedFrO   r   r   z2Expected all_satisfied==False without marker, got r   rG   marker_presentz$Expected marker_present==False, got )
r\   TemporaryDirectoryr   r   r_   r`   rd   ri   re   rf   )tmpdirmissing_markerchecksr   rt   rn   r   rq   s           r   .test_fixture_9r_evaluate_safe_remove_no_markerr	    sz    
	$	$	& 
&f(== &$'5
	
 /" e "e+  "e    #    ',    =VO=T<UV     "# u #u,  #u    $    (-    /v6F/G.HI     
 
s   E11E;c                    t        j                         5 } t        |       }|dz  }|dz  }|dz  }t        |||      }ddd       d   }d}||u }|st	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }	t	        j                  d|d          dz   d|	iz  }
t        t	        j                  |
            dx}x}}|d   }d}||u }|st	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }	t	        j                  d|d          dz   d|	iz  }
t        t	        j                  |
            dx}x}}y# 1 sw Y   AxY w)uI   9R-2: evaluate_durable_evidence without result.json → satisfied==False.zmissing.result.jsonz	report.mdzcollector_result.marker)r   report_pathcollector_result_marker_pathN	satisfiedFrO   r   r   z3Expected satisfied==False without result.json, got r   rG   result_json_existsz(Expected result_json_exists==False, got )
r\   r  r   r   r_   r`   rd   ri   re   rf   )r  td
missing_rjreportmarkerevr   rt   rn   r   rq   s              r   8test_fixture_9r_evaluate_durable_evidence_no_result_jsonr  +  s   		$	$	& 
&&\//
k!// '')/

 k? e ?e#  ?e        $    >bo=NO     "# u #u,  #u    $    (-    326J3K2LM     !
 
s   )E==F)r   r   r   boolreturnr   ):__doc__
__future__r   builtinsra   _pytest.assertion.rewrite	assertionrewriter_   r   r\   pathlibr   pytest utils.fallback_schedule_registryr   r   r   )utils.completion_callback_fallback_cancelr   r	   r
   r   r   r   r   r   r   r   5utils.normal_completion_callback_collector_entrypointr   r   r   r   r    r$   r'   r*   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r	  r   r!   r   <module>r#     s    #       
   MWKY$<T(\H""J#R$5N#VR/%0Z8B06,r!   