
    &ii             
       >   U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
mZmZ ddlmZmZmZ ddlmZ ddlmZmZmZmZmZ  ee      j3                         j4                  j4                  Zedz  dz  Zej:                  j=                  d ee             ej:                  j=                  d ee              e ed	
            Z dZ!dZ"dZ#dZ$dZ%dZ&g dZ'dgZ(ddiddiddiddidZ)eeeee*f   f   e+d<   dZ,dZ-dZ.edz  d z  Z/edz  d!z  Z0edz  d"z  Z1ed#z  d$z  d%z  Z2e/d&z  Z3e/e0e1e2j4                  fD ]  Z4e4jk                  d'd'(         ejl                  ejn                  d) ejp                   ee/d*z        d+,       ejr                  ejt                        g-        ejv                  d.      Z<e
 G d/ d0             Z=e
 G d1 d2             Z>d3e?fd4Z@d5eeef   d3e?fd6ZAd5eeef   d3e?fd7ZBd5eeef   d3e?fd8ZCeAeBeCd9ZDd: ZEdTd;ZFd<ZGd3efd=ZH G d> d?      ZI G d@ dA      ZJ G dB dC      ZK G dD dE      ZL G dF dG      ZM G dH dI      ZNdTdJZOdUdKePd3e*fdLZQdMee   d3ee   fdNZRd3ee   fdOZSdTdPZTd3ee>   fdQZUdTdRZVeWdSk(  r eV        yy)Va  
Alpha Evolve Daemon -- Autonomous Nightly Evolution System
===========================================================

Runs at 03:00 AEST daily. Applies Darwinian selection pressure to Genesis
revenue-generating assets (Phase 1: email templates via Instantly.ai).

6-Phase Cycle:
  HARVEST -> CULL -> MUTATE -> TEST -> ASCEND -> LOG

Usage:
  python scripts/alpha_evolve_daemon.py --cycle       # Run one evolution cycle
  python scripts/alpha_evolve_daemon.py --dry-run     # Simulate without changes
  python scripts/alpha_evolve_daemon.py --status      # Show current generation status
  python scripts/alpha_evolve_daemon.py --daemon      # Run as daemon (03:00 AEST daily)

Environment:
  INSTANTLY_API_KEY         Instantly.ai API key (fallback hardcoded for Phase 1)
  GEMINI_API_KEY            Gemini API key for mutation generation
  ALPHA_EVOLVE_PAUSE=true   Skip current cycle
  ALPHA_EVOLVE_DRY_RUN=true Log only, no changes
  ALPHA_EVOLVE_MUTATIONS=5  Mutations per elite (default 5)

Safety:
  - Minimum 3 survivors per asset type (never cull all)
  - Archive, never delete
  - Dry-run by default unless --cycle or --daemon is given
  - Pause flag checked at start of every cycle
  - NO SQLite -- PostgreSQL only (Elestio Bloodstream)
  - All paths on E: drive

CRITICAL: This script does NOT activate or unpause the Instantly.ai campaign.
          It only READS analytics from the API.

Version: 1.0.0
Phase: 1 (Email template evolution only)
    N)	dataclassasdictfield)datetimetimezone	timedelta)Path)AnyDictListOptionalTupledatazgenesis-memory
   )hoursz1.0.0      皙?g333333?   )email_templatevoice_promptwidget_configexecution_scriptr   sent2   total_calls   impressions   runs   MIN_DATA_THRESHOLDSz$cf21ba92-4c8c-443c-b67e-4b6530024118zDMjBjODUxNGYtNjA5MC00NjY4LWFhY2UtOWZmYTE3NDhhMmQ1OnRTYkpqRU94RHJveg==zgemini-2.5-prohivelogsprogressjules_evolve_tasksKNOWLEDGE_GRAPHaxiomszalpha_evolve_daemon.jsonlzmajor_improvements.logTparentsexist_okz3%(asctime)s [%(levelname)s] %(name)s -- %(message)szalpha_evolve.logutf-8encoding)levelformathandlersalpha_evolvec                       e Zd ZU dZeed<   eed<   eeef   ed<   eed<   e	e   ed<   e
ed<   eed<   eeef   ed	<   eed
<    ee      Zeeef   ed<   y)EvolutionAssetzA single evolvable asset.asset_id
asset_typecontentversion	parent_idfitness_scorestatusmetrics
created_at)default_factorymetadataN)__name__
__module____qualname____doc__str__annotations__r   r
   intr   floatr   dictr?        4/mnt/e/genesis-system/scripts/alpha_evolve_daemon.pyr4   r4   }   sa    #MO#s(^L}K#s(^O$T:Hd38n:rJ   r4   c                       e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   eed<   eed	<   ee   ed
<   ee   ed<   e	ed<   y)CycleReportzSummary of one evolution cycle.cycle_id
started_atcompleted_atassets_scoredassets_culledmutations_generatedmutations_tested
promotionsmajor_improvementserrorsdry_runN)
r@   rA   rB   rC   rD   rE   rF   r   r   boolrI   rJ   rK   rM   rM      sK    )MOOT
"IMrJ   rM   returnc                 J    |syt        t        |       t        |      z  d      S )z8Safe division returning 0.0 if denominator is 0 or None.           )roundrG   )	numeratordenominators     rK   safe_dividera      s$    y!E+$66::rJ   r<   c                    | j                  dd      }|dk  ryt        | j                  dd            }t        | j                  dd            }t        | j                  dd            }t        | j                  dd            }t        | j                  d	d            }|d
z  |dz  z   |dz  z   d|z
  dz  z   d|z
  dz  z   }t        d|dz        }t        ||z  d      S )a}  
    Composite fitness score for email templates.
    Range: 0.0 to 1.0 (higher = better)

    Weights:
      open_rate        20% -- necessary but not sufficient
      reply_rate       40% -- primary engagement signal
      conversion_rate  30% -- ultimate business value
      bounce_rate       5% -- deliverability health
      spam_rate         5% -- reputation protection
    r   r   r   r\   	open_rate
reply_rateconversion_ratebounce_rate	spam_rater   皙?333333?      ?g?g      Y@r]   getrG   minr^   )	r<   r   rc   rd   re   rf   rg   raw
confidences	            rK   email_fitnessrp      s     ;;vq!Dbygkk+q12Iw{{<34JGKK(91=>OM156Kgkk+q12I 	D
t
	
D
 	! 
$	% ?d
"		#  S$,'Jz!1%%rJ   c                    | j                  dd      }|dk  ryt        | j                  dd            }t        | j                  dd            }t        | j                  dd            }t        | j                  d	d
            }|dk  rd}n*|dk  rd|dz
  dz  dz  z   }n|dk  rd|dz
  dz  dz  z   }nd
}d
|z
  dz  |dz  z   |dz  z   |dz  z   }t        d
|dz        }t        ||z  d      S )zb
    Composite fitness score for voice agent prompts.
    Range: 0.0 to 1.0 (higher = better)
    r   r   r   r\   avg_duration_secbooking_ratesentiment_avgg      ?hangup_before_30s_raterj   r   皙?   ri   g     b@rh   iX  gffffff?g     @z@g333333?r   g      ?g      >@r]   rk   )	r<   r   avg_durationrs   	sentimenthangup_rateduration_scorern   ro   s	            rK   voice_fitnessr|      s,   
 ++mQ/KQ%7;<L^Q78Lgkk/378I$<cBCK b		r 1U:S@@		s 2e;cAA 
{	d"
4
	 

	 d
	  S+,-Jz!1%%rJ   c                     | j                  dd      }|dk  ryt        | j                  dd            }t        | j                  dd            }|dz  |dz  z   }t        d	|d
z        }t        ||z  d      S )zd
    Composite fitness score for widget configurations.
    Range: 0.0 to 1.0 (higher = better)
    r   r   r   r\   interaction_ratelead_capture_raterh   g333333?rj   g     @@r]   rk   )r<   r   r~   r   rn   ro   s         rK   widget_fitnessr      s    
 ++mQ/KRW[[);Q?@gkk*=qAB
T
!$5$<
<CS+-.Jz!1%%rJ   r   r   r   c                     	 ddl m}  ddl} |j                  di | j	                         }t
        j                  d       |S # t        $ r#}t
        j                  d| d       Y d}~nd}~ww xY w	 ddl	m
} ddl} |       }|j                  |j                  |j                  |j                  |j                  |j                  |j                         }t
        j                  d	       |S # t        $ r}t#        d
|       |d}~ww xY w)zt
    Connect to Elestio PostgreSQL Bloodstream.
    Uses elestio_config as primary, secrets_loader as fallback.
    r   )PostgresConfigNz*Connected to PostgreSQL via elestio_configz"elestio_config connection failed: z -- trying secrets_loader)get_postgres_config)hostportuserpassworddbnamesslmodez*Connected to PostgreSQL via secrets_loaderzCannot connect to PostgreSQL: rI   )elestio_configr   psycopg2connectget_connection_paramsloginfo	Exceptionwarningcore.secrets_loaderr   r   r   r   r   r   r   RuntimeError)r   r   connesl_pgcfgs         rK   get_pg_connectionr   	  s    
W1xI."F"F"HI=> W8;TUVVWHDg\\::KK   
 	=> H;A3?@aGHs1   A A 	A/A**A/3A:C. .	D7DDc                    t         dz  dz  }|j                         s t        j                  d| d       t        }n|j                  d      }	 | j                         }|j                  |       | j                          t        j                  d       y	# t        $ r2}| j                          t        j                  d|        Y d	}~y	d	}~ww xY w)
