
    jK                       d Z ddlm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
mZmZ ddlmZ ddlmZ dZd	Zd
ZdZ eeeh      ZdZd+dZ ed       G d d             Zd,dZd-dZdZe G d d             Zd.dZd/dZ	 	 	 	 	 	 	 	 	 	 d0dZ d1dZ!d1dZ"dZ#dZ$dZ%dZ&d Z'ed!	 	 	 d2d"Z(d3d#Z)ed!	 	 	 	 	 d4d$Z*ed!	 	 	 	 	 d5d%Z+d6d&Z,d7d'Z-d8d9d(Z.ded)	 	 	 	 	 	 	 d:d*Z/y);u0
  dispatch.executor_completion_contract — executor completion callback contract.

task-2553+32 — EXECUTOR COMPLETION CALLBACK MANDATORY RULE 복원 (코드/파일 자동화).

회장 정정 (memory/events/task-2553.normal-callback-mandatory-doctrine-correction
_260518.json):

    executor completion callback = MANDATORY lifecycle signal.
    NO-CRON ≠ executor completion callback 금지.

This standalone module encodes that rule as executable code so that ANY future
executor task cannot omit its normal completion callback (§1/§4).

Standalone, zero-mutation: imports/edits ZERO tracked module. The huge
dispatch/__init__.py body is NOT touched (§8 기존 산출물 수정 0). File-level
contract only — the same pattern as anu_v3.callback_4tuple_index /
anu_v3.result_ready_recovery (+29).

NO-CRON note (9-R.1): this module performs ZERO cron register/remove. It only
*declares and validates* the contract. The executor's normal completion
callback is a designed lifecycle signal — NOT an ad-hoc cron-add by the
registry/checkpoint, and NOT a cron-remove by +32 (회장 금지 준수).

Closeout note (9-R.2): requiring an executor's own normal-completion callback /
result.json / report / .done is a *lifecycle signal*, NOT an escalation of
repository/task-state finalization authority. The latter (closeout 확정 권한)
remains forbidden; the former is REQUIRED.

──────────────────────────────────────────────────────────────────────────────
L4 wiring (task-2630 — CALLBACK_RUNTIME_ENFORCEMENT L4): callback lifecycle
classifier 실결선. 이 모듈에 result.json fields 10~14(축A/B/C lifecycle 분류)를
**append-only** 로 확장하고, `memory/events/<task>.callback_lifecycle.json`
artifact writer 를 추가한다 (스펙
memory/specs/system_callback_lifecycle_state_schema_260522.md §8/§9).

zero-mutation 원칙 유지: 기존 9-field per-callback contract
(dispatch.normal_fallback_callback_helper._contract_fields, 회장 §10) 는 그대로
보존하고 위 5개 lifecycle field 만 덧붙인다(ANCHOR-2). 신규 의존은
`utils.callback_lifecycle_classifier` (순수 함수 · 파일/네트워크/subprocess I/O 0 ·
anu_v3 런타임 import 0) 하나뿐 — 기존 tracked module **본문**은 수정하지 않는다.
artifact writer 외 어떤 cron register/remove · callback 재발사 · subprocess 도
수행하지 않는다(§11 / ANCHOR-4: production enforcement 완료 판정은 L4 merge 후 별도).
    )annotationsN)	dataclassfield)DictListOptionalclassify_callback_lifecycle)DEFAULT_ANU_KEYSz(dispatch.executor_completion_contract.v1NORMAL_COLLECTOR_COMPLETEDRESULT_READY_NO_NORMAL_CALLBACKDISPATCH_FAILEDaR  NO-CRON == registry/checkpoint(+29/+30/+31) MUST NOT arbitrarily add/remove cron. It does NOT mean the executor is forbidden from sending its normal completion callback. The executor normal completion callback is a MANDATORY lifecycle signal, not a registry-initiated ad-hoc cron, and is therefore explicitly exempt from the cron-add ban.c                      y)u   §6.5 — executor completion callback is NOT a 'cron 신규 등록 금지' breach.

    Always False: the normal completion callback is a designed lifecycle
    signal, distinct from a registry/checkpoint ad-hoc cron add.
    F r       V/home/jay/workspace/.worktrees/task-2644-dev1/dispatch/executor_completion_contract.py0is_executor_completion_callback_a_cron_violationr   P   s     r   T)frozenc                  :    e Zd ZU dZded<   ded<   ded<   ded<   y)	Callback4Tupleu   {task_id, dispatch_cron_id, normal_collector_cron_id*, fallback_cron_id}.

    normal_collector_cron_id is MANDATORY (§4.5). A None/empty value makes the
    contract invalid (§6.12) — it is NOT a valid NO-CRON degradation.
    strtask_iddispatch_cron_idOptional[str]normal_collector_cron_idfallback_callback_cron_idN)__name__
