
    3jE\                       d Z ddlmZ ddlmZmZ ddlmZ ddlm	Z	m
Z
mZmZ ddlmZmZmZmZmZmZ dZdZd	Zd
ZdZ eeeh      ZdZdZdZdZ dZ!dZ"dZdZ#dZdZ$dZ% ed       G d d             Z&	 	 	 	 	 	 	 	 	 	 	 	 d*dZ'dddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d+d Z( ed       G d! d"             Z) G d# d$      Z*dd%	 	 	 	 	 d,d&Z+d-d'Z,g d(Z-y)).u*	  anu_v3.callback_event_trigger — normal-callback durable-success registry
write-back + read-only event-driven next_action resolver.

task-2553+47 (NORMAL_CALLBACK_REGISTRY_WRITEBACK_AND_EVENT_TRIGGER, 회장 결정).

Problem (회장 §2 / +45 실증): the +45 normal-callback collector finished at
14:24 but the durable 4-tuple ledger stayed ``role=dispatch / REGISTERED``
(``normal_collector_cron_id=null``), so ANU could not see the normal callback
as completed and the next step / summary was only recovered manually at
14:44. There was no write-back of the verified normal durable-success into
``callback_4tuple_index`` and no read-only event-trigger that detects a
COMPLETED record and computes the next action *without* a fixed-time gate or
a dead-man fallback as the progress trigger.

This module supplies the two missing seams (회장 §3):

  1. ``write_back_completed`` — the정정 표준 (a) normal-callback collector
     session, *after* verification, calls this exactly once to append a
     correctly-bound ``state=COMPLETED`` ledger record carrying the
     ``normal_collector_cron_id / fallback_cron_id / task_id / dispatch_id /
     chat_id / role`` binding. It uses the +44 registry's existing
     append-only API (``registry.append``) — the +44 module stays byte-0
     (additive-only carve-out unused; no escalation of the registry to a
     merge/dispatch executor, §6).

  2. ``CallbackEventTrigger.scan`` — a *read-only* resolver that detects a
     registry COMPLETED event and, when the dependency is satisfied,
     computes the next_action. **It never uses a fixed-time gate or a
     dead-man fallback as the progress trigger** (Layer A, 9-R.1): the only
     accepted progress trigger is the registry COMPLETED event. A dead-man
     signal is a missed-callback safety net, not a primary trigger; a
     fixed-time gate offered as the dependency trigger is rejected with
     ``FORBIDDEN_TRIGGER_SOURCE`` (regression §8.5).

Layer A / NO-CRON (9-R.1): this module performs ZERO cron register/remove,
ZERO dispatch, ZERO merge, ZERO ``cokacdir`` / ``subprocess`` exec. The only
write surface is the +44 append-only ledger (its designed Layer A role). A
next_action for which this session has no write authority is emitted as a
**proposal JSON only** — never auto-dispatched / merged / closed out (§6).
    )annotations)	dataclassfield)Path)DictListOptionalSequence)Callback4TupleRecordCallback4TupleRegistryNORMAL_CALLBACK_COMPLETEDNO_LEDGER_RECORDTRACK_MISMATCHmake_recordz#anu_v3.callback_4tuple_writeback.v1z anu_v3.callback_event_trigger.v1registry_completed_eventfixed_time_gatedead_man_fallbackWRITEBACK_COMPLETEDWRITEBACK_IDEMPOTENT_SKIPCALLBACK_MANDATORY_VIOLATIONNEXT_ACTION_READYNEXT_ACTION_DEFERREDNEXT_ACTION_BLOCKEDr   FORBIDDEN_TRIGGER_SOURCEr   noneproposalT)frozenc                  h    e Zd ZU dZded<   ded<   ded<   ded<   ded	<    ee
      Zded<   ddZy)WritebackResultz?Outcome of a single normal durable-success registry write-back.strschemastatustask_idboolappendedOptional[Dict[str, object]]recorddefault_factory	List[str]reasonsc                    | j                   | j                  | j                  | j                  | j                  t        | j                        dS )Nr!   r"   r#   r%   r'   r+   )r!   r"   r#   r%   r'   listr+   selfs    4/home/jay/workspace/anu_v3/callback_event_trigger.pyto_jsonzWritebackResult.to_jsonn   s;    kkkk||kkDLL)
 	
    NreturnDict[str, object]	__name__
