
    iE5                        d Z ddlZddl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d      Zedz  Zedz  d	z  Zed
z  Zedz  Zd ZdedefdZdededefdZdedefdZddededefdZdefdZdefdZd Zedk(  r e        yy)uD  
GENESIS EVOLUTION ENGINE  (GEN-028)
=====================================
Wires failure detection into the Genesis evolution cycle.

Every failure that flows through `on_failure_detected()` is:
  1. Logged to logs/failures.jsonl  (durable audit trail)
  2. Diagnosed — lesson extracted from the error context
  3. Axiom generated — a reusable rule to prevent recurrence
  4. Written to KNOWLEDGE_GRAPH/axioms/evolution_axioms.jsonl

This engine is the bridge between the failure detector and the living
knowledge base.  It implements RULE 14: "Failure Is Our Greatest Ally."

Relationship to existing engines:
  - evolution_engine.py       — deep 4-step diagnosis (Diagnose/Root-Cause/Pre-mortem/Evolve)
  - genesis_evolution_protocol.py — cross-session learning protocol (EvolutionProtocol class)
  - THIS FILE                 — lightweight hook-based failure wiring for GEN-028

Usage:
    from core.genesis_evolution_engine import on_failure_detected, record_lesson, get_evolution_stats

    on_failure_detected({
        "task_id": "RAI-009",
        "error": "Telnyx 422 — assistant clone failed",
        "context": "POST /ai/assistants/{id}/clone returned 422 Unprocessable"
    })

    # CLI:
    python3 genesis_evolution_engine.py --stats
    python3 genesis_evolution_engine.py --failure '{"task_id":"X","error":"timeout"}'
    N)datetimetimezone)Path)Optionalz/mnt/e/genesis-systemlogsKNOWLEDGE_GRAPHaxiomszfailures.jsonlzevolution_axioms.jsonlc                  `    t         j                  dd       t        j                  dd       y )NT)parentsexist_ok)LOGS_DIRmkdirKG_AXIOMS_DIR     6/mnt/e/genesis-system/core/genesis_evolution_engine.py_ensure_dirsr   4   s%    NN4$N/t4r   failure_eventreturnc                    t        | j                  dd            }t        | j                  dd            }t        | j                  dd            }d| dg}|r'|dd	 j                         }|j                  d
|        |r'|dd	 j                         }|j                  d|        |j	                         }d|v sd|v r|j                  d       nd|v sd|v sd|v sd|v r|j                  d       nd|v sd|v r|j                  d       nd|v sd|v sd|v r|j                  d       nd|v sd|v sd|v r|j                  d       nd|v sd |v sd!|v r|j                  d"       ngd#|v sd$|v sd%|v r|j                  d&       nId'|v sd(|v r|j                  d)       n/d*|v sd+|v sd,|v r|j                  d-       n|j                  d.       d/j                  |      S )0u   
    Derive a human-readable lesson from a failure event.

    Uses structured heuristics to extract meaning without requiring an LLM
    call at log time — keeping this path fast and side-effect free.
    error task_idunknowncontextzTask z failedNx   zwith error: z	Context: timeout	timed outzDLesson: Implement retry with exponential backoff for this operation.401403unauthorized	forbiddenzCLesson: Validate API credentials and token scopes before execution.422unprocessablezHLesson: Validate request payload schema against API spec before sending.
rate limit429too manyzILesson: Apply rate limiting / back-pressure before hitting external APIs.importmodule	no modulezOLesson: Verify all dependencies are installed and importable in the target env.
connectionrefusedunreachablezJLesson: Add health-check / circuit-breaker before dependent service calls.jsondecodeparsezHLesson: Always validate and sanitise external data before deserialising.keykeyerrorzHLesson: Use .get() with defaults; never assume external dict keys exist.nonenonetype	attributezGLesson: Guard against None values before attribute access or iteration.zFLesson: Add specific error handling and logging for this failure path.z  )strgetrstripappendlowerjoin)r   r   r   r   partsshort_error	short_ctxerror_lowers           r   _extract_lessonr@   9   s	    !!'2./E-##Iy9:G-##Ir23G WIW%&EDSk((*|K=12DSM((*	y,- ++-KK;+#=[\	+	+!5;9VZeitZtZ[	+	K!?_`		$(<
k@Y`a	[	 H$;{k?Yfg		$	[(@MU`D`ab	;	(k"9W=S_`	+	{!:_`	;	*";{k?Y^_]^99Ur   lessonc                 ^   t        | j                  dd            j                         }d|v sd|v ryd|v sd|v sd|v sd	|v ry
d|v sd|v ryd|v sd|v sd|v ryd|v sd|v sd|v ryd|v sd|v sd|v ryd|v sd|v sd|v ryd|v sd|v ry d!|v sd"|v sd#|v ry$| j                  d%d&      }d'| d(S ))u   
    Synthesise a concise, reusable axiom from the lesson.

    Axioms are written as imperative rules — short, memorable, actionable.
    r   r   r   r   z[ALWAYS retry time-sensitive operations with exponential backoff (base 1s, max 30s, jitter).r   r    r!   r"   zUALWAYS validate API credentials and required scopes at task start, not mid-execution.r#   r$   zPALWAYS schema-validate request payloads against the API contract before sending.r%   r&   r'   zWALWAYS implement back-pressure and rate limiting before calling external APIs at scale.r(   r)   r*   z_ALWAYS verify import availability in a try/except at module load, fail fast with clear message.r+   r,   r-   zWALWAYS wrap external service calls in a circuit breaker; degrade gracefully on failure.r.   r/   r0   zTALWAYS wrap JSON parsing in try/except and log raw input on failure for post-mortem.r2   r1   zOALWAYS use dict.get(key, default) and assert required keys at input boundaries.r3   r4   r5   zYALWAYS guard against None before attribute access; use Optional typing and early returns.r   r   zIALWAYS add explicit error handling for the failure mode observed in task .)r6   r7   r:   )r   rA   r   r   s       r   _generate_axiomrD   j   s    !!'2./557EE[E1l	%5E>^u-DW\H\f	%?e3a		%5.J%4Gh	U	h%/;%3Gp		)u"48Nh	5H-E1Ae	u	`	5J%/;%3Gj##Iy9Z[bZccdeer   c           
         t                dt        j                         j                  dd j	                          }t        |       }t        | |      }t        j                  t        j                        j                         }|| j                  dd      t        | j                  dd            t        | j                  dd            t        | j                  d	d            | j                  d