z}
    Create genesis schema and all required tables if they do not yet exist.
    Idempotent -- safe to call every cycle.
    scriptszalpha_evolve_create_tables.sqlzDDL file not found at z -- using embedded DDLr,   r-   z0Schema ensured (genesis tables created/verified)z8Schema DDL partially failed (tables may already exist): N)GENESIS_ROOTexistsr   r   _EMBEDDED_DDL	read_textcursorexecutecommitr   r   rollback)r   ddl_fileddlcurr   s        rK   ensure_schemar   )  s    
 i'*JJH??,XJ6LMN  ' 2TkkmCCD TNqcRSSTs   AB 	C (CCa  
CREATE SCHEMA IF NOT EXISTS genesis;

CREATE TABLE IF NOT EXISTS genesis.evolution_assets (
    asset_id        VARCHAR(255) PRIMARY KEY,
    asset_type      VARCHAR(50)  NOT NULL,
    content         JSONB        NOT NULL,
    version         INTEGER      NOT NULL DEFAULT 1,
    parent_id       VARCHAR(255),
    fitness_score   FLOAT        NOT NULL DEFAULT 0.0,
    status          VARCHAR(20)  NOT NULL DEFAULT 'active',
    metrics         JSONB        NOT NULL DEFAULT '{}',
    metadata        JSONB        NOT NULL DEFAULT '{}',
    created_at      TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ,
    archived_at     TIMESTAMPTZ,
    archived_reason VARCHAR(100),
    superseded_by   VARCHAR(255)
);

CREATE INDEX IF NOT EXISTS idx_evo_assets_type_status
    ON genesis.evolution_assets(asset_type, status);
CREATE INDEX IF NOT EXISTS idx_evo_assets_fitness
    ON genesis.evolution_assets(fitness_score DESC);
CREATE INDEX IF NOT EXISTS idx_evo_assets_parent
    ON genesis.evolution_assets(parent_id);

CREATE TABLE IF NOT EXISTS genesis.evolution_cycles (
    cycle_id            VARCHAR(20)  PRIMARY KEY,
    started_at          TIMESTAMPTZ  NOT NULL,
    completed_at        TIMESTAMPTZ  NOT NULL,
    assets_scored       INTEGER      NOT NULL DEFAULT 0,
    assets_culled       INTEGER      NOT NULL DEFAULT 0,
    mutations_generated INTEGER      NOT NULL DEFAULT 0,
    mutations_tested    INTEGER      NOT NULL DEFAULT 0,
    promotions          INTEGER      NOT NULL DEFAULT 0,
    major_improvements  JSONB        DEFAULT '[]',
    errors              JSONB        DEFAULT '[]',
    dry_run             BOOLEAN      NOT NULL DEFAULT FALSE,
    cycle_version       VARCHAR(20)  NOT NULL
);

CREATE TABLE IF NOT EXISTS genesis.mutation_test_results (
    mutation_id         VARCHAR(255) PRIMARY KEY,
    parent_id           VARCHAR(255) NOT NULL,
    asset_type          VARCHAR(50)  NOT NULL,
    mutation_content    JSONB        NOT NULL,
    mutation_fitness    FLOAT,
    parent_fitness      FLOAT        NOT NULL,
    test_verdict        VARCHAR(20),
    test_details        JSONB        DEFAULT '{}',
    promoted            BOOLEAN,
    promoted_asset_id   VARCHAR(255),
    cycle_id            VARCHAR(20)  NOT NULL,
    created_at          TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    evaluated_at        TIMESTAMPTZ,
    status              VARCHAR(20)  NOT NULL DEFAULT 'pending'
);

CREATE INDEX IF NOT EXISTS idx_mut_results_status
    ON genesis.mutation_test_results(status);
CREATE INDEX IF NOT EXISTS idx_mut_results_cycle
    ON genesis.mutation_test_results(cycle_id);

CREATE TABLE IF NOT EXISTS genesis.call_insights (
    id                  SERIAL       PRIMARY KEY,
    assistant_id        VARCHAR(255) NOT NULL,
    call_id             VARCHAR(255),
    duration_seconds    FLOAT,
    sentiment_score     FLOAT,
    booking_made        BOOLEAN      DEFAULT FALSE,
    transcript_summary  TEXT,
    caller_number       VARCHAR(30),
    created_at          TIMESTAMPTZ  NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_call_insights_assistant
    ON genesis.call_insights(assistant_id, created_at);

CREATE TABLE IF NOT EXISTS genesis.widget_events (
    id          SERIAL       PRIMARY KEY,
    widget_id   VARCHAR(255) NOT NULL,
    event_type  VARCHAR(30)  NOT NULL,
    event_data  JSONB        DEFAULT '{}',
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_widget_events_widget
    ON genesis.widget_events(widget_id, created_at);
c                     | i S t        | t        t        f      r| S 	 t        j                  |       S # t
        t        f$ r i cY S w xY w)z:Coerce a JSONB column (may arrive as str or dict) to dict.)
isinstancerH   listjsonloads
ValueError	TypeError)vals    rK   	jsonb_colr     sK    
{	#d|$
zz#	" 	s   3 AAc                       e Zd ZdZdZddefdZdee   fdZ	dee   ddfd	Z
d
edefdZdedeeef   fdZdedeeef   dee   fdZdededeeef   fdZy)MetricsHarvesterzAPull performance data from Instantly.ai and store in Bloodstream.zhttps://api.instantly.ai/api/v2rX   c                 x    || _         || _        t        j                  j	                  d      xs t
        | _        y )NINSTANTLY_API_KEY)r   rX   osenvironrl   INSTANTLY_API_KEY_FALLBACK_instantly_keyselfr   rX   s      rK   __init__zMetricsHarvester.__init__  s/    	JJNN./M3M 	rJ   rZ   c                 8   t         j                  d       	 ddl}d| j
                   dd}| j                  ||      }| j                  |||      }t         j                  dt        |       d	       |S # t        $ r t         j	                  d       g cY S w xY w)
