
    Ej                    2   d Z ddlm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
mZ ddlZ ee      j                         j                  d   Z ee      ej$                  vr"ej$                  j'                  d ee             ddlmZmZmZmZ dd	lmZmZmZ dd
lmZ ddlm Z m!Z! ddl"m#Z#m$Z$ ddl%m&Z&m'Z' edz  dz  dz  Z(edz  dz  dz  Z)edz  dz  Z*edz  dz  Z+ ejX                  d      dFd       Z- ejX                  d      dFd       Z.dFdZ/dGdZ0d Z1d Z2d Z3d Z4d  Z5d! Z6d" Z7d# Z8d$ Z9d% Z:d& Z;d' Z<d( Z=d) Z>d* Z?d+ Z@d, ZAd- ZBd. ZCd/ ZDd0 ZEd1 ZFd2 ZGd3 ZHd4 ZId5 ZJd6 ZKd7 ZLd8 ZMd9 ZNd: ZOd; ZPd< ZQd= ZRd> ZSd? ZTd@ ZUdA ZVdB ZWdC ZXdD ZYdE ZZy)Hu0  test_goal_activation_controller_2553plus7.py — task-2553+7 regression.

GOAL_DRIVEN_ACTIVATION_CONTROLLER_V0 — 18 required regression tests.

전부 부작용 0 (실 git mutation/PR open 0 — monkeypatch stub + 정적 AST/grep).
스타일: test_pre_authorized_activation_runner_2553plus6.py 踏袭.
    )annotationsN)Path)Any)patch   )GOAL_CONTRACT_SCHEMAGOAL_RESULT_SCHEMAGoalContractErrorparse_goal_contract)validate_boundaryparse_success_conditionparse_hold_conditions)run_goal_activation)STATUS_DRY_RUN_VERIFIEDSTATUS_HOLD)DECISION_ALLOWDECISION_HOLD)TASK_2553P1_EFFECTIVE_DIFF_6#TASK_2553P6_FORBIDDEN_WRITE_TARGETSmemoryfixturesz%task-2553+1.f1solo.goal-contract.jsonz2task-2553+1.f1solo.activation-evidence-bundle.jsonanu_v3zgoal_activation_controller.pyscriptsz!run_goal_activation_controller.pymodule)scopec                 T    t        j                  t        j                  d            S Nutf-8encoding)jsonloadsGOAL_CONTRACT_FIXTURE	read_text     =tests/regression/test_goal_activation_controller_2553plus7.pygoal_contractr(   E   s     ::+55w5GHHr&   c                 T    t        j                  t        j                  d            S r   )r!   r"   ACTIVATION_BUNDLE_FIXTUREr$   r%   r&   r'   evidence_bundler+   J   s     ::/9979KLLr&   c            	     J   i ddddddddd	d
dt         di ddd	d
ddddddddddddt        t              dt        t              dg ddddddddddd dd!dd"dd#ddd$dddd%d&t        d'd(d)ddddd*d+dd,dd-g d.d/dd0dd1dd2d3d4diS )5uK   6조건 전부 통과시키는 mock run_activation 결과 (dry-run 전용).schemaz*anu_v3.pre_authorized_activation_result.v1runner_module'anu_v3.pre_authorized_activation_runnerrunner_versionz1.0.0ts_utc2026-05-16T00:00:00Ztask_idtask-2553+1statusderived_contractz(anu_v3.pre_authorized_action_contract.v1action_typeclean_replacement_pr_open	source_prTf   task/task-2553-dev5	preservednumberbranchsame_branch_pushF
fresh_baseexpected_fileseffective_diff_filesforbidden_paths_touchedeffective_diff_contamination
critical_7credential_change_requiredowner_pat_touch_requiredactual_api_call_requiredreal_write_required!limited_real_write_entry_requiredscope_expansionPASS)normalfallback)codex_verdictcallbackmerge_requiredauto_closeoutdev_status_changegate_decisionbinding_preflight_statusDRY_RUN_PLAN_ONLYfour_conditionsderiver_status_derivedgate_decision_allowbinding_preflight_dry_run_plan.provenance_and_sha_and_decision_machine_4fieldall_four_conditions_metactivate_flagallowed_write_primitives)branch_create_fresh_basecommit_six_effective_diff_filespush_new_branch_onlyclean_replacement_pr_open_oncereal_write_performedgithub_mutation_performedmerge_performed	pr_numberNreport_to_chair)r   listr   r   r%   r&   r'   _all_pass_activation_resultrk   P   s   4>4B4 	'4 	(	4
 	=4 	)4 	 
@
}
 6
 tsF[\	

 
 $
 d#?@
 #D)E$F
 &r
 +E
 %
 )%
 '
 '
 "5
  0!