__module____qualname____doc____annotations__r   r   r   r   r   Z   s      L++""r   r   c                    g }| j                   s|j                  d       | j                  s|j                  d       | j                  s|j                  d       | j                  s|j                  d       |S )u3   Return invalidity reasons (empty == valid). §6.12.ztask_id emptyzdispatch_cron_id emptyu{   normal_collector_cron_id missing — MANDATORY lifecycle signal (§4.5/§6.12). NO-CRON does NOT exempt this (§6.3/§6.5).u4   fallback_callback_cron_id empty (safety path, §6.6))r   appendr   r   r   )treasonss     r   validate_4tupler&   h   sh    G99'/0%%J	
 &&MNNr   c                    t        |        S N)r&   )r$   s    r   tuple_is_validr)   y   s    q!!!r   )result_json_presentdone_marker_presentreport_present%normal_callback_registration_evidencec                  ~    e Zd ZU dZded<   dZded<   dZded<   dZded<   dZded	<   dZ	ded
<    e
e      Zded<   y)ExecutorCloseoutEvidenceFboolr*   r+   r,   r-   Nr   r   claims_finalization_authority)default_factory	List[str]notes)r   r   r   r*   r!   r+   r,   r-   r   r1   r   listr4   r   r   r   r/   r/      sV     %% %% ND  38)47.2m2 +0!4/T2E92r   r/   c                f   g }| j                   s|j                  d       | j                  s|j                  d       | j                  s|j                  d       | j                  s|j                  d       | j
                  s|j                  d       | j                  r|j                  d       |S )u   FAIL reasons for an executor closeout (§4.4 / §4.6 / 9-R.2).

    Missing normal callback registration evidence == FAIL (not a valid
    closeout) even when result.json/.done/report all exist.
    zresult.json missingz.done marker missingzreport missingu   normal callback registration evidence missing — executor normal completion callback is MANDATORY (§4.4/§4.6). Not a valid normal lifecycle completion.u<   normal_collector_cron_id absent in closeout evidence (§4.5)uz   closeout evidence claims repository/task-state finalization authority — forbidden (9-R.2/§6.15). Lifecycle signal only.)r*   r#   r+   r,   r-   r   r1   )evr%   s     r   validate_closeout_evidencer8      s     G!!,-!!-.'(331	

 &&J	
 
''M	
 Nr   c                    t        |        S r(   )r8   )r7   s    r   closeout_is_validr:      s    )"---r   c                J    | st         S |xs |}|rt        S |rt        S t         S )u   §4.6 / §6.8 / §6.9 completion classifier.

    result.json + .done exist but normal callback never registered ->
    RESULT_READY_NO_NORMAL_CALLBACK (a recovery state, NOT a normal
    lifecycle complete).
    )r   r   r   )dispatch_okresult_presentdone_presentnormal_callback_registered
