
    j\                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZddlZddlZddlmZ ddlmZ ddlZe
j(                  j+                  dd      Zeej.                  v rej.                  j1                  e       ej.                  j3                  de       ddZg d	Z ee      Zd
ZdZdddZ ddZ!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)y)u   task-2628 §3 회귀테스트: anu_v3 dependency closure 독립 검증.

테스터: 벨레스 (dev6팀)
팀장:  페룬 (closure 분석 / 격리 패턴 검증 완료)
작성일: 2026-05-21

대상 12개 anu_v3 모듈의 dependency closure가 완전하며 (외부 의존 0),
유일한 cross-layer 의존인 dispatch.callback_owner_enforcer 도 stdlib만
import 함을 독립 검증한다. functional 테스트(§3-5 ~ §3-8)는 실제 소스를
읽어 확인한 API 시그니처로 작성한다.

§3 회귀 항목 매핑:
  test_01  §3-1  12모듈 독립 import 성공
  test_02  §3-2  conftest/WORKSPACE_ROOT 미경유 + 정적 의존성 검증
  test_03  §3-3  callback enforcement 파일의 anu_v3 의존 ⊆ TWELVE
  test_04  §3-4  missing dep → ModuleNotFoundError (false-pass 차단)
  test_05  §3-5  self_collector_guard 동작 검증
  test_06  §3-6  callback_owner_validator ANU vs self-key 구분
  test_07  §3-7  dispatch_callback_contract schema 검증
  test_08  §3-8  runtime_reconcile dry-run side-effect 0 + recovery-layer 계약
    )annotationsN)Path)OptionalWORKSPACE_ROOTz/home/jay/workspacec                    t         t        j                  v r#t        j                  j                  t                t        j                  j	                  dt                dt        j
                  v xrC t        j
                  d   j                  t        j                  j                  t         dd      k7  } | rGt        t        j
                        D ]*  }|dk(  s|j                  d      st        j
                  |= , yy)u  functional 테스트 직전 호출: SRC 의 실제 dispatch 패키지가
    tests/dispatch 스텁에 의해 가려지지 않도록 sys.path 와 sys.modules
    를 정리한다.

    tests/ 가 sys.path 앞에 들어오면 tests/dispatch/__init__.py 가
    실제 dispatch/ 를 가려 'dispatch.callback_owner_enforcer' 를 못 찾게
    된다. SRC 를 맨 앞으로 재배치하고 캐시된 잘못된 dispatch 모듈을
    제거한다.
    r   dispatch__init__.pyz	dispatch.N)SRCsyspathremoveinsertmodules__file__osjoinlist
