Migrations

Schema migrations in Xema are workflow-orchestrated and isolated per schema. Every migration run records which of the platform's two runners applied it: one applies the migration files a biome ships inside its bundle, the other applies a typed schema-change plan generated by the platform.


Migration runners

Two are implemented, and a MigrationRun records which one applied it. MigrationRunnerKind also declares drizzle and flyway, but nothing emits them — they are placeholders, not supported tooling. Adding a runner is a platform change, not configuration.

The bundle-applying runner is handed a workspace and a short-lived connection, and reports the outcome of applying every pending migration in it:

ts
1interface MigrationRunInput {
2  connectionUrl: string;             // short-lived credential URL (see Connections)
3  workspaceDir: string;              // the biome's migration bundle on the executor
4  env: Record<string, string>;
5}
6
7interface MigrationRunResult {
8  exitCode: number;
9  output: string;
10}

The platform does not read the migration files themselves — it hands the runner a workspace and a short-lived connection and records the exit code and output on the MigrationRun row.


Default migration runner (v1)

The default runner. It:

  1. Connects to the schema using the provided connectionUrl.
  2. Runs all pending migrations in migrationsPath against the target schema.
  3. Records the migration history in the migration history table inside the target schema.
  4. Records the outcome — the runner used, the applied migrations, and any failure — on a MigrationRun row carrying a MigrationStatus.

Migration files in the biome bundle are organized by timestamp and name:

text
1biome/
2  migrations/
3    20260527_001_create_reviews/
4      migration.sql
5    20260527_002_add_comments/
6      migration.sql
7  schema.migration

When migrations run

Migrations run as part of three platform workflows:

Entry pointWhen triggeredWhat it does
ProvisionDatabaseWorkflowAt org provisioningCreates the org database and its first schema
EnsureSchemaWorkflowAt biome installEnsures the biome's schema exists on the org database
RunMigrationsActivityAt biome boot and on version updateApplies the biome's pending migrations
Promote-to-prodOn an app production deployApplies pending migrations to the prod schema (see App Studio Dev/Prod)

All of them record the runner they used on the MigrationRun row. The workflows are orchestrated by the Xema Workflow Runtime, which provides retry, timeout, and observability; the migration activity raises typed failures (MigrationFailedError, MigrationTimeoutError) rather than reporting a partial success.


Migration failure behavior

If a migration fails:

  1. The workflow marks the migration step as FAILED.
  2. The schema is left in the partially-migrated state (the database does not auto-rollback DDL across statements).
  3. The biome remains in schema-migration-failed state — it can read existing data but cannot write to newly-expected columns.
  4. The org admin receives an alert with the failed migration SQL and the database error.

To recover: fix the migration SQL, publish a new biome version, and re-run the migration.


Manual migration control

For migrations that require careful sequencing (large table rewrites, zero-downtime schema changes), the migration can be marked manual-only in the biome's migration manifest:

json
1{
2  "migration": "20260527_003_reindex_reviews",
3  "mode": "manual-only",
4  "instructions": "Run CONCURRENTLY to avoid table lock. See ops runbook."
5}

Manual-only migrations are skipped by the automatic runner and surfaced in the org admin dashboard with the instructions. An org admin must run them explicitly — from the org admin dashboard, or with the Shell's db migrate command.


Previous: ← Provisioning

Next: Connections →