Extracts the embedded schema from cmd/acb-api/db.go into a standalone migration file per §11.1 plan inventory. Creates all core tables: - Core: bots, matches, match_participants, jobs, rating_history - Season/series: seasons, season_snapshots, series, series_games - Predictions: predictions, predictor_stats - Maps: maps, map_scores, map_votes, map_fairness - Evolution: programs - Playlists: playlists, playlist_matches - Feedback: replay_feedback, feedback_upvotes - Enrichment: enrichment_requests Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
296 lines
11 KiB
SQL
296 lines
11 KiB
SQL
-- Initial schema for AI Code Battle
|
|
-- This migration creates all core tables for the application
|
|
-- Includes: bots, matches, jobs, ratings, seasons, series, maps, programs, playlists, feedback, enrichment
|
|
|
|
-- =====================================================
|
|
-- Core Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE bots (
|
|
bot_id VARCHAR(16) PRIMARY KEY,
|
|
name VARCHAR(32) UNIQUE NOT NULL,
|
|
owner VARCHAR(128) NOT NULL,
|
|
endpoint_url TEXT NOT NULL,
|
|
shared_secret TEXT NOT NULL,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
|
rating_mu DOUBLE PRECISION NOT NULL DEFAULT 1500.0,
|
|
rating_phi DOUBLE PRECISION NOT NULL DEFAULT 350.0,
|
|
rating_sigma DOUBLE PRECISION NOT NULL DEFAULT 0.06,
|
|
evolved BOOLEAN NOT NULL DEFAULT FALSE,
|
|
island VARCHAR(16),
|
|
generation INTEGER,
|
|
parent_ids JSONB,
|
|
description TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_active TIMESTAMPTZ,
|
|
consec_fails INTEGER NOT NULL DEFAULT 0,
|
|
archetype VARCHAR(64),
|
|
crash_strikes INTEGER NOT NULL DEFAULT 0,
|
|
cooldown_until TIMESTAMPTZ,
|
|
debug_public BOOLEAN NOT NULL DEFAULT FALSE
|
|
);
|
|
|
|
CREATE TABLE matches (
|
|
match_id VARCHAR(32) PRIMARY KEY,
|
|
map_id VARCHAR(32) NOT NULL,
|
|
map_seed BIGINT,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
|
winner INTEGER,
|
|
condition VARCHAR(32),
|
|
turn_count INTEGER,
|
|
combat_turns INTEGER NOT NULL DEFAULT 0,
|
|
scores_json JSONB,
|
|
enrichment_requested_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
completed_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE TABLE match_participants (
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
bot_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
player_slot INTEGER NOT NULL,
|
|
score INTEGER,
|
|
status VARCHAR(16),
|
|
PRIMARY KEY (match_id, bot_id)
|
|
);
|
|
|
|
CREATE TABLE jobs (
|
|
job_id VARCHAR(32) PRIMARY KEY,
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
|
worker_id VARCHAR(64),
|
|
config_json JSONB NOT NULL,
|
|
claimed_at TIMESTAMPTZ,
|
|
completed_at TIMESTAMPTZ,
|
|
heartbeat_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE rating_history (
|
|
bot_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
rating DOUBLE PRECISION NOT NULL,
|
|
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (bot_id, match_id)
|
|
);
|
|
CREATE INDEX idx_rating_history_bot ON rating_history(bot_id, recorded_at);
|
|
|
|
-- =====================================================
|
|
-- Season & Series Tables (Phase 9)
|
|
-- =====================================================
|
|
|
|
CREATE TABLE seasons (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
name VARCHAR(64) NOT NULL,
|
|
theme VARCHAR(128),
|
|
rules_version VARCHAR(32) NOT NULL DEFAULT '1.0',
|
|
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
|
champion_id VARCHAR(16),
|
|
starts_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
ends_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE season_snapshots (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
season_id BIGINT NOT NULL REFERENCES seasons(id),
|
|
bot_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
rank INTEGER NOT NULL,
|
|
rating DOUBLE PRECISION NOT NULL,
|
|
wins INTEGER NOT NULL DEFAULT 0,
|
|
losses INTEGER NOT NULL DEFAULT 0,
|
|
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX idx_season_snapshots_season ON season_snapshots(season_id, rank);
|
|
|
|
CREATE TABLE series (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
bot_a_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
bot_b_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
format INTEGER NOT NULL DEFAULT 5,
|
|
a_wins INTEGER NOT NULL DEFAULT 0,
|
|
b_wins INTEGER NOT NULL DEFAULT 0,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
|
winner_id VARCHAR(16),
|
|
season_id BIGINT REFERENCES seasons(id),
|
|
bracket_round VARCHAR(32),
|
|
bracket_position INTEGER,
|
|
featured BOOLEAN NOT NULL DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX idx_series_bots ON series(bot_a_id, bot_b_id);
|
|
CREATE INDEX idx_series_status ON series(status);
|
|
CREATE INDEX idx_series_season ON series(season_id);
|
|
CREATE INDEX idx_series_bracket ON series(season_id, bracket_round) WHERE bracket_round IS NOT NULL;
|
|
CREATE INDEX idx_series_featured ON series(featured, created_at DESC) WHERE featured = TRUE;
|
|
|
|
CREATE TABLE series_games (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
series_id BIGINT NOT NULL REFERENCES series(id),
|
|
match_id VARCHAR(32) REFERENCES matches(match_id),
|
|
game_num INTEGER NOT NULL,
|
|
winner_id VARCHAR(16),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX idx_series_games_series ON series_games(series_id);
|
|
|
|
-- =====================================================
|
|
-- Prediction Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE predictions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
predictor_id VARCHAR(64) NOT NULL,
|
|
predicted_bot VARCHAR(16) NOT NULL,
|
|
confidence SMALLINT CHECK (confidence >= 1 AND confidence <= 100),
|
|
correct BOOLEAN,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
resolved_at TIMESTAMPTZ,
|
|
UNIQUE(match_id, predictor_id)
|
|
);
|
|
CREATE INDEX idx_predictions_match ON predictions(match_id);
|
|
CREATE INDEX idx_predictions_predictor ON predictions(predictor_id);
|
|
|
|
CREATE TABLE predictor_stats (
|
|
predictor_id VARCHAR(64) PRIMARY KEY,
|
|
correct INTEGER NOT NULL DEFAULT 0,
|
|
incorrect INTEGER NOT NULL DEFAULT 0,
|
|
streak INTEGER NOT NULL DEFAULT 0,
|
|
best_streak INTEGER NOT NULL DEFAULT 0,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- =====================================================
|
|
-- Map Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE maps (
|
|
map_id VARCHAR(32) PRIMARY KEY,
|
|
player_count INTEGER NOT NULL,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
|
engagement DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
wall_density DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
energy_count INTEGER NOT NULL DEFAULT 0,
|
|
grid_width INTEGER NOT NULL,
|
|
grid_height INTEGER NOT NULL,
|
|
map_json JSONB NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
retired_at TIMESTAMPTZ
|
|
);
|
|
CREATE INDEX idx_maps_status ON maps(status, player_count);
|
|
CREATE INDEX idx_maps_engagement ON maps(player_count, engagement DESC);
|
|
|
|
CREATE TABLE map_scores (
|
|
map_id VARCHAR(32) PRIMARY KEY,
|
|
engagement DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
symmetry_score DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
wall_density DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
last_used_at TIMESTAMPTZ,
|
|
match_count INTEGER NOT NULL DEFAULT 0,
|
|
avg_turns DOUBLE PRECISION,
|
|
scored_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE map_votes (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
map_id VARCHAR(32) NOT NULL REFERENCES maps(map_id) ON DELETE CASCADE,
|
|
voter_id VARCHAR(64) NOT NULL,
|
|
vote SMALLINT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(map_id, voter_id)
|
|
);
|
|
CREATE INDEX idx_map_votes_map ON map_votes(map_id);
|
|
|
|
CREATE TABLE map_fairness (
|
|
map_id VARCHAR(32) NOT NULL REFERENCES maps(map_id) ON DELETE CASCADE,
|
|
player_slot INTEGER NOT NULL,
|
|
games INTEGER NOT NULL DEFAULT 0,
|
|
wins INTEGER NOT NULL DEFAULT 0,
|
|
last_check TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (map_id, player_slot)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- Program Tables (Evolution)
|
|
-- =====================================================
|
|
|
|
CREATE TABLE 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,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX idx_programs_island ON programs(island);
|
|
CREATE INDEX idx_programs_island_fitness ON programs(island, fitness DESC);
|
|
|
|
-- =====================================================
|
|
-- Playlist Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE playlists (
|
|
slug VARCHAR(64) PRIMARY KEY,
|
|
title VARCHAR(128) NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
category VARCHAR(32) NOT NULL DEFAULT 'featured',
|
|
is_auto BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE playlist_matches (
|
|
playlist_slug VARCHAR(64) NOT NULL REFERENCES playlists(slug) ON DELETE CASCADE,
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
curation_tag TEXT NOT NULL DEFAULT '',
|
|
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (playlist_slug, match_id)
|
|
);
|
|
CREATE INDEX idx_playlist_matches_playlist ON playlist_matches(playlist_slug, sort_order);
|
|
|
|
-- =====================================================
|
|
-- Feedback Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE replay_feedback (
|
|
feedback_id VARCHAR(32) PRIMARY KEY,
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
turn INTEGER NOT NULL,
|
|
type VARCHAR(16) NOT NULL CHECK (type IN ('insight', 'mistake', 'idea', 'highlight')),
|
|
body TEXT NOT NULL,
|
|
author VARCHAR(128) NOT NULL,
|
|
upvotes INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX idx_feedback_match ON replay_feedback(match_id, turn);
|
|
|
|
CREATE TABLE feedback_upvotes (
|
|
feedback_id VARCHAR(32) NOT NULL REFERENCES replay_feedback(feedback_id) ON DELETE CASCADE,
|
|
voter_id VARCHAR(36) NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (feedback_id, voter_id)
|
|
);
|
|
|
|
-- =====================================================
|
|
-- Enrichment Tables
|
|
-- =====================================================
|
|
|
|
CREATE TABLE enrichment_requests (
|
|
request_id VARCHAR(32) PRIMARY KEY,
|
|
match_id VARCHAR(32) NOT NULL REFERENCES matches(match_id),
|
|
bot_id VARCHAR(16) NOT NULL REFERENCES bots(bot_id),
|
|
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
|
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
processed_at TIMESTAMPTZ,
|
|
error_msg TEXT,
|
|
UNIQUE(match_id, bot_id)
|
|
);
|
|
CREATE INDEX idx_enrichment_requests_status ON enrichment_requests(status, requested_at);
|
|
CREATE INDEX idx_enrichment_requests_bot ON enrichment_requests(bot_id, requested_at);
|