
     j]I                       d Z ddlmZ ddlZddl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 G d dee      ZeZ G d dee      Z G d dee      Z eej0                  j2                  ej4                  j2                  h      Z eej8                  j2                  ej:                  j2                  ej<                  j2                  h      Z ed eD              Z  ed eD              Z! ed eD              Z"e Z# G d dee      Z$ ed e$D              Z%d$dZ&d%dZ'd%dZ(d&dZ)d&dZ*d'dZ+dZ,d(d Z-d)d!Z.d*d"Z/d+d#Z0y),u[  utils.callback_envelope_schema — normal completion callback envelope schema.

task-2635 — normal callback registration enforcement (Core hardening).
task-2635+1 — status schema 5축 분리 정정 (회장 PARTIAL_PASS_WITH_REQUIRED_FIX).

Spec: memory/specs/system_normal_callback_registration_implementation_spec_260523.md
sha256: 0fbd1dad1e110c49474dfbdf13a21fb3bdd9c7f094128004dba8472840bb832d

회장 verbatim (task-2635 §1):
    3. result.json 에 ``normal_callback_registration_status`` 필수화
    4. status enum 정의 (5값)
    5. ``NOT_REGISTERED`` / ``SENDFILE_ONLY`` 는 완료 성공으로 보지 않도록
       fail-closed

회장 verbatim (task-2635+1 §1):
    단일 ``registration_status`` 필드 의미가 흔들리므로 5축으로 분리한다.
    registration_intent · registration_attempted · registration_result_status ·
    callback_delivery_status · collector_receipt_status

ANCHOR-2 (task-2635+1): status schema 5축 분리 — 단일 필드 의미 흔들림 차단.
ANCHOR-3 (task-2635+1): 모순 조합 (schedule_id 존재 + NOT_REGISTERED 등)
regression FAIL 단언.
    )annotationsN)Enum)AnyDictListTuplez!utils.callback_envelope_schema.v2z!utils.callback_envelope_schema.v1z/home/jay/workspacei<  i
  i  c119085addb0f8b7c                  $    e Zd ZdZdZdZdZdZdZy)RegistrationResultStatusu   task-2635+1 §1 정본 — 시도 결과 enum (5값).

    fail-closed 동작:
        REGISTERED / SKIPPED_WITH_EXPLICIT_REASON → 완료 성공
        NOT_REGISTERED / SENDFILE_ONLY / REGISTER_FAILED → 완료 실패
    
REGISTEREDNOT_REGISTEREDREGISTER_FAILEDSENDFILE_ONLYSKIPPED_WITH_EXPLICIT_REASONN)	__name__
__module____qualname____doc__r   r   r   r   r        R/home/jay/workspace/.worktrees/task-2729-p2-dev6/utils/callback_envelope_schema.pyr   r   0   s#     J%N'O#M#A r   r   c                       e Zd ZdZdZdZdZdZy)CallbackDeliveryStatusu<   task-2635+1 §1 — 봇→cokacdir→ANU 전달 단계 enum.PENDING	DELIVEREDUNDELIVEREDNOT_APPLICABLEN)r   r   r   r   r   r   r   r   r   r   r   r   r   G   s    FGIK%Nr   r   c                       e Zd ZdZdZdZdZdZy)CollectorReceiptStatusu5   task-2635+1 §1 — ANU collector 수령 단계 enum.UNCONFIRMEDRECEIVED	TIMED_OUTr   N)r   r   r   r   r    r!   r"   r   r   r   r   r   r   Q   s    ?KHI%Nr   r   c              #  4   K   | ]  }|j                     y wNvalue.0ss     r   	<genexpr>r*   j   s     JAJ   c              #  4   K   | ]  }|j                     y wr$   r%   r'   s     r   r*   r*   k   s     !Ja!''!Jr+   c              #  4   K   | ]  }|j                     y wr$   r%   r'   s     r   r*   r*   l   s      IQ Ir+   c                      e Zd ZdZdZdZdZy)DeliveryMethodanu_cron_callbacksendfile_onlybothnoneN)r   r   r   ANU_CRON_CALLBACKr   BOTHNONEr   r   r   r/   r/   t   s    +#MDDr   r/   c              #  4   K   | ]  }|j                     y wr$   r%   )r(   ms     r   r*   r*   {   s      AQ Ar+   c                    | t         j                  j                  S t        | t               r| j                  S t	        |       j                         }|st         j                  j                  S |S )u   Coerce input to a string enum value (axis-3), defaulting → NOT_REGISTERED.

    spec §2 마지막 줄 — 미명시 시 자동 NOT_REGISTERED.
    )r   r   r&   