z
        Pull campaign-level analytics from Instantly.ai API v2.
        Returns a list of metric dicts, one per campaign.

        IMPORTANT: This is READ-ONLY. We never activate or unpause campaigns.
        z7HARVEST: Pulling email metrics from Instantly.ai API...r   Nz)HARVEST: 'requests' package not availableBearer application/json)AuthorizationContent-TypezHARVEST: Pulled metrics for 
 campaigns)	r   r   requestsImportErrorerrorr   _fetch_campaigns_fetch_campaign_analyticslen)r   r   headers	campaignsmetrics_lists        rK   harvest_email_metricsz&MetricsHarvester.harvest_email_metrics  s     	JK	  't':':&;<.
 ))(G<	 55hS/L0A/B*MN!  	IIABI	s   A6 6 BBr   Nc                 `   | j                   rt        j                  d       y| j                  j	                         }|D ]>  }|j                  dd      }|s|j                  dt        j                  |      |f       @ | j                  j                          t        j                  d       y)z
        Upsert campaign-level metrics into genesis.evolution_assets
        for any email_template assets that belong to those campaigns.
        z6HARVEST [DRY RUN]: Would update metrics in BloodstreamNcampaign_id a(  
                UPDATE genesis.evolution_assets
                SET metrics = metrics || %s::jsonb,
                    updated_at = NOW()
                WHERE asset_type = 'email_template'
                  AND status = 'active'
                  AND metadata->>'campaign_id' = %s
            z0HARVEST: Email metrics upserted into Bloodstream)
rX   r   r   r   r   rl   r   r   dumpsr   )r   r   r   mr   s        rK   #update_email_metrics_in_bloodstreamz4MetricsHarvester.update_email_metrics_in_bloodstream  s    
 <<HHMNii  	/A%%r2K KK  **Q--/	/ 			CDrJ   r6   c                    t         j                  d| d       t        j                  |      }|st         j	                  d|        y| j
                  j                         }|j                  d|f       |j                         }d}|D ]=  \  }}t        |      } ||      }	| j                  s|j                  d|	|f       |dz  }? | j                  s| j
                  j                          t         j                  d| d	|        |S )
z|
        Recalculate fitness scores for all active assets of a given type.
        Returns count of updated assets.
        z*HARVEST: Recalculating fitness scores for ...z!HARVEST: No fitness function for r   z
            SELECT asset_id, metrics
            FROM genesis.evolution_assets
            WHERE asset_type = %s AND status = 'active'
        z
                    UPDATE genesis.evolution_assets
                    SET fitness_score = %s, updated_at = NOW()
                    WHERE asset_id = %s
                   zHARVEST: Updated z fitness scores for )r   r   FITNESS_FUNCTIONSrl   r   r   r   r   fetchallr   rX   r   )
r   r6   
fitness_fnr   rowsupdatedr5   raw_metricsr<   scores
             rK   update_fitness_scoresz&MetricsHarvester.update_fitness_scores  s   
 	=j\MN&**:6
KK;J<HIii   ]		
 ||~%) 
	!Hk,Gw'E<<  X&	(
 qLG
	 ||II$WI-A*NOrJ   r   c                 <   	 |j                  | j                   d|ddid      }|j                          |j                         }i }|j                  dg       |j                  dg       z   D ]T  }|j                  d      xs |j                  d	d
      }|j                  d      xs |j                  dd
      }|sP|||<   V t        j                  dt        |       d       |S # t        $ r*}	t        j                  d|	        t        dicY d}	~	S d}	~	ww xY w)zAFetch campaign list, return {campaign_id: campaign_name} mapping.z
/campaignslimitd   r   r   paramstimeoutitemsr   idr   r   namecampaign_namezHARVEST: Found r   z(HARVEST: Could not fetch campaign list: z&Sunaiva Partner Outreach - AU AgenciesN)
rl   INSTANTLY_BASEraise_for_statusr   r   debugr   r   r   INSTANTLY_CAMPAIGN_ID)
r   r   r   respr   r   ccidcnamer   s
             rK   r   z!MetricsHarvester._fetch_campaigns  s   	U<<&&'z2~	   D !!#99;DIXXgr*TXXfb-AA +eeDk=QUU="%=fC)C%*IcN	+
 III'7zBC 	UKKB1#FG)+STT	Us$   B:C( =*C( (	D1DDDr   c                 ,   t        |j                               xs t        g}g }|D ]7  }	 |j                  | j                   d|d|id      }i }|j
                  dv r5|j                         }	t        |	t              r|	j                  d|	      ni }n|j
                  dk(  rt        j                  d|d	d
  d       i }nj|j
                  dk(  r0t        j                  d|d	d
  d       | j                  |||      }n+t        j                  d|j
                   d|d	d
  d       i }t        |j                  d      xs) |j                  d      xs |j                  dd      xs d      }
t        |j                  d      xs) |j                  d      xs |j                  dd      xs d      }t        |j                  d      xs) |j                  d      xs |j                  dd      xs d      }t        |j                  d      xs) |j                  d      xs |j                  dd      xs d      }t        |j                  d      xs) |j                  d       xs |j                  d!d      xs d      }|j                  ||j                  |d"      |
||||t        ||
      t        ||
      t        ||
      t        ||
      d#d#t!        j"                  t$              j'                         d$       |
dkD  r;t        j)                  d%|d	d
  d&|
 d't        ||
      d(d)t        ||
      d(       nt        j)                  d%|d	d
  d*       : |S # t*        $ r&}t        j-                  d+| d,|        Y d	}~gd	}~ww xY w)-a  
        Fetch analytics for each known campaign.

        Instantly v2 endpoint strategy:
          1. GET /api/v2/analytics/campaign/count?id=<campaign_id>
             Returns: total_count, open_count, reply_count, bounced_count, etc.
          2. Fallback: derive from emails list (GET /api/v2/emails?campaign_id=...)

        NOTE: Campaign must be active and have sent emails for analytics to
              contain non-zero data. Paused / empty campaigns return zeros --
              this is expected and handled gracefully.
        z/analytics/campaign/countr   r   r   )r      r   i  z'HARVEST: Analytics API auth failed for N   z@... -- API key may lack analytics scope. Returning zero metrics.i  z6HARVEST: analytics/campaign/count not found (404) for z"... -- trying email count fallbackz+HARVEST: Instantly analytics returned HTTP z for r   total_countemails_sentr   r   
open_countunique_opensopenedreply_countrepliesrepliedclick_countunique_clicksclickedbounced_countbouncesbouncedunknownr\   )r   r   r   r   r   r   r   rc   rd   
click_raterf   re   rg   harvested_at	HARVEST: z	... sent=z open=z.1%z reply=z[... sent=0 (campaign paused or not yet started -- zero metrics recorded, fitness unchanged)z&HARVEST: Error fetching analytics for : )r   keysr   rl   r   status_coder   r   rH   r   r   r   _fetch_analytics_from_emailsrF   appendra   r   nowAEST	isoformatr   r   r   )r   r   r   r   campaign_idsr   r   r   campaign_datarn   r   r   r   r   r   r   s                   rK   r   z*MetricsHarvester._fetch_campaign_analytics7  s$    INN,-H2G1H' f	WKeW||**++DE# +.	 $  13##z1))+C<FsD<QCGGFC$8WYM%%,KKA+bq/AR SW W %'M%%,IIP&r?++MO
 %)$E$E ';%M KKEdFVFVEW X*2A/s4 %'M !%%m4 $((7$((3 	 !%%l3 $((8$((15 	 !%%m4 $((3$((A6 	 !%%m4 $((9$((A6 	 !%%o6 $((3$((A6 	 ###.%.]];	%J $&&&!,VT!:"-gt"<"-gt"<#.w#='*!$$,LL$6$@$@$B% " !8HH#KO#4 5  $v &  +FD 9#> ?!!,Wd!;C @B HH#KO#4 5D Ef	WP   W		B;-rRSQTUVVWs   L3M$$	N-NNr   c           
         	 g }d}t        d      D ]  }|dd}|r||d<   |j                  | j                   d||d      }|j                  d	k7  r nY|j	                         }	|	j                  d
g       }
|j                  |
       t        |
      dk  r n|
d   j                  d      } |si S t        |      }t        d |D              }t        d |D              }t        d |D              }t        j                  d| d| d| d|        ||||dS # t        $ r$}t        j                  d|        i cY d}~S d}~ww xY w)z
        Fallback: derive campaign analytics by counting emails by their status.
        Uses GET /api/v2/emails?campaign_id=<id> and counts status fields.
        Nr   r   )r   r   starting_afterz/emailsr   r   r   r   r   c              3   f   K   | ])  }|j                  d       s|j                  d      s&d + yw)r   	is_openedr   Nrl   .0r   s     rK   	<genexpr>z@MetricsHarvester._fetch_analytics_from_emails.<locals>.<genexpr>  s%     Xq!%%/QUU;EWX   '11c              3   f   K   | ])  }|j                  d       s|j                  d      s&d + yw)r   
is_repliedr   Nr  r  s     rK   r  z@MetricsHarvester._fetch_analytics_from_emails.<locals>.<genexpr>  &     [1553Cquu\GZ![r  c              3   f   K   | ])  }|j                  d       s|j                  d      s&d + yw)r   
is_bouncedr   Nr  r  s     rK   r  z@MetricsHarvester._fetch_analytics_from_emails.<locals>.<genexpr>  r  r  zHARVEST email fallback: sent=z opened=z	 replied=z	 bounced=)r   r   r   r   z HARVEST: Email fallback failed: )rangerl   r   r  r   extendr   sumr   r   r   r   )r   r   r   r   
all_emailsr  _r   r   r   r   r   r   r   r   r   s                   rK   r  z-MetricsHarvester._fetch_analytics_from_emails  s   1	J!N2Y 5#. * "/=F+,||**+73#!	 $  ##s*yy{"-!!%(u:#!&rt!4+5. 	 z?DXJXXF[Z[[G[Z[[GII/vXfX F")9WI7
  $$&!(	   	KK:1#>?I	s%   B-D 0A(D 	E"E;EEF)r@   rA   rB   rC   r   rY   r   r   r   r   r   rD   rF   r   r   r   r  rI   rJ   rK   r   r     s    K6N
d 
tDz :ET
 Et E8$ $ $TU$ U4S> U0z!%z26sCx.z	dzx8!%8478	c3h8rJ   r   c                   4    e Zd ZdZddefdZdedee   fdZy)	AssetCullerz.Identify and archive bottom-performing assets.rX   c                      || _         || _        y Nr   rX   r   s      rK   r   zAssetCuller.__init__      	rJ   r6   rZ   c           	         t         j                  d| d       | j                  j                         }t        j                  |i       }t        t        |      d      }t        t        |j                               d      }|j                  d|f       |j                         }t        |      }|t        k  r%t         j                  d| d| dt         d	       g S g }|D ]A  \  }	}
}t        |      }|j                  |d
      |k\  s(|
dkD  s.|j                  |	|
d       C t        |      t        k  r't         j                  dt        |       d| d       g S t        |      }t        dt!        |dz              }||z
  }|t        k  r0|t        z
  }|d
k  r"t         j                  d| dt         d       g S |d| }|D cg c]  }|d   	 }}| j"                  r)t         j                  dt        |       d| d|        |S |D ]  }	|j                  d|	f        | j                  j%                          t         j                  dt        |       d| d|        |S c c}w )z
        Archive bottom 10% of assets for a given type.
        Safety: never go below MIN_SURVIVORS_PER_TYPE active assets.
        Returns list of archived asset_ids.
        zCULL: Evaluating z
 assets...r   r   z
            SELECT asset_id, fitness_score, metrics
            FROM genesis.evolution_assets
            WHERE asset_type = %s AND status = 'active'
            ORDER BY fitness_score ASC
        zCULL: Only z active z  assets -- skipping (minimum is )r   r\   )r5   r:   z
 eligible z< assets -- skipping cull (not enough data-sufficient assets)r   rv   zCULL: Cannot cull z without going below z survivors -- skippingNr5   zCULL [DRY RUN]: Would archive  z	 assets: z
                UPDATE genesis.evolution_assets
                SET status = 'archived',
                    archived_at = NOW(),
                    archived_reason = 'alpha_evolve_cull'
                WHERE asset_id = %s
            zCULL: Archived )r   r   r   r   r"   rl   nextitervaluesr   r   r   MIN_SURVIVORS_PER_TYPEr   r  maxrF   rX   r   )r   r6   r   min_datathreshold_keythreshold_valr   total_activeeligibler5   r:   r   r<   n
cull_countremaining_after_cullto_culla
culled_idss                      rK   cullzAssetCuller.cull  s    	$ZL
;<ii  '**:r:T(^V4T(//"34b9 
 ]	 ||~4y11HHl^8J< @((>'?qB I 48 	X0Hm[,G{{=!,=-RUBUX VW	X
 x=22HHc(m_Jzl CD D I MCDM*
  !:~"8833JQ(4I-..DF 	;J'-45a
m5
5<<HH0Z0A,i
|5  # 	HKK  	 			?3z?"31ZL	*VW+ 6s   	I)Nr"  )	r@   rA   rB   rC   rY   r   rD   r   r<  rI   rJ   rK   r$  r$    s+    8d Qs QtCy QrJ   r$  c                   l   e Zd ZdZddefdZdee   fdZdede	e
   fdZd	e
dede	e
   fd
Zde	e
   de
dedede	e
   f
dZd	e
defdZd	e
defdZd	e
defdZdedee	e
      fdZdedee	e
      fdZdedee	e
      fdZde
defdZde	e
   dede	e
   fdZdede	e   fdZd	e
dede	e
   fdZy)AssetMutatorzIGenerate mutations of elite assets using Gemini (or OpenRouter fallback).NrX   c                 @   || _         || _        || _        t        t        j
                  j                  dt                    | _        t        j
                  j                  d      xs1 t        j
                  j                  d      xs | j                         | _
        y )NALPHA_EVOLVE_MUTATIONSGEMINI_API_KEYGOOGLE_API_KEY)r   qdrantrX   rF   r   r   rl   DEFAULT_MUTATIONS_PER_ELITEmutations_per_elite_load_gemini_key_from_genesis_gemini_key)r   r   qdrant_clientrX   s       rK   r   zAssetMutator.__init__T  sz    	##&JJNN35PQ$
  JJNN+, 4zz~~./4113 	rJ   rZ   c                 <    	 ddl m}  |       S # t        $ r Y yw xY w)z3Try to load Gemini key from genesis secrets_loader.r   )get_gemini_api_keyN)r   rJ  r   )r   rJ  s     rK   rF  z*AssetMutator._load_gemini_key_from_genesisa  s%    	>%'' 		s    	r6   c                    | j                   j                         }|j                  d|f       |j                         }|st        j                  d| d       g S t        dt        t        |      dz              }g }|d| D ]P  }|j                  |d   t        |d         t        |d         |d	   t        |d
         t        |d         d       R t        j                  dt        |       d| dt        |       d       |S )z:Fetch top 10% performing active assets (by fitness score).z
            SELECT asset_id, fitness_score, content, version, metadata, metrics
            FROM genesis.evolution_assets
            WHERE asset_type = %s AND status = 'active' AND fitness_score > 0
            ORDER BY fitness_score DESC
        zMUTATE: No scored z assets -- nothing to mutater   rv   Nr      r   r]   r   )r5   r:   r7   r8   r?   r<   MUTATE: z elite z assets selected (top 10% of r*  )r   r   r   r   r   r   r0  rF   r   r  rG   r   )r   r6   r   r   elite_countelitesrows          rK   
get_eliteszAssetMutator.get_elitesi  s   ii  
 ]	 ||~HH)*5QRSI!STT!123% 	CMMF!&s1v$SV,q6%c!f-$SV, 	 	s6{m7:, 7t9+Q(	
 rJ   elitec                 Z   | j                   r:t        j                  d| j                   d|d           | j	                  ||      S t        j                  d| j                   d|d    d|d   dd       | j
                  | j                  | j                  d	}|j                  |      }|st        j                  d
|        g S  ||      }| j                  |      xs | j                  |      }|st        j                  d|d    d       g S |dk(  r!|D cg c]  }| j                  |      s| }}| j                  ||      }t        j                  dt        |       dt        |       dt!        t        |      | j                                |d| j                   S c c}w )z
        Ask Gemini to produce N mutations of an elite asset.
        Each mutation is a dict with the new content + mutation metadata.
        Returns list of mutation dicts (may be empty on API failure).
        z!MUTATE [DRY RUN]: Would generate z mutations of r5   zMUTATE: Generating z
 (fitness=r:   .4fr*  r   zMUTATE: No mutation prompt for z.MUTATE: Both Gemini and OpenRouter failed for z -- skipping mutationsr   rM  z generated, z unique after dedup, returning N)rX   r   r   rE  _make_dummy_mutations_email_mutation_prompt_voice_mutation_prompt_widget_mutation_promptrl   r   _call_gemini_call_openrouterr   _validate_email_mutation_deduplicate_via_qdrantr   rm   )	r   rR  r6   prompt_buildersbuilderprompt	mutationsr   uniques	            rK   generate_mutationszAssetMutator.generate_mutations  s    <<HH3D4L4L3M N  %j 124 --eZ@@!$":":!; <
#$Ju_/Ec.J!M	
 #99 77!99

 "%%j1KK9*FGI %%f-N1F1Fv1N	II@$%%;= I ))$-Rq1N1Nq1QRIR --iDs9~&l3v;- @&&)#f+t7O7O&P%QS	
 /t//00 Ss   F(4F(r`  parentrN   c                 r   | j                   r,t        |      D cg c]  \  }}d| d|d|d   |d c}}S | j                  j                         }g }t        |      D ]  \  }}	d| d|dd j	                          d|ddt        j                         j                  dd	  }