startswith)badks     M/home/jay/workspace/tests/regression/test_anu_v3_dependency_isolation_2628.py_ensure_src_dispatchr   4   s     chhHHOOAs 	ckk! 	]KK
#,,S*m0\\  ckk" 	#AJ!,,{";KKN	#     )active_dispatch_scannerauthoritative_verdict_selectorcallback_4tuple_registrycallback_owner_validatordispatch_callback_contractexecutor_callback_contractruntime_batch_state_updaterruntime_next_action_resolverruntime_reconcile_checkpoint+runtime_reconcile_checkpoint_recovery_layerself_collector_guardtask_artifact_detectorc119085addb0f8b71e41a2324a3ccdd0c           
     `   |xs
 t               }t        j                  t        j                  j	                  | d             t        j                  t        j                  j	                  | d             t        t        j                  j	                  | dd      d      j                          t        t        j                  j	                  | dd      d      j                          |rXt        j                  t        j                  j	                  t        dd      t        j                  j	                  | dd             t        D ]e  }||v rt        j                  t        j                  j	                  t        d| d      t        j                  j	                  | d| d             g y)u   12개 anu_v3 + (옵션)dispatch.callback_owner_enforcer 를 tmp에 격리 복사.

    dispatch/__init__.py 는 빈 stub (실제 192KB __init__ 의 무거운 의존성 회피).
    anu_v3r   r	   wzcallback_owner_enforcer.py.pyN)setr   makedirsr   r   opencloseshutilcopyr
   TWELVE)tmpomitinclude_enforcerms       r   _build_closure_dirr7   b   s!   
 =35DKKS(+,KKS*-.c8]	3S9??Ac:}	5s;AACGGLLj*FGGGLLj*FG	
  
9GGLLh1#S	2GGLLh1#S	2	

r   c                v   t        j                  d      }t        t        j                        }| |d<   d|d}	 t        j                  t        j                  d|g||ddd	      }	 t        j                  |d
       |j                  dk(  ry|j                  j                         }|s>|j                  j                         j!                         }|r|d   nd|j                   }|dd S # t
        j                  $ r Y t        j                  |d
       yw xY w# t        j                  |d
       w xY w)uR  완전 격리 subprocess import. 성공 시 None, 실패 시 'ErrType: msg'.

    회장 지시(§2 #2): in-process sys.path 누수를 원천 차단하기 위해 별도
    파이썬 프로세스를 다음 조건으로 실행한다.
      - cwd = 빈 중립 디렉토리 → `python -c` 의 sys.path[0]("")=cwd 가
        anu_v3 를 해소하지 못하게 한다(worktree root/cwd 누수 차단).
      - PYTHONPATH = closure tmp (오직 이 경로로만 anu_v3/dispatch 해소).
        부모(pytest)의 PYTHONPATH 는 명시 override 로 차단한다.
    따라서 import 성공 여부는 'closure tmp 안에 모듈이 있는가' 에만 의존하며
    workspace/worktree/pytest rootdir/conftest 어느 경로에도 의존하지 않는다.
    conftest sys.path 주입은 자식 프로세스에 전파되지 않으므로 false-pass 불가.
    iso_cwd_prefix
PYTHONPATHz7import importlib, sys
try:
    importlib.import_module(zn)
except Exception as e:
    sys.stdout.write(type(e).__name__ + ': ' + str(e)[:120])
    raise SystemExit(3)
z-cT<   )cwdenvcapture_outputtexttimeoutignore_errorsz,TimeoutExpired: isolated import exceeded 60sr   Nzsubprocess returncode=x   )tempfilemkdtempdictr   environ
subprocessrunr   
executableTimeoutExpiredr0   rmtree
returncodestdoutstripstderr
splitlines)r3   modnameneutral_cwdr?   codeprocmsg	err_liness           r   _isolated_importr[   z   s#    ""*5K
rzz
CC	''.k 2$	$ 	7~~^^T4(
 	k6!
++


CKK%%'224	(im0FtFW.Xt9 $$ >=k6> 	k6s#   ,C/ /DD DD D8c                 Z   t        j                  d      } 	 t        |        g }t        D ]*  }t	        | d|       }||j                  | d|        , | }|st        j                  dt        |       ddj                  |      z         d	z   d
dt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            d}t!        j"                  | d       y# t!        j"                  | d       w xY w)u_  §3-1: 완전 closure 디렉토리에서 12개 모듈 전부 독립 import 성공 assert.

    12개 anu_v3 모듈과 유일한 외부 의존 dispatch.callback_owner_enforcer 를
    격리 tmp에 복사 후 WORKSPACE_ROOT 를 sys.path 에서 제거한 상태로
    importlib.import_module 을 실행한다. 실패 모듈은 메시지에 포함.
    t01_closure_r:   anu_v3.Nz: u   격리 import 실패 모듈 z/12:


>assert not %(py0)spy0failedTrC   )rG   rH   r7   r2   r[   append
@pytest_ar_format_assertmsglenr   @py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr0   rO   )r3   rb   r6   err@py_assert1@py_format2s         r   +test_01_anu_v3_12_modules_import_standalonerp      s    

.
1C/3 	-A"3'!6C2cUm,	- z 	
z 	
  +3v;-v>6ARR	
 	
	6	
 	
   	
 	
 		  	
 	
 	
 	
 	
 	c.c.s   'D  B9D D*c            	        t        j                  d      } 	 t        | 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}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       d
z   d|iz  }t        t        j                  |            dx}}t        j                  | d       g }t        D ]p  }t        j                   j#                  t$        d| d      }	t'        j(                  t+        |	      j-                  d            }
t'        j.                  |
      D ]  }t1        |t&        j2                        rb|j4                  s*|j4                  j7                  d      sF|j4                  t9        d      d }|t:        vsg|j=                  | d| d       t1        |t&        j>                        s|j@                  D ]V  }|jB                  j7                  d      s|jB                  t9        d      d }|t:        vs@|j=                  | d| d       X  s | }|st        j                  ddj#                  |      z         dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            d}y# t        j                  | d       w xY w)u  §3-2: (a) closure 없는 빈 tmp → ModuleNotFoundError 확인 (false-pass 차단).
             (b) 12개 소스 ast.parse → anu_v3.X import X ⊆ TWELVE 정적 검증.

    (a): WORKSPACE_ROOT/conftest 주입이 제거된 상태에서 import 가 실패해야
         한다. 이는 test_01 의 성공이 conftest 주입 덕분이 아님을 증명한다.
    (b): 12개 소스 파일에서 ImportFrom(anu_v3.*) 노드를 순회하여 참조하는
         서브모듈이 TWELVE 집합 내에만 있음을 정적으로 검증한다.
    
t02_empty_r:   #anu_v3.runtime_reconcile_checkpointNis notz%(py0)s is not %(py3)srm   ra   py3u   빈 closure tmp 에서 ModuleNotFoundError 가 발생하지 않음 — WORKSPACE_ROOT 제거가 동작하지 않아 false-pass 위험
>assert %(py5)spy5ModuleNotFoundErrorinz%(py1)s in %(py3)spy1rx   1   예상 ModuleNotFoundError 대신 다른 오류: TrC   r)   r+   utf-8encodingr^   z imports anu_v3. (outside TWELVE)u>   12개 외 추가 anu_v3 의존성 발견 (closure 불완전):
r_   r`   ra   
extra_deps)"rG   rH   r[   rd   _call_reprcomparerg   rh   ri   rj   re   rk   rl   r0   rO   r2   r   r   r   r
   astparser   	read_textwalk