has_results        r   classify_completionrA      s/     /<J!))..r   c                    | t         k(  S )uE   §6.9 — RESULT_READY_NO_NORMAL_CALLBACK is NOT a normal completion.)r   classifications    r   is_accepted_normal_lifecyclerE      s    777r   c                    | t         k(  S )u4   §6.6/§6.9 — recovery target, not a task failure.)r   rC   s    r   is_recovery_staterG      s    <<<r   delivery_outcomenormal_callback_miss_causeroot_cause_tagslifecycle_state_evidenceclassified_byapplied_count)	callback_prompt_utf8_bytescallback_prompt_charscallback_cron_idcallback_registration_statuscallback_roleenvelope_only_compliancefallback_prompt_utf8_bytesfallback_safety_net_registered'fallback_safety_net_role_single_purposez'dispatch.callback_lifecycle_artifact.v1callback_lifecycle_classifierz.callback_lifecycle.jsonanu_keysc                   t        | |      S )u  callback_lifecycle_classifier 결선 진입점 (필수구현 2).

    evidence snapshot(dict) → classifier(순수 함수) 호출 → 분류 결과(dict).
    추정 금지: evidence 결핍 시 classifier 가 UNKNOWN/INSUFFICIENT_EVIDENCE 로
    판정한다(필수구현 7). CALLBACK_DELIVERY_GAP residual-only ·
    SELF_KEY_FAIL_CLOSED vs SELF_KEY_FIRED_NON_AUTHORITATIVE 분리는 classifier
    가 보존(필수구현 8/9 · ANCHOR-3). 이 함수는 I/O 0 · cron 0 · 발사 0.
    rY   r	   )evidencerZ   s     r   classify_completion_lifecycler]     s     'x(CCr   c                    | xs i }|j                  d      du xr |j                  d      du }t        |      t        |j                  d            t        |j                  d            dS )u   §0 핵심구분 3단계를 분리 기록 (필수구현 5).

    callback **gate PASS ≠ callback fired ≠ collector received**. classifier 가
    이미 산출한 lifecycle_state_evidence 신호에서 결정적으로 파생한다(추정 0).
    git_gate_blockedFcontract_violationnormal_callback_firedauthoritative_cron_collection)callback_gate_passnotification_sentcollector_received)getr0   )rL   lse	gate_passs      r   callback_stage_separationri     sp     #
(bC +,5cCGGDX<Y]b<bI #9o!#''*A"BC"377+J#KL r   c          	         t        | xs i       }t        ||      }|d   |d   |d   |d   |d   |d   xs i j                  dd      d	}t        t	        |      t	        |      z        }|rt        d
| d      |j                  |       |S )u  9-field callback contract 위에 fields 10~14 를 append-only 로 확장
    (필수구현 1/4 · ANCHOR-2).

    - 입력 dict 는 변형하지 않는다(shallow copy 후 확장).
    - 9-field 와 lifecycle field 키가 충돌하면 ValueError (append-only 보증 —
      기존 field 를 절대 덮어쓰지 않는다).
    반환: 9 fields + fields 10~14 가 동시 존재하는 result.json closeout dict.
    rY   rI   rJ   rK   rL   rM   rN   r   rH   uj   append-only violation — lifecycle fields 10~14 가 기존 callback contract field 를 덮어쓰려 함: z (ANCHOR-2))dictr]   rf   sortedset
ValueErrorupdate)contract_9_fieldsr\   rZ   baseresultappendedoverlaps          r   append_lifecycle_fieldsru   *  s     !'R(D*8hGF"#56&,-I&J!"34$*+E$F0 !;<BGGYZ[H SYX./G44;9KI
 	
 	KKKr   c                   t        ||      }|d   }t        t        | |d   |d   |d   |d   |d   |d   |t        |      |d	   |xs i j	                  d
d      dS )u   `<task>.callback_lifecycle.json` artifact 내용(dict) 구성 — 순수·결정적.

    동일 입력 → 동일 출력 (I/O 0). fallback collector artifact 와 구분되도록
    artifact_kind 를 명시한다(필수구현 6).
    rY   rL   rI   rJ   rK   evidence_completenessmissing_evidence_sourcesrD   rM   rN   r   )schemaartifact_kindr   rI   rJ   rK   rw   rx   rD   rL   ri   rM   rN   )r]   "CALLBACK_LIFECYCLE_ARTIFACT_SCHEMA CALLBACK_LIFECYCLE_ARTIFACT_KINDri   rf   )r   r\   rZ   rr   rg   s        r   !build_callback_lifecycle_artifactr}   J  s     +8hGF