|j                  d
|
|d   |t        j                  |	      |d   |f       |j                  |
|d   ||	|d   d        | j                  j                          t        j                  dt        |       d       |S c c}}w )z
        Write mutation records into genesis.mutation_test_results for testing.
        Returns list of stored records with mutation_id assigned.
        DRY_r!  03dr5   )mutation_idr9   mutationMUT_Nr      aD  
                INSERT INTO genesis.mutation_test_results
                (mutation_id, parent_id, asset_type, mutation_content,
                 parent_fitness, cycle_id, status, created_at)
                VALUES (%s, %s, %s, %s, %s, %s, 'pending', NOW())
                ON CONFLICT (mutation_id) DO NOTHING
            r:   )rg  r9   r6   rh  parent_fitnesszMUTATE: Stored z+ mutations in genesis.mutation_test_results)rX   	enumerater   r   upperuuiduuid4hexr   r   r   r  r   r   r   r   )r   r`  rc  r6   rN   ir   r   storedrh  rg  s              rK   store_mutationszAssetMutator.store_mutations  sr    << &i0 Aq	 &*(1QsG#<!'
!3 !  ii $Y/ 	KAx 
!JrN,@,@,B+C1QsG1TZZ\M]M]^`_`MaLbcKKK  z"

8$' MM*#J/($"("9 !	0 			c&k] +, ,	
 Qs   D3c                     |d   }d|d   dd|j                  dd       d|j                  d	d       d
t        j                  |j                  di       d       d| j                   dS )Nr7   zYou are Alpha Evolve, a Darwinian optimisation engine for sales email templates.

ELITE PARENT (current top performer, fitness_score=r:   rT  z):
Subject: subject_lineN/Az
Body:
bodyz

Campaign metrics: r<   rL  indentz

Available Instantly personalisation variables:
  {{email}}, {{firstName}}, {{companyName}}, {{website}},
  {{agencyType}}, {{location}}

YOUR TASK: Generate exactly a   MUTATED versions of this email.

MUTATION DIMENSIONS (vary one or more per mutation):
1. Subject line: different hooks, lengths, personalisation
2. Opening line: different rapport-building approaches
3. Value proposition framing: different angles on the same offer
4. Social proof: add/remove/change numbers or proof points
5. Call-to-action: different asks (reply, click, book)
6. Tone: shift along formal <-> casual spectrum
7. Length: shorter (under 80 words) or longer (up to 200 words) variants
8. Urgency: add/remove scarcity or time pressure

CONSTRAINTS (HARD RULES -- must not be violated):
- Keep ALL {{variable}} merge tags intact and spelled correctly
- Must comply with Australian Spam Act 2003 -- include [Unsubscribe] at the end
- Must be truthful (no fabricated statistics or fake testimonials)
- Maximum 200 words per email body
- EACH mutation must differ meaningfully from the parent (not just a word swap)
- Do NOT reproduce the parent verbatim in any mutation

OUTPUT FORMAT -- JSON array ONLY, no markdown, no prose:
[
  {
    "subject_line": "...",
    "body": "...",
    "mutation_dimensions": ["tone", "cta"],
    "mutation_rationale": "Shifted to casual tone with reply-based CTA"
  }
]rl   r   r   rE  r   rR  r7   s      rK   rV  z#AssetMutator._email_mutation_prompt  s    	"449/4J33O P

++ne
,	- .VU  ::eii	26qAB C "556 7) )	rJ   c                     |d   }d|d   dd|j                  dd       dt        j                  |j                  d	i       d
       d| j                   d	S )Nr7   zhYou are Alpha Evolve, optimising AI voice agent system prompts for Telnyx.

ELITE PARENT (fitness_score=r:   rT  ):
system_promptrv  

Metrics: r<   rL  rx  

YOUR TASK: Generate exactly ah   MUTATED versions.

MUTATION DIMENSIONS:
1. Greeting style: formal vs warm vs direct
2. Question ordering: change discovery sequence
3. Objection handling: different rebuttal strategies
4. Booking CTA timing: earlier vs later in conversation
5. Verbal padding phrases: different natural stall tactics
6. Personality warmth: adjust empathy markers
7. Information density: more/less detail per response
8. Closing technique: different ways to secure the booking

CONSTRAINTS:
- Maintain brand identity (Australian, professional, helpful)
- Include booking/scheduling capability
- Handle price, timing, "just looking" objections
- Maximum 2000 characters for system prompt

OUTPUT FORMAT -- JSON array ONLY:
[
  {
    "system_prompt": "...",
    "mutation_dimensions": ["greeting", "booking_timing"],
    "mutation_rationale": "Earlier booking CTA with warmer greeting"
  }
]rz  r{  s      rK   rW  z#AssetMutator._voice_mutation_prompt  su    	""?3C8 9_e$ % &

**UYYy"-a
8	9 :!556 7   	rJ   c                     |d   }|j                  d|      }d|d   ddt        j                  |d       d	t        j                  |j                  d
i       d       d| j                   d	S )Nr7   configzdYou are Alpha Evolve, optimising website widget configurations.

ELITE PARENT config (fitness_score=r:   rT  r}  rL  rx  r  r<   r  a   config variants.

MUTATION DIMENSIONS:
1. Greeting text: different opening messages
2. CTA text: different button labels
3. Auto-open delay: timing variations 0-15000ms
4. Position: bottom-right vs bottom-left
5. Theme colour: variations within brand palette
6. Voice selection: different Telnyx NaturalHD voice names

CONSTRAINTS:
- Must include greeting, CTA text, and position
- Colours must be valid hex codes
- auto_open_delay_ms between 0 and 30000
- voice_id must be valid Telnyx NaturalHD voice name

OUTPUT FORMAT -- JSON array ONLY:
[
  {
    "config": {"greeting": "...", "theme_color": "...", ...},
    "mutation_dimensions": ["greeting", "auto_open_delay"],
    "mutation_rationale": "Shorter delay with more casual greeting"
  }
]rz  )r   rR  r7   r  s       rK   rX  z$AssetMutator._widget_mutation_promptC  s    	"Xw/$$)/$:3#? @F1  

**UYYy"-a
8	9 :!556 7 	rJ   r_  c                    | j                   st        j                  d       y	 ddlm} |j                  | j                          |j                  t              }|j                  ||j                  j                  ddd      	      }|j                  j                         }| j                  |      S # t        $ r"}t        j                  d
|        Y d}~yd}~ww xY w)z0Call Gemini generative API to produce mutations.z#MUTATE: No Gemini API key availableNr   api_key?    r   )temperaturemax_output_tokensresponse_mime_type)generation_configzMUTATE: Gemini call failed: )rG  r   r   google.generativeaigenerativeai	configureGenerativeModelGEMINI_MUTATION_MODELgenerate_contenttypesGenerationConfigtextstrip_parse_mutation_jsonr   )r   r_  genaimodelresponsern   r   s          rK   rY  zAssetMutator._call_geminij  s    KK=>	/OOD$4$4O5))*?@E--"'++">"> #&*'9 #? # . H --%%'C,,S11 	KK6qc:;	s   BB5 5	C >CC c           	         t         j                  j                  d      }|st        j	                  d       y	 ddl}|j                  dd| ddd	d
ddddd|dgdddd      }|j                          |j                         d   d   d   d   }| j                  |      S # t        $ r"}t        j	                  d|        Y d}~yd}~ww xY w)zAFall back to OpenRouter (Kimi K2.5 / MiniMax M2.5) for mutations.OPENROUTER_API_KEYz6MUTATE: OPENROUTER_API_KEY not set -- cannot fall backNr   z-https://openrouter.ai/api/v1/chat/completionsr   r   zhttps://sunaivadigital.comzAlpha Evolve Daemon)r   r   zHTTP-RefererzX-Titlezmoonshotai/kimi-k2-5systemzYou are Alpha Evolve.)roler7   r   r  r  )r  messagesr  
max_tokens<   )r   r   r   choicesmessager7   z$MUTATE: OpenRouter fallback failed: )r   r   rl   r   r   r   postr   r   r  r   )r   r_  r  r   r   r7   r   s          rK   rZ  zAssetMutator._call_openrouter  s    **..!56KKPQ	==?'.wi%8$6$@4	 4!)6MN!'F;! $'"& # ! D& !!#iik),Q/	:9EG,,W55 	KK>qcBC	s   A*B$ $	C-C

Crn   c                    t        j                  dd|j                         t         j                        }t        j                  dd|j                         t         j                        }	 t	        j
                  |      }t        |t              r|S t        |t              r|j                  d|g      S y# t        j                  $ r=}t        j                  d|        t        j                  d|dd	         Y d}~yd}~ww xY w)
z?Extract a JSON array from LLM output (handles markdown fences).z^```(?:json)?\s*r   )flagsz\s*```$r`  Nz+MUTATE: Could not parse LLM JSON response: zMUTATE: Raw response was: i,  )resubr  	MULTILINEr   r   r   r   rH   rl   JSONDecodeErrorr   r   r   )r   rn   parsedr   s       rK   r  z!AssetMutator._parse_mutation_json  s     ff("ciikNffZSYY[E
	ZZ_F&$'&$'zz+x88## 	KKEaSIJII23t9+>?	s   ,&B7 "B7 7D
3DDrh  c                    |j                  dd      }|j                  dd      }|r|st        j                  d       yt        |j	                               }|dkD  rt        j                  d| d       yd	|j                         vr|j                         d
z   |d<   ||fD ]B  } |j                  d      } |j                  d      }||k7  s-t        j                  d        y y)z
        Validate that an email mutation meets hard constraints.
        Returns True if valid, False if it should be rejected.
        ru  r   rw  z4MUTATE: Rejected mutation -- missing subject or bodyF   z,MUTATE: Rejected mutation -- body too long (z words)z[unsubscribe]z

