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
This commit is contained in:
jedarden 2026-05-26 13:28:44 -04:00
parent 6fbe221fe7
commit 04b860a8be
3 changed files with 55 additions and 19 deletions

View file

@ -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()

View file

@ -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

View file

@ -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)