__module____qualname____doc____annotations__r   r.   r+   r2    r3   r1   r   r   c   s3    IKKLN''t4GY4
r3   r   c                   | j                  |      D ]L  }|j                  dk(  s|j                  |k(  s#|j                  t	        |      k(  s<|j
                  |k(  sL y y)u   True iff an append-only COMPLETED line with the SAME normal-collector
    binding already exists (idempotency key — repeated write-back is a skip,
    never a duplicate append; regression §8.6).	COMPLETEDTF)history_forr"   dispatch_idchat_idr    normal_collector_cron_id)registryr#   rA   rB   rC   rs         r1   _completed_identity_presentrF   y   s]     !!'* HH#,		S\)**.FF r3   executorF )roleno_fallbackts_kstc       
        `   g }|s*|j                  d       t        t        t        |dd|      S t	        | ||||      rt        t        t
        |dddg      S t        ||||t        |      |||d|	|
      }| j                  |       t        t        t        |d	|j                         g       S )
u  Append a correctly-bound ``state=COMPLETED`` ledger record (회장 §3/§4.1).

    Called ONCE by the 정정 표준 (a) normal-callback collector session after
    it has verified the normal durable-success. Unlike a naive
    ``registry.mark_completed`` (which would copy a stale ``role=dispatch /
    normal_collector_cron_id=null`` line — the exact +45 14:24→14:44 defect),
    this binds the *verified* normal-collector identity:
    ``normal_collector_cron_id / fallback_cron_id / task_id / dispatch_id /
    chat_id / role``.

    Append-only & idempotent (§4.4 / §8.6): a repeated call with the same
    normal-collector binding is a ``WRITEBACK_IDEMPOTENT_SKIP`` — no duplicate
    line. ``normal_collector_cron_id`` missing -> ``CALLBACK_MANDATORY_
    VIOLATION`` with NO append (callback mandatory rule preserved, §8.10).

    Uses only the +44 registry's existing append-only API — the +44 module
    stays byte-0; the registry is NOT escalated to a merge/dispatch executor
    (§6).
    u   normal_collector_cron_id missing — MANDATORY executor normal completion callback lifecycle signal; write-back refused, callback mandatory rule NOT weakened (§8.10).FNr-   )r#   rA   rB   rC   ue   COMPLETED record with identical normal-collector binding already present — idempotent skip (§8.6).r?   )r#   rA   dispatch_cron_idrG   rB   rC   fallback_callback_cron_idrI   r"   rJ   rK   T)
appendr   WRITEBACK_SCHEMAr   rF   r   r   r    r   r2   )rD   r#   rA   rM   rG   rB   rC   rN   rI   rJ   rK   r+   recs                r1   write_back_completedrR      s    B G#=	

 #/
 	
 #!9 #,L M
 	
 !,)G!9";!C OOC"{{} 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<   ded<   ded<    ee      Zded<   ddZy)NextActionResultu  Read-only event-trigger verdict for one task.

    ``trigger_source`` is ALWAYS the registry COMPLETED event when ``ready``
    is True (9-R.1 Layer A). ``fixed_time_used`` / ``dead_man_used`` are
    explicit FALSE evidence (회장 §9-4 보고 항목).
    r    r!   r#   verdictr$   readyOptional[str]trigger_sourcefixed_time_useddead_man_usedfallback_pending_non_blockingsummary_candidater*   summary_inputs_completedr&   next_actionr(   r+   c                4   | j                   | j                  | j                  | j                  | j                  | j
                  | j                  | j                  | j                  t        | j                        | j                  t        | j                        dS )Nr!   r#   rU   rV   rX   rY   rZ   r[   r\   r]   r^   r+   )r!   r#   rU   rV   rX   rY   rZ   r[   r\   r.   r]   r^   r+   r/   s    r1   r2   zNextActionResult.to_json   sx    kk||||ZZ"11#33!//22!%!7!7(,T-J-J(K++DLL)
 	
r3   Nr4   r7   r=   r3   r1   rT   rT      s[     KLLK!!#'''',,t4GY4
r3   rT   c            	          e Zd ZdZddZ	 	 	 	 ddZddZdddeddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd
Z	y)CallbackEventTriggerup  Read-only event-driven next_action resolver over the +44 ledger.

    ZERO write (the only write surface is ``write_back_completed`` above,
    explicitly invoked once by the collector). ZERO cron / dispatch / merge /
    subprocess. ``scan`` is idempotent: repeated scans of the same ledger
    state yield an identical verdict and never append a record (§8.6).
    c                    || _         y )N)rD   )r0   rD   s     r1   __init__zCallbackEventTrigger.__init__  s	     r3   c                `    | j                   j                  |      }||j                  dk(  r|S y )Nr?   )rD   