[Unsubscribe]z{{z}}z8MUTATE: Rejected mutation -- mismatched merge tag bracesT)rl   r   r   r   splitlowerrstripcount)r   rh  subjectrw  
word_countr   open_bracesclose_bracess           rK   r[  z%AssetMutator._validate_email_mutation  s    
 ,,~r2||FB' dIILM &
IIDZLPWXY $**,.#{{}/BBHV t_ 	E%%++d+K&5;;t,Ll*		TV	 rJ   c                    | j                   s|S g }|D ]  }t        j                  |d      }	 ddlm}m}m} | j                   j                  d| j                  |      dt         | |d ||      	      g
            }	|	s|j                  |       n't        j                  d|	d   j                  dd        |S # t        $ r4}
t        j                  d|
 d       |j                  |       Y d}
~
d}
~
ww xY w)z
        Use Qdrant semantic similarity to reject near-duplicate mutations.
        Skips gracefully if Qdrant is unavailable.
        T)	sort_keysr   )FilterFieldCondition
MatchValuealpha_evolve_assetsr   r6   )value)keymatch)must)collection_namequery_vectorr   score_thresholdquery_filterz,MUTATE: Rejected near-duplicate (similarity=z.3fr*  zMUTATE: Qdrant dedup error: z -- accepting mutationN)rC  r   r   qdrant_client.modelsr  r  r  search_embed_textSIMILARITY_THRESHOLDr  r   r   r   r   r   )r   r`  r6   ra  rh  r  r  r  r  resultsr   s              rK   r\  z$AssetMutator._deduplicate_via_qdrant  s    {{! 	(H::h$7D(SS++,,$9!%!1!1$!7$8!'*$0&0z&B" -  MM(+II''.qz'7'7&<A?+	(: 	  (:1#=STUh''(s   BC	C>
*C99C>r  c                     	 ddl m} | j                  r4|j                  | j                         |j	                  d|      }|d   S 	 dgdz  S # t
        $ r Y w xY w)	z,Generate embedding vector for deduplication.r   Nr  zmodels/text-embedding-004)r  r7   	embeddingr\   i   )r  r  rG  r  embed_contentr   )r   r  r  results       rK   r  zAssetMutator._embed_text  sv    
	/(8(89,,5  -  k**   us{  		s   AA 	AAc                 B   |dk(  rkt        | j                        D cg c]L  }d|dz    d|d   j                  dd       d|dz    d	|d   j                  d
d      dd  ddgd|dz    dN c}S t        | j                        D cg c]  }d|d	 c}S c c}w c c}w )z.Return placeholder mutations for dry-run mode.r   z[DRY RUN] Variant r   r  r7   ru  rv  z[DRY RUN mutation z] rw  r   Nr   z...

[Unsubscribe]tonezDry-run dummy variant )ru  rw  mutation_dimensionsmutation_rationaleT)rX   variant)r  rE  rl   )r   rR  r6   rq  s       rK   rU  z"AssetMutator._make_dummy_mutations  s    )) t778  '91Ri@P@T@TUcej@k?l$m01Ri8H8L8LVUW8XY\Z\8]7^^rs,28,B1Q3%*H	  :?t?W?W9XYADQ/YY Zs   ABBNF)r@   rA   rB   rC   rY   r   r   rD   rF  r   r   rQ  rb  rs  rV  rW  rX  rY  rZ  r  r[  r\  rG   r  rU  rI   rJ   rK   r>  r>  Q  s   S
$ 
x} S T$Z @11 11# 11$t* 11f0d0-10?B0NQ0	d0l+D +S +Z"D "S "H!T !c !N3 8DJ+? . s  xT
/C  D d0D *   $  L(d(14(	d(T U &Z4 ZS ZT$Z ZrJ   r>  c                       e Zd ZdZddefdZdee   dedededee   f
d	Z	d
edededefdZ
d
edededefdZd
edededefdZdee   dedededdf
dZy)MutationTesterz
    Phase 1: Generate Jules Pro task descriptions for manual submission.
    Phase 2+: Auto-submit via Jules API when available.
    rX   c                 N    || _         || _        t        j                  dd       y )NTr)   )r   rX   JULES_TASKS_DIRmkdirr   s      rK   r   zMutationTester.__init__4  s#    	dT:rJ   stored_mutationsrc  r6   rN   rZ   c                    |sg S g }| j                   | j                  | j                  d}|j                  |      }|sg S |D ]#  } ||d   |d   |      }	|j	                  |	       % t        j                  t              j                  d      }
t        d| d| d|
 dz  }| j                  ||||       | j                  rdnd	}t        j                  d
| dt        |       d|        |S )z
        Generate Jules Pro task descriptions for each mutation.
        Writes markdown file to hive/jules_evolve_tasks/.
        Returns list of task dicts.
        r   rg  rh  z%Y%m%d_%H%Mjules_tasks_r!  .mdz
[DRY RUN] r   zTEST: z
Generated z Jules tasks -> )_email_test_task_voice_test_task_widget_test_taskrl   r  r   r	  r
  strftimer  _write_tasks_filerX   r   r   r   )r   r  rc  r6   rN   taskstask_buildersr^  recordtask	timestampoutput_filemodes                rK   generate_test_tasksz"MutationTester.generate_test_tasks9  s     I"33 11!33

  ##J/I& 	F}%z"D
 LL	 LL&//>	8*Aj\9+SAB 	 	uk8ZH#|||6$z#e*5Ek]STrJ   task_idrh  c                 |   |j                  di       }|d|j                  dd       ddd| d|d	    d
|d   dd|j                  dd       d|j                  dd      d d  d|j                  dd       d|j                  dg        d|j                  dd       d|j                  dd      d d  d|d   dddS )Nr7   zTest email mutation: r  r  mediumr   z=# Alpha Evolve Mutation Test -- Email Template

**Task ID**: z
**Parent ID**: r5   
**Parent fitness**: r:   rT  z+

## Parent (current winner)

**Subject**: ru  rv  z

**Body**:
```
rw    z 
```

## Mutation

**Subject**: z
**Dimensions changed**: r  
**Rationale**: a  
```

## Test Instructions

1. Load 10 representative Australian agency lead profiles (job title, location, agency type)
2. Use Gemini Flash to role-play as each lead persona and predict:
   - Would they open this email? (Yes/No + confidence 0-1)
   - Would they reply? (Yes/No + confidence 0-1)
   - What objection would they raise?
3. Calculate predicted open_rate, reply_rate
4. Apply `email_fitness()` to compute predicted_fitness_score
5. Compare against parent fitness of a  
6. Write result to genesis.mutation_test_results table:
   - Set `mutation_fitness` = predicted score
   - Set `test_verdict` = 'win' | 'lose' | 'inconclusive'
   - Set `status` = 'completed'

## Acceptance Criteria

- [ ] 10 persona simulations completed
- [ ] predicted_fitness_score calculated using email_fitness()
- [ ] Comparison against parent documented
- [ ] Result written to genesis.mutation_test_results (status='completed')
r  titlepriorityestimated_minutesdescriptionr  r   r  rh  rc  parent_contents        rK   r  zMutationTester._email_test_taskj  s,     Ir2,X\\:NPY-Z,[\ !" Y z"# $O,S1 2   78 9 FE"4C( ) *
 ll>512 3!&;R@A B159: ; 
feTc" # $& '-_&=c%B CG/5
 5	
rJ   c                    |j                  di       }|d|j                  dd       ddd| d|d	   d
d|j                  dd      d d  d|j                  dg        d|j                  dd       d|j                  dd      d d  ddS )Nr7   zTest voice prompt mutation: r  r  r  r   z;# Alpha Evolve Mutation Test -- Voice Prompt

