
    Mju              
         U d Z ddlmZ ddlmZmZ ddlmZmZm	Z	m
Z
 dZdZdZdZd	Zd
ZdZdZdZdZdZdZdZeeeeeeeefZded<   dZdZdZdZ eeh      ZdZ dZ!dZ" ee!e"h      Z#d3dZ$ ed       G d d             Z%e G d  d!             Z&d"d#d$e" e'e      d$eedd%		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d4d&Z(	 	 	 	 	 	 	 	 	 	 	 	 d5d'Z)e G d( d)             Z*	 	 	 	 	 	 	 	 d6d*Z+d"d$d$d"d+	 	 	 	 	 	 	 	 	 	 	 d7d,Z,d"d$d"d-	 	 	 	 	 	 	 	 	 d8d.Z-e G d/ d0             Z.	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d9d1Z/g d2Z0y"):u  dispatch.callback_owner_enforcer — executor self-collector / self-dispatch /
self-adjudication 구조적 차단 + write-back role/fallback binding conflict 감사.

task-2553+49 (MICRO-HARDENING, 회장 결정 — 코드/파일 자동화).

Problem (봉쇄 ref: memory/events/task-2553-selfcollector-violation-containment
-decision_260518.json): task-2553+47 dispatch 시 ANU 가 normal callback 을
독립 ANU key 로 발사하도록 못박지 않아, executor(dev3)가 자기 key 채널로
normal/fallback callback 을 등록하고 그 자가세션이 +47 자가 회수·자가 Codex
audit·자가 adjudication·+48 자가 dispatch 를 수행했다. 분리원칙·collector
독립성·executor 신규 dispatch 0·ANU 단독 dispatch 권한 4중 위반. 근본원인 =
ANU orchestration 설계 결함(ANU-key 소유 핀 누락).

This standalone module encodes the missing fail-closed pins as executable
code so a future executor session *cannot* own its own completion collector,
self-adjudicate, or self-dispatch (회장 §1/§2):

  * ``enforce_callback_owner`` — normal/fallback callback owner MUST be an
    independent ANU key. executor_key == collector_key, collector owner ==
    executor key, collector_role != ANU, prompt-says-ANU-but-owner-is-executor
    -> FAIL (§2 / regression 2~6).
  * ``assert_not_self_adjudication`` — executor self session running a Codex
    audit / ANU-Codex adjudication -> FAIL (§2 / regression 7).
  * ``assert_not_self_dispatch`` — executor self session dispatching a
    follow-up task -> FAIL (§2 / regression 8).
  * ``audit_writeback_binding_conflict`` — the +47 idempotency key was LOW:
    it keyed only on (dispatch_id, chat_id, normal_collector_cron_id) and
    ignored role/fallback binding. This audits role/fallback binding conflict
    over the +44 read-only ledger and emits ``WRITEBACK_BINDING_CONFLICT``
    instead of a silent idempotent skip (§2 line 20/21).

Standalone, zero-mutation (회장 §5 / byte-0 우선): imports/edits ZERO
byte-0-pinned tracked module. ``anu_v3.callback_4tuple_registry`` and
``dispatch.executor_completion_contract`` are byte-0 (pinned by the existing
+44/+48 regression FROZEN_SHA256 autouse invariant); they are imported
read-only / lazily and NEVER mutated. The §2 4-tuple owner fields
(executor_key / collector_key / collector_owner_key / collector_role) are
carried by THIS module's own ``CallbackOwner4Tuple`` record + schema +
decision artifact — NOT by mutating the frozen +44 legacy record (9-R /
byte-0 우선 carve-out, +42/+43/+45 선례 동형).