" u#
$ $#'T:#"!&-
4> 	?4@ 	#$7A4B 	&*#'.2>B	
C4N 	"4O4P 	Q4R 	# %
S4^ 	_4` 	$Ua4b 	5c4d 	Te4f 	5g4 4r&   c                B    ddddt         d| gdddt        ddddddd	dd
S )u   HOLD 결과 mock.z(anu_v3.pre_authorized_activation_hold.v1r/   r2   r4   mockFTNrY   )r-   r.   r1   r3   r5   stagehold_reasonsre   rf   ri   rU   rV   r6   rX   r^   )r   r   )reasons    r'   _hold_activation_resultrq      sM     =B(  %%*&$( &+#(.3>C	
 $)) r&   c           	         t               }t        d|      5  t        | t        |dz  |dz  |dz        }ddd       j	                  di       j	                  d	i       }|j	                  d
      du sJ |j	                  d      du sJ |j	                  d      du sJ |j	                  d      du sJ |j	                  d      du sJ |j	                  d      du sJ |j	                  di       j	                  d      du sJ y# 1 sw Y   xY w)u   1. goal contract allows clean_replacement_pr_open + gate ALLOW
       → activation proceeds (all 6 conditions True, activation_permitted True
       if not force_dry_run).0anu_v3.goal_activation_controller.run_activationreturn_valuedec.jsonres.json	hold.jsonr(   	repo_pathdecision_out_pathresult_out_pathhold_out_pathNactivation_decisionsix_conditions0cond1_allowed_actions_contains_clean_replacementTcond2_gate_decision_allowcond3_boundary_validator_pass$cond4_binding_preflight_dry_run_plancond5_hold_conditions_zerocond6_callback_collector_readyactivation_permittedrk   r   r   	WORKSPACEget)r(   tmp_pathall_passresultsixs        r'   :test_01_goal_contract_allow_gate_allow_activation_proceedsr      s3    +,H 
:
 
 %'&3$z1"[0
	
 ***B
/
3
34Db
IC77EF$NNN77./47777723t;;;779:dBBB77/0D8887734<<<::+R0445KLPTTTT-
 
s   DDc           	     6   t         dddddgg dg d}t               }t        d|      5  t        |t        | d	z  | d
z  | dz        }ddd       j                  d      du sJ |j                  d      xs i }|j                  d      du sJ y# 1 sw Y   IxY w)u8   2. action not in goal contract allowed_actions → HOLD.ztest-2r4   testcommitpushr-   goal_idtarget_task_idgoalallowed_actionsforbidden_actionssuccess_conditionhold_conditionsrs   rt   rv   rw   rx   ry   Nhold_for_chairTr~   r   Fr   rk   r   r   r   r   )r   gcr   r   act_decs        r'   *test_02_action_not_in_allowed_actions_holdr      s     ''$f-#	
B +,H	:
 

 %&3$z1"[0
	

 ::&'4///jj./52G;;-.%777

 

s   BBc           	         t         dddg ddgdg d}t               }t        d|      5  t        |t        | d	z  | d
z  | dz        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u1   3. planned action ∈ forbidden_actions → HOLD.ztest-3r4   r   )clean_replacement_branch_creater   r   r8   r   r   rs   rt   rv   rw   rx   ry   Nr   Tr   )r   r   r   r   s       r'   'test_03_forbidden_actions_conflict_holdr      s     ''
 'Z#
B  +,H	:
 

 %&3$z1"[0
	

 ::&'4///

 

s   A&&A/c           	     @   t        d      }t        d|      5  t        | t        |dz  |dz  |dz        }ddd       j	                  d	      d
u sJ |j	                  d      du sJ |j	                  d      xs i }|j	                  d      du sJ y# 1 sw Y   ^xY w)uH   4. gate HOLD → activation refuses (activate stays False, HOLD packet).zgate decision HOLD (mock)rs   rt   rv   rw   rx   ry   Nr   Tre   Fr~   r   rq   r   r   r   r   )r(   r   hold_resultr   r   s        r'   $test_04_gate_hold_activation_refusesr     s    )*EFK	: 
 

 %'&3$z1"[0
	

 ::&'4///::,-666jj./52G;;-.%777

 

s   BBc           	     ,   t        t                     }ddddd|d<   d|d<   t        |d<   t        d|      5  t	        | t
        |d	z  |d
z  |dz        }ddd       j                  d      du sJ |j                  d      du sJ y# 1 sw Y   4xY w)u    5. provenance mismatch → HOLD.TFrY   rX   r^   rV   rs   rt   rv   rw   rx   ry   Nr   re   )dictrk   r   r   r   r   r   )r(   r   	prov_failr   s       r'    test_05_provenance_mismatch_holdr   !  s    023I"&#*/:?	$I  ,1I'(,7I()	:
 

 %'&3$z1"[0
	

 ::&'4///::,-666

 

s   B

Bc           	         t        d      }t        d|      5  t        | t        |dz  |dz  |dz        }ddd       j	                  d	      d
u sJ y# 1 sw Y   xY w)u8   6. evidence mismatch → HOLD (gate HOLD from mismatch).z)evidence mismatch mock (fresh_base false)rs   rt   rv   rw   rx   ry   Nr   Tr   )r(   r   ev_failr   s       r'   test_06_evidence_mismatch_holdr   <  st    %&QRG	:
 

 %'&3$z1"[0
	

 ::&'4///

 

s   AAc           	     V   t        t                     }t        |d         |d<   ddg|d   d<   t        t              |d   d<   t        |d<   t
        |d<   t        d|	      5  t        | t        |d
z  |dz  |dz        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u$   7. expected_files mismatch → HOLD.r6   zanu_v2/owner_trigger_pat.pyzsome/unexpected/file.pyrC   rB   rU   r5   rs   rt   rv   rw   rx   ry   Nr   T
r   rk   rj   r   r   r   r   r   r   r   )r(   r   mismatchr   s       r'   $test_07_expected_files_mismatch_holdr   N  s    /12H#'1C(D#EH %!<H !78 6::V5WH !12 -H_$HX	:
 

 %'&3$z1"[0
	

 ::&'4///

 

s   #BB(c           	     0   t        t                     }t        |d         |d<   dddd|d   d<   t        |d<   t        |d<   t	        d	|
      5  t        | t        |dz  |dz  |dz        }ddd       j                  d      du sJ y# 1 sw Y   xY w)uI   8. PR#102 preserved=false → HOLD (gate HOLD because source PR changed).r6   Fr:   r;   r<   r9   rU   r5   rs   rt   rv   rw   rx   ry   Nr   Tr   rk   r   r   r   r   r   r   )r(   r   pr_failr   s       r'   "test_08_pr102_preserved_false_holdr   i  s    .01G"&w/A'B"CG'0G,
  -GO#GH	:
 

 %'&3$z1"[0
	

 ::&'4///

 

s   BBc           	     (   t        t                     }t        |d         |d<   d|d   d<   t        |d<   t        |d<   t	        d|      5  t        | t        |dz  |d	z  |d
z        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u"   9. same_branch_push=true → HOLD.r6   Tr@   rU   r5   rs   rt   rv   rw   rx   ry   Nr   r   )r(   r   sbp_failr   s       r'   "test_09_same_branch_push_true_holdr     s    /12H#'1C(D#EH 7;H !34 -H_$HX	:
 

 %'&3$z1"[0
	

 ::&'4///

 

   BBc           	     Z   t        t                     }t        |d         |d<   t        t              dgz   |d   d<   d|d   d<   t        |d<   t
        |d<   t        d|	      5  t        | t        |d
z  |dz  |dz        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u<   10. F2 changed → HOLD (scope expansion / unexpected diff).r6   zanu_v2/token_transport.pyrC   TrL   rU   r5   rs   rt   rv   rw   rx   ry   Nr   r   )r(   r   f2_failr   s       r'   test_10_f2_changed_holdr     s    .01G"&w/A'B"CG:>$;	$%;&G 67 6:G 12,GO#GH	:
 

 %'&3$z1"[0
	

 ::&'4///

 

s   %B!!B*c           	     \   t        t                     }t        |d         |d<   t        t              dgz   |d   d<   dg|d   d<   t        |d<   t
        |d<   t        d|      5  t        | t        |d	z  |d
z  |dz        }ddd       j                  d      du sJ y# 1 sw Y   xY w)uC   11. phase3/mqe changed → HOLD (forbidden path / scope expansion).r6   zmerge_queue_executor.pyrC   rD   rU   r5   rs   rt   rv   rw   rx   ry   Nr   Tr   )r(   r   mqe_failr   s       r'   test_11_phase3_mqe_changed_holdr     s    /12H#'1C(D#EH ;?$<	"#<$H !78 @Y>YH !:; -H_$HX	:
 

 %'&3$z1"[0
	

 ::&'4///

 

s   &B""B+c           	     (   t        t                     }t        |d         |d<   d|d   d<   t        |d<   t        |d<   t	        d|      5  t        | t        |dz  |d	z  |d
z        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u!   12. merge_required=true → HOLD.r6   TrR   rU   r5   rs   rt   rv   rw   rx   ry   Nr   r   )r(   r   
merge_failr   s       r'    test_12_merge_required_true_holdr     s    134J%)*5G*H%IJ!"7;J!"#34"/J&Jx	:
 

 %'&3$z1"[0
	

 ::&'4///

 

r   c           	     (   t        t                     }t        |d         |d<   d|d   d<   t        |d<   t        |d<   t	        d|      5  t        | t        |dz  |d	z  |d
z        }ddd       j                  d      du sJ y# 1 sw Y   xY w)u4   13. credential/API beyond PR-open required → HOLD.r6   TrG   rU   r5   rs   rt   rv   rw   rx   ry   Nr   r   )r(   r   	cred_failr   s       r'   $test_13_credential_api_required_holdr     s    023I$(3E)F$GI !BFI !">?!.Io%Ih	:
 

 %'&3$z1"[0
	

 ::&'4///

 

r   c           
     $   t               }dddddgd}t        d|      5  t        | t        ||d	z  |d
z  |dz        }ddd       j	                  d      du sJ |j	                  di       }|j	                  d      du sJ y# 1 sw Y   FxY w)u#   14. Codex HIGH unresolved → HOLD.PASS_WITH_RECOMMENDATIONSF1HIGHzmock HIGH findingidseveritydescriptionverdictunresolved_findingsrs   rt   rv   rw   rx   r(   rz   codex_auditr{   r|   r}   Nr   Tcodex_post_result_audit
force_holdr   r(   r   r   r   r   cas         r'   "test_14_codex_high_unresolved_holdr     s    *,H.V<OP 
K 
:
 
 %'#&3$z1"[0
	
 ::&'4///	-r	2B66,4'''
 
   BBc           
        t               }ddddddddd}t        d|      5  t        d|      5  t        | t        |d	z  |d
z  |dz        }ddd       ddd       j	                  d      du sJ y# 1 sw Y   'xY w# 1 sw Y   +xY w)u;   15. callback contract missing (invalid collector) → HOLD.z anu_v3.goal_callback_contract.v1FTcollector_only)r-   rN   rO   	authorityno_writeno_dev_reactivationno_dispatchno_closeoutrs   rt   z:anu_v3.goal_activation_controller._build_callback_contractrv   rw   rx   ry   Nr   r   )r(   r   r   bad_cbr   s        r'   &test_15_callback_contract_missing_holdr     s    *,H 5%#	F 
:
  H
 
	 )+#"*Z"7 (: 5&4F	
		 ::&'4///
	 
		 s"   BA5B5A>	:BB
c                    t         j                  d      } t        j                  |       }d}|D ]  }|| vrJ d|        t        j                  |      D ]  }t        |t        j                        sdj                  d t        j                  |      D              }|j                  dd      }d|vsJ d	|d
d         d|vsJ d       d|vsJ d       d|v sd|vrJ d        y
)a^  16. controller source has zero merge/auto/merge_pull_request/same_branch_push
       as string literals in Call expressions (static grep).

    Finding fix: forbidden_strings are now asserted against the raw source text
    as well as per-Call AST node literals to ensure merge-related strings are
    entirely absent from the controller module.
    r   r   )zgh pr merge--automerge_pull_requestz-controller source contains forbidden string:  c              3     K   | ]R  }t        |t        j                        r6t        |j                  t              r|j                  j                          T y wN)
isinstanceastConstantvaluestrlower).0as     r'   	<genexpr>zBtest_16_only_allowed_pr_open_reaches_write_path.<locals>.<genexpr>P  s=      a.:aggs3K s   AA 	ghprmergeu'   controller: gh pr merge 호출 발견: NP   r   u)   controller: --auto merge 플래그 발견r   u%   controller: merge_pull_request 발견r   r;   u%   controller: source-branch push 발견)	CONTROLLER_SRCr$   r   parsewalkr   Calljoinreplace)srctreeforbidden_strings	forbiddennodelitsjoineds          r'   /test_16_only_allowed_pr_open_reaches_write_pathr  4  s-    
"
"G
"
4C99S>D ' 
	# 	
;I=I	
#
  dCHH%88 $ D
 \\#r*Ff, 9$s)E, 4'T)TT''v5 75 ,D8 ;8!r&   c           	     <   t               }t        d|      5  t        | t        |dz  |dz  |dz  d      }ddd       j	                  d	i       }|j	                  d
      du sJ |j	                  d      du sJ |j	                  dd      du sJ y# 1 sw Y   \xY w)uL   17. merge path unreachable — even all-pass fixture: merge_performed=False.rs   rt   rv   rw   rx   Tr(   rz   r{   r|   r}   _force_dry_runNmerge_possibility_judgmentmerge_permittedFrg   re   r   )r(   r   r   r   mps        r'   test_17_merge_path_unreachabler  d  s    *,H	:
 
 %'&3$z1"[0
	
 
0"	5B66#$---66#$---::,e4===!
 
s   BBc           	     $   t               }t        d|      5  t        | t        |dz  |dz  |dz  d      }ddd       j	                  d	      t
        k(  sJ d
|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ |j	                  d      xs i }t        |t              sJ d|v sJ |j	                  d      xs i }|j	                  d      dk(  sJ y# 1 sw Y   xY w)u   18. WITHOUT chair 1~13 procedure, goal contract ALONE is sufficient.

    Only goal_contract fixture + stubbed all-pass run_activation →
    complete final packet with activation decision (no extra procedural input).
    rs   rt   rv   rw   rx   Tr  Nr-   implementation_filesr(   rU   r~   effective_diff_6_file_matchpr_102_preservation_evidencef2_byte_identical_evidencephase3_mqe_unchanged_evidenceregression_resultr   anu_codex_adjudicationcallback_collector_resultr  r   r   r   ztask-2553+1-f1solo-pr-open)rk   r   r   r   r   r	   r   r   )r(   r   r   r   r   gc_in_results         r'   &test_18_goal_contract_alone_sufficientr  {  s    +,H	:
 
 %' '3$z1"[0	
	
  ::h#5555!V+++f$$$f$$$ F***(F222)V333'6111*f444&((($...#v---&&000'6111v%%% jj./52Ggt$$$!W,,, ::o.4"LI&*FFFFO
 
s   DDc           
        t               }dddddg dd}t        d|	      5  t        | t        ||d
z  |dz  |dz  d      }ddd       j	                  di       }|j	                  d      du s
J d|        |j	                  d      du sJ |j	                  d      dk(  sJ |j	                  d      du sJ y# 1 sw Y   yxY w)uP   Finding 1: codex_audit param provided → codex_post_result_audit.attached=True.z!anu_v3.codex_post_result_audit.v1codexr   r   r   zround-1 findings remediated)r-   auditorroundr   unresolved_high_or_criticalfindingsnoters   rt   task-2553+7.dec.jsontask-2553+7.res.jsontask-2553+7.hold.jsonTr(   rz   r   r{   r|   r}   r  Nr   attachedzExpected attached=True, got: r   Fr   r   r   r   s         r'   0test_finding1_codex_audit_attached_when_providedr     s    *,H 6.'(-K 
:
 
 %'#&)??$'=="%<<
	
 
-r	2B66*%K)Frd'KK%66,5(((66) ;;;;::&'5000#
 
s    B::Cc           
     $   t               }dddddgd}t        d|      5  t        | t        ||d	z  |d
z  |dz        }ddd       j	                  di       }|j	                  d      du sJ |j	                  d      du sJ y# 1 sw Y   FxY w)uT   Finding 1: codex_audit with unresolved HIGH → force HOLD even on all-pass dry-run.NEEDS_REFINEMENTzF-HIGHr   zunresolved HIGHr   r   rs   rt   r  r  r  r   Nr   r  Tr   r   r   s         r'   :test_finding1_codex_high_unresolved_forces_hold_on_dry_runr#    s    *,H%@QR 
K 
:
 
 %'#&)??$'=="%<<
	
 
-r	2B66*%%%66,4'''
 
r   c           
        t               }t        d|      5  t        | t        d|dz  |dz  |dz  d      }d	d	d	       j	                  d
      dk(  sJ d|j	                  d
             |j	                  d      dk(  sJ d|j	                  d             |j	                  d      dk(  sJ |j	                  d      xs i }|j	                  d
      dk(  sJ |j	                  d      dk(  sJ y	# 1 sw Y   xY w)zLFinding 2: result packet has separate controller_task_id and target_task_id.rs   rt   task-2553+7r  r  r  T)r(   rz   controller_task_idr{   r|   r}   r  Nr&  zcontroller_task_id mismatch: r   r4   ztarget_task_id mismatch: r3   r~   r   )r(   r   r   r   decs        r'   5test_finding2_controller_task_id_target_task_id_splitr(    s)   *,H	:
 
 %',&)??$'=="%<<
	
 ::*+}< 
'

3G(H'KL< ::&'=8 
#FJJ/?$@#CD8 ::i M111
***
+
1rC77'(M99977#$5551
 
s    C22C;c                    ddl m} | dz  }t        j                  t        d      5   ||d       ddd       y# 1 sw Y   yxY w)z<Finding 2: mismatched output path task id raises ValueError.r   _validate_output_path_task_idztask-9999.result.jsonr   matchr%  N!anu_v3.goal_activation_controllerr+  pytestraises
ValueError)r   r+  bad_paths      r'   )test_finding2_output_path_mismatch_raisesr4    s=    O11H	z	4 ?%h>? ? ?s	   
:Ac                ,    ddl m} | dz  } ||d       y)zCFinding 2: matching output path task id passes validation silently.r   r*  ztask-2553+7.result.jsonr%  N)r/  r+  )r   r+  	good_paths      r'   "test_finding2_output_path_match_okr7    s    O44I!)];r&   c           
        t               }dddddd}t        d|      5  t        | t        ||dz  |d	z  |d
z  d      }ddd       j	                  di       }|j	                  d      dk(  sJ |j	                  d      dk(  sJ |j	                  d      dk(  sJ d|vs|j	                  d      dvsJ yy# 1 sw Y   xxY w)zFFinding 3: --regression-result path populates field 11 with real data.   r   z2026-05-16T10:00:00ZzGpytest tests/regression/test_goal_activation_controller_2553plus7.py -q)passedfailedtotalr1   commandrs   rt   r  r  r  T)r(   rz   r  r{   r|   r}   r  Nr  r:  r;  r<  r5   )RUN_SEPARATELYNOT_PROVIDEDr   )r(   r   r   real_regr   rrs         r'   (test_finding3_regression_result_providedrB    s    *,H(\H 
:
 
 %'&&)??$'=="%<<
	
 
'	,B66(r!!!66(q   66'?b   2!19[![[[![#
 
s    B77C c           	        t               }t        d|      5  t        | t        |dz  |dz  |dz  d      }ddd       j	                  d	i       }|j	                  d
      dk(  sJ d|j	                  d
             y# 1 sw Y   IxY w)uX   Finding 3: no --regression-result → field 11 status=NOT_PROVIDED (not RUN_SEPARATELY).rs   rt   r  r  r  Tr  Nr  r5   r?  zExpected NOT_PROVIDED, got: r   )r(   r   r   r   rA  s        r'   6test_finding3_regression_result_absent_is_not_providedrD  6  s    *,H	:
 
 %'&)??$'=="%<<
	
 
'	,B66(~- 
&rvvh'7&:;-
 
s   A??Bc           	        t               }t        d|      5  t        | t        |dz  |dz  |dz  d      }ddd       j	                  d	      }|t        |t              rt        |      d
kD  s
J d|       |j	                  d      xs i }|j	                  d	      |k(  sJ d|j	                  d	      d|d       y# 1 sw Y   xY w)zFFinding 4: run_id (uuid4) is stamped into result and decision packets.rs   rt   r  r  r  Tr  Nrun_idr   z*run_id missing or empty in result packet: r~   zrun_id in decision (z) != result ())rk   r   r   r   r   r   r   len)r(   r   r   r   rF  r'  s         r'   +test_finding4_run_id_present_in_all_packetsrI  M  s    *,H	:
 
 %'&)??$'=="%<<
	
 ZZ!F*VS"9c&kAo 
4VJ?M ***
+
1rC778& 
swwx03=
!L&'
 
s   CC
c           
     Z   t               }|dz  }|j                  dd       |j                         sJ d       t        d|      5  t	        | t
        |dz  |d	z  t        |      d
      }ddd       j                  d      du sJ |j                         rJ d       y# 1 sw Y   6xY w)zYFinding 4: pre-existing hold.json is deleted when outcome is non-HOLD (DRY_RUN_VERIFIED).r  z?{"schema": "stale", "status": "HOLD_FOR_CHAIR", "stale": true}
r   r   z+pre-condition: stale hold.json should existrs   rt   r  r  Tr  Nr   Fz<stale hold.json should have been deleted on non-HOLD outcome)rk   
write_textexistsr   r   r   r   r   )r(   r   r   	hold_pathr   s        r'   ,test_finding4_stale_hold_deleted_on_non_holdrN  h  s    *,H22IJ   LLL	:
 
 %'&)??$'==i.
	
 ::&'5000! F!!
 
s   %B!!B*c           
        t               }dddddgd}t        d|      5  t        | t        ||d	z  |d
z  |dz  d      }ddd       j	                  d      du sJ d|j	                  d             |j	                  d      t
        k(  sJ d|j	                  d             |j	                  di       }|j	                  d      du s
J d|        y# 1 sw Y   xY w)u   HIGH-1 fix: --dry-run + all-six-pass + codex_audit with unresolved HIGH
    → result status HOLD_FOR_CHAIR, hold_for_chair=True (bypass was present before fix).
    r"  z
HIGH-1-BUGr   zgenuine fail-closed bypassr   r   rs   rt   r  r  r  Tr  Nr   z6Expected hold_for_chair=True (HIGH-1 fix), got status=r5   zExpected HOLD status, got: r   r   z(codex_result.force_hold should be True: )rk   r   r   r   r   r   )r(   r   r   codex_audit_highr   r   s         r'   9test_r2_high1_dry_run_all_six_pass_codex_high_forces_holdrQ    s#    +,H &VD`a 
 
