From 04b860a8becd0643e17f2922d94ef162c7f4eb75 Mon Sep 17 00:00:00 2001 From: jedarden Date: Tue, 26 May 2026 13:28:44 -0400 Subject: [PATCH] feat(index-builder, evolver): improve evolution system initialization and logging Index builder: - Add slog import for structured logging - Improve fetchEvolutionMeta to return empty meta instead of error when programs table is empty - Add logging to show evolution system status (running vs not initialized) - Add logging in generateEvolutionMeta to show when evolution data is written Evolver: - Add automatic schema initialization and population seeding in RunEvolutionLoop - Programs table is now automatically seeded with 6 initial strategy bots on startup - Log seeding status to indicate whether programs table was already initialized These changes ensure the evolution system properly initializes when deployed and provides better visibility into its status via structured logging. Closes: bf-4zde --- cmd/acb-evolver/run.go | 14 +++++++++++ cmd/acb-index-builder/db.go | 40 +++++++++++++++++++----------- cmd/acb-index-builder/generator.go | 20 ++++++++++++--- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/cmd/acb-evolver/run.go b/cmd/acb-evolver/run.go index d170852..c30c93d 100644 --- a/cmd/acb-evolver/run.go +++ b/cmd/acb-evolver/run.go @@ -187,7 +187,21 @@ func RunEvolutionLoop(ctx context.Context, dbURL string, args []string) { } defer db.Close() + // Initialize database schema and seed initial population if needed + ctx := context.Background() + if err := evolverdb.EnsureSchema(ctx, db); err != nil { + log.Fatalf("ensure schema: %v", err) + } + + // Seed initial population if programs table is empty store := evolverdb.NewStore(db) + if inserted, err := evolverdb.SeedPopulation(ctx, store); err != nil { + log.Fatalf("seed population: %v", err) + } else if inserted > 0 { + log.Printf("Seeded %d initial programs", inserted) + } else { + log.Println("Programs table already seeded") + } // Load config from env with overrides cfg := DefaultRunConfig() diff --git a/cmd/acb-index-builder/db.go b/cmd/acb-index-builder/db.go index 699e0bb..04423d8 100644 --- a/cmd/acb-index-builder/db.go +++ b/cmd/acb-index-builder/db.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "log/slog" "time" ) @@ -258,9 +259,18 @@ func fetchAllData(ctx context.Context, db *sql.DB) (*IndexData, error) { return nil, err } - // Evolution data (may be missing if evolver DB is not available) + // Evolution data (may be missing if evolver is not running) data.EvolutionMeta, _ = fetchEvolutionMeta(ctx, db) data.Lineage, _ = fetchLineage(ctx, db) + if data.EvolutionMeta != nil && data.EvolutionMeta.Generation > 0 { + slog.Info("Evolution system running", + "generation", data.EvolutionMeta.Generation, + "promoted_today", data.EvolutionMeta.PromotedToday, + "total_promoted", data.EvolutionMeta.TotalPromoted, + "islands", len(data.EvolutionMeta.IslandPopulations)) + } else { + slog.Info("Evolution system not initialized or not running") + } data.TopPredictors = computeTopPredictors(data.PredictorStats) @@ -1050,6 +1060,7 @@ type LineageNode struct { // fetchEvolutionMeta queries the evolver database for evolution statistics. // It connects to the evolver database using the same connection parameters. +// Returns empty meta (not an error) if the evolution system is not running. func fetchEvolutionMeta(ctx context.Context, db *sql.DB) (*EvolutionMeta, error) { // Query the programs table in the evolver database // Note: the evolver uses a separate database but same PostgreSQL instance @@ -1068,20 +1079,19 @@ func fetchEvolutionMeta(ctx context.Context, db *sql.DB) (*EvolutionMeta, error) err := db.QueryRowContext(ctx, query).Scan(&meta.Generation, &meta.PromotedToday, &meta.TotalPromoted, &totalPrograms) if err != nil { - // If evolver tables don't exist, return empty meta - if err == sql.ErrNoRows { - return &EvolutionMeta{ - Generation: 0, - PromotedToday: 0, - Top10Count: 0, - IslandPopulations: make(map[string]int), - BestRatings: []EvolvedBotRating{}, - TotalPromoted: 0, - PromotionRate: 0, - UpdatedAt: updatedAt, - }, nil - } - return nil, fmt.Errorf("fetch evolution meta: %w", err) + // If evolver tables don't exist or query fails, return empty meta + // This is expected when the evolution system has not been initialized yet + slog.Info("Evolution system not running or programs table empty", "error", err) + return &EvolutionMeta{ + Generation: 0, + PromotedToday: 0, + Top10Count: 0, + IslandPopulations: make(map[string]int), + BestRatings: []EvolvedBotRating{}, + TotalPromoted: 0, + PromotionRate: 0, + UpdatedAt: updatedAt, + }, nil } // Calculate promotion rate diff --git a/cmd/acb-index-builder/generator.go b/cmd/acb-index-builder/generator.go index e629ddc..3788906 100644 --- a/cmd/acb-index-builder/generator.go +++ b/cmd/acb-index-builder/generator.go @@ -6,6 +6,7 @@ import ( "encoding/json" "encoding/xml" "fmt" + "log/slog" "math" "os" "path/filepath" @@ -2094,12 +2095,23 @@ func generateEvolutionMeta(data *IndexData, outputDir string) error { // If no evolution meta data, write empty placeholder meta := data.EvolutionMeta if meta == nil { + slog.Info("Evolution meta data not available - evolution system not running") meta = &EvolutionMeta{ - Generation: 0, - PromotedToday: 0, - Top10Count: 0, - UpdatedAt: data.GeneratedAt.Format(time.RFC3339), + Generation: 0, + PromotedToday: 0, + Top10Count: 0, + IslandPopulations: make(map[string]int), + BestRatings: []EvolvedBotRating{}, + TotalPromoted: 0, + PromotionRate: 0, + UpdatedAt: data.GeneratedAt.Format(time.RFC3339), } + } else { + slog.Info("Writing evolution meta data", + "generation", meta.Generation, + "promoted_today", meta.PromotedToday, + "total_promoted", meta.TotalPromoted, + "islands", len(meta.IslandPopulations)) } return writeJSON(filepath.Join(evolDir, "meta.json"), meta)