isinstancestrstrip)
raw_statustexts     r   normalize_statusr?   ~   s_    
 '66<<<*67z?  "D'66<<<Kr   c                    | t         j                  j                  S t        | t               r| j                  S t	        |       j                         }|st         j                  j                  S |S )uD   Axis-4 normalizer. Missing → NOT_APPLICABLE (fail-closed default).)r   r   r&   r:   r;   r<   rawr>   s     r   normalize_delivery_statusrC      X    
{%44:::#-.yys8>>D%44:::Kr   c                    | t         j                  j                  S t        | t               r| j                  S t	        |       j                         }|st         j                  j                  S |S )uD   Axis-5 normalizer. Missing → NOT_APPLICABLE (fail-closed default).)r   r   r&   r:   r;   r<   rA   s     r   normalize_receipt_statusrF      rD   r   c                $    t        |       t        v S )u   fail-closed 분기 단일 함수 (spec §3.2).

    True iff registration_result_status ∈ {REGISTERED, SKIPPED_WITH_EXPLICIT_REASON}.
    Unknown / missing → False (fail-closed default).
    )r?   SUCCESS_STATUSES)statuss    r   is_success_statusrJ      s     F#'777r   c                :    t        |       }|t        v ry|t        vS )z?True iff status forces executor exit != 0 + fallback no-cancel.T)r?   FAIL_CLOSED_STATUSESALL_RESULT_STATUSES)rI   norms     r   is_fail_closed_statusrO      s$    F#D##***r   c                f    ddl }|j                  | dd      }t        |j                  d            S )zFReturn the UTF-8 byte length of the canonical envelope representation.r   NFT)ensure_ascii	sort_keyszutf-8)jsondumpslenencode)envelope_jsonpayloads      r   envelope_utf8_byte_countrZ      s-    kk($kGGw~~g&''r   )task_idexecutor_nameresult_pathreport_pathattempted_callback_registrationregistration_statusregistration_result_statusregistration_intentregistration_attemptedcallback_delivery_statuscollector_receipt_statusdelivery_methodc                r    d| v rt        | j                  d            S t        | j                  d            S )zRead axis-3 from an envelope, preferring the new field but accepting
    the legacy ``registration_status`` for backward-compat callers.ra   r`   )r?   get)rW   s    r   _axis_3_valueri      s6     $x/-I JKKHLL)>?@@r   c                R   t        | t              sdgS g }t        |       }t        | j	                  d            }t        | j	                  d            }| j	                  d      }t        | j	                  d| j	                  d                  }t        | j	                  d            }|t        j                  j                  k(  r|r|j                  d       |t        j                  j                  k(  r|s|j                  d       |sK|t        j                  j                  t        j                  j                  fv r|j                  d	|d
       |t        j                  j                  k(  r|du r|j                  d       |t        j                  j                  k(  r2|t        j                  j                  k7  r|j                  d|d       |t         j"                  j                  k(  r2|t        j                  j                  k7  r|j                  d|d       |S )u  task-2635+1 §7 — 6 모순 조합 단언. Returns the human-readable mismatches.

    Each contradiction in this list is a hard FAIL — the envelope is internally
    inconsistent and the executor must not exit-0 on it.

    1. NOT_REGISTERED + cron_schedule_id != None         (registered yet unregistered)
    2. REGISTERED     + cron_schedule_id == None         (no proof of registration)
    3. attempted=False + result in (REGISTERED, REGISTER_FAILED)  (impossible)
    4. SENDFILE_ONLY + attempted_callback_registration=True       (channel mismatch)
    5. delivery=DELIVERED + result != REGISTERED                  (delivered nothing)
    6. receipt=RECEIVED  + delivery != DELIVERED                  (received air)
    envelope is not a dictrd   re   cron_schedule_idrc   r_   zbNOT_REGISTERED + cron_schedule_id present (envelope claims unregistered but carries a schedule id)zXREGISTERED + cron_schedule_id missing (claims registered but no schedule id to prove it)z<registration_attempted=False but registration_result_status=z2 (REGISTERED/REGISTER_FAILED require attempt=True)TzsSENDFILE_ONLY + attempted_callback_registration=True (sendfile-only path must not have attempted cron registration)zBcallback_delivery_status=DELIVERED but registration_result_status=z+ (delivery only meaningful when REGISTERED)z?collector_receipt_status=RECEIVED but callback_delivery_status=z4 (collector cannot receive what was never delivered))r:   dictri   rC   rh   rF   boolr   r   r&   appendr   r   r   r   r   r   r!   )rW   contradictionsresultdeliveryreceiptschedule_id	attemptedattempted_legacys           r   detect_status_contradictionsrw      s    h%()) "N8$F(6P)QRH&x||4N'OPG,,12K
 -x||<]/^_I HLL)JKL )88>>>;G	
 )44:::;A	
  ++11 0066$  	J6* U@ @	
 	*88>>>$M	
 	*44:::.99???PQWPZ [9 9	
 	)22888.88>>>Mh\ ZB B	

 r   c                .   g }t        | t              sddgfS t        D ]  }|| vs|j                  d|         | j	                  d| j	                  d            }|Qt        |      }|t        vr'|j                  d|dt        t               d       n|t        j                  j                  k(  r"| j	                  d	      s|j                  d
       |t        j                  j                  k(  r"| j	                  d      s|j                  d       |t        j                  j                  k(  r"| j	                  d      s|j                  d       |t        j                  j                  k(  r=| j	                  d      t        j                  j                  k7  r|j                  d       | j	                  d      }|8t        |      }|t         vr%|j                  d|dt        t                d       | j	                  d      }|8t#        |      }|t$        vr%|j                  d|dt        t$               d       | j	                  d      }	|	-|	t&        vr%|j                  d|	dt        t&               d       d| v r| j	                  d      }
|
t        |
t(              s(|j                  dt+        |
      j,                          nH|
s|j                  d       n4t.        j0                  j3                  |
      s|j                  d|
d       |j5                  t7        |              | j	                  d      }| j	                  d      }|1|/t        |      t        |      k7  r|j                  d|d|d       | j	                  d      }| j	                  d      }|1|/t9        |      t9        |      k7  r|j                  d |d!|d       t;        |       }|t<        kD  r|j                  d"| d#t<         d$       | |fS )%u   Return (ok, errors). Spec §3.2 + task-2635+1 §7.

    Checks include all 5-axis fields, REQUIRED_ENVELOPE_KEYS, enum range,
    cross-axis contradictions, and the UTF-8 byte budget.
    Frk   zrequired key missing: ra   r`   z$registration_result_status invalid: z (expected one of )rl   uY   cron_schedule_id required when registration_result_status == REGISTERED (task-2635+1 §2)error_messageuV   error_message required when registration_result_status == REGISTER_FAILED (spec §4.2)explicit_skip_reasonuj   explicit_skip_reason required when registration_result_status == SKIPPED_WITH_EXPLICIT_REASON (spec §4.2)rf   zndelivery_method must be 'sendfile_only' when registration_result_status == SENDFILE_ONLY (ANCHOR-3 separation)rd   z"callback_delivery_status invalid: re   z"collector_receipt_status invalid: zdelivery_method invalid: canonical_rootz-canonical_root must be str when present: got u=   canonical_root must be non-empty when present (task-2636 §4)z&canonical_root must be absolute path: u    (task-2636 §4)z(registration_status alias drift: legacy=z vs registration_result_status=z (must agree)rc   r_   z5axis-2 attempted alias drift: registration_attempted=z$ vs attempted_callback_registration=zenvelope UTF-8 byte size z > hard limit u    (spec §3.1))r:   rm   REQUIRED_ENVELOPE_KEYSro   rh   r?   rM   sortedr   r   r&   r   r   r   r/   rC   ALL_DELIVERY_STATUSESrF   ALL_RECEIPT_STATUSESALL_DELIVERY_METHODSr;   typer   ospathisabsextendrw   rn   rZ   ENVELOPE_BYTE_LIMIT)rW   errorskeyrI   rN   delivery_axisnorm_dreceipt_axisnorm_rrf   r|   legacynewnew_attemptedlegacy_attempted
