
    3j                        d Z ddlm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mZmZmZ dZdZd	Zd
ZddZ	 	 	 	 	 	 	 	 	 	 ddZe G d d             Z	 	 	 	 	 	 	 	 ddZg dZy)u  anu_v3.enactor_idempotency — deterministic enact-id + idempotent-skip ledger
for the bounded runtime-event enactor (task-2553+55 §2.9).

회장 §2.9 verbatim: "같은 proposal 재처리 시 idempotent." The bounded enactor
must be a pure deterministic function of the proposal candidate: re-processing
the *same* proposal must NOT produce a second additive artifact, a duplicate
decision, or a re-write. This module supplies:

  * ``enact_id(...)`` — a deterministic content hash over the *normative*
    fields of a proposal (proposal_type · source_id · settle identity ·
    additive artifact target). Free-form note text is deliberately excluded
    so a cosmetic note change never forces a duplicate enact.
  * ``already_enacted(...)`` — reads an existing additive artifact (if any)
    READ-ONLY and reports whether it already carries this exact ``enact_id``
    (idempotent skip) or a *different* one at the same path (a binding
    conflict — never silently overwritten).

The module performs ZERO writes itself — it only *decides* idempotency. The
caller (the enactor) performs the allowlisted additive write only when this
module reports ``should_write is True``.

Layer A / NO-CRON: pure. ZERO cron / dispatch / subprocess / cokacdir /
merge / PR / branch / credential.
    )annotationsN)	dataclassfield)Path)DictListOptionalSequencezanu_v3.enactor_idempotency.v1FIRST_WRITEIDEMPOTENT_SKIPENACT_BINDING_CONFLICTc                4    t        j                  | ddd      S )z@Stable JSON canonicalisation (sorted keys, no whitespace drift).FT),:)ensure_ascii	sort_keys
separators)jsondumps)values    1/home/jay/workspace/anu_v3/enactor_idempotency.py
_canonicalr   )   s    ::ETj     c                    t        | |t        |      |d      }t        j                  |j	                  d            j                         S )aI  Deterministic enact identity.

    ``settle_identity`` = the normative tuple that makes this enact unique
    (e.g. batch_id + sorted track task_ids + all_authoritative_pass). It is
    canonicalised so element ordering / dict key ordering never perturbs the
    hash. The same proposal -> the same id -> an idempotent skip.
    )proposal_type	source_idsettle_identityartifact_targetutf-8)r   listhashlibsha256encode	hexdigest)r   r   r   r   payloads        r   enact_idr&   0   sG     *"#O4.		
G >>'..1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	<    ee
      Zded<   edd       ZddZ	y)IdempotencyDecisionstrschemar&   r   classificationboolshould_writezOptional[str]existing_enact_id)default_factoryz	List[str]reasonsc                (    | j                   t        k(  S )N)r+   r   selfs    r   idempotent_skipz#IdempotencyDecision.idempotent_skipS   s    ""o55r   c           	         | j                   | j                  | j                  | j                  | j                  | j
                  t        | j                        dS )Nr*   r&   r   r+   r-   r.   r0   )r*   r&   r   r+   r-   r.   r    r0   r2   s    r   to_jsonzIdempotencyDecision.to_jsonW   sJ    kk#33"11 --!%!7!7DLL)
 	
r   N)returnr,   )r8   zDict[str, object])
__name__
__module____qualname____annotations__r   r    r0   propertyr4   r7    r   r   r(   r(   I   sJ    KM$$t4GY46 6	
r   r(   c                   t        t        |            |z  }|j                         st        t        | |t
        dddg      S 	 t        j                  |j                  d            }t        |t              r+|j                  d      rt        |j                  d            nd}|| k(  rt        t        | |t        d|d	g      S t        t        | |t        d|d
|d| dg      S # t        t        f$ r d}Y Uw xY w)u  READ-ONLY idempotency probe over an existing additive artifact.

    * absent target               -> FIRST_WRITE  (should_write=True)
    * present & embedded id == eid -> IDEMPOTENT_SKIP (should_write=False,
                                      byte-0 — never re-written)
    * present & embedded id != eid -> ENACT_BINDING_CONFLICT (should_write
                                      =False — a different enact already
                                      owns this path; never silently
                                      overwritten, escalated by the caller)
    * present but unreadable/no id -> ENACT_BINDING_CONFLICT (fail-closed —
                                      an opaque pre-existing file at the
                                      additive path is never clobbered)
    TNuh   additive artifact absent — first deterministic enact (additive create only; no existing file touched).r6   r   )encodingr&   Fu   additive artifact already carries this exact enact_id — idempotent skip; the file is left byte-0 (no re-write, no duplicate decision; 회장 §2.9).z<a DIFFERENT enact already owns this additive path (existing=z != uz   ) — never silently overwritten; the enactor escalates instead of clobbering (additive-only invariant, 회장 §2.5/§6).)r   r)   is_filer(   IDEMPOTENCY_SCHEMAIDEMPOTENT_FIRST_WRITEr   loads	read_text
isinstancedictgetOSError
ValueErrorr   IDEMPOTENT_BINDING_CONFLICT)eidr   workspace_rootpathexistingexisting_ids         r   already_enactedrQ   c   s)   & N#$6D<<>"%+1"D
 	
::dnngn>? (D)hll:.F Z() 	 c"%+*)5
 	
 !'2%$tC7 3;;
 ! Z  s   A"C( (C<;C<)rB   rC   r   rK   r&   r(   rQ   )r   objectr8   r)   )
r   r)   r   r)   r   zSequence[object]r   r)   r8   r)   )rL   r)   r   r)   rM   rR   r8   r(   )__doc__
__future__r   r!   r   dataclassesr   r   pathlibr   typingr   r   r	   r
   rB   rC   r   rK   r   r&   r(   rQ   __all__r>   r   r   <module>rY      s   0 #   (  1 14 & #6 ?? ? &	?
 ? 	?2 
 
 
2E	E E 	E
 EPr   