Layer A / NO-CRON (9-R.1): this module performs ZERO cron register/remove,
ZERO dispatch, ZERO merge, ZERO ``cokacdir`` / ``subprocess`` exec. It only
*validates* owner/key bindings and *records* a verdict. The executor's own
normal completion callback (fired by an authorized session with the ANU key,
§10) is a designed lifecycle signal — NOT an ad-hoc registry cron — and the
firing happens outside this module; the module never execs it.
    )annotations)	dataclassfield)ListOptionalSequenceTuplez&dispatch.callback_owner_enforcement.v1z!dispatch.callback_owner_4tuple.v1PASSFAILHOLD_FOR_CHAIRSELF_COLLECTOR_FORBIDDEN$EXECUTOR_SELF_ADJUDICATION_FORBIDDENSELF_DISPATCH_FORBIDDENCALLBACK_OWNER_MISMATCHCALLBACK_COLLECTOR_NOT_ANUCALLBACK_4TUPLE_INVALIDDISPATCH_PATH_BYPASSED_CONTRACTWRITEBACK_BINDING_CONFLICTzTuple[str, ...]ALL_CLASSIFICATIONSWRITEBACK_NO_CONFLICTWRITEBACK_IDEMPOTENT_SKIPANUc119085addb0f8b71e41a2324a3ccdd0zdispatch.core.maincokacdir_cron_directc                6    t        |       xr | t        |      v S )z?True iff ``key`` is a non-empty configured independent ANU key.)boolset)keyanu_keyss     Q/home/jay/workspace/.worktrees/task-2643-dev6/dispatch/callback_owner_enforcer.py
is_anu_keyr"   p   s    9-H--    T)frozenc                      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Zded<   eZded<   eZded<   dZ	ded<   ddZ
ddZy)CallbackOwner4Tupleu  The §2/§10 owner-bound callback 4-tuple.

    Base 4-tuple {task_id, dispatch_cron_id, normal_collector_cron_id*,
    fallback_callback_cron_id} + the owner binding the +44 frozen legacy
    record cannot carry (executor_key / collector_key / collector_owner_key /
    collector_role=ANU / normal_collector_cron_id / fallback_cron_id /
    normal_collector_cron_id_role). normal_collector_cron_id is MANDATORY.
    strtask_iddispatch_cron_idOptional[str]normal_collector_cron_idfallback_callback_cron_idexecutor_keycollector_keycollector_owner_keycollector_role chat_idnormal_collector_cron_id_rolefallback_cron_id_roleFr   no_fallbackc           	         | j                   | j                  | j                  | j                  | j                  | j
                  | j                  | j                  dS )Nr(   r)   r+   r,   r-   r.   r/   r0   r7   selfs    r!   identityzCallbackOwner4Tuple.identity   sR    || $ 5 5(,(E(E)-)G)G --!//#'#;#;"11	
 		
r#   c                    t        | j                               }|j                  t        | j                  | j
                  | j                  | j                  d       |S )N)schemar2   r3   r4   r5   )dictr:   updateOWNER_4TUPLE_SCHEMAr2   r3   r4   r5   )r9   ds     r!   to_jsonzCallbackOwner4Tuple.to_json   sO    !	-<<66)-)C)C#//
	
 r#   Nreturnr=   )__name__
__module____qualname____doc____annotations__r2   COLLECTOR_ROLE_ANUr3   r4   r5   r:   rA    r#   r!   r&   r&   v   sl     L++,,GS);!3;!333K

r#   r&   c                      e Zd ZU 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<   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<   edd       Zedd       Z	ddZ
y)CallbackOwnerEnforcementResultr'   r<   verdict	List[str]classificationsr(   r-   r.   r/   r0   r*   r+   r,   r)   r3   r4   
entry_pathr   prompt_claims_anu_collectorowner_is_independent_anudefault_factoryreasonsc                (    | j                   t        k(  S NrM   r
   r8   s    r!   okz!CallbackOwnerEnforcementResult.ok       ||t##r#   c                <    | j                   r| j                   d   S d S )Nr   )rO   r8   s    r!   primary_classificationz5CallbackOwnerEnforcementResult.primary_classification   s     *.*>*>t##A&HDHr#   c                    i d| j                   d| j                  dt        | j                        d| j                  d| j
                  d| j                  d| j                  d| j                  d	| j                  d
| j                  d| j                  d| j                  d| j                  d| j                  d| j                  d| j                   d| j"                  dt        | j$                        iS )Nr<   rM   rO   r\   r(   r-   r.   r/   r0   r+   r,   r)   r3   r4   rP   rQ   rR   rU   )r<   rM   listrO   r\   r(   r-   r.   r/   r0   r+   r,   r)   r3   r4   rP   rQ   rR   rU   r8   s    r!   rA   z&CallbackOwnerEnforcementResult.to_json   sM   
dkk
t||
 tD$8$89
 %d&A&A	

 t||
 D--
 T//
 "4#;#;
 d11
 '(E(E
 ()G)G
  5 5
 ,22
  $T%?%?!
" $//#
$ *4+K+K%
& '(E(E'
( tDLL))
 	
