
    4jd                    J   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mZm	Z	 ddl
mZ ddlmZ  ee      j                         j                   j                   j                   Zedz  dz  Zd	Zed
z  dz  dz  Z ed      Z ed      ZdZdZddlmZmZ ddlmZ ddlmZ ddlm Z! ddlm"Z" ddl#m$Z$m%Z%  G d d      Z& G d d      Z' G d d      Z(d.dZ)d/dZ*d0dZ+d1d Z,d2d!Z-d"d#ddd$	 	 	 	 	 	 	 	 	 d3d%Z.e G d& d'             Z/d(Z0d)Z1d4d*Z2	 	 	 	 	 	 	 	 	 	 d5d+Z3d6d,Z4g d-Z5y)7u9  tests/fixtures/cancel_on_success_observation_harness.py

task-2553+28 — TRACK D: CALLBACK CANCEL-ON-SUCCESS LIVE OBSERVATION FIXTURE.

목적 (task-2553+28 §2 / §3.1, 회장 verbatim 의도):
  `task-2553-cancel-on-success-live-verification-contract_260517.json` 의
  **6-step 라이브 관측**을 mock/fixture harness 코드로 사전 구현한다. 향후
  실 post-+25 task callback 사이클이 도래하면 본 harness 로 6-step 을
  결정적(passive)으로 검증할 수 있다. md 박제만 금지 — 코드 자동화 산출.

mock-only / 실 운영 무접촉 (§5 / §7 / 9-R.1 / 9-R.3):
  * 실 cron-list·실 cron-remove·실 schedule_history·실 callback 4-tuple
    **무접촉**. contract 는 read-only 참조만.
  * 본 harness 는 frozen anchor 와 +25 결선 모듈을 **read-only import** 만
    한다 (1 byte 수정 0). 6-step 은 frozen `run_completion_callback_collector`
    인접 결선 wrapper `run_operational_completion_callback_collector`
    (task-2553+25, frozen byte-0 밖 공개 API) 에 **주입 Fake/Spy + 격리 FS**
    만 물려 결정적으로 관측한다. 실 RealCokacdir* subprocess 경로는 본
    harness 에서 절대 실행되지 않는다 (allowlist 차단 + subprocess boom).
  * 9-R.3 격리 강제: ① subprocess 전면 차단 ② cron API entrypoint
    (RealCokacdirCronLister/Remover) 차단 ③ callback 등록 entrypoint
    (cokacdir 바이너리) 차단 ④ live schedule_history / live workspace
    경로 접근 allowlist 차단. harness 는 격리 sandbox FS·Fake/Spy 만.
  * 9-R.4 exact-once = **명시 claim artifact/key** (event_id 기반 격리
    marker, ``O_CREAT|O_EXCL`` create-if-absent 의미를 실 FS 로 mirror)
    + first-winner=1 invoke·second=즉시 no-op. 약한 in-memory mutex 대체 0.

본 모듈 자체는 실 cron/callback 을 1회도 등록·제거·발화하지 않는다. 실 task
적용 시에도 본 harness 는 passive observer — cron-list 1회(주입 fake)·
schedule_history read-only(주입 spy) 만 수행하며 write/cron-remove/dispatch 0
(contract.anu_observation_method 와 동형).
    )annotationsN)	dataclassfield)Path)Optionalutilsz%anu_delegation_completion_callback.py@83b3e307c8207c76a3e311c408aab4951373bd317896e51687d3007907b0c3d4memoryeventszBtask-2553-cancel-on-success-live-verification-contract_260517.jsonz$/home/jay/.cokacdir/schedule_historyz/usr/local/bin/cokacdirl   L5: ztask-2553+28)CallbackInputCallbackType)ANU_CHAT_ID)ANU_KEY)FALLBACK_ROLE)RemoverResult)OperationalSeamParams-run_operational_completion_callback_collectorc                  .    e Zd ZdZddddZddZd	dZy)