d      |||d	}t        t        dd      5 }|j!                  t#        j$                  |      dz          ddd       dt        j                         j                  dd j	                          || j                  dd      |||d}t        t&        dd      5 }|j!                  t#        j$                  |      dz          ddd       |S # 1 sw Y   xY w# 1 sw Y   |S xY w)a8  
    Primary hook: called whenever a failure is detected anywhere in Genesis.

    Performs:
      1. Assigns a unique failure_id
      2. Extracts a lesson from the failure context
      3. Generates a reusable axiom
      4. Persists to logs/failures.jsonl
      5. Writes axiom to KNOWLEDGE_GRAPH/axioms/evolution_axioms.jsonl

    Args:
        failure_event: dict with at minimum {"task_id": str, "error": str}
                       Optional keys: "context", "component", "severity"

    Returns:
        The full enriched failure record that was persisted.
    zFAIL-N   r   r   r   r   r   	componentseveritymedium)	
failure_idr   r   r   rG   rH   rA   axiom_generated	timestampautf-8encoding
AX-axiom_idsource_failure_idsource_task_idaxiomrA   generated_at)r   uuiduuid4hexupperr@   rD   r   nowr   utc	isoformatr7   r6   openFAILURES_LOG_PATHwriter.   dumpsEVOLUTION_AXIOMS_PATH)r   rJ   rA   rW   r]   failure_recordfaxiom_records           r   on_failure_detectedrh      s   $ N))"1-33567J]+FM62E
,,x||
$
.
.
0C ! $$Y	:]&&w34}((B78**;;<!%%j(; 
N 
w	7 31	

>*T123
 $**,**2A.44678''++IyAL 
#S7	; 1q	

<(4/01 3 31 s   (G(GGG!sourcec                    t                dt        j                         j                  dd j	                          d|| | t        j                  t        j                        j                         d}t        t        dd      5 }|j                  t        j                  |      dz          ddd       |S # 1 sw Y   |S xY w)	ac  
    Directly append a lesson (and derived axiom) to the evolution axioms log.
    Use this when a lesson is known upfront without a specific failure event.

    Args:
        lesson: The lesson string to record.
        source: Where this lesson came from (e.g. "manual", "review", task ID).

    Returns:
        The axiom record that was appended.
    rR   NrF   rS   rM   rN   rO   rQ   )r   rY   rZ   r[   r\   r   r]   r   r^   r_   r`   rd   rb   r.   rc   )rA   ri   rg   rf   s       r   record_lessonrk      s     N $**,**2A.44678!  X\\2<<>L 
#S7	; 1q	

<(4/01 1 s   (B77Cc                  h   t                g } t        j                         r[t        t        dd      5 }|D ]:  }|j	                         }|s	 | j                  t        j                  |             < 	 ddd       g }t        j                         r[t        t        dd      5 }|D ]:  }|j	                         }|s	 |j                  t        j                  |             < 	 ddd       i }| D ],  }|j                  dd      }|j                  |d      dz   ||<   . t        d	 |D              }t        d
 |D              }| r| d   nd}	t        |       t        |      ||||	dS # t        j                  $ r Y Hw xY w# 1 sw Y   xY w# t        j                  $ r Y w xY w# 1 sw Y   xY w)ai  
    Count lessons learned and axioms generated from failures.

    Returns:
        {
            total_failures_logged:   int,
            total_axioms_generated:  int,
            axioms_from_failures:    int,
            axioms_from_manual:      int,
            severity_breakdown:      dict,
            most_recent_failure:     dict | None
        }
    rrN   rO   NrH   rI   r      c              3   D   K   | ]  }|j                  d       d  ywrU   Nrn   r7   .0rM   s     r   	<genexpr>z&get_evolution_stats.<locals>.<genexpr>  s     [QAEE:M4N4Zq[     c              3   D   K   | ]  }|j                  d       d  ywrp   rq   rr   s     r   rt   z&get_evolution_stats.<locals>.<genexpr>  s     U1!%%8K2L2TQUru   )total_failures_loggedtotal_axioms_generatedaxioms_from_failuresaxioms_from_manualseverity_breakdownmost_recent_failure)r   ra   existsr`   stripr9   r.   loadsJSONDecodeErrorrd   r7   sumlen)
failuresrf   liner	   r|   f_recsevrz   r{   most_recents
             r   get_evolution_statsr      s    N H!#S7; 	!q !zz|! 

4(89	!	! F##%'w? 	!1 !zz|!djj&67	!	!  " Eii
H-"4"8"8a"@1"D3E [&[[UUU"*(2,K "%X"%f+ 400* 3  // ! !	! 	!$  // ! !	! 	!s_   F$E(1F$F(>$F"F((E?;F>E??FFF%"F($F%%F((F1statsc                    t        d       t        d| d           t        d| d           t        d| d           t        d| d	           | d
   rt        d| d
           | d   r| d   }t        d|d    d|d           t        dt        |d         d d         t        dt        |d         d d         t        dt        |d         d d         t        d|d           t                y )Nz'
=== GENESIS EVOLUTION ENGINE STATS ===z  Failures logged       : rx   z  Total axioms generated: ry   z    - from failures     : rz   z    - manual lessons    : r{   r|   z  Severity breakdown    : r}   z
  Most recent failure   : [rJ   z] r   z    Error  : r   P   z    Lesson : rA   d   z    Axiom  : rK   z    Time   : rL   )printr6   )r   mrfs     r   _print_statsr     s8   	
45	&u-D'E&F
GH	&u-E'F&G
HI	&u-C'D&E
FG	&u-A'B&C
DE!"*51E+F*GHI"#)*-c,.?-@3y>BRSTc#g,/4567c#h-0#6789c#&7"89$3?@ABc+./01	Gr   c                  B   t        j                  d      } | j                  ddd       | j                  ddd	
       | j                  ddd
       | j                  dddd       | j                         }|j                  rN	 t        j                  |j                        }t              }t        dt        j                  |d              |j                  rDt        |j                  |j                        }t        dt        j                  |d              |j                   s|j                  s#|j                  st#               }t%        |       y y y # t
        j                  $ r-}t        d|        t        j                  d       Y d }~d }~ww xY w)NuC   Genesis Evolution Engine — wire failures into the knowledge graph)descriptionz--stats
