
    i6                         d 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Zej                  j                  dd       ddlmZ dZdZ eh d	      Z eh d
      Z G d de      Z G d d      Zy)u  
QueenRegistry — AIVA's identity store.
Reads Queen identity from Postgres with Redis cache.
Logs capability gains and retrieves capability history.

AIVA RLM Nexus — Stories 2.01 + 2.02, Track A
File: core/registry/queen_registry.py

RULE COMPLIANCE:
  - Rule 7:  No SQLite — all storage via Elestio Postgres/Redis
  - Rule 6:  E: drive only — module lives at /mnt/e/genesis-system/core/registry/
  - Rule 3:  Verification stamp present at bottom of file
    N)datetimetimezone)Optionalz/mnt/e/genesis-system)connectionszaiva:identityi,  >   	bug_fixed	new_skillimproved_recallpattern_learned>   namerolevoice_modellast_evolvedactive_capabilitiesrecent_improvementstotal_conversationsc                       e Zd ZdZy)RegistryErrorzHRaised when registry operations fail unrecoverably (e.g. Postgres down).N)__name__
__module____qualname____doc__     5/mnt/e/genesis-system/core/registry/queen_registry.pyr   r   8   s    Rr   r   c                       e Zd ZdZdddZdefdZddZ	 	 ddeded	ed
edef
dZ	dde
defdZdee   fdZdeddfdZdefdZy)QueenRegistryu  
    AIVA Queen identity registry.

    Provides a single, authoritative view of AIVA's identity.

    Read path:
      1. Try Redis cache (CACHE_KEY). Hit → decode JSON and return.
      2. Miss → query Postgres (royal_conversations + aiva_capability_log).
      3. Write result back to Redis (CACHE_TTL seconds).

    Failure semantics:
      - Redis failure at any point: non-fatal, silently skipped.
      - Postgres failure: raises RegistryError.
    Nreturnc                 "    |xs t         | _        y)z
        Args:
            connection_factory: A ConnectionFactory instance.
                                Defaults to the module-level singleton ``connections``.
                                Inject a mock for unit tests.
        N)r   _cf)selfconnection_factorys     r   __init__zQueenRegistry.__init__Q   s     &4r   c                 p    | j                         }||S | j                         }| j                  |       |S )u  
        Return AIVA's current identity object.

        Tries Redis cache first; falls back to Postgres on miss or Redis error.

        Returns:
            dict with exactly these keys:
                name                (str)   — always "AIVA"
                role                (str)   — human-readable role label
                voice_model         (str)   — env-overrideable voice model ID
                total_conversations (int)   — COUNT(*) from royal_conversations
                last_evolved        (str)   — ISO-8601 timestamp of most recent capability log
                active_capabilities (list)  — list[str] of currently active capability names
                recent_improvements (list)  — list[str] of the 5 most recent capability descriptions

        Raises:
            RegistryError: when Postgres is unavailable or returns an unexpected error.
        )_try_cache_read_read_from_postgres_try_cache_write)r    cachedidentitys      r   get_identityzQueenRegistry.get_identity^   sB    , %%'M
 ++-
 	h'r   c                     	 | j                   j                         }|j                  t               y# t        $ r Y yw xY w)u   
        Delete the Redis identity cache entry.

        Redis failure is silently swallowed — invalidation is best-effort.
        N)r   	get_redisdelete	CACHE_KEY	Exception)r    rediss     r   invalidate_cachezQueenRegistry.invalidate_cache   s7    	HH&&(ELL# 		s   /2 	>>descriptioncapability_typemetricsepoch_idc                 *   |t         vrt        d| dt        t                      t        t	        j
                               }t        j                  t        j                        }||ni }	 | j                  j                         }|j                         }	|	j                  d|||||t        j                  |      f       |j!                          |	j#                          | j)                          |S # t$        $ r}
t'        d|
       |
d}
~
ww xY w)a  
        Log a new capability gain to ``aiva_capability_log``.

        Validates ``capability_type`` before touching the database.
        After a successful write, invalidates the Redis identity cache so
        that the next ``get_identity()`` call reflects the new log entry.

        Args:
            description:      Human-readable description of the capability gained.
            capability_type:  One of VALID_CAPABILITY_TYPES.
            metrics:          Optional dict of supporting metrics. Stored as JSONB.
                              Defaults to ``{}`` (never NULL in Postgres).
            epoch_id:         Optional epoch identifier string.

        Returns:
            log_id: UUID string for the newly inserted row.

        Raises:
            ValueError:    If ``capability_type`` is not in VALID_CAPABILITY_TYPES.
            RegistryError: If the Postgres write fails.
        zInvalid capability_type 'z'. Must be one of: Nz
                INSERT INTO aiva_capability_log
                    (log_id, logged_at, capability_type, description, epoch_id, metrics)
                VALUES
                    (%s, %s, %s, %s, %s, %s)
                zFailed to log capability gain: )VALID_CAPABILITY_TYPES