latest_forr"   r0   r#   rQ   s      r1   _completed_recordz&CallbackEventTrigger._completed_record  s0     mm&&w/?szz[8Jr3   c                r    | j                   j                  |      }|y|j                  s|j                  syy)u  A fallback cron is bound and still pending (not yet cancelled).

        Layer A NO-CRON never cancels a fallback, so a bound fallback stays
        pending after normal durable-success until +45/+48 enacts the
        verified single cancel. This is surfaced ONLY to ASSERT non-blocking
        (§8.3): a pending fallback never blocks a completed task's progress.
        An ABSENT fallback (or an explicit no_fallback contract) returns
        False — it is never treated as a cancel target (§8.7).FT)rD   rf   rJ   rN   rg   s      r1   _fallback_pendingz&CallbackEventTrigger._fallback_pending'  s5     mm&&w/;??#"?"?r3   Ndispatch_next_taskF)expected_dispatch_idexpected_chat_idexpected_dispatch_cron_iddependency_trigger_sourceconsolidated_inputsnext_action_kindnext_action_payloaddead_man_signalc       	           g }
|t         v r4|
j                  d|d       t        t        |t        ddddddg d|
      S | j
                  j                  ||||      }|t        k(  r0|
j                  d       t        t        |t        ddddddg d|
      S | j                  |      }|t        k7  s|f|t        k(  rt        }|
j                  d       nt        }|
j                  d	       |	r|
j                  d
       t        t        ||ddddddg d|
      S | j                  |      }|r|