+
,C49"#56&,-I&J!"34!'(?!@$*+E$F !12$'%>s%C0)!< r   c                :    t        j                  | ddd      dz   S )u   결정적 직렬화 — sort_keys + 고정 indent + trailing newline.

    동일 dict → byte-identical 문자열 (idempotent artifact 보증).
    FT   )ensure_ascii	sort_keysindent
)jsondumps)artifacts    r   _serialize_lifecycle_artifactr   f  s    
 ::hUd1MPTTTr   c                    t         j                  j                  d      } | r!t         j                  j	                  | dd      S t         j                  j                  t         j                  j                  t                    }t         j                  j	                  t         j                  j                  |      dd      S )u   artifact 기본 디렉터리 = <workspace>/memory/events.

    WORKSPACE_ROOT env override 우선(테스트 live-workspace 의존 0 보장), 없으면
    이 파일(dispatch/) 기준 repo 상대 경로로 해석.
    WORKSPACE_ROOTmemoryevents)osenvironrf   pathjoindirnameabspath__file__)rootheres     r   default_events_dirr   n  st     ::>>*+Dww||D(H5577??277??845D77<<-xBBr   c                n    ||n	t               }t        j                  j                  ||  t               S )uF   artifact 파일 경로. events_dir 미지정 시 default_events_dir().)r   r   r   r   "CALLBACK_LIFECYCLE_ARTIFACT_SUFFIX)r   
events_dirrq   s      r    callback_lifecycle_artifact_pathr   {  s2    #/:5G5ID77<<	*L)MNOOr   )r   rZ   c                  t        | ||      }t        |      }t        | |      }d}	 t        |dd      5 }|j	                         }ddd       ||k7  rt        j                  j                  |      xs d}	t        j                  |	d       t        j                  |	d	d
      \  }
}	 t        j                  |
dd      5 }|j                  |       ddd       t        j                   ||       |S |S # 1 sw Y   xY w# t
        t        t        f$ r Y w xY w# 1 sw Y   IxY w# t"        $ r' 	 t        j$                  |        # t&        $ r Y  w xY ww xY w)u  `memory/events/<task>.callback_lifecycle.json` 기록 (idempotent · 필수구현 3).

    - 동일 입력 2회 실행 → byte-identical (결정적 직렬화).
    - 기존 파일 내용이 이미 동일하면 재기록하지 않는다(중복 write 0 · mtime 보존).
    - cron 0 · callback 재발사 0 · subprocess 0 — 오직 lifecycle artifact 파일
      1개만 생성/갱신한다(필수구현 6: fallback collector artifact 미접촉).
    반환: artifact 파일 절대/상대 경로.
    rY   Nrzutf-8)encoding.T)exist_okz.callback_lifecycle.z.tmp)dirprefixsuffixw)r}   r   r   openreadFileNotFoundErrorIsADirectoryErrorPermissionErrorr   r   r   makedirstempfilemkstempfdopenwritereplaceBaseExceptionunlinkOSError)r   r\   r   rZ   r   payloadr   existingfhdirpathfdtmps               r   !write_callback_lifecycle_artifactr     sQ    1(XVH+H5G+GZ@D"H$g. 	!"wwyH	! 7''//$'.3
Gd+ ""w7MV\]C		2sW5 "!"JJsD! K4K)	! 	!0/B " "  			#   	sp   D C7D +D) DD) 7D <D DDD&"D) )	E3E	E		EEEE)returnr0   )r$   r   r   r3   )r$   r   r   r0   )r7   r/   r   r3   )r7   r/   r   r0   )
r<   r0   r=   r0   r>   r0   r?   r0   r   r   )rD   r   r   r0   )r\   r   r   r   )rL   r   r   r   )rp   r   r\   r   r   r   )r   r   r\   r   r   r   )r   r   r   r   )r   r   r(   )r   r   r   r   r   r   )r   r   r\   r   r   r   r   r   )0r    
__future__r   r   r   r   dataclassesr   r   typingr   r   r   #utils.callback_lifecycle_classifierr
   utils.callback_lifecycle_statesr   CONTRACT_SCHEMAr   r   r   	frozensetNON_NORMAL_LIFECYCLENO_CRON_CORRECTED_DEFINITIONr   r   r&   r)   EXECUTOR_CLOSEOUT_CHECKLISTr/   r8   r:   rA   rE   rG   LIFECYCLE_RESULT_FIELDSCALLBACK_CONTRACT_9_FIELDSr{   r|   r   r]   ri   ru   r}   r   r   r   r   r   r   r   <module>r      s  *V #  	  ( ' ' K << :  #D # !$o6 M  $
# 
# 
#""  3 3 3>.  	
 !% 	.8
=" 
  &O "
 $C  %? " !1DD	D* :J'+	B /? 	8U
CP !%*** 	* 	*r   