ValueErrorsortedstruuiduuid4r   nowr   utcr   get_postgrescursorexecutejsondumpscommitcloser.   r   r0   )r    r1   r2   r3   r4   log_id	logged_atsafe_metricsconncurexcs              r   log_capability_gainz!QueenRegistry.log_capability_gain   s   : "88+O+< =##)*@#A"BD 
 TZZ\"LL.	")"5w2	R88((*D++-CKK #JJ|,  KKMIIK
 	  	R"A# GHcQ	Rs   .A5C5 5	D>DDlast_nc           	         	 | j                   j                         }|j                         }|j                  d|f       |j	                         }|j                          g }|D ]v  }|\  }}	}
}}t        |d      r|j                         }nt        |      }t        |t              r|}n|rt        j                  |      }ni }|j                  ||	|
||d       x |S # t        $ r}t        d|       |d}~ww xY w)u  
        Return the last N capability gains, ordered newest first.

        Args:
            last_n: Maximum number of rows to return. Defaults to 10.

        Returns:
            List of dicts, each containing:
                log_id          (str)   — UUID of the log entry
                description     (str)   — human-readable capability description
                capability_type (str)   — one of VALID_CAPABILITY_TYPES
                logged_at       (str)   — ISO-8601 timestamp
                metrics         (dict)  — supporting metrics (empty dict if none)

        Raises:
            RegistryError: If the Postgres query fails.
        z
                SELECT log_id, description, capability_type, logged_at, metrics
                FROM aiva_capability_log
                ORDER BY logged_at DESC
                LIMIT %s
                z$Failed to fetch capability history: N	isoformat)rE   r1   r2   rF   r3   )r   r>   r?   r@   fetchallrD   r.   r   hasattrrN   r9   
isinstancedictrA   loadsappend)r    rL   rH   rI   rowsrJ   resultrowrE   r1   r2   rF   r3   logged_at_strmetrics_dicts                  r   get_capability_historyz$QueenRegistry.get_capability_history   s   $	W88((*D++-CKK 	 <<>DIIK  	CGJDFK)W y+. ) 3 3 5 #I '4(&#zz'2!MM *#2*' #	2 ;  	W"Fse LMSVV	Ws   AC 	C;'C66C;c                     	 | j                   j                         }|j                  t              }|rt	        j
                  |      S 	 y# t        $ r Y yw xY w)z
        Attempt to read identity from Redis.

        Returns:
            Parsed dict if a valid cache entry exists, otherwise None.
        N)r   r+   getr-   rA   rS   r.   )r    r/   raws      r   r$   zQueenRegistry._try_cache_read  sY    	HH&&(E))I&Czz#&    		s   AA
 
	AAr(   c                     	 | j                   j                         }|j                  t        t        t        j                  |t                     y# t        $ r Y yw xY w)zk
        Attempt to write identity dict to Redis with TTL.

        Failure is silently swallowed.
        )defaultN)	r   r+   setexr-   	CACHE_TTLrA   rB   r9   r.   )r    r(   r/   s      r   r&   zQueenRegistry._try_cache_write'  sF    	HH&&(EKK	9djj3.OP 		s   AA 	AAc                 x   	 | j                   j                         }|j                         }|j                  d       |j	                         d   }|j                  d       |j                         }|D cg c]  }|d   	 }}|r1|d   d   }t        |d      r|j                         }n=t        |      }n1t        j                  t        j                        j                         }|j                          dd	t!        j"                  d
d      ||g d|dS c c}w # t        $ r}	t        d|	       |	d}	~	ww xY w)u  
        Build the identity dict from Postgres tables.

        Queries:
            royal_conversations     → COUNT(*) for total_conversations
            aiva_capability_log     → 5 most-recent rows for recent_improvements
                                      + last_evolved timestamp

        Returns:
            Fully-populated identity dict.

        Raises:
            RegistryError: wraps any Postgres exception.
        z(SELECT COUNT(*) FROM royal_conversationsr   z
                SELECT description, logged_at
                FROM aiva_capability_log
                ORDER BY logged_at DESC
                LIMIT 5
                   rN   zFailed to read Queen identity: NAIVAu   Queen AI — Lead OrchestratorAIVA_VOICE_MODELztelnyx-eucalyptus)voice_telephonymemory_persistenceknowledge_graphweb_researchtelegram_interfacecron_scheduling)r   r   r   r   r   r   r   )r   r>   r?   r@   fetchonerO   rP   rN   r9   r   r<   r   r=   rD   r.   r   osgetenv)
r    rH   rI   r   rU   rW   r   raw_tsr   rJ   s
             r   r%   z!QueenRegistry._read_from_postgres3  s5   #	R88((*D++-C KKBC'*||~a'8 KK <<>D;?(@CQ(@(@ a6;/(.(8(8(:L#&v;L'||HLL9CCEIIK 499%79LM#6($ $7
 	
% )A  	R"A# GHcQ	Rs+   A3D 5DA6D D 	D9%D44D9)N)r   N)NN)
   )r   r   r   r   r"   rR   r)   r0   r9   rK   intlistrZ   r   r$   r&   r%   r   r   r   r   r   A   s    5$d $L
  BB B 	B
 B 
BH=S =$ =F$  
 
$ 
C
T C
r   r   )r   rA   rm   r:   r   r   typingr   syspathinsertcore.db.connectionsr   r-   ra   	frozensetr6   REQUIRED_FIELDSr.   r   r   r   r   r   <module>rz      s{     	  '  
 * + + 		 # $     	I 	u
 u
r   