**Task ID**: r  r:   rT  z(

## Parent system prompt (excerpt)
```
r~  rv  r  z%...
```

## Mutation
**Dimensions**: r  r  z

```
a  ...
```

## Test Instructions

1. Load 5 historical call transcripts from genesis.call_insights
2. Simulate 5 conversations using Gemini Flash as caller persona
3. Track: call duration, booking attempt, sentiment score
4. Apply voice_fitness() to compute predicted_fitness_score
5. Compare vs parent, write result to genesis.mutation_test_results

## Acceptance Criteria
- [ ] 5 simulations completed
- [ ] Predicted fitness score calculated
- [ ] Result written to genesis.mutation_test_results
r  r  r  s        rK   r  zMutationTester._voice_test_task  s      Ir23HLLAUW`4a3bc !" Y O,S1 2 OU+DS1 2 3 3R89 :159: ; 
ou%ds+ , -$
 $	
rJ   c                     |d|j                  dd       ddd| d|d   d	d
t        j                  |j                  di       d      d d  dt        j                  |j                  d|      d      d d  d|j                  dg        ddS )NzTest widget config mutation: r  r  lowr   z<# Alpha Evolve Mutation Test -- Widget Config

**Task ID**: r  r:   rT  z

## Parent config
```json
r7   rL  rx  r  z!
```

## Mutation config
```json
r  z
```
**Dimensions**: r  a  

## Test Instructions

1. Evaluate UX principles (contrast ratio, CTA visibility, timing)
2. Score predicted interaction_rate and lead_capture_rate
3. Apply widget_fitness() for predicted_fitness_score
4. Compare vs parent, write result to genesis.mutation_test_results
r  )rl   r   r   )r   r  rh  rc  s       rK   r  z MutationTester._widget_test_task  s     4X\\BVXa5b4cd!" Y O,S1 2 FJJy"%a0#6 7 8
 HLL8,Q7= > ?3R89 :
 	
rJ   r  r  Nc                 j   d| dd| dt        j                  t              j                          dt	        |       ddt
         ddd	dg
}|d t
         D ]7  }|j                  d
|d    d|d    d|d    d|d    dd|d   dd	dg       9 |j                  dj                  |      d       y )Nz(# Jules Pro Tasks -- Alpha Evolve Cycle r   z**Asset Type**: z**Generated**: z**Tasks**: z'**Submit at**: https://jules.google.comz**Cycle budget**: z tasks max this cyclez---z## r  r  r  z**Priority**: r  z**Estimated**: r  z minutesr  
r,   r-   )	r   r	  r
  r  r   MAX_JULES_TASKS_PER_CYCLEr  
write_textjoin)r   r  r  rN   r6   linesr  s          rK   r  z MutationTester._write_tasks_file  s    7xjAzl+hll40::<=>#e*&5 !: ;;PQ
 445 
	DLLd9o&bg8 j!1 23!$':";!<HE]#	 	
	 	tyy/'BrJ   r"  )r@   rA   rB   rC   rY   r   r   r   rD   r  r  r  r  r	   r  rI   rJ   rK   r  r  .  s    
;d ;
+t*+ + 	+
 + 
d+b9
9
&*9
489
	9
v(
(
&*(
48(
	(
T

&*
48
	
JC$ZC.2C>ACORC	CrJ   r  c                   H    e Zd ZdZddefdZdedee   fdZ	ded	ede
fd
Zy)AssetPromoterz
    Compare mutation test results against parents.
    Promote winners. Discard losers.

    Phase 1: Checks results populated by Jules from the PREVIOUS cycle.
    NrX   c                 .    || _         || _        || _        y r&  )r   redisrX   )r   r   redis_clientrX   s       rK   r   zAssetPromoter.__init__  s    	!
rJ   rN   rZ   c                 x   t         j                  d       | j                  j                         }|j	                  d       |j                         }|st         j                  d       g S t         j                  dt        |       d       g }|D ]0  }|\  }}}}	}
}}t        |	xs d      }t        |
xs d      }
t        |      }||
k  rAt         j                  d| d|dd	|
dd
       | j                  s|j	                  d|f       }|
dkD  r||
z
  |
z  nd}t         j                  d| d|dd|
dd|dz  dd	       | j                  r|j                  ||||t        k\  d       | j                  |||      }| d| }|j	                  d|f       |j                         }|rt        |d         ni }|j	                  d||t        j                  |      |||t        j                  |      f       |j	                  d||f       |j	                  d||f       | j                   rs	 d| d| }| j                   j#                  |t        j                  |      d       | j                   j%                  d| d|        t         j'                  d|        |j                  |||||t        k\  d!       3 | j                  s| j                  j-                          t         j                  dt        |       d"       |S # t(        $ r"}t         j+                  d|        Y d }~d }~ww xY w)#z
        Find completed mutation test results with no promotion decision yet.
        Promote mutations that beat their parent. Discard losers.
        Returns list of promotion records.
        z7ASCEND: Checking for completed mutation test results...aJ  
            SELECT mutation_id, parent_id, asset_type,
                   mutation_fitness, parent_fitness, mutation_content, test_verdict
            FROM genesis.mutation_test_results
            WHERE status = 'completed' AND promoted IS NULL
            ORDER BY (COALESCE(mutation_fitness, 0) - parent_fitness) DESC
        z5ASCEND: No completed tests pending promotion decisionzASCEND: z completed tests to evaluater\   z lost (rT  z <= z) -- discardingz
                        UPDATE genesis.mutation_test_results
                        SET promoted = FALSE, evaluated_at = NOW()
                        WHERE mutation_id = %s
                    r   rj   z BEATS parent (z > z, +r   .1fz%))rg  r9   improvementis_major_vz_
                SELECT metadata FROM genesis.evolution_assets WHERE asset_id = %s
            aJ  
                INSERT INTO genesis.evolution_assets
                (asset_id, asset_type, content, version, parent_id,
                 fitness_score, status, metrics, metadata, created_at)
                VALUES (%s, %s, %s, %s, %s, %s, 'active', '{}', %s, NOW())
                ON CONFLICT (asset_id) DO NOTHING
            z
                UPDATE genesis.evolution_assets
                SET status = 'superseded',
                    superseded_by = %s,
                    updated_at = NOW()
                WHERE asset_id = %s
            z
                UPDATE genesis.mutation_test_results
                SET promoted = TRUE,
                    promoted_asset_id = %s,
                    evaluated_at = NOW()
                WHERE mutation_id = %s
            zactive_asset::iQ )exz ASCEND: Redis cache updated for z#ASCEND: Redis cache update failed: N)rg  new_asset_idr9   r  r  z promotions this cycle)r   r   r   r   r   r   r   rG   r   rX   r  MAJOR_IMPROVEMENT_THRESHOLD_get_next_versionfetchoner   r   r  setdeleter   r   r   r   )r   rN   r   r   rU   rP  rg  r9   r6   raw_mut_fitnessrk  raw_contenttest_verdictmutation_fitnessmutation_contentr  new_versionr
  
parent_rowparent_metadata	cache_keyr   s                         rK   evaluate_and_promotez"AssetPromoter.evaluate_and_promote"  s    	JKii   	 ||~HHLMI8CI;&BCD
 e	CKNI[)Znk<$_%;<">#8S9N(5>1{m ,(-T.1E_V ||KK ! &	)
  "A% "N2nD+.  HH;- ($S)^C,@ AOC(, ||!!#.!*#. +/J J	#   00JLK'[;-8L KK  J:Di
16"OKK  j

+,Y 

?+ KK  	*, KK  ,. zzK"/
|1\N KIJJNN!4::.>#?E #  JJ%%j\9+&NOII @OP * ,&*'+FF e	N ||II8C
O,,BCD ! KKK"EaS IJJKs   1A2L	L9L44L9r9   r6   c                 d    |j                  d|||f       |j                         }|d   xs ddz   S )Nz
            SELECT MAX(version)
            FROM genesis.evolution_assets
            WHERE (asset_id = %s OR parent_id = %s) AND asset_type = %s
        r   r   )r   r  )r   r9   r6   r   rP  s        rK   r  zAssetPromoter._get_next_version  s?      J/		1
 llnA!q  rJ   r  )r@   rA   rB   rC   rY   r   rD   r   r   r  rF   r  rI   rJ   rK   r  r    sH     
CS CT$Z CJ!3 !C ! !rJ   r  c                   n    e Zd ZdZddefdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZy)CycleLoggerz5Record evolution cycle results to Bloodstream and KG.rX   c                      || _         || _        y r&  r'  r   s      rK   r   zCycleLogger.__init__  r(  rJ   reportrZ   Nc                 f   t         j                  d|j                   d       | j                  s| j	                  |       |j
                  dkD  r| j                  |       | j                  |       |j                  r| j                  |       t         j                  d|j                   d       y)zCWrite cycle report to PostgreSQL, KG axiom file, and markdown file.zLOG: Recording cycle r   r   zLOG: Cycle z recorded successfullyN)
r   r   rN   rX   _write_to_postgresrU   _write_kg_axiom_write_report_filerV   _notify_major_improvements)r   r  s     rK   	log_cyclezCycleLogger.log_cycle  s    ((9=>||##F+q   ('$$++F3;v//EFGrJ   c                    | j                   j                         }	 |j                  d|j                  |j                  |j
                  |j                  |j                  |j                  |j                  |j                  t        j                  |j                        t        j                  |j                        |j                  t         f       | j                   j#                          t$        j'                  d       y # t(        $ r<}| j                   j+                          t$        j-                  d|        Y d }~y d }~ww xY w)Na  
                INSERT INTO genesis.evolution_cycles
                (cycle_id, started_at, completed_at, assets_scored,
                 assets_culled, mutations_generated, mutations_tested,
                 promotions, major_improvements, errors, dry_run, cycle_version)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
                ON CONFLICT (cycle_id) DO UPDATE SET
                    completed_at = EXCLUDED.completed_at,
                    assets_scored = EXCLUDED.assets_scored,
                    assets_culled = EXCLUDED.assets_culled,
                    mutations_generated = EXCLUDED.mutations_generated,
                    mutations_tested = EXCLUDED.mutations_tested,
                    promotions = EXCLUDED.promotions,
                    major_improvements = EXCLUDED.major_improvements,
                    errors = EXCLUDED.errors
            z5LOG: Cycle record written to genesis.evolution_cyclesz*LOG: Failed to write cycle to PostgreSQL: )r   r   r   rN   rO   rP   rQ   rR   rS   rT   rU   r   r   rV   rW   rX   CYCLE_VERSIONr   r   r   r   r   r   )r   r  r   r   s       rK   r   zCycleLogger._write_to_postgres  s    ii "	HKK   !!##$$$$**''!!

6445

6==): IIHHLM 	HII IIB1#FGG	Hs   C%D 	E2EEc                 ^   d|j                    d|j                    d|j                   d|j                   d|j                   d|j                   d|j
                  rdt        |j
                         d	nd
z   dddd|j                  d}	 t        t        t              dd      5 }|j                  t        j                  |      dz          ddd       t        j                  dt                y# 1 sw Y   &xY w# t         $ r"}t        j#                  d|        Y d}~yd}~ww xY w)zDAppend an axiom to KNOWLEDGE_GRAPH/axioms/alpha_evolve_daemon.jsonl.zAED-zAlpha Evolve Daemon cycle z	: Scored z assets, culled z, generated z mutations, promoted z
 winners. zMajor improvements: .z!No major improvements this cycle.zscripts/alpha_evolve_daemon.pygGz?	evolutionalpha_evolve_daemon)r   axiomsourcero   categorydomainr  r:  r,   r-   r  NzLOG: KG axiom written to zLOG: Failed to write KG axiom: )rN   rQ   rR   rS   rU   rV   r   rP   openrD   KG_AXIOMS_FILEwriter   r   r   r   r   r   )r   r  r+  fr   s        rK   r!  zCycleLogger._write_kg_axiom  s7    )*,V__,= > ..//?@T@T?U V#778 9"--.j: 00 +3v/H/H+I*J!L< 7#+,,#
&	=c.)3A 2Q

5)D012HH00@AB2 2  	=II7s;<<	=s0   D ((C5$D 5C>:D 	D,
D''D,c                    t         j                  dd       t         d|j                   dz  }|j                  rdnd}d|j                   | d|j                   d	|j
                   d
|j                   dt         d|j                   d|j                   d|j                   d|j                   d|j                   dt        |j                         dt        |j                         d}|j                  re|j                  D ]U  }|d|j                  d|j                  dd             d|j                  dd      dz  dd|j                  dd       dz  }W n|d z  }|j                  r|d!z  }|j                  D ]  }|d"| dz  } |d#t         d$z  }	 |j!                  |d%&       t"        j%                  d'|        y)# t&        $ r"}t"        j)                  d(|        Y d)}~y)d)}~ww xY w)*z+Write human-readable markdown cycle report.Tr)   alpha_evolve_r  z
 (DRY RUN)r   z# Alpha Evolve Cycle Report: z

**Started**: z
**Completed**: z
**Dry Run**: z
**Version**: zF

## Results

| Metric | Value |
|--------|-------|
| Assets Scored | z |
| Assets Culled | z |
| Mutations Generated | z& |
| Mutations Tested (Jules tasks) | z |
| Promotions | z! |
| Major Improvements (>20%) | z |
| Errors | z |

## Major Improvements

z- **r
  rg  r   z**: +r  r   r   r  z% over parent r9   r  z_None this cycle._
z
## Errors

z- z+
---
_Generated by alpha_evolve_daemon.py vz_
r,   r-   zLOG: Cycle report written to z"LOG: Failed to write report file: N)PROGRESS_DIRr  rN   rX   rO   rP   r&  rQ   rR   rS   rT   rU   r   rV   rW   rl   r  r   r   r   r   )r   r  report_filedry_tagr7   imperrr   s           rK   r"  zCycleLogger._write_report_file  sD   4$7"}V__4ES%II"(..,b3FOO3DWI N  !##$ %nn _  ''( )''( )334 5$$*$;$;#< =!!" #"6#<#<=> ?  #, $$00 377>377=)3TUV Wq1#5c:.ww{I67r; --G==((G}} (RuB<'( 	B=/QTUU	@""7W"=HH4[MBC 	@II:1#>??	@s   +G 	G2G--G2c                    t         j                  dt        |j                         d       	 t	        t        t              dd      5 }|j                  D ]p  }|j                  d|j                  dd            }|j                  d	|j                   d
| d|j                  dd       d|j                  dd      dz  dd	       r 	 ddd       y# 1 sw Y   yxY w# t        $ r"}t         j                  d|        Y d}~yd}~ww xY w)z
        Phase 1: Write major improvements to notification log file.
        Phase 2: Send Telegram via AIVA bot.
        Phase 3: Email via Instantly.ai to kinan@protonmail.com.
        zLOG: z MAJOR improvements detected!r:  r,   r-   r
  rg  r   [z	] MAJOR: z beats r9   z by r  r   r   r  z%
Nz-LOG: Failed to write major improvements log: )r   r   r   rV   r/  rD   MAJOR_IMPROVEMENTS_LOGrl   r1  rP   r   r   )r   r  r2  r8  r5   r   s         rK   r#  z&CycleLogger._notify_major_improvementsB  s    	5V66788UVW
	Kc013I Q!44 C"ww~sww}i7XYHGGF//0	( D!!$i!@ A B!ggmQ7;C@E    	KIIEaSIJJ	Ks6   C 	B C
C CC C 	D
(DD
r"  )r@   rA   rB   rC   rY   r   rM   r$  r   r!  r"  r#  rI   rJ   rK   r  r    s~    ?d H H H"$H $H $HL=k =d =81@ 1@ 1@fK K KrJ   r  c                 v   | j                         }t        d       t        d       t        d       |j                  d       |j                         }|rt        d       t        dddd	d
dd	ddd	ddd	dd
       t        d       |D ]J  }t        d|d   dd	|d   dd	|d   dd	t	        |d   xs d      dd	t	        |d   xs d      d
       L nt        d       t        d       |j                  d       |j                         }|rt        dt        |       d       t        dddd	ddd	d dd	d!dd	d"dd	d#d$d	d%d$       t        d&       |D ]S  }t        d|d   dd	t        |d         d'd( dd	|d   dd	|d   dd	|d   dd	|d)   d$d	t        |d*         d$       U nt        d+       |j                  d,       |j                         d   }|j                  d-       |j                         d   }t        d.       t        d/|        t        d0|        t                y')1z*Print current generation status to stdout.=
============================================================z(ALPHA EVOLVE DAEMON -- Generation Status<============================================================a
  
        SELECT asset_type, status, COUNT(*) as cnt,
               AVG(fitness_score) as avg_fitness,
               MAX(fitness_score) as max_fitness
        FROM genesis.evolution_assets
        GROUP BY asset_type, status
        ORDER BY asset_type, status
    z
Assets by type/status:z  z
Asset Typez<20r+  Statusz<12Countz>6zAvg Fitnessz>12zMax FitnesszB  ----------------------------------------------------------------r   r   rL  r   z>12.4fr]   z+
No assets in genesis.evolution_assets yet.zGRun --cycle --seed to populate from INSTANTLY_SEQUENCE_PARTNER_PITCH.mdz
        SELECT cycle_id, started_at, assets_scored, assets_culled,
               mutations_generated, promotions, dry_run
        FROM genesis.evolution_cycles
        ORDER BY started_at DESC
        LIMIT 10
    z
Recent cycles (last z):zCycle IDStartedz<25ScoredCulledMutsPromotedz>8DryRunzM  ---------------------------------------------------------------------------N   r   rj  z
No evolution cycles run yet.za
        SELECT COUNT(*) FROM genesis.mutation_test_results
        WHERE status = 'pending'
    zx
        SELECT COUNT(*) FROM genesis.mutation_test_results
        WHERE status = 'completed' AND promoted IS NULL
    z
Mutation pipeline:z  Pending Jules tests:   z  Awaiting ASCEND:       )r   printr   r   rG   r   rD   r  )r   r   r   rcyclesr   pendingawaiting_ascends           rK   show_statusrN  Z  s   
++-C	/	
45	(O KK  	 <<>D()<$Ahs^1WRL-PSATTUVcdgUhijo 	AQqT#Ja!Sz1Q4)11#F+1U1Q491-=f,EG	 	<=WX KK  	 \\^F&s6{m267C )Cm1XbM6"+Qz"oQxPRmU	
 	o 	AQqT#JaAaD	#2s31Q4)1QqT"IQqtBiq1b	3qt9R.J	 	./ KK  	 llnQGKK  	 llnQ'O	 "	%gY
/0	%o%6
78	GrJ   rX   c                 V   t         dz  dz  dz  }|j                         st        j                  d|        y|j	                  d      }t        j                  d|      d	d
 }t        |      }|st        j                  d       t               }| j                         }d}|D ]  }|r!t        j                  d|d           |d	z  }&	 |j                  d|d   t        j                  |d         t        j                  t        |j                  dd	      |j                  dd      d      f       |j                   dkD  r|d	z  } |s| j%                          t        j                  d| d       |S # t"        $ r(}	t        j                  d|d    d|	        Y d
}	~	d
}	~	ww xY w)a
  
    Seed genesis.evolution_assets with the 5 email steps x 2 subject variants
    from data/LEADS/INSTANTLY_SEQUENCE_PARTNER_PITCH.md.

    This is a one-time bootstrap operation. Existing assets are not overwritten.
    Returns count of newly inserted assets.
    r   LEADSz#INSTANTLY_SEQUENCE_PARTNER_PITCH.mdz!SEED: Sequence file not found at r   r,   r-   z(?m)^## EMAIL \d+:r   Nz<SEED: Could not parse email sequence -- using hardcoded seedzSEED [DRY RUN]: Would insert r5   aZ  
                INSERT INTO genesis.evolution_assets
                (asset_id, asset_type, content, version, parent_id,
                 fitness_score, status, metrics, metadata, created_at)
                VALUES (%s, 'email_template', %s, 1, NULL, 0.0, 'active', '{}', %s, NOW())
                ON CONFLICT (asset_id) DO NOTHING
            r7   stepr  A)r   rQ  r  zSEED: Failed to insert r  zSEED: Inserted z initial email_template assets)r   r   r   r   r   r  r  _parse_email_sequence_hardcoded_seed_assetsr   r   r   r   r   r   rl   rowcountr   r   )
r   rX   sequence_filer7   email_blocksassets_to_seedr   insertedassetr   s
             rK   seed_initial_email_assetsr[    s    	v'*OO  !7GH %%w%7G 8817;AB?L +<8NRS/1
++-CH LHH4U:5F4GHIMH	LKK  j!

5+,

#8!IIfa0$yyC8  ||aA-L4 HHxj(FGHO  	LKK1%
2C1DBqcJKK	Ls   A=E77	F( F##F(rW  c           
         g }t        | d      D ]  \  }}t        j                  d|t        j                        }t        j                  d|t        j                        }|r|j                  d      j                         nd}|D ]C  \  }}d| d|j                          d}	|j                  |	|||j                         |d	d
       E  |S )z?Parse email blocks from the INSTANTLY_SEQUENCE markdown format.r   )startz*### Subject Line ([A-Z])\n+```\n(.*?)\n```z### Body\n+```\n(.*?)\n```r   email_partner_pitch_stepr  _seedru  rw  r5   rQ  r  r7   )	rl  r  findallDOTALLr  groupr  r  r  )
rW  assetsstep_idxblocksubjects
body_matchrw  variant_letterr  r5   s
             rK   rS  rS    s    F$\; %::9II
 YY)II


 /9z"((*b'/ 	#NG*8*B~7K7K7M6NeT  MM$ )$+MMO 	 		: MrJ   c                  *    dddddddddd	d
dddgS )z)Minimal hardcoded seeds if parsing fails.!email_partner_pitch_step1_va_seedr   rR  z7New revenue stream for {{companyName}} -- zero dev worka4  Hey team,

I'm reaching out because {{companyName}} is exactly the kind of agency that could add $50K-$150K in annual recurring revenue with almost no extra work.

We've built an AI-powered Talking Widget that agencies embed on their clients' websites. It answers visitor questions with a real human-sounding voice, captures leads 24/7, and books appointments.

Your clients pay a monthly subscription. You earn 20-33% recurring commission for life.

Interested in a 15-min demo? Reply "interested".

Cheers,
Kinan
Sunaiva Digital
sunaivadigital.com

[Unsubscribe]r`  ra  !email_partner_pitch_step1_vb_seedBz6{{companyName}} + AI voice widgets = recurring revenueaL  Hey team,

Quick question -- how much of your agency's revenue is recurring vs project-based?

We've built an AI Talking Widget that agencies white-label for their clients. $197-$597/mo per client, 20-33% goes to you.

One script tag install. We handle everything.

Worth a 15-min look?

Cheers,
Kinan
Sunaiva Digital

[Unsubscribe]rI   rI   rJ   rK   rT  rT    sE     <# Y:	
& <# X_		
'! !rJ   c                     t         j                  d       t         j                  d       t         j                  d       	 t        j                  t              } | j                  dddd      }| |k\  r|t        d	      z  }|| z
  j                         }t         j                  d