FakeCronListeruG  주입 fake cron-list. 실 ``cokacdir --cron-list`` 무호출.

    ``entries`` = verifier 가 보는 정규화 목록. ``removed_after`` True 면
    한 번 lister 가 호출된 *뒤* 두 번째 조회부터 대상 id 부재로 응답하여
    step4 (remove 후 cron-list 교차확인) 를 결정적으로 재현한다.
    okN)statusdrop_id_after_firstc               t    |D cg c]  }t        |       c}| _        || _        || _        d| _        y c c}w Nr   )dictentriesr   r   calls)selfr   r   r   es        K/home/jay/workspace/tests/fixtures/cancel_on_success_observation_harness.py__init__zFakeCronLister.__init__b   s3    )01AQ1#6 
 2s   5c                h   | xj                   dz  c_         | j                  dk7  r| j                  g ddidS | j                  D cg c]  }t        |       }}| j                  =| j                   dkD  r.|D cg c]#  }|j                  d      | j                  k7  s"|% }}d|ddidS c c}w c c}w )N   r   fakeT)r   r   rawid)r   r   r   r   r   get)r   r   curs      r    __call__zFakeCronLister.__call__h   s    

a
;;$"kkb&$PP $-1tAw--##/DJJN!MQUU4[D4L4L%L1MCM3~FF .Ms   B*8#B/B/c                Z     |        }t        fd|j                  dg       D              S )Nc              3  F   K   | ]  }|j                  d       k(    yw)r&   Nr'   ).0r   cron_ids     r    	<genexpr>z,FakeCronLister.id_present.<locals>.<genexpr>s   s     Ka155;')Ks   !r   )anyr'   )r   r.   snaps    ` r    
id_presentzFakeCronLister.id_presentq   s&    vK488Ir3JKKK    returnr   )r.   strr5   bool)__name__
__module____qualname____doc__r!   r)   r2    r3   r    r   r   Z   s     +/D GLr3   r   c                  (    e Zd ZdZdddZddd	dZy)

SpyRemoveru@   주입 spy cron-remover. 실 cron 제거 0 — 호출만 기록.c                     || _         g | _        y N)r   r   )r   r   s     r    r!   zSpyRemover.__init__y   s    
r3   T)dry_runc                   | j                   j                  ||d       t        | j                  d| j                         S )N)r.   rA   zfake:)r   detail)r   appendr   r   )r   r.   rA   s      r    r)   zSpyRemover.__call__}   s5    

g'BCDKK%}8MNNr3   N)removed)r   r6   )r.   r6   rA   r7   r5   r   )r8   r9   r:   r;   r!   r)   r<   r3   r    r>   r>   v   s    J 9= Or3   r>   c                  6    e Zd ZdZddddZd	dZd
dZddZy)SpyScheduleHistoryu  주입 spy schedule_history reader. 실 schedule_history 무접촉.

    실 운영에서 step5 는 ``<fallback_id>.log`` 무발화 확인이다. 본 spy 는
    **격리 sandbox 내부** log 파일만 read-only 조회하며 (실
    ``/home/jay/.cokacdir/schedule_history`` 경로는 절대 열지 않음), 기본
    적으로 발화 0(빈 로그/파일부재) 을 반환한다.
    N)	fired_idsc               Z    t        |      | _        t        |xs g       | _        g | _        y r@   )r   sandboxsetrH   reads)r   rJ   rH   s      r    r!   zSpyScheduleHistory.__init__   s$    G}Y_"-
r3   c                    | j                   dz  | dz  j                         }t        |      j                  t        | j                   j                                     st	        d      |S )Nschedule_historyz.logu6   schedule_history 경로가 sandbox 밖 (격리 위반))rJ   resolver6   
startswithAssertionError)r   fallback_idps      r    	_log_pathzSpyScheduleHistory._log_path   s[    \\..K=1EENNP1v  T\\%9%9%;!<= !YZZr3   c                    | j                   j                  |       || j                  v ry| j                  |      }|j	                         syt        d |j                  d      j                         D              S )Nr#   r   c              3  B   K   | ]  }|j                         sd   yw)r#   N)strip)r-   lns     r    r/   z1SpyScheduleHistory.fired_count.<locals>.<genexpr>   s     Ybhhj1Ys   utf-8encoding)rL   rD   rH   rT   existssum	read_text
splitlines)r   rR   lps      r    fired_countzSpyScheduleHistory.fired_count   sb    

