You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Build migration intelligence: a tool that takes a spec diff (or an entity-class diff) and proposes the SQL needed to safely evolve the database — column adds, renames, drops, type changes, foreign key adjustments — including data-migration where required. Surfaced via bin/altair db:migration-plan and the framework__plan_migration MCP tool.
Today migrations are emitted as skeletons. The agent has to fill in the data-shape changes by hand. Migration intelligence fills them in correctly, with awareness of safety pitfalls (NOT NULL on existing rows, FK orphaning, etc.).
Why
Spec-driven scaffolding is great for greenfield endpoints. Real systems do refactors:
"Rename User.password to User.password_hash"
"Split User.full_name into first_name + last_name"
"Make email unique (turns out it wasn't before)"
"Drop legacy_role, but keep its data in a sidecar table for 90 days"
Each is a non-trivial migration. Without intelligence, the agent writes "ALTER TABLE users ADD COLUMN ..." and hopes. With it, the agent gets a proposal that includes:
The DDL operations in the right order
Backfill SQL for data migrations
Rollback steps
Safety warnings (e.g. "This NOT NULL change will fail on 23 existing rows — backfill required first")
How it works
Compute the diff — either from two spec files (api/users/create.yaml@v1 vs @v2), or from current spec vs. current schema, or from a given Entity class vs. its current DB shape
Walk the diff producing intents — add_column, rename_column, drop_column, change_type, add_index, add_foreign_key, migrate_data
Plan the SQL — per intent, emit SQL appropriate to the target driver (Postgres / MySQL / SQLite via Cycle's dialect layer)
Run safety checks — query the live DB (read-only) to verify the migration won't fail. E.g. for ALTER COLUMN ... SET NOT NULL, count rows where the column is null; warn if non-zero.
Output the proposal — as a Cycle migration class, ready for review and db:migrate
CLI surface
bin/altair db:migration-plan # detect drift between specs and DB
bin/altair db:migration-plan api/users.yaml # for one spec file
bin/altair db:migration-plan --from-entity=App\\User\\User # entity-vs-schema diff
bin/altair db:migration-plan --output=database/migrations/ # write the proposed migration
bin/altair db:migration-plan --dry-run # print, don't write
bin/altair db:migration-plan --format=json # for MCP
Output (pretty mode):
Proposed migration: add_display_name_to_users.php
Operations:
→ ADD COLUMN display_name VARCHAR(255) NULL DEFAULT NULL
→ UPDATE users SET display_name = COALESCE(first_name || ' ' || last_name, email) WHERE display_name IS NULL
→ ALTER COLUMN display_name SET NOT NULL
✓ Safe: backfill query would affect 247 rows; estimated <1s on production-sized table.
Rollback:
→ DROP COLUMN display_name
Count rows where new column would be NULL (post-rename, etc.) — if > 0, propose backfill
error
Add UNIQUE
Count duplicates — if > 0, propose dedup strategy or refuse
error
Drop column
Warn unconditionally; require --force if column has non-null values
warn
Rename column
Recommend two-phase: add new column, copy data, deploy, drop old column
info
Add foreign key
Count orphan rows in the foreign-key column — if > 0, refuse
error
Change column type
Sample 100 rows, try the cast in a transaction (rolled back); warn on cast failures
warn or error
Drop index
Warn if the index is used in queries from pg_stat_user_indexes / equivalent
info
Large table operation (>1M rows)
Warn that this should run during a maintenance window or with CONCURRENTLY (Postgres)
warn
The checks query the dev DB read-only. On staging/prod the agent shouldn't run this directly — instead generate the plan locally and apply via the team's deploy process.
MCP tool
{
"name": "framework__plan_migration",
"description": "Compute a migration plan from a spec change or entity diff",
"inputSchema": {
"from": "string (spec path or entity class)",
"to": "string (spec path or entity class, optional — defaults to current state)",
"safety_check": "boolean (default true)"
}
}
The agent calls this before committing to a refactor. Sees the proposed operations + safety. Decides whether to proceed.
Two-phase migrations
For renames and incompatible type changes, the tool produces two migrations:
No new external deps — leverages Cycle's existing schema builder and migration libraries.
Why this matters
Migrations are the deploy-time disaster vector. An agent that writes a migration without intelligence will eventually generate ALTER COLUMN SET NOT NULL on a column with 50% nulls and bring down production. With intelligence, the same agent gets warned and produces the correct two-phase plan. The single feature that makes "agent-written migrations" production-safe.
Goal
Build migration intelligence: a tool that takes a spec diff (or an entity-class diff) and proposes the SQL needed to safely evolve the database — column adds, renames, drops, type changes, foreign key adjustments — including data-migration where required. Surfaced via
bin/altair db:migration-planand theframework__plan_migrationMCP tool.Today migrations are emitted as skeletons. The agent has to fill in the data-shape changes by hand. Migration intelligence fills them in correctly, with awareness of safety pitfalls (NOT NULL on existing rows, FK orphaning, etc.).
Why
Spec-driven scaffolding is great for greenfield endpoints. Real systems do refactors:
User.passwordtoUser.password_hash"User.full_nameintofirst_name+last_name"emailunique (turns out it wasn't before)"legacy_role, but keep its data in a sidecar table for 90 days"Each is a non-trivial migration. Without intelligence, the agent writes "ALTER TABLE users ADD COLUMN ..." and hopes. With it, the agent gets a proposal that includes:
How it works
api/users/create.yaml@v1vs@v2), or from current spec vs. current schema, or from a given Entity class vs. its current DB shapeadd_column,rename_column,drop_column,change_type,add_index,add_foreign_key,migrate_dataALTER COLUMN ... SET NOT NULL, count rows where the column is null; warn if non-zero.db:migrateCLI surface
Output (pretty mode):
Output (JSON mode for MCP):
{ "migration_name": "add_display_name_to_users", "filename": "database/migrations/2026_05_27_114523_add_display_name_to_users.php", "operations": [ { "op": "add_column", "table": "users", "column": "display_name", "type": "varchar(255)", "nullable": true }, { "op": "data_migration", "sql": "UPDATE users SET ..." }, { "op": "alter_column", "table": "users", "column": "display_name", "nullable": false } ], "rollback": [ { "op": "drop_column", "table": "users", "column": "display_name" } ], "safety": { "issues": [], "warnings": [ { "level": "warn", "message": "Backfill will affect 247 rows" } ], "estimated_duration_ms": 800 } }Safety checks performed
--forceif column has non-null valuespg_stat_user_indexes/ equivalentCONCURRENTLY(Postgres)The checks query the dev DB read-only. On staging/prod the agent shouldn't run this directly — instead generate the plan locally and apply via the team's deploy process.
MCP tool
{ "name": "framework__plan_migration", "description": "Compute a migration plan from a spec change or entity diff", "inputSchema": { "from": "string (spec path or entity class)", "to": "string (spec path or entity class, optional — defaults to current state)", "safety_check": "boolean (default true)" } }The agent calls this before committing to a refactor. Sees the proposed operations + safety. Decides whether to proceed.
Two-phase migrations
For renames and incompatible type changes, the tool produces two migrations:
2026_05_27_114523_phase1_add_new_column_for_rename.php— additive, deploys safely2026_05_27_114600_phase2_drop_old_column_after_rename.php— destructive, deploy after phase 1 is verified in prodThe plan output explicitly labels them and the safety section warns that phase 2 should not run in the same release.
Shape
Acceptance criteria
db:migration-planproduces a correct Cycle migration for: add column, drop column, rename column, add index, add unique constraint, add foreign key, change typeframework__plan_migrationMCP tool returns the structured plandb:migration-plan, get a migration that brings DB in line with new spec--from-entity, get a migrationusers.yaml@v1+users.yaml@v2→ expected migration file content (deterministic)Out of scope
pt-online-schema-change,gh-ost) integrationDependencies
univeros/cli) — requireduniveros/scaffold) — spec diffing reuses scaffolder's ASTuniveros/persistence) — Cycle ORM + schema introspector + migration emissionuniveros/mcp) — required for the tool exposureNo new external deps — leverages Cycle's existing schema builder and migration libraries.
Why this matters
Migrations are the deploy-time disaster vector. An agent that writes a migration without intelligence will eventually generate
ALTER COLUMN SET NOT NULLon a column with 50% nulls and bring down production. With intelligence, the same agent gets warned and produces the correct two-phase plan. The single feature that makes "agent-written migrations" production-safe.