r#   NrC   r   )rC   r*   rB   )rD   rE   rF   rH   r   r^   rU   propertyrY   r\   rA   rJ   r#   r!   rL   rL      s    KLL++,,#&&O!%%""t4GY4$ $ I I
r#   rL   Nr1   F)	r/   r2   rQ   rP   r    r5   r3   r4   anu_keys_resolvablec                   |xs |xs d}g }|rt        |      sEt        d i dt        dt        dg d| d|d|d|d	|d
|d|d|d|d|d|
d|	ddddgS g }|
t        vr*|j                  t               |j                  d|
d       |t        k7  r*|j                  t               |j                  d|d       |r-|r+||k(  r&|j                  t               |j                  d       t        |      xr ||k(  }|r&|j                  t               |j                  d       t        ||      }|	r*|s|s&|j                  t               |j                  d       |s,|s*|j                  t               |j                  d|d       t        | ||||      }|r&|j                  t               |j                  |       t!               }g }|D ])  }||vs|j#                  |       |j                  |       + |st$        nt&        }|xr | xr	 |t        k(  }|t$        k(  r|j                  d       t        d i dt        d|d|d| d|d|d|d	|d
|d|d|d|d|d|
d|	d|d|S )!u#  Fail-closed gate: normal/fallback callback owner MUST be an
    independent ANU key (회장 §2/§8/§10).

    Verdict logic (every violating condition is recorded — NOT silently
    dropped; §2 line 21):

      * ANU key set unresolvable in this env   -> HOLD_FOR_CHAIR (§6 — the
                                                   ONLY conditional escalation;
                                                   un-codeable sub-control)
      * entry_path unrecognised                -> DISPATCH_PATH_BYPASSED_
                                                   CONTRACT (cron-direct bypass)
      * collector_role != "ANU"                -> CALLBACK_COLLECTOR_NOT_ANU
                                                   (regression 5)
      * executor_key == collector_key          -> SELF_COLLECTOR_FORBIDDEN
                                                   (regression 4)
      * effective owner == executor key        -> SELF_COLLECTOR_FORBIDDEN
                                                   (regression 2/3 — normal &
                                                   fallback self callback)
      * prompt says ANU collector but owner is
        the executor key / not an ANU key      -> CALLBACK_OWNER_MISMATCH
                                                   (regression 6)
      * owner not an independent ANU key       -> CALLBACK_OWNER_MISMATCH
      * 4-tuple invalid (task_id/dispatch_cron
        /normal_collector_cron_id mandatory;
        fallback unless no_fallback)           -> CALLBACK_4TUPLE_INVALID
      * else                                   -> PASS (owner is an
                                                   independent ANU key,
                                                   regression 1).

    Layer A: pure validation. ZERO cron / dispatch / subprocess / cokacdir.
    r1   r<   rM   rO   r(   r-   r.   r/   r0   r+   r,   r)   r3   r4   rP   rQ   rR   FrU   u   ANU key set unresolvable in this environment — the callback owner=ANU pin cannot be code-enforced here (§6 HOLD_FOR_CHAIR; the only conditional escalation, 9-R.2). NO silent pass.zentry_path u    not a recognised gated dispatch path — a cron-direct contract bypass (§3 / 9-R.1). The ANU control layer (prompt gen · 4-tuple · post-reg owner cross-check) must fail-closed here.zcollector_role=um    != 'ANU' — the completion collector MUST run as an independent ANU collector session (§2 / regression 5).ui   executor_key == collector_key — executor self-collector is structurally forbidden (§2 / regression 4).u   normal/fallback callback owner key == executor self key — executor self-callback (normal & fallback) is structurally forbidden; owner MUST be an independent ANU key (§2/§10 / regression 2/3).u   prompt declares an 'ANU Result Collector' but the schedule owner key is the executor key / not an ANU key — invalid; owner identity, not prompt text, is authoritative (§2 / regression 6).zcallback owner key uF    is not a configured independent ANU key — owner mismatch (§2/§8).)r(   r)   r+   r,   r5   u   callback owner is an independent ANU key (collector_role=ANU, owner != executor key, 4-tuple valid) — executor self-collector structurally impossible from here (§2 / regression 1).rJ   )r^   rL   ENFORCER_SCHEMAHOLDKNOWN_ENTRY_PATHSappendr   rI   r   r   r   r"   r   _validate_owner_4tupler   extendr   addr
   r   )r(   r-   r.   r/   r0   r+   r,   r)   r2   rQ   rP   r    r5   r3   r4   ra   	owner_keyrU   clsowner_is_executorowner_is_anutuple_reasonsseenorderedcrM   rR   s                              r!   enforce_callback_ownerrr      s   d $:}:IG
 d8n- 