+&$..(^^K(yy{Yr||W|=HHJYYYr3   c                *    | j                  |      dk(  S r   )ra   )r   rR   s     r    	no_firingzSpyScheduleHistory.no_firing   s    ,11r3   )rJ   r   )rR   r6   r5   r   )rR   r6   r5   int)rR   r6   r5   r7   )r8   r9   r:   r;   r!   rT   ra   rc   r<   r3   r    rG   rG      s!     48 
Z2r3   rG   c                >  	
 t              j                         d }| j                  t        d|       | j                  t        d|       | j                  t        d|       | j                  t        d|       | j                  t        d|       | j                  t        d|       dd	lm} dd	lm} | j                  |j                  d
|d       | j                  |j                  d
|d       t        j                  
t        j                  t        t        dz        t        t        dz        t        t              t        t               f	d	fdddd
fd	}fd}| j                  t        d|       | j                  t        d|       y	)uR  harness 실행 전 모든 라이브 경로를 monkeypatch/allowlist 로 차단한다.

    ① subprocess.run/Popen/call/check_output + os.system → 즉시 AssertionError
    ② cron API entrypoint: live_cron_state_verifier.RealCokacdirCronLister /
       completion_callback_fallback_cancel.RealCokacdirCronRemover __call__
       → 즉시 AssertionError (주입 Fake/Spy 만 허용)
    ③ callback 등록 entrypoint: ``/usr/local/bin/cokacdir`` 접근 차단
    ④ live schedule_history / live workspace mutation:
       open()/os.open 을 sandbox+read-only-allowlist 로 제한.
    c                     t        d      )Nug   실 subprocess/cron/callback 호출 금지 (§5 / 9-R.3) — 주입 Fake/Spy·격리 sandbox 만 허용rQ   )aks     r    _boomz'install_isolation_guards.<locals>._boom   s    9
 	
r3   runPopencallcheck_output
check_callsystemr   Nr)   F)raisingr   testsc                   	 t        t        t        j                  |             j	                               }|j                  t                    ry|j                  t        t        j	                                     ry|j                  t        t                    ry|j                        ryy# t
        t        f$ r Y yw xY w)Ndenyrwro)
r6   r   osfspathrO   	TypeError
ValueErrorrP   LIVE_SCHEDULE_HISTORYLIVE_COKACDIR_BIN)pathapallow_read_prefixesrJ   s     r    	_classifyz+install_isolation_guards.<locals>._classify   s    	T"))D/*2245B ==W&==2::<=>==./0==,- :& 		s   5B2 2CCc                ,     t         fddD              S )Nc              3  &   K   | ]  }|v  
 y wr@   r<   )r-   cmodes     r    r/   z>install_isolation_guards.<locals>._is_write.<locals>.<genexpr>   s     ;19;   )wrh   x+)r0   )r   s   `r    	_is_writez+install_isolation_guards.<locals>._is_write   s    ;&:;;;r3   c                     |       }|dk(  r | |g|i |S |dk(  r |      s | |g|i |S t        d| d|d      )Nru   rv   u    live-path 차단 (§5 / 9-R.3): z mode=u?    — harness 는 격리 sandbox·read-only allowlist 만 접근rg   )filer   rh   ri   clsr   r   	real_opens        r    _guarded_openz/install_isolation_guards.<locals>._guarded_open   su    o$;T41!1q11$;yT41!1q11.thfTH EI I
 	
r3   c                "    |       }t        |t        j                  t        j                  z  t        j                  z  t        j
                  z  z        }|dk(  r | |g|i |S |dk(  r|s | |g|i |S t        d| d| d      )Nru   rv   u(   live-path 차단 (§5 / 9-R.3): os.open z flags=u0    — harness 는 격리 sandbox 만 write 가능)r7   rw   O_WRONLYO_RDWRO_CREATO_APPENDrQ   )r}   flagsrh   ri   r   wrr   real_os_opens         r    _guarded_os_openz2install_isolation_guards.<locals>._guarded_os_open   s    o%2;;2RZZ?"++MNO$;e5a5155$;re5a51556thgeW M: :
 	
r3   open)r5   r6   )r   r6   r5   r7   )r)r   rO   setattr
subprocessrw   )utils.completion_callback_fallback_cancel#completion_callback_fallback_cancelutils.live_cron_state_verifierlive_cron_state_verifierRealCokacdirCronListerRealCokacdirCronRemoverbuiltinsr   r6   	WORKSPACECONTRACT_PATHFROZEN_ANCHOR)monkeypatchrJ   rj   _fc_lvr   r   r   r   r   r   r   s    `     @@@@@r    install_isolation_guardsr      sR    7m##%G
 