store_truezPrint evolution stats)actionhelpz	--failureJSONzIProcess a failure event. JSON string e.g. '{"task_id":"X","error":"msg"}')metavarr   z--lessonLESSONzRecord a manual lesson/axiomz--sourceSOURCEmanualz+Source label for --lesson (default: manual))r   defaultr   u(   ERROR: Could not parse failure JSON — rn   zFailure processed:
   )indent)ri   zLesson recorded:
)argparseArgumentParseradd_argument
parse_argsfailurer.   r   r   r   sysexitrh   rc   rA   rk   ri   r   r   r   )parserargseventerecordrecr   s          r   mainr   0  st   $$YF 	,=TU
Z  
 +  
 :	   D||	JJt||,E %U+$TZZq%A$BCD{{DKK<"4::c!#<"=>?zz$,,t{{#%U 0;, ## 	<QC@AHHQKK	s   E F1#FF__main__)r   )__doc__r.   r   rY   r   r   r   pathlibr   typingr   GENESIS_ROOTr   r   ra   rd   r   dictr6   r@   rD   rh   rk   r   r   r   __name__r   r   r   <module>r      s   B  
   '   +,& 008;// %(@@ 5
.4 .C .bf4 f f f>5t 5 5p# s $ :9T 9@ $'T zF r   