
    i.                        U d Z ddlmZ ddlZddlmZ ddlmZ ddlmZm	Z	m
Z
 h dZded	<    G d
 de      ZddZddZddZddZddZddZddZ G d d      Z e       Z ed       G d d             Zy)u  
core/coherence/state_delta.py

StateDelta — RFC 6902 JSON Patch proposal for multi-agent coherence.

This module provides:
  - VALID_OPS: the 6 RFC 6902 operation names
  - PatchConflictError: raised when an "op: test" assertion fails
  - validate_patch(patch): True/False validation of RFC 6902 structure
  - apply_patch(state, patch): apply a patch list to a state dict, returning a NEW dict
  - StateDelta: frozen dataclass representing a proposed state change

No external libraries (jsonpatch, etc.) are used. All 6 RFC 6902 operations
are implemented manually against plain Python dicts.

RFC 6902 operations:
  add     — set value at path (creates intermediate keys as needed for "" root;
              for list indices, appends if index == "-")
  remove  — delete value at path (must exist)
  replace — set value at path (must already exist)
  move    — remove from "from" path + add at "path"
  copy    — copy value from "from" path to "path"
  test    — assert that value at path equals "value"; raise PatchConflictError if not

# VERIFICATION_STAMP
# Story: 6.01
# Verified By: parallel-builder
# Verified At: 2026-02-25
# Tests: 10/10
# Coverage: 100%
    )annotationsN)	dataclass)datetime)AnyListOptional>   addcopymovetestremovereplaceset	VALID_OPSc                  $     e Zd ZdZd fdZ xZS )PatchConflictErrorz/Raised when an RFC 6902 'test' operation fails.c                `    || _         || _        || _        t        |   d| d|d|       y )NzPatch conflict at z: expected z, got )pathexpectedactualsuper__init__)selfr   r   r   	__class__s       3/mnt/e/genesis-system/core/coherence/state_delta.pyr   zPatchConflictError.__init__6   s;    	  k(VF:N	
    )r   strr   r   r   r   returnNone)__name__
__module____qualname____doc__r   __classcell__)r   s   @r   r   r   3   s    9
 
r   r   c                    | dk(  rg S | j                  d      st        d| d      | dd j                  d      }|D cg c]$  }|j                  dd      j                  dd	      & c}S c c}w )
a5  
    Convert an RFC 6902 JSON Pointer path to a list of keys/indices.

    Examples:
        ""        -> []
        "/"       -> [""]
        "/a"      -> ["a"]
        "/a/b"    -> ["a", "b"]
        "/a/0/b"  -> ["a", "0", "b"]

    Tilde escaping per RFC 6901:
        "~1" -> "/"
        "~0" -> "~"
     /zInvalid JSON Pointer path: u    — must start with '/'   Nz~1z~0~)
startswith
ValueErrorsplitr   )r   tokensts      r   _parse_pathr/   D   sr     rz	??36th>VWXX!"X^^C F=CDAIIdC ((s3DDDs    )A,c                   | }|D ]w  }t        |t              r||vrt        |      ||   }(t        |t              rt	        |t        |      d      }||   }Ut        dt        |      j                   d|       |S )zu
    Traverse obj along keys and return the final value.
    Raises KeyError / IndexError if any key is missing.
    Fallow_appendzCannot traverse into z
 with key )
isinstancedictKeyErrorlist_list_indexlentyper    )objkeyscurrentkeyidxs        r   _get_atr?   \   s    
 G 	^gt$'!sm#clG&c3w<eDCclG24=3I3I2J*UXT[\]]	^ Nr   c                L   |st        d      t        | |dd       }|d   }t        |t              r|||<   yt        |t              r=t        |t        |      d      }|t        |      k(  r|j                  |       y|||<   yt        dt        |      j                         )z
    Set obj[keys[-1]] = value, traversing obj along keys[:-1].
    Mutates obj in-place (caller must pass a deep copy).
    For list targets, index "-" appends.
    Raises KeyError / IndexError / TypeError on bad traversal.
    z=Cannot set root value via _set_at (use special-case handling)NTr1   zCannot set key on )r+   r?   r3   r4   r6   r7   r8   append	TypeErrorr9   r    )r:   r;   valueparent	final_keyr>   s         r   _set_atrG   o   s     XYYS$s)$FRI&$!y	FD	!)S[tD#f+MM% F3K,T&\-B-B,CDEEr   c                V   |st        d      t        | |dd       }|d   }t        |t              r ||vrt	        |      |j                  |      S t        |t              r(t        |t        |      d      }|j                  |      S t        dt        |      j                         )z
    Remove and return the value at keys[-1] in obj (traversing keys[:-1]).
    Mutates obj in-place (caller must pass a deep copy).
    Raises KeyError / IndexError if path does not exist.
    zCannot remove root valueNrA   Fr1   zCannot remove key from )r+   r?   r3   r4   r5   popr6   r7   r8   rC   r9   r    )r:   r;   rE   rF   r>   s        r   
_remove_atrJ      s     344S$s)$FRI&$F"9%%zz)$$	FD	!)S[uEzz#1$v,2G2G1HIJJr   c                    | dk(  r|r|S t        d      	 t        |       }|dk  s||k\  rt        d| d| d      |S # t        $ r t        d|       w xY w)z
    Convert a string key to a list index.
    "-" means append (index == length) when allow_append=True.
    Raises IndexError for out-of-range or ValueError for non-integer.
    -z/'-' index not allowed for get/remove operationszInvalid list index: r   zList index z out of range (length ))
IndexErrorintr+   r5   )r=   lengthr2   r>   s       r   r7   r7      s     czMJKK7#h Qw#-;se+A&KLLJ	  7-cW5667s	   ? Ac                @   t        | t        t        f      sy| D ]  }t        |t              s yd|vr y|d   }|t        vr yd|vst        |j                  d      t              s y|dv r%d|vst        |j                  d      t              s y|dv s}d|vs y y)	u&  
    Validate an RFC 6902 patch (list of operation dicts).

    Returns True if the patch is structurally valid, False otherwise.
    Does NOT raise — validation failures return False.

    Rules:
      - patch must be a list
      - every element must be a dict
      - every dict must have "op" key
      - "op" value must be in VALID_OPS
      - "path" key must be present and be a string
      - "move" and "copy" ops require a "from" key
      - "add", "replace", "test" ops require a "value" key
      - "remove" does NOT require "value"
    Fopr   )r   r
   from)r	   r   r   rD   T)r3   r6   tupler4   r   getr   )patchop_dictrR   s      r   validate_patchrX      s    " edE]+ '4(wT]Y 
7;;v3F(L!!W$Jw{{67JC,P++g%  r   c           	        t        j                  |       }|D ]  }|j                  d      }|t        vrt	        d|      |j                  dd      }t        |      }|dk(  r&|d   }	 t        ||      }||k7  set        |||      |dk(  r+t        j                  |d         }	|s|	}t        |||	       |d	k(  r|st	        d
      t        ||       |dk(  rL|st        j                  |d         }t        ||       t        ||t        j                  |d                |dk(  r1|d   }
t        |
      }t        ||      }|s|}:t        |||       I|dk(  sP|d   }
t        |
      }t        j                  t        ||            }|s|}t        |||        |S # t        t        f$ r
 t        }Y Pw xY w)a  
    Apply an RFC 6902 patch to state dict. Returns a NEW dict (original unchanged).

    Supported operations: add, remove, replace, move, copy, test

    Raises:
        PatchConflictError: if "test" op fails (value mismatch)
        KeyError:           if path does not exist for remove/replace/test
        ValueError:         if patch is structurally invalid
        TypeError:          if an operation targets a non-container
    rR   zUnknown op: r   r&   r   rD   )r   r   r   r	   r   z!Cannot 'remove' the root documentr   r   rS   r
   )r
   deepcopyrU   r   r+   r/   r?   r5   rN   _MISSINGr   rG   rJ   )staterV   resultrW   rR   r   r;   r   r   rD   	from_path	from_keysmoved_valuecopied_values                 r   apply_patchrb      s    ]]5!F 74[[Y|B6233{{62&4 <w'H" . !(dXfUU5[MM''"23Ee,8^ !DEEvt$9_ww'78 %dmmGG4D&EF6\I#I.I$VY7K$k26\I#I.I==)CDL%l3o74r M[ j) "!"s   +F//GGc                      e Zd Zd Zy)_MissingTypec                     y)Nz	<MISSING> )r   s    r   __repr__z_MissingType.__repr__+  s    r   N)r    r!   r"   rg   rf   r   r   rd   rd   *  s    r   rd   T)frozenc                  L    e Zd ZU dZded<   ded<   ded<   ded<   d	ed
<   ddZy)
StateDeltaur  
    Immutable proposal for a state change, expressed as an RFC 6902 JSON Patch.

    Attributes:
        agent_id:        ID of the agent proposing this change
        session_id:      Session this change belongs to
        version_at_read: The state version this patch was built against
        patch:           Tuple of RFC 6902 operation dicts (frozen for hashability)
        submitted_at:    When this delta was submitted

    The patch field uses a tuple (not list) so the frozen dataclass remains
    hashable. Convert to list before calling apply_patch if needed — or use
    the convenience method apply_to().
    r   agent_id
session_idrO   version_at_readrT   rV   r   submitted_atc                @    t        |t        | j                              S )aJ  
        Apply this delta's patches to state dict.
        Returns a NEW dict. The original state dict is never mutated.

        Raises:
            PatchConflictError: if a 'test' operation fails
            KeyError:           if a required path does not exist
            ValueError:         if the patch is malformed
        )rb   r6   rV   )r   r\   s     r   apply_tozStateDelta.apply_toN  s     5$tzz"233r   N)r\   r4   r   r4   )r    r!   r"   r#   __annotations__rp   rf   r   r   rj   rj   7  s)     MOL
4r   rj   )r   r   r   r6   )r:   r   r;   r6   r   r   )r:   r   r;   r6   rD   r   r   r   )r=   r   rP   rO   r2   boolr   rO   )rV   r6   r   rr   )r\   r4   rV   r6   r   r4   )r#   
__future__r   r
   dataclassesr   r   typingr   r   r   r   rq   	Exceptionr   r/   r?   rG   rJ   r7   rX   rb   rd   r[   rj   rf   r   r   <module>rw      s   @ #  !  & & F	3 E	
 	
"E0&F.K*0#VHX 
 > $ 4  4  4r   