E51
GU3
FE2
NE:
L%8He, <022JuU##Z   I77LI I MM	<	


 &-8F$45r3   c                    t          d|  dS )uG   exact-once 명시 claim key (event_id 기반 격리 marker 명, 9-R.4).z.seam-claim.z.json)TASK_MARKER_PREFIX)event_ids    r    	claim_keyr     s     !hZu==r3   c                   t        |       } | j                  dd       | t        |      z  }	 t        j                  t        |      t        j                  t        j                  z  t        j                  z  d      }t        j                  |dd      5 }t        j                  d|d	d
|d       ddd       y# t        $ r Y yw xY w# 1 sw Y   yxY w)u|  ``O_CREAT|O_EXCL|O_WRONLY`` 원자 선점 (실 FS, in-memory mutex 아님).

    +25 ``utils.operational_collector_wiring._atomic_claim`` 의 의미를
    충실 mirror: True = first-winner(seam 1회 진입 허용), False = 이미 선점
    (retries/duplicate/concurrent) → 즉시 no-op. claim artifact 는 격리
    sandbox 내부 ``task-2553+28.*`` 네임스페이스.
    Tparentsexist_oki  Fr   rY   rZ   ztask-2553+28.seam-claim_v1zO_CREAT|O_EXCL)schemar   atomic_create_method)ensure_asciiN)r   mkdirr   rw   r   r6   r   O_EXCLr   FileExistsErrorfdopenjsondump)	claim_dirr   cpfdfs        r    attempt_exact_once_claimr     s     YIOOD4O0	Yx(	(BWWSWbjj2994r{{BEJ 
2sW	- 	
		6$(8
 	
	
   	
 s   AB< C<	CCCc                     g d
 fd} |d      } |d      }||t              rd   nd|du |du xr |du xr t              dk(  d	S )uM  동시 2호출 emulation — first-winner=1 invoke, second=즉시 no-op.

    각 호출이 동일 event_id 로 ``attempt_exact_once_claim`` 을 시도한다.
    O_EXCL 의미상 정확히 1개만 winner. invoke 카운터로 seam 1회·no-op 1회
    를 단언 가능하게 반환한다 (9-R.4 — 약한 mutex 대체 아님).
    c                F    t              }|rj                  |        |S r@   )r   rD   )tagclaimedr   r   invokess     r    _onez(simulate_concurrent_double.<locals>._one0  s#    *9h?NN3r3   firstsecondr   NFTr#   )first_claimedsecond_claimedseam_invoke_countwinnersecond_is_noopexact_once_ok)r   r6   )len)r   r   r   r   r   r   s   ``   @r    simulate_concurrent_doubler   '  sk     G ME(^F  \ ''!*T!U?4-QFeOQGPQ@Q r3   c                R    t        | ddt        j                  dddddddd	id

      S )u   frozen collector 가 PASS(durable-success) 를 산출하는 최소 입력.

    +25 regression `_pass_input` 과 동형 (frozen 공개 API). 실 4-tuple 무사용
    — 합성 격리 입력만.
    zdev-sim-2553p28DISP2553P28NORM2553P28r   	completedT)result_jsonreportfrozen_anchormatch)
task_idexecutordispatch_cron_idcallback_typecallback_cron_idcron_statustask_statusrequired_closeout_markerspreservation_anchors
dev_sunset)r   r   NORMAL)r   s    r    pass_callback_inputr   E  s?     "&"))&26$"G-w7 r3   ztask-2553+28-obsFBOBS2553P28)r   target_cron_idverifier_entriesr   c               >   t        |       } | j                  dd       | | dz  }|j                  t        j                  d||t
        t        t        dd      d       | | d	z  }|j                  t        j                  |d