isinstance
ImportFrommoduler   rf   
TWELVE_SETrc   Importnamesname)	empty_tmprm   @py_assert2rn   @py_format4@py_format6@py_assert0r   r6   src_pathtreenodesubaliasro   s                  r   1test_02_import_without_conftest_syspath_injectionr      sF      5I
5y*OP 	
s$ 	
 	
s$ 	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
	  	
 	
 M	
 	
 	
 	
 	
 % 	
$+ 	
 	
$ 	
 	
 		 % 	
 	
	6	
 	
  ), 	
 	
 		 ), 	
 	
  @uE	
 	
 	
 	
 	
 	it4 J 77<<X!Cy9yyh1171CDHHTN 	D$/;;4;;#9#9)#D++c)no6C*,"))QC/?uDU*VWD#**-!ZZ Ezz,,Y7#jjY9j0&--#$#%5cU:K L		" > >   	J
))J
	               + 	it4s   FO Oc            	         t         j                  j                  t        dd      t         j                  j                  t        dd      g} d}g }| D ]  }t         j                  j	                  |      s$t         j                  j                  |      }t        j                  t        |      j                  d            }t        j                  |      D ]  }t        |t        j                        rb|j                  s*|j                  j                  d      sF|j                  t        d      d }|t         vsg|j#                  | d	| d
       t        |t        j$                        s|j&                  D ]V  }|j(                  j                  d      s|j(                  t        d      d }|t         vs@|j#                  | d	| d
       X  |dz  } |dk(  rt+        j,                  d       | }	|	st/        j0                  ddj                  |      z         dz   ddt3        j4                         v st/        j6                  |      rt/        j8                  |      ndiz  }
t;        t/        j<                  |
            d}	y)u8  §3-3: callback enforcement 파일 2개의 anu_v3.* import 집합 ⊆ TWELVE.

    /dispatch/cron_dispatch_guard.py 및 /dispatch/normal_fallback_callback_helper.py
    를 ast.parse 하여 anu_v3.* 의존 추출 → TWELVE 부분집합 검증.
    최소 1개 파일이 검사돼야 한다(없으면 skip).
    r   zcron_dispatch_guard.pyz"normal_fallback_callback_helper.pyr   r   r   r^   Nu    → anu_v3.r      u7   callback enforcement 파일 2개 모두 없음 — skipu>   origin/main + 12개로 충족 불가한 anu_v3 의존 발견:
r_   r`   ra   extra)r   r   r   r
   isfilebasenamer   r   r   r   r   r   r   r   r   rf   r   rc   r   r   r   pytestskiprd   re   rg   rh   ri   rj   rk   rl   )
candidatescheckedr   fpathfnamer   r   r   r   rn   ro   s              r   @test_03_callback_enforcement_resolvable_from_origin_main_plus_12r      s    	S*&>?
S*&JKJ GE ww~~e$  'yye...@AHHTN 	WD$/;;4;;#9#9)#D++c)no6C*,wl3%?P%QRD#**-!ZZ WEzz,,Y7#jjY9j0!LLE7,seCT)UV	W	W 	1#& !|MN9 9   	J
))E
	              r   c                    t        j                  d      } 	 t        | dh       t        | 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}}d}||v }|st	        j
                  d|fd||f      t	        j                  |      d	t        j                         v st	        j                  |      rt	        j                  |      nd	dz  }t	        j                  d|       dz   d|iz  }t        t	        j                  |            dx}}t        j                  | d       y# t        j                  | d       w xY w)u  §3-4: runtime_batch_state_updater 제거 시 runtime_reconcile_checkpoint
    import → ModuleNotFoundError 발생 확인 (false-pass 아님, missing dep 검출).

    내부 의존 목록: runtime_reconcile_checkpoint →
      {active_dispatch_scanner, task_artifact_detector,
       runtime_next_action_resolver, runtime_batch_state_updater}.
    하나라도 빠지면 격리 import 가 실패해야 한다.
    t04_missing_dep_r:   r    )r4   rs   Nrt   rv   rm   rw   ur   runtime_batch_state_updater 가 없는데 import 성공 — 격리 패턴이 동작하지 않아 false-pass 위험ry   rz   r{   r|   r~   r   r   TrC   )rG   rH   r7   r[   rd   r   rg   rh   ri   rj   re   rk   rl   r0   rO   )r3   rm   r   rn   r   r   r   s          r   'test_04_missing_anu_v3_dependency_failsr      s    

"4
5C/3&C%DEs$IJ 	
s$ 	
 	
s$ 	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
	  	
 	
 E	
 	
 	
 	
 	
 % 	
$+ 	
 	
$ 	
 	
 		 % 	
 	
	6	
 	
  ), 	
 	
 		 ), 	
 	
  @uE	
 	
 	
 	
 	
 	c.c.s   FG Gc                    t                ddlm} m}m}m}  |t        t        d      }|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                  |       rt        j                  |       ndd	z  }t        j                  d
|j                         dz   d|iz  }t        t        j                   |            dx}}|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                  |      rt        j                  |      ndd	z  }t        j                  d|j"                         dz   d|iz  }t        t        j                   |            d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        t(        d      }|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                  |      rt        j                  |      ndd	z  }t        j                  d|j                         dz   d|iz  }t        t        j                   |            d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  §3-5: self_collector_guard — executor self-session 은 FAIL·SELF_COLLECTOR_FORBIDDEN,
    독립 ANU collector session 은 PASS.

    검증 API:
      guard_self_collector_session(executor_key, collector_key, collector_role)
        → SelfCollectorGuardResult
          .verdict: PASS | FAIL
          .classification: SELF_COLLECTOR_FORBIDDEN | None
          .is_executor_self_session: bool
          .ok: bool (verdict == PASS)
    r   )FAILPASSSELF_COLLECTOR_FORBIDDENguard_self_collector_sessionANU)executor_keycollector_keycollector_role==z/%(py2)s
{%(py2)s = %(py0)s.verdict
} == %(py4)sself_resr   ra   py2py4u)   self-session 은 FAIL 이어야 함, got 
>assert %(py6)spy6N)z6%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py4)sr   zclassification=Tis)z@%(py2)s
{%(py2)s = %(py0)s.is_executor_self_session
} is %(py5)sra   r   rz   zassert %(py7)spy7F)z*%(py2)s
{%(py2)s = %(py0)s.ok
} is %(py5)sanu_resr   u1   독립 ANU collector 는 PASS 이어야 함, got )r   anu_v3.self_collector_guardr   r   r   r   SELF_KEYverdictrd   r   rg   rh   ri   rj   re   rk   rl   classificationis_executor_self_sessionokANU_KEY)r   r   r   r   r   rn   @py_assert3@py_format5@py_format7@py_assert4r   @py_format8r   s                r   /test_05_self_collector_result_not_authoritativer   :  s6      ,H
  t#  t                     $     $    4H4D4D3EF     "" "&>>  "&>              #      '?    '?    (1123     ,,44,4444,44444484448444,4444444444;;%;%;%88;% +G
 ?? ?d"  ?d                    #    #    <GOO;LM     ::::77:r   c            	     D   t                ddlm} m}m} ddlm}m}  |t        |      }d}||u }|sUt        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }t        j                  dt        d      dz   d|iz  }	t!        t        j"                  |	            dx}x}} |t$        |      }d}||u }|sUt        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t$              rt        j                  t$              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }t        j                  dt$        d      dz   d|iz  }	t!        t        j"                  |	            dx}x}}t'        dt$        ddddd      }
 |d.dt        i|
}|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d z  }t        j                  d!|j(                   d"|j*                         d#z   d$|iz  }t!        t        j"                  |            d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}} |d.dt$        i|
}|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                  |       rt        j                  |       nd+d z  }t        j                  d,|j(                         d#z   d$|iz  }t!        t        j"                  |            d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}}y)/u  §3-6: callback_owner_validator — ANU key vs executor self-key 구분,
    PASS/FAIL 판정 검증.

    검증 API:
      is_anu_key(key, DEFAULT_ANU_KEYS) → bool
      validate_callback_owner_runtime(
          task_id, executor_key, collector_key, collector_role,
          normal_collector_cron_id, fallback_callback_cron_id,
          dispatch_cron_id, entry_path, ...) → CallbackOwnerValidationResult
            .verdict: PASS | FAIL
            .registration_allowed: bool
    r   )r   r   validate_callback_owner_runtime)DEFAULT_ANU_KEYS
is_anu_keyTr   )z9%(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
} is %(py7)sr   r   r   )ra   r   r   r   r   zANU key u"    는 is_anu_key=True 이어야 함z
>assert %(py9)spy9NFr   z	SELF key u#    는 is_anu_key=False 이어야 함	task-2628r   zcron-normal-1z	cron-fb-1zcron-dispatch-1cokacdir_cron_direct)task_idr   r   normal_collector_cron_idfallback_callback_cron_iddispatch_cron_id
entry_pathr   r   r   pass_resr   r   u.   ANU collector_key 는 PASS 이어야 함, got z

reasons: r   r   )z<%(py2)s
{%(py2)s = %(py0)s.registration_allowed
} is %(py5)sr   u0   PASS 면 registration_allowed=True 이어야 함
>assert %(py7)sr   fail_resr   u/   self-key collector 는 FAIL 이어야 함, got u4   FAIL 이면 registration_allowed=False 이어야 함 )r   anu_v3.callback_owner_validatorr   r   r    dispatch.callback_owner_enforcerr   r   r   rd   r   rg   rh   ri   rj   re   rk   rl   r   rI   r   reasonsregistration_allowed)r   r   r   r   r   r   @py_assert6@py_assert5r   @py_format10common_kwargsr   rn   r   r   r   r   r   s                     r   >test_06_callback_owner_validator_distinguishes_anu_vs_self_keyr   l  s;     
 N g/0 D 0D8   0D                           0     0    1    59    7+?@     h 01 U 1U:   1U                          !1    !1    2    6;    H<BC     !0"-*)M / 
H  t#   t                     $     $    99I9I8J K$$%	'     (( D (D0  (D              )    -1    	;    
 / 
H  t#  t                     $     $    :(:J:J9KL     (( E (E1  (E              )    -2    	?     r   c                    t                ddlm} m}m}m}m}m}m}m	}m
}  ||       t        j                  |      5   ||       ddd       t        j                  |      5   |i        ddd       ddi}	|D ]  }
d|	|
<   	  ||	      }|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
dz  }t        j(                  d|j                  d|       dz   d|iz  }t+        t        j,                  |            dx}}t/        |      }|j0                  }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}} |       }t3        |t4              }|s7t        j(                  dt/        |      j0                         dz   dt!        j"                         v st        j$                  t2              rt        j&                  t2              nddt!        j"                         v st        j$                  |      rt        j&                  |      nddt!        j"                         v st        j$                  t4              rt        j&                  t4              ndt        j&                  |      dz  }t+        t        j,                  |            d}y# 1 sw Y   xY w# 1 sw Y   xY w)um  §3-7: dispatch_callback_contract — schema validation, key 제약, evaluate().

    검증 API:
      assert_collector_key_is_independent_anu(key) → None or raises ExecutorSelfKeyForbidden
      evaluate(observation: dict) → DispatchContractRecord or raises InvalidObservation
      run_self_check() → dict
      CONTRACT_SCHEMA, _OBSERVATION_BOOL_KEYS
    r   )	CONTRACT_SCHEMAEXECUTOR_SELF_KEY_FORBIDDENINDEPENDENT_ANU_KEYInvalidObservation_OBSERVATION_BOOL_KEYS'assert_collector_key_is_independent_anuevaluaterun_self_checkExecutorSelfKeyForbiddenNr   r   Tr   )z.%(py2)s
{%(py2)s = %(py0)s.schema
} == %(py4)srecordr   r   u   schema 필드 불일치: z != r   r   DispatchContractRecord)zN%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py0)s(%(py1)s)
}.__name__
} == %(py8)stype)ra   r   rx   rz   py8zassert %(py10)spy10u4   run_self_check() 는 dict 를 반환해야 함, got 7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r   resultrI   ra   r   r   r   )r   !anu_v3.dispatch_callback_contractr   r   r   r   r   r   r   r   r   r   raisesschemard   r   rg   rh   ri   rj   re   rk   rl   r   __name__r   rI   )r   r   r   r   r   r   r   r   r   	valid_obsr   r   rn   r   r   r   r   r   @py_assert7r   @py_format9@py_format11r   s                          r   4test_07_dispatch_callback_contract_schema_validationr    sI    
 
 
 ,,?@ 
/	0 M/0KLM 
)	*  !+.I# 	!i F== =O+   =O                    ,    ,    $FMM#4D8KL     <<<  <$<< $<<<<< $<<<<<<<4<<<4<<<<<<<<<<<<<<<< <<<$<<<<<<<< Ffd# #   ?tF|?T?T>UV                         #    #    $     )M M s   	P,$	P9,P69Qc                    t                ddlm} m} ddlm}m}m}m} t        j                  d      }t        j                         }	 t        j                  |      } | dddddddd	d
	      }	 ||	      }
t        j                  |      }||k(  }|st        j                  d|fd||f      dt!        j"                         v st        j$                  |      rt        j&                  |      nddt!        j"                         v st        j$                  |      rt        j&                  |      nddz  }t        j(                  d| d|       dz   d|iz  }t+        t        j,                  |            d}t        j                  } |       }||k(  }|st        j                  d|fd||f      dt!        j"                         v st        j$                  t              rt        j&                  t              ndt        j&                  |      t        j&                  |      dt!        j"                         v st        j$                  |      rt        j&                  |      nddz  }t        j(                  d      dz   d|iz  }t+        t        j,                  |            dx}x}}g }t/        |
t0              }|}|r|
}|sd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d t!        j"                         v st        j$                  t0              rt        j&                  t0              nd t        j&                  |      d!z  }|j3                  |       |rXd"ddt!        j"                         v st        j$                  |
      rt        j&                  |
      ndiz  }|j3                  |       t        j4                  |d      i z  }t        j(                  d#      d$z   d%|iz  }t+        t        j,                  |            dx}x}}t7        j8                  |d&        |       }d}||u }|st        j                  d'|fd(||f      d)t!        j"                         v st        j$                  |      rt        j&                  |      nd)t        j&                  |      t        j&                  |      d*z  }t        j(                  d+      d,z   d-|iz  }t+        t        j,                  |            dx}x}} |       }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/        |t:              }|s7t        j(                  d0t=        |      j>                         d1z   dt!        j"                         v st        j$                  t.              rt        j&                  t.              ndd2t!        j"                         v st        j$                  |      rt        j&                  |      nd2d3t!        j"                         v st        j$                  t:              rt        j&                  t:              nd3t        j&                  |      d4z  }t+        t        j,                  |            d}|j@                  }d5} ||      }d}||u }|st        j                  d'|fd6||f      d2t!        j"                         v st        j$                  |      rt        j&                  |      nd2t        j&                  |      t        j&                  |      t        j&                  |      t        j&                  |      d7z  }d8d%|iz  }t+        t        j,                  |            dx}x}x}x}}|j@                  }d9} ||      }d}||u }|st        j                  d'|fd6||f      d2t!        j"                         v st        j$                  |      rt        j&                  |      nd2t        j&                  |      t        j&                  |      t        j&                  |      t        j&                  |      d7z  }d8d%|iz  }t+        t        j,                  |            dx}x}x}x}} |       }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>jC                  |      z         d?z   d@|iz  }t+        t        j,                  |            dx}}y# t7        j8                  |d&       w xY w)Au
  §3-8: runtime_reconcile_checkpoint dry-run 파일 side-effect 0 검증,
    recovery_layer 계약 검증.

    (A) RuntimeTaskObservation / classify_observation 사용 — 파일 I/O 없는 경로.
        before/after os.listdir(d) 로 side-effect 0 assert.
    (B) recovery_layer:
        checkpoint_replaces_callback_primary_path() is False
        checkpoint_discards_fallback_safety_path() is False
        recovery_layer_contract() → dict
        assert_checkpoint_is_recovery_not_primary() → [] (위반 0)
    r   )RuntimeTaskObservationclassify_observation))assert_checkpoint_is_recovery_not_primary(checkpoint_discards_fallback_safety_path)checkpoint_replaces_callback_primary_pathrecovery_layer_contractt08_dryrun_r:   ztask-2628-dryrunTFNONEDONE)	r   dispatch_okresult_presentdone_presentnormal_collector_registerednormal_collector_executedby_design_no_normal_collectorfallback_stateterminal_outcomer   )z%(py0)s == %(py2)sbeforeafter)ra   r   uE   classify_observation 이 파일 side-effect 를 발생시킴: before=z, after=z
>assert %(py4)sr   N)zE%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.getcwd
}()
} == %(py6)sr   	saved_cwd)ra   r   r   r   u   cwd 가 변경됨z
>assert %(py8)sr   z.%(py6)s
{%(py6)s = %(py2)s(%(py3)s, %(py4)s)
}r   r   str)r   rx   r   r   z%(py8)su-   classify_observation 이 빈 문자열 반환z
>assert %(py11)spy11rC   r   )z)%(py2)s
{%(py2)s = %(py0)s()
} is %(py5)sr
  r   uS   checkpoint_replaces_callback_primary_path() 는 항상 False 이어야 함 (§6.13)r   r   r	  uR   checkpoint_discards_fallback_safety_path() 는 항상 False 이어야 함 (§6.14)u=   recovery_layer_contract() 는 dict 를 반환해야 함, got r   contractrI   r   replaces_primary)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)s)ra   r   r   r   r   zassert %(py11)sdiscards_fallback)z%(py0)s == %(py3)s
violationsrw   u4   recovery-layer 위반 발견 (§6.7/§6.13/§6.14):
r_   ry   rz   )"r   #anu_v3.runtime_reconcile_checkpointr  r  2anu_v3.runtime_reconcile_checkpoint_recovery_layerr  r	  r
  r  rG   rH   r   getcwdlistdirrd   r   rg   rh   ri   rj   re   rk   rl   r   r  rc   _format_boolopr0   rO   rI   r   r   getr   )r  r  r  r	  r
  r  dr  r  obsr   r  rn   @py_format3r   r   r   r   r  r   r   @py_format12r   r   r   r  @py_assert8r  r  r   r   s                                  r   1test_08_runtime_reconcile_dry_run_no_side_effectsr+    s      	.A		I-A$&(-&+*.!#

 .c2

1 	
 	
v 	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
6	
 	
   	
 	
 
	  	
 	
 XXeW.	
 	
 	
 	
 	
 yy<y{<{i'<<<{i<<<<<<r<<<r<<<y<<<{<<<<<<i<<<i<<<<)<<<<<<<<	
z.#. 	
. 	
.> 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ) 	
 	
 		 ) 	
 	
	6	
 	
  +. 	
 	
 		 +. 	
 	
 		 / 	
 	
	6	
	 / 	
	6	
 	
  4B 	
 	
 		 4B 	
 	
	6	
		
 	
 	
  <	
 	
 	
 	
 	
 	at, 56 % 6%?  6%      5    5    7    ;@    	^     45  5>  5      4    4    6    :?    	]     '(Hh% %   HXH_H_G`a                         !%    !%    &      <<4*4<*+4u4+u4444+u44444484448444<444*444+444u4444444<<5+5<+,55,5555,55555585558555<555+555,5555555555:<J :  :                  @
))J
	     % 	at,s   
P,i   i9)returnNone)NT)r3   r  r5   boolr,  r-  )r3   r  rU   r  r,  zOptional[str])*__doc__
__future__r   builtinsrg   _pytest.assertion.rewrite	assertionrewriterd   r   r   r0   rK   r   rG   pathlibr   typingr   r   rJ   r%  r
   r   r   r   r   r2   r,   r   r   r   r7   r[   rp   r   r   r   r   r   r  r+  r   r   r   <module>r7     s   * #   
 	   
     	jjnn%'<= #((?HHOOC 3 #0
 [



0+`/0-d'X/4-d>F0jKr   