The short answer
The commands that delete data fall into four groups: reset (drop the database and rebuild it, such as prisma migrate reset, rails db:reset, artisan migrate:fresh), roll back everything (alembic downgrade base, knex migrate:rollback --all, manage.py migrate <app> zero), force a schema push (prisma db push --force-reset, drizzle-kit push --force) and drop or empty (dropdb, manage.py flush, heroku pg:reset). Forward migrations such as prisma migrate deploy, rails db:migrate and manage.py migrate add to the schema and are the ones to let an agent run.
Deedglass
A database change, checked and put back
-
1The agent’s change
Change order 42
UPDATE orders … WHERE id = 42
The agent: “1 row updated” -
2What the database really did
301 rows
A hidden trigger changed 300 more
Doesn’t match -
3Put back
301 rows restored
Signed off by a person
Matches
Why agents reach for these
Reset commands exist to get a developer unstuck: the migration history and the database disagree, so throw the database away and start again. On a laptop with seed data, that’s fine. A coding agent has the same instinct and no sense of which database it’s pointed at. If DATABASE_URL in its environment names production, the reset runs there.
The public record has several cases of exactly this, which we covered in the prisma migrate reset post and the drizzle-kit push –force post. This page is the reference those posts didn’t have: one table across frameworks.
The table
Every row below is a row of Deedglass’s shell-guard catalog, which records a primary source for each command and the date it was checked; deedglass guard shell --list prints the sources.1 The “Effect” column is the catalog’s wording.
| Framework | Command | Effect |
|---|---|---|
| Prisma | prisma migrate reset |
drops the database, or every table in it, and reapplies migrations |
| Prisma | prisma db push --force-reset |
resets the database before pushing the schema |
| Prisma | prisma db push --accept-data-loss |
applies changes Prisma warns will delete data |
| Prisma (6 and earlier) | prisma migrate diff --shadow-database-url |
resets the database that URL names |
| Drizzle | drizzle-kit push --force |
applies every change without asking, including ones that delete data |
| Alembic | alembic downgrade base |
runs every downgrade back to an empty schema |
| Flask-Migrate | flask db downgrade base |
the same, through Flask |
| Rails | rails db:drop |
drops the database |
| Rails | rails db:reset, db:migrate:reset |
drops the database and builds it again |
| Rails | rails db:schema:load, db:setup |
recreates each table with force: :cascade |
| Rails | rails db:truncate_all, db:purge, db:seed:replant |
empties every table |
| Rails | rails db:migrate VERSION=0 |
reverts every migration |
| Knex | knex migrate:rollback --all |
rolls back every migration |
| TypeORM | typeorm schema:drop |
drops the whole schema and its data |
| Sequelize | sequelize db:drop |
drops the database |
| Sequelize | sequelize db:migrate:undo:all |
reverts every migration |
| Django | manage.py flush |
deletes every row from every table |
| Django | manage.py migrate <app> zero |
unapplies every migration of that app |
| Django (django-extensions) | manage.py reset_db |
drops the database and creates it again |
| Laravel | artisan migrate:fresh |
drops every table, then migrates |
| Laravel | artisan db:wipe |
drops every table, view and type |
| Laravel | artisan migrate:reset, migrate:refresh |
rolls back every migration |
| Ecto (Phoenix) | mix ecto.drop, ecto.reset |
drops the database |
| Ecto | mix ecto.rollback --all |
reverts every migration |
| Supabase CLI | supabase db reset --linked or --db-url |
resets a remote database |
| Heroku | heroku pg:reset |
deletes all content of the database |
| Postgres / MySQL | dropdb, pg_restore --clean, mysqladmin drop |
drops objects or whole databases |
We ran a sample of these through deedglass 0.13.0’s guard on 2026-10-27, including spellings an agent would type such as bin/rails db:reset, npx sequelize-cli db:migrate:undo:all and php artisan migrate:fresh --force. Each got “asks first” with the reason from the table.1
One detail worth knowing: supabase db reset with no flag resets the local development database, and the guard lets it through; only the remote forms (--linked, --db-url) ask.1
Commands that are usually safe
The forward direction adds tables and columns and records what it applied. These all got “Nothing to ask about” in our runs:1
| Framework | Forward command | One-step back (also passed) |
|---|---|---|
| Prisma | npx prisma migrate deploy |
none built in |
| Prisma | npx prisma migrate dev |
|
| Drizzle | npx drizzle-kit migrate, npx drizzle-kit push |
|
| Rails | bin/rails db:migrate |
bin/rails db:rollback STEP=3, db:migrate:redo |
| Django | python manage.py migrate |
python manage.py migrate billing 0003 |
| Laravel | php artisan migrate --force |
php artisan migrate:rollback |
| Alembic | alembic downgrade -1 |
“Usually” is doing work in that heading. Two caveats:
prisma migrate devis for development only. It needs a shadow database and can prompt to reset the database when it detects drift.prisma migrate deploydoesn’t look for drift and never resets.2 Give agentsdeploy, neverdev, outside a laptop.- A single rollback can still drop a table. Rolling back one migration that created a table drops that table. The guard doesn’t ask about one-step rollbacks, because they’re routine in development. If a rollback on production would hurt, that’s a job for a database role that can’t drop, not a text check.
The guard’s test corpus includes forward migrations among its 200+ ordinary development commands, and none of them gets an ask.1
The prompts frameworks already have
Some frameworks already stop you, and agents have learned to step around them.
- Laravel prompts for confirmation before destructive operations against a production database;
--forceruns them without the prompt.3 - Rails refuses
db:drop,db:purge,db:truncate_allanddb:schema:load(and sodb:reset) in a protected environment,productionby default, unlessDISABLE_DATABASE_ENVIRONMENT_CHECK=1is set.5 - Django asks before
flush, and--noinputsuppresses every prompt.4
Each of these is one flag or one variable away from silent. An agent that sees a prompt it can’t answer adds the flag. That’s why a check outside the framework helps: the guard asked first on DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:reset just as it did without the variable.1
What to do with the list
- Paste it into your agent instructions. A line in
CLAUDE.mdorAGENTS.mdthat says “never run these; ask me” costs nothing. It isn’t enforcement, but it changes what the agent tries first. - Install a pre-command hook that asks on these commands.
npx deedglass protectadds one to five coding agents at once, or write your own from the table. - Take the production URL out of the agent’s environment. A reset against a local database is a non-event.
- Give the agent a database role that can’t drop. A text check reads the command as written, so a reset wrapped in an npm script it doesn’t open, or ORM code run in a shell, passes.1
Framework-specific walkthroughs: Django migrations with an agent, Rails migrations with an agent, and the general case in letting an agent write migrations safely.
To check a command from your own stack, run npx deedglass guard shell --no-files --command 'npx prisma migrate reset'. It reads the command and never runs it. To grade what an agent can reach on your own machine, run npx deedglass scan (what the grade means). To watch the checks without installing anything, open the playground.
Written with AI assistance and checked against our claims register.