d      d       | | dz  }|j                  d| dd       | | dz  }|j                  t        j                  ddd      d       |||t        ddddg}||| ||||| | dz  | | dz  | | dz  | | dz  | dz  ||dS )u   격리 sandbox 에 6-step 관측용 fixture 파일을 만든다.

    실 schedule_history / 실 4-tuple / 실 cron 무사용 — 전부 sandbox 내부.
    Tr   z.dispatch-fired.jsondispatch_fired_v1)fallback_callback_cron_idchat_idanu_keyfallback_role)r   r   callback_policy_arY   rZ   .result.jsonr   )r   r   z
.report.mdz# z report
completed
z.collector-result.jsonPASS)classificationcloseout_candidatefallbackF)r&   r   r   rolefiredrE   z.fallback-cancelled.jsonz.cancel.lockz.plus23-cancel-audit.jsonz.plus28-cancel-audit.jsonclaims)r   r   rJ   dispatch_fired_marker_pathresult_json_pathreport_pathcollector_result_marker_pathfallback_cancelled_marker_pathcancel_lock_path
audit_pathcancel_audit_pathr   r   r   )	r   r   
write_textr   dumps_FC_ANU_CHAT_ID_FC_ANU_KEY_FC_FALLBACK_ROLEr   )	rJ   r   r   r   r   dfmrjrepcrms	            r    materialize_scenarior  Y  s    7mGMM$M.
wi34
4CNN

-"
 2@.*%6	&	
 !  $ 
gYl+	+BMM

w+>?'   wiz*
*CNNRy 45NH
wi56
6CNN

fDIJ  
  %"&" 	
 (&)(+*1I-
.+/#	&>>7)+D EE$'2K'LLx',2 r3   c                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   d
ed<    ee      Zded<   dZded<    ee	      Z
ded<   ddZy)SixStepObservationu;   contract 6-step 관측 결과 (전부 mock/fixture 유래).r7   step1_normal_collector_successstep2_seam_invoked_oncestep3_verifier_five_and_passstep4_fallback_cron_remove_okstep5_no_fallback_firingstep6_cancel_audit_schema_okall_passrd   r   remove_call_count)default_factoryr   cancel_auditT	mock_onlylistnotesc                    | j                   | j                  | j                  | j                  | j                  | j
                  dS )N)step1step2step3step4step5step6)r
  r  r  r  r  r  )r   s    r    step_mapzSixStepObservation.step_map  sB    881166772266
 	
r3   Nr4   )r8   r9   r:   r;   __annotations__r   r   r  r  r  r  r  r<   r3   r    r	  r	    sg    E$((!!"&&#''"""&&Nt4L$4It-E4-
r3   r	  )r   r   five_condition_resultsremove_attemptedremove_resultskip_reasonalready_removed_or_missingnormal_success_unchanged)c1_task_id_matchc2_chat_id_ownedc3_role_fallbackc4_marker_id_crosscheck c5_pending_not_fired_not_removedc                     t         fdt        D              sy j                  d      xs i t        fdt        D              sy j                  d      du S )Nc              3  &   K   | ]  }|v  
 y wr@   r<   )r-   ri   audits     r    r/   z#_audit_schema_ok.<locals>.<genexpr>  s     8aqEz8r   Fr   c              3  &   K   | ]  }|v  
 y wr@   r<   r-   ri   fives     r    r/   z#_audit_schema_ok.<locals>.<genexpr>  s     -QqDy-r   r%  T)all_AUDIT_REQUIRED_KEYSr'   
