Follow-up to #693 (fixed in the fix/drizzle-v3-alter-column work). That change made rewriteEncryptedAlterColumns match the EQL v3 domain family and wired the sweep into stash eql migration --drizzle, which is the only v3 command that had a natural place for it. That is a bounded improvement, not a complete fix — this issue records the gap.
The sweep runs at the wrong moment
In the intended v3 flow the sweep sees an empty directory:
1. stash eql migration --drizzle -> writes 0000_install-eql.sql, sweeps (nothing there yet)
2. edit the schema to use v3 columns
3. drizzle-kit generate -> writes 0001 with the broken ALTER COLUMN
4. drizzle-kit migrate -> fails
The broken migration is created at step 3, after the sweep has already run. Today the only way to get it repaired is to run stash eql migration --drizzle a second time at step 4 — which works, and is how people actually find this, but it means generating a redundant install migration to trigger an unrelated repair. The same shape applies to the v2 path (stash eql install --drizzle, install.ts step 5); this is not new to v3.
Proposal
A standalone repair that a user can run at step 4:
stash eql repair --drizzle (or similar), sweeping the migrations directory and reporting what it rewrote; or
- fold the check into
stash encrypt plan, which already inspects intent vs. observed state and is the command a user is likely to reach for next.
Either removes the need to regenerate an install migration to get the repair.
Known hazard to handle in the design
The sweep is unfiltered — it rewrites any matching file regardless of whether it has already been applied. That is safe for essentially every match, because a matching statement is un-runnable by construction (Postgres rejects both the mangled "undefined"."eql_v3_*" form and the valid-looking bare form, since there is no cast from text/numeric to an EQL domain), so the migration failed on apply and is unapplied.
The exception: a jsonb column changed to a v3 domain coerces successfully, so that migration can be applied. Rewriting it afterwards changes the file's hash, and drizzle-kit migrate would then treat it as unapplied and re-run an ADD/DROP/RENAME sequence against a table already in the target shape.
Narrow, and pre-existing in the v2 sweep, but a standalone repair command is the right place to fix it — it can consult the drizzle journal (and, given --database-url, __drizzle_migrations) and refuse to touch an applied migration, which the offline migration-first path cannot.
Notes
An ordering filter (only sweep entries after the install migration) was considered and rejected: whether the broken migration sorts before or after the install migration depends on when each was generated, so the filter would suppress the repair in exactly the recovery path above.
Follow-up to #693 (fixed in the
fix/drizzle-v3-alter-columnwork). That change maderewriteEncryptedAlterColumnsmatch the EQL v3 domain family and wired the sweep intostash eql migration --drizzle, which is the only v3 command that had a natural place for it. That is a bounded improvement, not a complete fix — this issue records the gap.The sweep runs at the wrong moment
In the intended v3 flow the sweep sees an empty directory:
The broken migration is created at step 3, after the sweep has already run. Today the only way to get it repaired is to run
stash eql migration --drizzlea second time at step 4 — which works, and is how people actually find this, but it means generating a redundant install migration to trigger an unrelated repair. The same shape applies to the v2 path (stash eql install --drizzle,install.tsstep 5); this is not new to v3.Proposal
A standalone repair that a user can run at step 4:
stash eql repair --drizzle(or similar), sweeping the migrations directory and reporting what it rewrote; orstash encrypt plan, which already inspects intent vs. observed state and is the command a user is likely to reach for next.Either removes the need to regenerate an install migration to get the repair.
Known hazard to handle in the design
The sweep is unfiltered — it rewrites any matching file regardless of whether it has already been applied. That is safe for essentially every match, because a matching statement is un-runnable by construction (Postgres rejects both the mangled
"undefined"."eql_v3_*"form and the valid-looking bare form, since there is no cast fromtext/numericto an EQL domain), so the migration failed on apply and is unapplied.The exception: a
jsonbcolumn changed to a v3 domain coerces successfully, so that migration can be applied. Rewriting it afterwards changes the file's hash, anddrizzle-kit migratewould then treat it as unapplied and re-run anADD/DROP/RENAMEsequence against a table already in the target shape.Narrow, and pre-existing in the v2 sweep, but a standalone repair command is the right place to fix it — it can consult the drizzle journal (and, given
--database-url,__drizzle_migrations) and refuse to touch an applied migration, which the offline migration-first path cannot.Notes
An ordering filter (only sweep entries after the install migration) was considered and rejected: whether the broken migration sorts before or after the install migration depends on when each was generated, so the filter would suppress the repair in exactly the recovery path above.