:
 
 %'(&)??$'=="%<<
	
 ::&'4/ 
@HAU@XY/ ::h;. 
%fjj&:%=>. 
-r	2B66,4'X+STVSW)XX'+
 
s    CC c           
     d   t               }ddg d}t        d|      5  t        | t        ||dz  |dz  |dz  d	
      }ddd       j	                  d      d	u sJ d       |j	                  di       }|j	                  d      d	u s
J d|        |j	                  d      dk(  sJ y# 1 sw Y   ixY w)u   HIGH-2 fix: codex_audit with verdict=PASS_WITH_RECOMMENDATIONS but
    unresolved_high_or_critical=2 scalar field → force_hold / HOLD_FOR_CHAIR.
    Previously auto-continued incorrectly.
    r   r   r   r  r   rs   rt   r  r  r  Tr  Nr   zIPASS_WITH_RECOMMENDATIONS + unresolved_high_or_critical=2 must force HOLDr   r   z'force_hold should be True from scalar: r  r   )r(   r   r   codex_audit_scalarr   r   s         r'   <test_r2_high2_scalar_unresolved_high_or_critical_forces_holdrU    s    
 +,H /'(!
 
:
 
 %'*&)??$'=="%<<
	
 ::&'4/ S/ 
-r	2B66,4'W+RSURV)WW'66/0A555%
 
s    B&&B/c           
     T   t               }ddg d}t        d|      5  t        | t        ||dz  |dz  |dz  d	
      }ddd       j	                  d      d	u sJ d       |j	                  di       }|j	                  d      d	u sJ |j	                  d      dk(  sJ y# 1 sw Y   axY w)u   HIGH-2 fix: codex_audit verdict=NEEDS_REFINEMENT → force_hold=True / HOLD.
    NEEDS_REFINEMENT is not a pass verdict and must never auto-continue.
    r"  r   rS  rs   rt   r  r  r  Tr  Nr   z2verdict=NEEDS_REFINEMENT must force HOLD_FOR_CHAIRr   r   r   r   )r(   r   r   codex_needs_refinementr   r   s         r'   2test_r2_high2_needs_refinement_verdict_forces_holdrX    s     +,H%'(!
 
:
 
 %'.&)??$'=="%<<
	
 ::&'4/ </ 
-r	2B66,4'''66) 2222%
 
s    BB'c           
     8   t               }ddg d}t        d|      5  t        | t        ||dz  |dz  |dz  d	
      }ddd       j	                  di       }|j	                  d      du s
J d|        |j	                  d      du sJ d       y# 1 sw Y   SxY w)u   HIGH-2 fix: codex_audit verdict=PASS_WITH_RECOMMENDATIONS,
    unresolved_high_or_critical=0, no HIGH/CRITICAL findings → NOT force_hold.
    Auto-continue is allowed in this exact configuration.
    r   r   rS  rs   rt   r  r  r  Tr  Nr   r   Fz@PASS_WITH_RECOMMENDATIONS + 0 unresolved should NOT force_hold: r   z8PASS_WITH_RECOMMENDATIONS + 0 unresolved should not HOLDr   )r(   r   r   
codex_passr   r   s         r'   ?test_r2_high2_pass_with_recommendations_zero_unresolved_no_holdr[    s    
 +,H.'(!J
 
:
 
 %'"&)??$'=="%<<
	
 
-r	2B66,5( 
J2$O( ::&'50 B0#
 
s    BBc                    ddl m} | dz  }t        j                  t        d      5   ||d       ddd       | dz  } ||d       y# 1 sw Y   xY w)	zMEDIUM-4 fix: hold_path default derived from controller_task_id.
    A mismatched controller_task_id in hold_out_path should raise ValueError.
    r   r*  ztask-WRONG.hold.jsonr   r,  r%  Nr  r.  )r   r+  bad_hold	good_holds       r'   9test_r2_medium4_hold_path_derived_from_controller_task_idr_    sY     P 00H	z	4 ?%h>? 22I!)];? ?s   
AAc                   t               }|dz  }t        d|      5  t        j                  t        d      5  t        | t        |dz  |dz  t        |      dd	
       ddd       ddd       y# 1 sw Y   xY w# 1 sw Y   yxY w)zMEDIUM-4 fix: running with controller_task_id != hold_path task_id raises ValueError
    during run_goal_activation (early validation).
    ztask-9999.hold.jsonrs   rt   r   r,  r  r  r%  T)r(   rz   r{   r|   r}   r&  r  N)rk   r   r0  r1  r2  r   r   r   )r(   r   r   r]  s       r'   :test_r2_medium4_hold_path_wrong_controller_id_raises_earlyra    s     +,H//H	:
  ]]:Z8 		+#"*-C"C (+A A!(m#0#			 		 			 s"   A<&A0A<0A9	5A<<Bc           
     d   ddd}| dz  }| dz  }| dz  }t        j                  t        d      5  t        |t        ||t        |      d	
       ddd       |j                         r
J d|        |j                         r
J d|        |j                         r
J d|        y# 1 sw Y   XxY w)u  LOW-1 regression: invalid goal contract (schema mismatch) AND a mismatched
    hold_out_path (task id != controller_task_id) → controller raises ValueError
    BEFORE writing ANY packet (decision/result/hold must not be created at wrong path).

    This exercises the parse-failure + bad-path combination introduced by the MEDIUM fix:
    path validation now runs BEFORE parse_goal_contract, so a mis-routed hold_out_path
    must be rejected even when the contract itself is invalid.
    zwrong.schema.that.will.failbar)r-   fooztask-WRONG-ID.hold.jsonr  r  r   r,  r%  )r(   rz   r{   r|   r}   r&  Nz:Bad hold path must NOT be written before path validation: z>Decision path must NOT be written when path validation fails: z<Result path must NOT be written when path validation fails: )r0  r1  r2  r   r   r   rL  )r   invalid_contractr]  bad_decbad_ress        r'   :test_low1_parse_failure_bad_hold_path_rejects_before_writerh  4  s     #@N33H//G//G	z	4 
*%#h-,	

   
DXJO  ~~ 
H	R ~~ 
FwiP%
 
s   B&&B/c                H    t        |       }|d   t        k(  sJ d|d   v sJ y )Nr-   r8   r   )r   r   )r(   r   s     r'   test_parse_goal_contract_validrj  Z  s3    	]	+Bh<////&"->*????r&   c                     t        j                  t              5 } t        ddi       d d d        d j                  j
                  v sJ y # 1 sw Y   $xY w)Nr-   zwrong.schema)r0  r1  r
   r   r   code)exc_infos    r'   (test_parse_goal_contract_schema_mismatchrn  `  sK    	(	) 8XX~678x~~*****8 8s   AAc                     t        t                     dgd} t        | t        t                     t        t              t        t                    }|g k(  sJ y )Nmerge)r   r   )planned_effective_actionsrC   forbidden_write_targets)rj   _PLANNED_VALID_ACTIONSr   r   r   )r   
violationss     r'   test_validate_boundary_passru  f  sW     6 89%Y
B #
"&'='?"@!">? $%H I	J r&   c                      y)N)
r   r   r   r8   result_marker_writereport_writenormal_callback_registerfallback_callback_registerr   r  r%   r%   r&   r'   rs  rs  t  s    r&   c                    t        |       }d|v sJ d|v sJ d|v sJ t        |d   t              sJ |d   t        |d         k(  sJ t        |d   t              sJ y)uV   parse_success_condition: 공개 API — raw / parts / part_count 구조 반환 검증.rawparts
part_countN)r   r   rj   rH  r   )r(   parseds     r'   "test_parse_success_condition_partsr    sx    $]3FF??f6!!!fWot,,,,3vg#7777fUmS)))r&   c                 V    t        i       } | d   dk(  sJ | d   g k(  sJ | d   dk(  sJ y)uJ   parse_success_condition: success_condition 없는 경우 빈 parts 반환.r|  r   r}  r~  r   N)r   )r  s    r'   "test_parse_success_condition_emptyr    sC    $R(F%=B'?b   ,1$$$r&   c                f    t        |       }t        |t              sJ t        d |D              sJ y)uX   parse_hold_conditions: 공개 API — hold_conditions 리스트 정규화 반환 검증.c              3  <   K   | ]  }t        |t                y wr   )r   r   )r   cs     r'   r   z2test_parse_hold_conditions_list.<locals>.<genexpr>  s     1az!S!1s   N)r   r   rj   all)r(   condss     r'   test_parse_hold_conditions_listr    s0    !-0EeT"""151111r&   c                 l    t        i       g k(  sJ t        ddi      g k(  sJ t        ddi      g k(  sJ y)uN   parse_hold_conditions: hold_conditions 가 list 아닌 경우 빈 list 반환.r   z
not-a-listN)r   r%   r&   r'   #test_parse_hold_conditions_non_listr    sG     $*** "3\!BCrIII "3T!:;rAAAr&   )returndict[str, Any])rp   r   r  r  )[__doc__
__future__r   r   r!   syspathlibr   typingr   unittest.mockr   r0  __file__resolveparentsr   r   pathinsertanu_v3.goal_execution_contractr   r	   r
   r   anu_v3.goal_boundary_validatorr   r   r   r/  r   'anu_v3.pre_authorized_activation_runnerr   r   !anu_v3.pre_authorized_action_gater   r   -anu_v3.pre_authorized_evidence_bundle_builderr   r   r#   r*   r   CLI_SRCfixturer(   r+   rk   rq   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r#  r(  r4  r7  rB  rD  rI  rN  rQ  rU  rX  r[  r_  ra  rh  rj  rn  ru  rs  r  r  r  r  r%   r&   r'   <module>r     s(   # 
  
    N""$,,Q/	y>!HHOOAs9~&  
  ..   ;;  X%(GG
i
"E
E hI  I hM  M
6r6#UN8<0F8*760$06040,04020,0,(60B,`>..Gd1B(66<?<\<.6>!YH6B3>B< 0"L@+	*%2Br&   