
    %jW                    \   d Z ddl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dZeej                  v rej                  j!                  e       ej                  j#                  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)Optionalz/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     9tests/regression/test_anu_v3_dependency_isolation_2628.py_ensure_src_dispatchr   1   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_dirr6   _   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                   t        t        j                        }t        j                  j	                         D ci c]  \  }}|j                  d      r|| }}}t        t        j                        D ]%  }|j                  d      st        j                  |= ' |D cg c]K  }t        j                  j                  |xs d      t        j                  j                  t              k7  r|M }}| g|z   t        j                  dd 	 t        j                  |       	 |t        j                  dd t        t        j                        D ]%  }|j                  d      st        j                  |= ' t        j                  j                  |       yc c}}w c c}w # t        $ r}t        |      j                   dt        |      dd  cY d}~|t        j                  dd t        t        j                        D ]%  }|j                  d      st        j                  |= ' t        j                  j                  |       S d}~ww xY w# |t        j                  dd t        t        j                        D ]%  }|j                  d      st        j                  |= ' t        j                  j                  |       w xY w)u   conftest/WORKSPACE_ROOT 미경유 격리 import.

    성공 시 None, 실패 시 'ErrType: msg'.
    핵심: WORKSPACE_ROOT(SRC)만 sys.path에서 제거하고 stdlib 경로는 유지.
    )r(   r   .N: x   )r   r
   r   r   itemsr   r   realpathr	   	importlibimport_moduleupdate	Exceptiontype__name__str)	r2   modname
saved_pathr   v
saved_modspkeptes	            r   _isolated_importrK   w   s2    chhJ KK%%'Aq<<./ 	
1J 
 #++ <<./A 77AH%)9)9#)>> 	
D  %$,CHHQK
'( !ckk" 	#A||23KKN	# 	:&/  5q'""#2c!fTcl^44 ckk" 	#A||23KKN	# 	:&5 !ckk" 	#A||23KKN	# 	:&sC   F!AFF   	I)%III II AK3Kc                 j   t        j                  d      } 	 t        |        g }t        D ]*  }t	        | d|       }||j                  | d|        , |r&J dt        |       ddj                  |      z          	 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_prefixanu_v3.Nr9   u   격리 import 실패 모듈 z/12:

Tignore_errors)
tempfilemkdtempr6   r1   rK   appendlenr   r/   rmtree)r2   failedr5   errs       r   +test_01_anu_v3_12_modules_import_standaloner[      s     

.
1C/3 	-A"3'!6C2cUm,	-  	
*3v;-v>6ARR	
z6 	c.c.s   'B  A B B2c            	        t        j                  d      } 	 t        | d      }|J d       d|v s
J d|        	 t        j                  | d	       g }t
        D ]p  }t        j                  j                  t        d
| d      }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  }|j0                  j%                  d      s|j0                  t'        d      d }|t(        vs@|j+                  | d| d       X  s |rJ ddj                  |      z          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_rN   #anu_v3.runtime_reconcile_checkpointNu   빈 closure tmp 에서 ModuleNotFoundError 가 발생하지 않음 — WORKSPACE_ROOT 제거가 동작하지 않아 false-pass 위험ModuleNotFoundError1   예상 ModuleNotFoundError 대신 다른 오류: TrR   r(   r*   utf-8encodingrP   z imports anu_v3. (outside TWELVE)u>   12개 외 추가 anu_v3 의존성 발견 (closure 불완전):
rQ   )rT   rU   rK   r/   rX   r1   r   r   r   r	   astparser   	read_textwalk
isinstance
ImportFrommoduler   rW   
TWELVE_SETrV   Importnamesname)		empty_tmprZ   
extra_depsr5   src_pathtreenodesubaliass	            r   1test_02_import_without_conftest_syspath_injectionrw      s      5I
5y*OP 	
M	
 %+ 	
?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		"  I
))J
	 >z+ 	it4s   #G+ +H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       |rJ ddj                  |      z          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   ra   rb   rP   Nu    → anu_v3.rd      u7   callback enforcement 파일 2개 모두 없음 — skipu>   origin/main + 12개로 충족 불가한 anu_v3 의존 발견:
rQ   )r   r   r   r	   isfilebasenamere   rf   r   rg   rh   ri   rj   rk   r   rW   rl   rV   rm   rn   ro   pytestskip)	
candidatescheckedextrafpathfnamers   rt   ru   rv   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 I
))E
	9ur   c                     t        j                  d      } 	 t        | dh       t        | d      }|J d       d|v s