_FIVE_KEYS)r-  r0  s   `@r    _audit_schema_okr4    sO    8#78899-.4"D-*--99/0D88r3   c                  t        | d   | d   | d   | d   | d   | d   ||| d   | d   	
      }t        t        | d
         | d   | d
    dz  || d   | d         }|j                  xs i }|j	                  d      xs i t        |j                        }t        |j                        }| d   }	t        |	      j                         r*t        d t        |	      j                  d      D              ng }
|xr t        |
      dk(  }t              xr t        fdt        D              }t        |j                         }t        |      dk(  xr0 |d   d   | d   k(  xr  |d   d   du xr |j	                  d      dk(  }|j#                  | d          }|xr |}|j%                  | d         }t'        |      }t        ||||||f      }t)        |||||||t        |
      t        |      |dddd|
 dg       S )!u2  contract 6-step 을 결정적으로 passive 관측한다 (mock-only).

    frozen collector 인접 +25 wrapper 에 주입 Fake/Spy + 격리 FS 만 물려
    1회 실행한 뒤, 그 공개 결과를 contract 6-step 으로 매핑한다. 실
    cron-list/cron-remove/schedule_history/4-tuple 무접촉.
    r   r   r   r   r   r   r   r   )
r   r   r   r   r   r   cron_listerremoverr   r   r   rJ   z.ackr   r   )seam_paramsr   r   r   c              3  4   K   | ]  }|j                     y wr@   )name)r-   rS   s     r    r/   z#observe_six_step.<locals>.<genexpr>  s     K!qvvKs   z*.seam-claim.*.jsonr#   c              3  D   K   | ]  }j                  |      d u   yw)TNr,   r/  s     r    r/   z#observe_six_step.<locals>.<genexpr>  s     DADHHQK4/Ds    r   r.   rA   Fr"  	CANCELLEDTuC   frozen byte-0 read-only, +25 wrapper 공개 API 에 주입 Fake/Spyu<   실 cron-list/cron-remove/schedule_history/4-tuple 무접촉zclaim artifact(O_EXCL) u    — exact-once 명시 key)r
  r  r  r  r  r  r  r   r  r  r  r  )r   r   r   r  r'   r7   durable_successseam_invokedr   r\   sortedglobr   r1  r3  r  r   r2   rc   r4  r	  )scenariolisterr7  historyparamsresr-  s1r>  r   claim_filess2s3remove_calls
removed_okcrosscheck_absents4s5s6r  r0  s                       @r    observe_six_steprP    sy    # 01#+,H#I!"45]+%-.L%ML)'/,(
 ""45F 8HY/0)!4 5T::;'"#67C "E99-.4"D 
c!!	"B(()L%I 	?!!# 	KtI334IJKK 
 
	/#k*a/B	d	DDDDB&LLQ 	6OI&(3C*DD	6OI&%/	6 IIo&+5	  #--h7G.HII		))B			8$45	6B	%	 BBBB+,H') "%'&(!#%'k*l+QJ%k]2LM
 r3   c               "   t        |       } | j                  dd       | t         dz  }| t         dz  }|j                  t	        j
                  |dd      d	       |j                  t         d
d	       t        |      t        |      dS )u  정상종료 시 result.json + .done 만 산출 (9-R.1 / callback (a) +28 변형).

    executor 는 실 normal-collector cron 자가등록 0 — 회수는 ANU-registered
    fallback callback 이 result.json/.done 존재로 정상수렴 판정한다. 본
    함수는 화이트리스트 경로(``task-2553+28.result.json`` /
    ``task-2553+28.done``) 만 기록하며 실 cron 등록 0.
    Tr   r   z.doneF   )r   indentrY   rZ   uv    DONE — normal convergence marker (real cron self-register 0; ANU fallback recovers via result.json/.done presence)
)r   done)r   r   r   r   r   r   r6   )
events_dirresult_packetr  dns       r    emit_normal_convergence_markersrX  7  s     j!JTD1	+,L9	9B	+,E2	2BMM

=uQ?'   MM
 ( 	( 	   r7CG44r3   )r   r   
FROZEN_SHAr   r{   r   r   r   r>   rG   r   r   r   r   r   r  r	  rP  rX  )rJ   r   r5   None)r   r6   r5   r6   )r   r   r   r6   r5   r7   )r   r   r   r6   r5   r   )r   r6   r5   r   )
rJ   r   r   r6   r   r6   r   zOptional[str]r5   r   )r-  r   r5   r7   )
rA  r   rB  r   r7  r>   rC  rG   r5   r	  )rU  r   rV  r   r5   r   )6r;   
__future__r   r   r   rw   r   dataclassesr   r   pathlibr   typingr   __file__rO   parentr   r   rY  r   r{   r|   r   r   (utils.anu_delegation_completion_callbackr   r   r   r   r   r  r   r  r   "utils.operational_collector_wiringr   r   r   r>   rG   r   r   r   r   r   r  r	  r2  r3  r4  rP  rX  __all__r<   r3   r    <module>rd     s  @ #   	  (  N""$++2299	G#&MMF   KK  CD 23  $ L L8	O 	O2 2JY6~>
8<. &()-JJ J 	J 'J 
J` 
 
 
6	 
9SS S 	S
  S Sl50r3   