Repository navigation
TML-3474: shape the migration-statements project: the planner refuses data loss, and the user states renames, deletes, conversions and backfills on the command line - #30604
Conversation
Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (4)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The project spec, design notes and plan in this PR land on |
|
Superseded by #30638, which merged with the project folder (projects/migration-statements/) on main. |
At a glance
A user renames the
Profilemodel toUser, renames itsnamefield and removes theLegacymodel in the contract source, then says what they mean on the command line:Without
--delete Legacy, the command refuses and prints the statements that would resolve it:Today the same change plans
dropTable "Profile"pluscreateTable "User", and the rows are gone after one consent prompt that covers the whole plan.What this PR changes
This is a shaping PR. It adds
projects/migration-statements/with the project spec, the design notes and a plan placeholder. No code changes. Merging it records the design so the slices can reference it; the slices follow as their own PRs.The problem
The migration planner diffs two states, a destination contract and an origin schema, and cannot tell intent from the diff. A renamed model looks exactly like a dropped table plus a created one. Today the planner plans the destruction, and the only protection is a consent prompt that covers every destructive operation in the plan at once, or a hand-written
migration.ts. Projects that usedb updateand keep no migration history cannot hand-write anything.The decision
Two things become true.
The planner refuses data loss unless the user has said what they mean about that specific operation. Destructive means data is lost: dropping a model or field, deleting a value rows still carry, or a type change the database cannot keep. Drops of indexes, constraints, checks, policies and defaults lose nothing and are widening.
db updatealready refuses through its consent prompt;migration plangains the same refusal for every plan, not only auto-baselines.The user says it on the command line, in the vocabulary of their contract. Four verbs,
rename,delete,convertandbackfill, over four nouns, namespace, model, field and value, addressed as the contract source names them:User,User.name,Status.ARCHIVED,Address.street. The framework resolves the coordinates against the origin and destination contracts; the family turns the resolved statement into storage operations, so the same--rename User.name:User.fullNamerenames a column on Postgres and rewrites documents on Mongo. Nothing on the command line is a table, a column, a SQL expression or a literal value.A statement is intent, not a hint. One that cannot be resolved or applied is an error and nothing is planned.
--deleteis per-operation consent and replaces the blanket--confirm.--convertand--backfillscaffold the placeholder migration thatmigration planalready knows how to write, with the placeholder in the operation that uses it, such as theusingexpression on a Postgres type change; the user fills it inmigration.tsand re-emits, as today.The design notes carry the full verb and noun matrix with what each statement does per family, sixteen scenarios that stress the model, a walkthrough of a type change with its evaluation, and a survey of how Prisma Migrate, ActiveRecord, Django, Drizzle, Atlas and Terraform handle the same problem.
Relationship to the shelved attempt
The first attempt, #30557 and #30570, stated intent in the contract source with
@@hint(was: "Profile"). It was shelved on 2026-10-05 as unintuitive. Its planner substrate, a working schema shared by the planner and the hand-written facade, constraint names taken from the destination contract, and the reclassification of non-data drops as widening, is reused by the first slice. Its contract section is not.Alternatives considered
renamed_fromand Terraform'smovedblock do. Shelved: unintuitive, and it forces rules about spent and ignored hints because the statement must survive until every environment has consumed it.--confirmbeside--delete. Rejected: two consent mechanisms confuse which one is required, and blanket consent is what the project removes.Refs: TML-3474
Agent: scylla-59
🤖 Generated with Claude Code