|dz  dd|j                                 	 t        j                  |       t         j                  dt        j                  t              j                                 	  G d d      }t         |              # t        $ r t         j                  d       Y yw xY w# t        $ r$}t         j                  d| d       Y d}~Md}~ww xY w)z
    Run as a long-running daemon, executing a cycle at 03:00 AEST daily.
    Uses a simple sleep loop -- for production, prefer systemd timer instead.
    z3DAEMON: Alpha Evolve Daemon starting in daemon modez:DAEMON: Will execute cycle daily at 03:00 AEST (17:00 UTC)zDAEMON: Press Ctrl+C to stopTr   r   )hourminutesecondmicrosecondr   )dayszDAEMON: Sleeping i  r  zh until z%DAEMON: Shutdown requested -- exitingNzDAEMON: Waking up at c                       e Zd ZdZdZdZdZy)run_daemon_loop.<locals>._ArgsFN)r@   rA   rB   rX   forcer6   seedrI   rJ   rK   _Argsrv  Q  s    !
rJ   ry  zDAEMON: Cycle failed: exc_info)r   r   r   r	  r
  replacer   total_secondsr  timesleepKeyboardInterrupt	run_cycler   r   )r	  next_runsleep_secondsry  r   s        rK   run_daemon_loopr  2  sU   
 HHBCHHIJHH+,
ll4 ;;Aaq;I(?	q))H!C668d 237x!!#$&	
	JJ}% 	(d);)E)E)G(HIJ
	C  eg9  ! 	HH<=	  	CII.qc2TIBB	Cs*   D2 E 2EE	FE>>Fc                 N   t         j                  j                  dd      j                         dk(  r#t	        | dd      st
        j                  d       yt	        | dd      xs1 t         j                  j                  d	d      j                         dk(  }|rt
        j                  d
       t        j                  t              j                  d      }t        j                  t              j                         }t
        j                  d       t
        j                  dt         d|        t
        j                  d|        t
        j                  d|        t
        j                  d       t
        j                  d       g }d}d}d}d}g }		 t               }