byte_counts                   r   validate_enveloper   2  s:    Fh%/000% :hMM23%89:
 \\6EZ8[\F'**MM6vj@R-./q2
 /::@@@||$67MM7 /??EEE||O4MM7 /LLRRR||$:;MM'
 /==CCC<< 12n6R6R6X6XXMM\ LL!;<M *=9..MM4]4EEW/014 << :;L),7--MM4\4DDV./03 ll#45O">R'R''::L*+,A/	
 8#!&67%nc2CN+4457 $&
 ww}}^4MM@),,<> MM.x89 \\/0F
,,3
4Cco2B62JN^_bNc2c6vj A**-?	
 LL!9:M||$EF!(4(8#99CMCT U//?.B-Q	

 *(3J'''
|>"#=2	

 Jr   c                    t        | t              syt        |       }|t        j                  j
                  k7  ryt        | j                  d            S )u8  High-level predicate: was a real normal callback registered?

    True iff registration_result_status == REGISTERED **and** cron_schedule_id present.
    SKIPPED_WITH_EXPLICIT_REASON is treated as success for fail-closed gating but
    is NOT a real callback firing — this predicate distinguishes the two.
    Frl   )r:   rm   ri   r   r   r&   rn   rh   )rW   rN   s     r   is_callback_completer     sH     h%"D'22888/011r   )r=   objectreturnr;   )rB   r   r   r;   )rI   r   r   rn   )rW   r   r   int)rW   zDict[str, Any]r   r;   )rW   r   r   z	List[str])rW   r   r   zTuple[bool, List[str]])rW   r   r   rn   )1r   
__future__r   r   enumr   typingr   r   r   r   SCHEMA	SCHEMA_V1CANONICAL_ROOT_DEFAULTr   ENVELOPE_WARN_LOWERENVELOPE_WARN_UPPERANU_CALLBACK_KEYr;   r    NormalCallbackRegistrationStatusr   r   	frozensetr   r&   r   rH   r   r   r   rL   rM   r   r   ALL_STATUSESr/   r   r?   rC   rF   rJ   rO   rZ   r}   ri   rw   r   r   r   r   r   <module>r      s  . # 	  ) )	,/	 /      & BsD B& $<  &S$ &&S$ &  ++11 ==CC  ! //55 ..44 0066   J1IJJ !!J3I!JJ   I2H II  #
S$  ! A. AA 		8+(  AVrE P2r   