ai-code-battle/cmd/acb-evolver/internal/db/db.go
jedarden 7a0de02059 feat(evolver): persist cross-pollination state to Postgres per §10.2
Add crosspoll_state table to persist per-island generation counters
across evolver restarts. Load state on startup and save after each
cross-pollination check. Add persistence pattern and translation
structure tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:04:15 -04:00

70 lines
2.7 KiB
Go

// Package db provides database access for the evolution pipeline.
package db
import (
"context"
"database/sql"
)
// schemaSQL creates the programs and validation_log tables with their indexes.
const schemaSQL = `
CREATE TABLE IF NOT EXISTS programs (
id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL,
language VARCHAR(32) NOT NULL,
island VARCHAR(16) NOT NULL,
generation INTEGER NOT NULL DEFAULT 0,
parent_ids JSONB NOT NULL DEFAULT '[]',
behavior_vector DOUBLE PRECISION[] NOT NULL DEFAULT '{}',
fitness DOUBLE PRECISION NOT NULL DEFAULT 0.0,
promoted BOOLEAN NOT NULL DEFAULT FALSE,
bot_id VARCHAR(16),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_programs_island ON programs(island);
CREATE INDEX IF NOT EXISTS idx_programs_island_fitness ON programs(island, fitness DESC);
CREATE TABLE IF NOT EXISTS validation_log (
id BIGSERIAL PRIMARY KEY,
island VARCHAR(16) NOT NULL,
language VARCHAR(32) NOT NULL,
stage VARCHAR(16) NOT NULL, -- last stage attempted: syntax / schema / sandbox
passed BOOLEAN NOT NULL,
error_text TEXT NOT NULL DEFAULT '',
llm_output TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_validation_log_island ON validation_log(island);
CREATE INDEX IF NOT EXISTS idx_validation_log_island_passed ON validation_log(island, passed);
`
// crosspollStateSQL creates the crosspoll_state table for persisting per-island
// last-pollinated generation numbers across evolver restarts.
const crosspollStateSQL = `
CREATE TABLE IF NOT EXISTS crosspoll_state (
island VARCHAR(16) PRIMARY KEY,
last_pollinated_gen INTEGER NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
`
// migrationSQL holds additive migrations run after the base schema is ensured.
// Each statement is idempotent (ALTER TABLE … ADD COLUMN IF NOT EXISTS).
const migrationSQL = `
ALTER TABLE programs ADD COLUMN IF NOT EXISTS bot_id VARCHAR(16);
ALTER TABLE programs ADD COLUMN IF NOT EXISTS bot_name VARCHAR(64);
ALTER TABLE programs ADD COLUMN IF NOT EXISTS bot_secret TEXT;
`
// EnsureSchema creates the programs and validation_log tables if they do not
// already exist, then applies any pending additive migrations.
func EnsureSchema(ctx context.Context, db *sql.DB) error {
if _, err := db.ExecContext(ctx, schemaSQL); err != nil {
return err
}
if _, err := db.ExecContext(ctx, crosspollStateSQL); err != nil {
return err
}
_, err := db.ExecContext(ctx, migrationSQL)
return err
}