"

 
 	

 &
 (
 !*
 *
 &>
 '@
 .
 +H
 #8
 "
 )D
  &+!
$*#
 	
2 C**

23* (, ,	
 ++

-.n/ 0$ $	
 ,-*G

+,;	

 YEI,E

+,	
 i2L"(9

*+	
  1

*+!) /@ @	

 +)!9";M 

*+}% DG D=HHQKNN1
 "dtG 	1!!	100 
 $P	
 *    	
 " $ & & ": #< * 'D 4  %@  ":!" # r#   c                    g }| s|j                  d       |s|j                  d       |s|j                  d       |s|s|j                  d       |S )z@4-tuple invalidity reasons (mirrors the +32/+44 mandatory rule).z'task_id empty (CALLBACK_4TUPLE_INVALID)z0dispatch_cron_id empty (CALLBACK_4TUPLE_INVALID)u   normal_collector_cron_id missing — MANDATORY independent-ANU normal completion callback lifecycle signal; NO-CRON does NOT exempt this (CALLBACK_4TUPLE_INVALID).znfallback_callback_cron_id missing and no explicit no_fallback contract (safety path, CALLBACK_4TUPLE_INVALID).)rf   )r(   r)   r+   r,   r5   rs         r!   rg   rg     s\     A	:;	CD#	5	

 %[	?	
 Hr#   c                  l    e Zd ZU ded<   ded<   ded<   ded<    ee      Zd	ed
<   edd       ZddZ	y)SelfActionGuardResultr'   r<   rM   r*   classificationr   is_executor_self_sessionrS   rN   rU   c                (    | j                   t        k(  S rW   rX   r8   s    r!   rY   zSelfActionGuardResult.ok  rZ   r#   c                    | j                   | j                  | j                  | j                  t	        | j
                        dS )Nr<   rM   rw   rx   rU   )r<   rM   rw   rx   r^   rU   r8   s    r!   rA   zSelfActionGuardResult.to_json  s8    kk||"11(,(E(EDLL)
 	