t!        |
       t	        | dd      rt#        |
|       d}	 ddlm} ddl} |j*                  dIi |j-                         }|j/                          t
        j                  d       d}	 ddlm} ddlm}  |dIi |j;                         }t
        j                  d       t=        |
|      }t?        |
|      }tA        |
||      }tC        |
|      }tE        |
||      }tG        |
|      }t	        | dd      }|r|g}ntH        }|D ]U  }t
        j                  d d!        t
        j                  d"|        t
        j                  d!        	 t
        j                  d#| d$       |d%k(  r#|jK                         }|r|jM                  |       |jO                  |       |
jQ                         }|jS                  d&|f       |jU                         d   }||z  }t
        j                  d'| d(| d)       t
        j                  d*| d$       |jW                  |      }|tY        |      z  }t
        j                  d+| d$       |j[                  |      }g } |D ]J  }!|j]                  |!|      }"|"s|j_                  |"|!||      }#| ja                  |#       |tY        |#      z  }L t
        j                  d,| d$       | r|r|D ci c]  }|d-   |
 }$}i }%| D ](  }&|&d.   }'|%jc                  |'g       je                  |&       * |%jg                         D ]=  \  }(})|$j                  |(|(d/i d0      }*|ji                  |)|*||      }+|tY        |+      z  }? X t
        j                  d5       	 |jm                  |      }-|	ja                  |-       t
        j                  d7       t        j                  t              j                         }.|	D /cg c]  }/|/j                  d8      s|/ }0}/to        |||.||||tY        |	      |0||9      }1|jq                  |1       t
        j                  d:       t
        j                  d;| d<       t
        j                  d=|        t
        j                  d>|        t
        j                  d?|        t
        j                  d@|        t
        j                  dAtY        |	              t
        j                  dBtY        |0              t
        j                  dCtY        |              t
        j                  dD|        t
        j                  dE| dF|.        t
        j                  d       |r1t
        j                  dG       t
        j                  dHtr                	 |
ju                          |1S # t        $ r"}t
        j                  d|        Y d}~yd}~ww xY w# t0        $ r$}t
        j3                  d| d       Y d}~d}~ww xY w# t0        $ r$}t
        j3                  d| d       Y d}~d}~ww xY wc c}w # t0        $ r\}d1| d2| },t
        j                  |,d34       |je                  |,       	 |
jk                          n# t0        $ r Y nw xY wY d}~d}~ww xY w# t0        $ r8}d6| },t
        j                  |,d34       |je                  |,       Y d}~Cd}~ww xY wc c}/w # t0        $ r Y |1S w xY w)Jz7Execute one complete Alpha Evolve cycle (all 6 phases).ALPHA_EVOLVE_PAUSEr   truerw  FzGAlpha Evolve PAUSED (ALPHA_EVOLVE_PAUSE=true). Use --force to override.NrX   ALPHA_EVOLVE_DRY_RUNz/=== DRY RUN MODE -- No changes will be made ===z%Y%m%d_%H%M%Sr?  zALPHA EVOLVE DAEMON vz
 -- Cycle z
Started:  z
Dry Run:  z"Phase:    1 (email templates only)r   zFATAL: rx  )rX   )RedisConfigzRedis connectedzRedis unavailable: z$ -- continuing without cache updates)QdrantClient)QdrantConfigzQdrant connectedzQdrant unavailable: z% -- continuing without semantic dedupr6   r  z(========================================zProcessing: z--- Phase 1: HARVEST (z) ---r   z
                SELECT COUNT(*) FROM genesis.evolution_assets
                WHERE asset_type = %s AND status = 'active' AND fitness_score > 0
            r  r+  z assets with fitness > 0z--- Phase 2: CULL (z--- Phase 3: MUTATE (z--- Phase 4: TEST (r5   r9   r\   )r5   r:   r7   zError processing r  Trz  z
--- Phase 5: ASCEND ---zASCEND phase error: z
--- Phase 6: LOG ---r  )rN   rO   rP   rQ   rR   rS   rT   rU   rV   rW   rX   r>  zALPHA EVOLVE CYCLE z	 COMPLETEz  Assets Scored:      z  Assets Culled:      z  Mutations Generated:z  Jules Tasks Created:z  Promotions:         z  Major (>20%):       z  Errors:             z  Dry Run:            z  Duration:           z -> z7DRY RUN complete. No changes were made to the database.zJules tasks (for review): rI   );r   r   rl   r  getattrr   r   r   r	  r
  r  r  r&  r   r   r   r   r[  r   r  r  Redisr   pingr   r   rH  r  r  get_client_paramsr   r$  r>  r  r  r  PHASE_1_ASSET_TYPESr   r   r   r   r   r  r<  r   rQ  rb  rs  r  
setdefaultr  r   r  r   r  rM   r$  r  close)2argsrX   rN   rO   rW   total_scoredtotal_culledtotal_mutationstotal_testedpromotions_listpg_connr   r  r  	redis_librH  r  r  	harvestercullermutatortesterpromoterloggerrequested_typeasset_typesr6   r   r   scored_countr;  rO  all_stored_mutationsrR  raw_mutationsrr  
parent_map	by_parentr   pidr9   parent_mutationsrc  r  err_msgrU   rP   prV   r  s2                                                     rK   r  r  `  s    
zz~~*B/5576A'RVX_afJg'	
 dIu- 


-r288:fD  BC||D!**?;Hd#--/JHHXHH$]O:hZHIHHz*&'HHz'#$HH13HHXFLLOL"$O
#% ' tVU#!'7; LS.!&yM)J)J)LM"#
 MU./$H|'E'E'GH#$ !'2I'*F7M7;GGW-FWlG<H'*F
 T<6N%&)
 " E
2fX<
|,-F8@	HH-j\?@--(>>@AA,O++J7 .."CKK    <<>!,LL(LHHya
|;STU HH*:,e<=Z0JC
O+L HH,ZL>?''
3F/1  	/ ' : :5* M$ 00!5*h %++F33v;.	/ HH*:,e<=#IO.PAq}a/?.P
.P35	- <AK.C((b188;< 4=??3D //I/'^^II`cpr7stF"66(&*hE !CJ.L/oET HH()228<
z* HH%&<<%//1L%4Jj8I!JJ!""+%'-F V
 HH_HH"8*I67HH%l^45HH%l^45HH%o%678HH%l^45HH%c/&:%;<=HH%c*<&=%>?@HH%c&k]34HH%gY/0HH%j\l^DEHHXJK-o->?@ Mu  		GA3- &  S)!,PQRRS  U*1#-RSTTUT /Q  	)*Rs;GIIgI-MM'"  " 	   (,		'D	)g KR  Ms   
] A^ 7^1 E=_&>_!B_&%"a bbb 	]>]99]>	^.
^))^.1	_:__!_&&	a/0a `10a1	`=:a<`==aa	b-b

b	b$#b$c                  F   t        j                  dt         j                  d      } | j                  d      }|j	                  ddd	       |j	                  d
dd	       |j	                  ddd	       |j	                  ddd	       | j	                  ddd	       | j	                  dt
        dd       | j	                  ddd	       | j                         }|j                  r2	 t               }t        |       t        |       |j                          y |j"                  rt%                y t'        |      }|r#|j(                  rt        j                   d       y y y # t        $ r7}t        j                  d|        t        j                   d       Y d }~y d }~ww xY w)NzFAlpha Evolve Daemon -- Autonomous nightly evolution for Genesis assetsa  
Examples:
  python scripts/alpha_evolve_daemon.py --dry-run        # Simulate without changes
  python scripts/alpha_evolve_daemon.py --cycle          # Run one evolution cycle
  python scripts/alpha_evolve_daemon.py --cycle --seed   # Seed + run cycle
  python scripts/alpha_evolve_daemon.py --status         # Show generation status
  python scripts/alpha_evolve_daemon.py --daemon         # Run as daemon (03:00 AEST)

Environment vars:
  INSTANTLY_API_KEY         Override hardcoded Instantly key
  GEMINI_API_KEY            Gemini key for LLM mutations
  OPENROUTER_API_KEY        Fallback LLM if Gemini unavailable
  ALPHA_EVOLVE_PAUSE=true   Pause the daemon
  ALPHA_EVOLVE_DRY_RUN=true Force dry-run mode
  ALPHA_EVOLVE_MUTATIONS=N  Mutations per elite (default 5)
        )r  formatter_classepilogT)requiredz--cycle
store_truez Run one complete evolution cycle)actionhelpz	--dry-runzASimulate a cycle without making any changes (safe to run anytime)z--statusz*Show current generation status (read-only)z--daemonz6Run as long-running daemon (fires at 03:00 AEST daily)z--forcez#Override ALPHA_EVOLVE_PAUSE env varz--asset-typer6   z9Process only this asset type (default: all Phase 1 types))r  destr  z--seedzQSeed initial email assets from INSTANTLY_SEQUENCE_PARTNER_PITCH.md before cyclingz$STATUS: Cannot connect to database: r   )argparseArgumentParserRawDescriptionHelpFormatteradd_mutually_exclusive_groupadd_argumentASSET_TYPES
parse_argsr;   r   r   rN  r  r   r   r   sysexitdaemonr  r  rW   )parser
mode_groupr  r   r   r  s         rK   mainr  J	  s   $$\ <<F* 44d4CJ,/   LP   <9   <E  
 ,2   ,H   `  
 D
 {{	$&D$JJL 	{{ t_F&--  v  	II<QC@AHHQKK	s   '0E   	F )-FF __main__)rZ   Nr"  )XrC   r  r   loggingr   r  r  r~  rn  dataclassesr   r   r   r   r   r   pathlibr	   typingr
   r   r   r   r   __file__resolverc  r   GENESIS_MEMORY_PATHpathinsertrD   r
  r&  r/  rD  r  r  r  r  r  r"   rF   rE   r   r   r  LOG_DIRr5  r  r0  r<  _dr  basicConfigINFOFileHandlerStreamHandlerstdout	getLoggerr   r4   rM   rG   ra   rp   r|   r   r   r   r   r   r   r   r$  r>  r  r  r  rN  rY   r[  rS  rT  r  r  r  r@   rI   rJ   rK   <module>r     s  $L    	 	 
   0 0 2 2  3 3 H~%%'..55"V+.>>  3|$ % 3*+ , 	#$  "   U ((  rl"B'#S)	2 T#tCH~-.  ? c (  
&
(f$z1'*>> 11H<?ZZ #;;  L/>3H3H
I *BHHTDH)*   
,,@C*< <=Pcjj) g' ; ; ;   &;5 ; &4S>  &e  &F &4S>  &e  &F&DcN &u &$ $!# H@T.Yx	c 	  L
X X~VZ VZz`C `CNY! Y!@^K ^KJEX<T <c <~ S	  d4j  F#T
 #T'C\cx, cTM` zF rJ   