
     iw                        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  ej                  e      ZdZe dZe d	Ze d
Ze dZe G d d             Z G d d      Zy)u  
core/merge/merge_telemetry.py

MergeTelemetry — observability layer for the SemanticMergeInterceptor pipeline.

Tracks:
    - Total merge count (Redis counter: genesis:merge:stats:total)
    - Conflict count   (Redis counter: genesis:merge:stats:conflicts)
    - Opus invocation  (Redis counter: genesis:merge:stats:opus_calls)
    - Latency list     (Redis list:    genesis:merge:stats:latencies)
    - Full event log   (append-only JSONL: data/observability/events.jsonl)

Design notes:
    - Redis client is injected (optional). When None, only the in-memory
      latency list and events.jsonl are used. Conflict / Opus counters cannot
      be tracked without Redis in the no-Redis fallback mode (they stay at 0).
    - Division-by-zero is always guarded: rates return 0.0 when total == 0.
    - MergeRecord is a plain dataclass serialisable via dataclasses.asdict().

# VERIFICATION_STAMP
# Story: 7.05
# Verified By: parallel-builder
# Verified At: 2026-02-25
# Tests: 12/12
# Coverage: 100%
    )annotationsN)	dataclassasdict)Optional)Pathzgenesis:merge:statsz:totalz
:conflictsz:opus_callsz
:latenciesc                  N    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<   y)MergeRecordz:Immutable record describing one completed merge operation.str
session_idintdelta_countconflict_countbool	used_opusfloatmerge_latency_mssuccessN)__name__
__module____qualname____doc____annotations__     3/mnt/e/genesis-system/core/merge/merge_telemetry.pyr	   r	   7   s&    DOOMr   r	   c                  <    e Zd ZdZ	 	 d	 	 	 ddZd	dZd
dZddZy)MergeTelemetryaB  
    Observability collector for merge operations.

    Args:
        redis_client: An optional redis.Redis instance (or compatible mock).
                      When *None*, only the in-memory latency list and
                      events.jsonl are used.
        events_path:  Path to the append-only JSONL event log.
    Nc                @    || _         t        |      | _        g | _        y )N)redisr   events_path
_latencies)selfredis_clientr    s      r   __init__zMergeTelemetry.__init__S   s!    
 "
, (*r   c           	        | j                   | j                   j                  t               |j                  dkD  r| j                   j                  t               |j
                  r| j                   j                  t               | j                   j                  t        t        |j                               | j                  j                  |j                         | j                  |       t        j                  d|j                   |j"                  |j                  |j
                  |j                  |j$                         y)z
        Record a completed merge event.

        Increments Redis counters (when available) and appends the full
        MergeRecord to events.jsonl.

        Args:
            merge_result: A fully populated MergeRecord from the caller.
        Nr   zZMergeTelemetry.record: session=%s deltas=%d conflicts=%d opus=%s latency=%.2fms success=%s)r   incr
_KEY_TOTALr   _KEY_CONFLICTSr   _KEY_OPUS_CALLSrpush_KEY_LATENCIESr
   r   r!   append_write_eventloggerdebugr   r   r   )r"   merge_results     r   recordzMergeTelemetry.recordc   s     ::!JJOOJ'**Q.

/%%

0JJ^S1N1N-OP 	|<<= 	,'0##$$''""))  		
r   c                ~   | j                   t        | j                   j                  t              xs d      }t        | j                   j                  t              xs d      }t        | j                   j                  t
              xs d      }| j                   j                  t        dd      }|r|D cg c]  }t        |       c}ng }n.t        | j                        }d}d}t        | j                        }||dkD  r||z  dz  nd|dkD  r||z  dz  nd|rt        |      t        |      z  dS ddS c c}w )u\  
        Return aggregated merge statistics.

        Returns:
            dict with keys:
                total_merges      (int)
                conflict_rate_pct (float) — 0.0 when total is 0
                opus_rate_pct     (float) — 0.0 when total is 0
                avg_latency_ms    (float) — 0.0 when no latencies recorded
        r   d   g        )total_mergesconflict_rate_pctopus_rate_pctavg_latency_ms)r   r   getr'   r(   r)   lranger+   r   lenr!   listsum)r"   total	conflicts
opus_callsrawx	latenciess          r   	get_statszMergeTelemetry.get_stats   s    ::!

z27a8EDJJNN>:?a@ITZZ^^O<ABJ**##NAr:C@C%<1eAh%<I (EIJT__-I ">Cai)e"3c"9S;@19j5036#CLs9~I>	
 	
 SV	
 	
 &=s   8D:c                   	 | j                   j                  j                  dd       t        j                  t        |      d      }| j                   j                  dd      5 }|j                  |dz          d	d	d	       y	# 1 sw Y   y	xY w# t        $ r+}t        j                  d
| j                   |       Y d	}~y	d	}~ww xY w)u   
        Append *record* as a single JSON line to events.jsonl.

        Creates parent directories if they do not exist.
        Failures are logged as warnings — telemetry must never crash the
        caller.
        T)parentsexist_okF)ensure_asciiazutf-8)encoding
Nu9   MergeTelemetry._write_event: could not write to %s — %s)r    parentmkdirjsondumpsr   openwriteOSErrorr.   warning)r"   r1   linefhexcs        r   r-   zMergeTelemetry._write_event   s    
	##))$)F::fVn5AD!!&&sW&= &%& & & 	NNK   	s6   A$B &B;B B	B B 	C!B??C)Nzdata/observability/events.jsonl)r    r
   returnNone)r0   r	   rW   rX   )rW   dict)r1   r	   rW   rX   )r   r   r   r   r$   r1   rD   r-   r   r   r   r   r   H   s9     <
* 
* 
	
* "
H
Hr   r   )r   
__future__r   rN   loggingdataclassesr   r   typingr   pathlibr   	getLoggerr   r.   REDIS_PREFIXr'   r(   r)   r+   r	   r   r   r   r   <module>ra      s   6 #   )  			8	$ %~V$
 >,!N+. >,    u ur   