r#   Nr_   rB   
rD   rE   rF   rH   r   r^   rU   r`   rY   rA   rJ   r#   r!   rv   rv     s>    KL!!""t4GY4$ $
r#   rv   c                P    t        |      xr || k(  }|t        |      nd}|xs |S )NF)r   )r-   	actor_keyrx   key_derivedexplicits        r!   _is_self_sessionr     s?     y/?i<&?K $/ 	%& 
 "("r#   )r~   is_codex_auditis_adjudicationrx   c           
     
   t        | ||      }|r\|s|rXg }|r|j                  d       |r|j                  d       t        t        t        t
        dddj                  |       dg      S t        t        t        d|d	g      S )
u!  executor self session 이 Codex audit / ANU-Codex adjudication 을 수행
    하면 invalid (§2 / regression 7).

    Codex audit · adjudication · 후속 회수·검증 은 반드시 독립 ANU collector
    세션이 수행한다. executor self session 이 그것을 하면 FAIL.
    zCodex auditzANU-Codex adjudicationTz!executor self session performing z + u    — invalid; this MUST be done by the independent ANU collector session, never the executor self session (§2 / regression 7).r{   Nzano executor self-adjudication (Codex audit/adjudication is not run by the executor self session).)r   rf   rv   rc   r   r   joinr
   )r-   r~   r   r   rx   self_sessionwhats          r!   assert_not_self_adjudicationr     s     $i!9L ?KK&KK01$"?%)3EJJt4D3E F( (
 	
 !!-5
	 	r#   )r~   is_followup_dispatchrx   c                    t        | ||      }|r|rt        t        t        t        ddg      S t        t        t
        d|dg      S )u   executor self session 이 후속 task dispatch 를 수행하면 invalid
    (§2 / regression 8). 신규 dispatch·delegation 은 독립 ANU 세션만.Tu   executor self session performing a follow-up task dispatch/delegation — invalid; executor 자기작업중 신규 dispatch·delegation 0, only the independent ANU session may dispatch (§2/§10 / regression 8).r{   Nzno executor self-dispatch.)r   rv   rc   r   r   r
   )r-   r~   r   rx   r   s        r!   assert_not_self_dispatchr     sb     $i!9L ,$"2%):
 	
 !!--. r#   c                      e Zd ZU 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<   edd       ZddZ	y)WritebackBindingAuditResultr'   r<   rM   rw   r(   r   conflictmatched_idempotency_keyrN   conflicting_fieldsrS   rU   c                (    | j                   t        k(  S rW   rX   r8   s    r!   rY   zWritebackBindingAuditResult.ok?  rZ   r#   c           
         | j                   | j                  | j                  | j                  | j                  | j
                  t        | j                        t        | j                        dS )Nr<   rM   rw   r(   r   r   r   rU   )	r<   rM   rw   r(   r   r   r^   r   rU   r8   s    r!   rA   z#WritebackBindingAuditResult.to_jsonC  sT    kk||"11||'+'C'C"&t'>'>"?DLL)	
 		
r#   Nr_   rB   r|   rJ   r#   r!   r   r   4  sN    KLLN!!!!t4GY4$ $

r#   r   c                  d}| D ]  }	 t        |dd      }	t        |dd      }
t        |dd      }t        |dd      }t        |dd      }t        |dd      }|	d	k7  rX|
|k(  s^t        |      t        |      k(  sv||k(  s|d
}g }||k7  r|j                  d|d|d       ||k7  r|j                  d|d|d       |st	        t
        t        t        |d
d
|ddj                  |      z   g      c S  |r t	        t
        t        t        |dd
g dg      S t	        t
        t        t        |ddg dg      S # t        $ r Y @w xY w)u  +47 idempotency key 보강 (§2 line 20/21).

    The +47 ``write_back_completed`` idempotency key is
    ``(dispatch_id, chat_id, normal_collector_cron_id)`` ONLY — it ignores
    ``role`` / ``fallback_callback_cron_id``. So a SECOND write-back that
    shares the collector identity but carries a DIFFERENT role/fallback
    binding is silently treated as an idempotent skip (LOW). This audit runs
    over the +44 read-only ledger ``history`` (the +44 module stays byte-0;
    history is the value of ``registry.history_for(task_id)``):

      * a COMPLETED record with the SAME idempotency key AND the SAME
        role/fallback binding -> ``WRITEBACK_IDEMPOTENT_SKIP`` (PASS,
        regression 12 — a true duplicate).
      * a COMPLETED record with the SAME idempotency key but a DIFFERENT
        role / fallback binding -> ``WRITEBACK_BINDING_CONFLICT`` (FAIL,
        regression 11) — RECORDED, never a silent skip (§2 line 21).
      * no idempotency-key match -> ``WRITEBACK_NO_CONFLICT`` (PASS — a
        normal new append).

    Read-only. ZERO cron / dispatch / subprocess. The +44 registry is NOT
    escalated to a merge/dispatch executor (§5/§6).
    FstatusNdispatch_idr2   r+   roler,   	COMPLETEDTzrole(record=z vs candidate=)z!fallback_callback_cron_id(record=u  an existing COMPLETED record shares the +47 idempotency key (dispatch_id, chat_id, normal_collector_cron_id) but has a DIFFERENT role/fallback binding — WRITEBACK_BINDING_CONFLICT. This is RECORDED, NOT a silent idempotent skip (§2 line 20/21 / regression 11). Conflicting: z; r   u   COMPLETED record with identical idempotency key AND identical role/fallback binding — a true duplicate; idempotent SKIP, no duplicate append (regression 12).u^   no COMPLETED record matches the +47 idempotency key — a normal new write-back (no conflict).)getattr	Exceptionr'   rf   r   rc   r   r   r   r
   r   r   )historyr(   r   r2   r+   candidate_rolecandidate_fallback_cron_idmatchedrt   r   r_dispatch_id	r_chat_idr_nccr_roler_fbfieldss                   r!    audit_writeback_binding_conflictr   P  s   @ G 0	Q$/F#A}d;M9d3IA94@EQ-F194@D [  [(I#g,.11G "F'"6*N%(+ 117x @!!; >aA 2* #=#!,0'-J ))F+, ?0b *"4$(!H
 	
 ', %3
 q  		s   AD99	EE) rc   r?   r
   r   rd   r   r   r   r   r   r   r   r   r   r   r   rI   ANU_KEY_2553DEFAULT_ANU_KEYSEXECUTOR_SELF_KEY_EXAMPLEPATH_DISPATCH_PYPATH_CRON_DIRECTre   r"   r&   rL   rr   rv   r   r   r   r   )r   r*   r    Sequence[str]rC   r   )"r(   r'   r-   r'   r.   r'   r/   r*   r0   r'   r+   r*   r,   r*   r)   r'   r2   r'   rQ   r   rP   r'   r    r   r5   r   r3   r'   r4   r'   ra   r   rC   rL   )r(   r'   r)   r'   r+   r*   r,   r*   r5   r   rC   rN   )r-   r'   r~   r*   rx   Optional[bool]rC   r   )r-   r'   r~   r*   r   r   r   r   rx   r   rC   rv   )
r-   r'   r~   r*   r   r   rx   r   rC   rv   )r   zSequence[object]r(   r'   r   r'   r2   r'   r+   r*   r   r'   r   r*   rC   r   )1rG   
__future__r   dataclassesr   r   typingr   r   r   r	   rc   r?   r
   r   rd   r   r   r   r   r   r   r   r   r   rH   r   r   rI   r   	frozensetr   r   r   r   re   r"   r&   rL   tuplerr   rg   rv   r   r   r   r   r   __all__rJ   r#   r!   <module>r      s  /` # ( 2 2:9   6 'M $3 3 9 3 "C 9  (#	( _ 	 0 7    "l^, 
 / 
 ( ) /1ABC . $0 0 0f 1
 1
 1
r *.
 (-&#$45);!3 $#xx x 	x
 'x x ,x  -x x x "&x x x x $'x  !x" #x$ $%xv  ,	
  -  : 
 
 
*### -# 
	#0  $ !/3,, , 	,
 , -, ,d  $!&/3  	
 - F 
 
 
6mm m 	m
 m ,m m !.m !m`!r#   