j                  d       g }d}|ro|D ]7  }| j
                  j                  |      t        k(  s'|j                  |       9 t        |      t        t!        |            k(  }|r|
j                  d       | j#                  ||||||      }|
j                  d       t        t        |t$        dt&        dd|||||
      S )u  Detect a registry COMPLETED event and compute the next_action.

        * ``dependency_trigger_source`` MUST be ``registry_completed_event``.
          A ``fixed_time_gate`` / ``dead_man_fallback`` offered as the
          dependency trigger -> ``FORBIDDEN_TRIGGER_SOURCE`` (FAIL, §8.5).
        * No COMPLETED record -> ``NEXT_ACTION_DEFERRED`` (fail-safe wait;
          a ``dead_man_signal`` alone does NOT promote this to ready —
          §8.4: dead-man is a safety net, not a primary trigger).
        * COMPLETED + identity match -> ``NEXT_ACTION_READY``. A pending
          fallback is recorded non-blocking (§8.3) and an absent fallback
          is never treated as a cancel target (§8.7).
        * ``consolidated_inputs`` all COMPLETED -> ``summary_candidate``
          emitted immediately (§3 consolidated summary).
        * The emitted ``next_action`` is a proposal only (no write/dispatch
          authority — §6); idempotent across repeated scans (§8.6).
        zdependency_trigger_source=u~    is forbidden as a progress/dependency-stage trigger; only registry_completed_event is permitted (회장 §6 / 9-R.1 Layer A).FNr`   )r#   rl   rm   rn   ul   registry identity mismatch — an unrelated track's callback is never cited as this task's progress (§8.9).un   no ledger record — fail-safe DEFER (defer to schedule_history + canonical artifact; not a progress trigger).un   registry record present but not COMPLETED — wait for the normal-callback durable-success write-back (§8.4).u   a dead-man signal was observed but is EXPLICITLY NOT promoted to a progress trigger — safety net only (§8.4, 회장 §6 verbatim). ready stays False.u   fallback binding pending — recorded NON-BLOCKING; the completed task proceeds (§8.3). A pending/absent fallback is never a cancel target here (§8.7, Layer A NO-CRON).)r#   uY   all consolidated inputs COMPLETED — summary candidate emitted immediately (회장 §3).)r#   	completedkindpayloadr\   r]   u   registry COMPLETED event detected — next_action computed via the registry_completed_event trigger ONLY (no fixed-time gate, no dead-man fallback; 9-R.1 Layer A).T)FORBIDDEN_PROGRESS_TRIGGERSrO   rT   EVENT_TRIGGER_SCHEMAr   rD   classifyREG_TRACK_MISMATCHr   rh   r   REG_NO_LEDGER_RECORDr   r   rj   lenr.   _build_proposalr   TRIGGER_REGISTRY_COMPLETED)r0   r#   rl   rm   rn   ro   rp   rq   rr   rs   r+   verdict_regru   rU   
fb_pendingr]   r\   depr^   s                      r1   scanzCallbackEventTrigger.scan7  s   :   %(CCNN,-F,I J  $+0# %#.3"')+    mm,,!5-&?	 - 
 ,,NNB $+&# %#.3"')+   **73	33y7H 22*) /N G
 $+# % $.3"')+  $ ++G4
NNJ /1 !* 9MM**3*701 -33C89 !$$< =()B ! !8
 **!'/%= + 
 	4	

  '%5!*4/%=#
 	
r3   c                   dt         t        d||t        |j                  |j                  |j
                  |j                  |j                  |j                  |j                  d|t        |      t        |xs i       ddS )u   Build a write-authority-free next_action **proposal** (§6).

        This resolver never auto-dispatches / merges / closes out. The
        proposal is data only; an authorized session must enact it.
        z,anu_v3.callback_event_trigger.next_action.v1F)r#   rA   rM   rC   rN   rB   rI   u   PROPOSAL ONLY — write 권한 없는 next_action 은 proposal JSON 으로만 산출(자동 dispatch/merge/closeout 0, 회장 §3/§6). An authorized session must enact this.)r!   	authorityaction_modeauto_executedrv   source_task_idrX   bindingr\   r]   rw   note)AUTHORITY_NONEACTION_MODE_PROPOSALr   r#   rA   rM   rC   rN   rB   rI   r.   dict)r0   r#   ru   rv   rw   r\   r]   s          r1   r~   z$CallbackEventTrigger._build_proposal  s      E'/"%8$,,(44$-$>$>66 77$,,! "3(,-E(FGMr*93
 	
r3   )rD   r   r5   None)r#   r    r5   zOptional[Callback4TupleRecord])r#   r    r5   r$   )r#   r    rl   rW   rm   rW   rn   rW   ro   r    rp   zOptional[Sequence[str]]rq   r    rr   r&   rs   r$   r5   rT   )r#   r    ru   r   rv   r    rw   r&   r\   r$   r]   r*   r5   r6   )
r8   r9   r:   r;   rd   rh   rj   r   r   r~   r=   r3   r1   rb   rb     s    !	'( /3*.37)C7; 4;? %q
 q
 ,	q

 (q
 $1q
 $'q
 5q
 q
 9q
 q
 
q
f,
 ,
 (	,

 ,
 -,
  ,
 #,,
 
,
r3   rb   )generated_at_kstc               @    d|t         t        d| j                         dS )um   Serialize a NextActionResult as the standalone next-action-proposal
    artifact (회장 §5 expected_files).z$task-2553+47.next-action-proposal.v1F)r!   r   r   r   r   result)r   r   r2   )r   r   s     r1   proposal_enveloper     s'     9,#+.." r3   c                *    t        t        |             S )zEConvenience: build a read-only registry over an explicit ledger path.)r   r   )ledger_paths    r1   load_registryr   (  s    !${"344r3   )rP   ry   r   TRIGGER_FIXED_TIMETRIGGER_DEAD_MANrx   r   r   r   r   r   r   r   r   r   r   rT   rR   rb   r   r   N)rD   r   r#   r    rA   r    rB   r    rC   r    r5   r$   )rD   r   r#   r    rA   r    rM   r    rG   r    rB   r    rC   rW   rN   rW   rI   r    rJ   r$   rK   r    r5   r   )r   rT   r   r    r5   r6   )r5   r   ).r;   
__future__r   dataclassesr   r   pathlibr   typingr   r   r	   r
   anu_v3.callback_4tuple_registryr   r   r   r   r|   r   r{   r   rP   ry   r   r   r   	frozensetrx   r   r   r   r   r   r   r   r   r   r   rF   rR   rT   rb   r   r   __all__r=   r3   r1   <module>r      s  'P # (  1 1  9 9  8  ' & ')* 
 , 7   >  (  . + !5 %  !  $
 
 
*$  	
  " 
@ W$W W 	W
 W W W ,W  -W W W W Wt $%
 %
 %
PD
 D
P :<365
r3   