Database Migrations¶
The project uses Alembic for database schema migrations and data seeding.
Applying Migrations¶
# Apply all pending migrations
uv run alembic upgrade head
# Check current migration version
uv run alembic current
# View migration history
uv run alembic history
Creating a Schema Migration¶
When you modify tables in src/db/tables.py, auto-generate a migration:
Warning
Always review the generated migration file in src/db/migrations/versions/. Auto-generation can miss some changes (e.g., column renames, data type changes).
Creating a Data Migration¶
Agent prompts and seed data are loaded via data migrations, not application code. To add or update a prompt:
-
Create a new migration file:
-
Write the
upgrade()anddowngrade()functions:from alembic import op import sqlalchemy as sa def upgrade() -> None: # Deactivate previous version op.execute( sa.text( "UPDATE agent_prompts SET active = false " "WHERE agent_name = 'agent_name' AND active = true" ) ) # Insert new version op.execute( sa.text( "INSERT INTO agent_prompts " "(agent_name, version, model, system_prompt, active) " "VALUES (:name, :version, :model, :prompt, true)" ).bindparams( name="agent_name", version=2, model="claude-sonnet-4-6", prompt="Your prompt text here...", ) ) def downgrade() -> None: op.execute( sa.text( "DELETE FROM agent_prompts " "WHERE agent_name = 'agent_name' AND version = 2" ) ) op.execute( sa.text( "UPDATE agent_prompts SET active = true " "WHERE agent_name = 'agent_name' AND version = 1" ) ) -
Apply the migration:
Migrations User¶
In production, migrations run under a dedicated migrations database user with DDL privileges, separate from the runtime pipeline user. Set DATABASE_URL_MIGRATIONS to use a different connection string for Alembic.
Testing Against Staging¶
Before applying to production, test against the Neon staging branch:
DATABASE_URL_MIGRATIONS="postgresql+asyncpg://migrations:PASSWORD@ep-staging.neon.tech/db?sslmode=require" \
uv run alembic upgrade head
See Deployment for staging branch setup.