J d	|        	 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_rN   r   )r3   r^   Nur   runtime_batch_state_updater 가 없는데 import 성공 — 격리 패턴이 동작하지 않아 false-pass 위험r_   r`   TrR   )rT   rU   r6   rK   r/   rX   )r2   rZ   s     r   'test_04_missing_anu_v3_dependency_failsr     s     

"4
5C/3&C%DEs$IJ 	
E	
 %+ 	
?uE	
+ 	c.c.s   1A" "A;c                    t                ddlm} m}m}m}  |t        t        d      }|j                  | k(  sJ d|j                          |j                  |k(  sJ d|j                          |j                  du sJ |j                  du sJ  |t        t        d      }|j                  |k(  sJ d	|j                          |j                  du sJ 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_roleu)   self-session 은 FAIL 이어야 함, got zclassification=TFu1   독립 ANU collector 는 PASS 이어야 함, got N)r   anu_v3.self_collector_guardr   r   r   r   SELF_KEYverdictclassificationis_executor_self_sessionokANU_KEY)r   r   r   r   self_resanu_ress         r   /test_05_self_collector_result_not_authoritativer   *  s      ,H
 t# 
3H4D4D3EF# ""&>> 
(1123> ,,444;;% +G
 ??d" 
;GOO;LM" ::r   c            	        t                ddlm} m}m} ddlm}m}  |t        |      du sJ dt        d        |t        |      du sJ dt        d	       t        d
t        ddddd      } |ddt        i|}|j                  |k(  s!J d|j                   d|j                          |j                  du sJ d        |ddt        i|}|j                  | k(  sJ d|j                          |j                  du sJ d       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_keyTzANU key u"    는 is_anu_key=True 이어야 함Fz	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   u.   ANU collector_key 는 PASS 이어야 함, got z

reasons: u0   PASS 면 registration_allowed=True 이어야 함u/   self-key collector 는 FAIL 이어야 함, got u4   FAIL 이면 registration_allowed=False 이어야 함N )r   anu_v3.callback_owner_validatorr   r   r    dispatch.callback_owner_enforcerr   r   r   r   dictr   reasonsregistration_allowed)r   r   r   r   r   common_kwargspass_resfail_ress           r   >test_06_callback_owner_validator_distinguishes_anu_vs_self_keyr   \  s{     
 N g/0D8 
7+?@8 h 01U: 
H<BC: !0"-*)M / 
H t# 
89I9I8J K$$%	'# ((D0 :0
 / 
H t# 
9(:J:J9KL# ((E1 >1r   c                 4   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J d|j                  d|        t        |      j                  d	k(  sJ  |       }t!        |t"              sJ d
t        |      j                          y# 1 sw Y   xY w# 1 sw Y   xY w)u  §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, _OBSERVATION_REQUIRED_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   Tu   schema 필드 불일치: z != DispatchContractRecordu4   run_self_check() 는 dict 를 반환해야 함, got )r   !anu_v3.dispatch_callback_contractr   r   r   r   r   r   r   r   r   r|   raisesschemarA   rB   ri   r   )r   r   r   r   r   r   r   r   r   	valid_obsr   recordresults                r   4test_07_dispatch_callback_contract_schema_validationr     s.    
 
 
 ,,?@ 
/	0 M/0KLM 
)	*  !+.I# 	!i F==O+ 
#FMM#4D8KL+ <  $<<<< Ffd# 
>tF|?T?T>UV#)M M s   	D$	DDD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(  sJ d| d|        t        j                         |k(  sJ d       t        |
t              r|
sJ d       	 t!        j"                  |d        |       du sJ d        |       du sJ d        |       }t        |t$              sJ dt'        |      j(                          |j+                  d      du sJ |j+                  d      du sJ  |       }|g k(  sJ ddj-                  |      z          y# t!        j"                  |d       w xY w)u
  §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_rN   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_outcomeuE   classify_observation 이 파일 side-effect 를 발생시킴: before=z, after=u   cwd 가 변경됨u-   classify_observation 이 빈 문자열 반환rR   uS   checkpoint_replaces_callback_primary_path() 는 항상 False 이어야 함 (§6.13)uR   checkpoint_discards_fallback_safety_path() 는 항상 False 이어야 함 (§6.14)u=   recovery_layer_contract() 는 dict 를 반환해야 함, got replaces_primarydiscards_fallbacku4   recovery-layer 위반 발견 (§6.7/§6.13/§6.14):
rQ   N)r   #anu_v3.runtime_reconcile_checkpointr   r   2anu_v3.runtime_reconcile_checkpoint_recovery_layerr   r   r   r   rT   rU   r   getcwdlistdirri   rC   r/   rX   r   rA   rB   getr   )r   r   r   r   r   r   d	saved_cwdbeforeobsr   aftercontract
violationss                 r   1test_08_runtime_reconcile_dry_run_no_side_effectsr     s      	.A		I-A$&(-&+*.!#

 .c2

1 	
XXeW.	
 yy{i'<)<<'.#.> 	
;	
A> 	at, 56%? ]? 45> \> '(Hh% 
GXH_H_G`a% <<*+u444<<+,555:<J 
?
))J
	 % 	at,s   
BF F+)returnNone)NT)r2   rC   r4   boolr   r   )r2   rC   rD   rC   r   zOptional[str])"__doc__
__future__r   re   r=   r   r/   r
   rT   pathlibr   typingr   r|   r	   r   r   r   r   r1   r+   rl   r   r   r6   rK   r[   rw   r   r   r   r   r   r   r   r   r   <module>r      s   * # 
  	  
      #((?HHOOC 3 #0
 [

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