# Deterministic checks for AI-written migrations

> Source: <https://dev.to/bolvrk/deterministic-checks-for-ai-written-migrations-3m9h>
> Published: 2026-09-17 15:30:44+00:00

A coding agent asked to "add a `status` column to `orders`" will do it in seconds. It will also, more often than not, write the version that fails on a table with rows in it, or the version that takes an exclusive lock and holds it for the whole rewrite. Not because the model is bad at SQL, because the dangerous form and the safe form look almost identical, and the difference only matters at production scale, which the model never sees.

```
-- what the agent wrote
ALTER TABLE orders ADD COLUMN status text NOT NULL;

-- what survives contact with a table that has rows
ALTER TABLE orders ADD COLUMN status text;
UPDATE orders SET status = 'open' WHERE status IS NULL;  -- in batches
ALTER TABLE orders ALTER COLUMN status SET NOT NULL;
```

That first statement is [BV002](https://bolvrk.com/rules/bv002). It is one of the oldest and best-understood migration mistakes there is, and it is exactly the kind of thing an AI reviewer will sometimes catch and sometimes wave through, depending on the prompt, the context window, and the day.

The obvious fix is to ask a second model to review the first model's migration. It works often enough to feel like it works. It does not work in the way a team can build a process on, for three reasons.

`ACCESS EXCLUSIVE` lock", that claim was tested against a real Postgres and the fixture is in the repository. A model's claim is a sentence.
None of this is an argument against using agents to write migrations. It is an argument about where the trust boundary goes. The agent proposes; something deterministic disposes.

The check has to sit where the agent can reach it, and it has to return the same shape every time so the agent can act on it without interpretation. Three seperate placements cover most setups.

`check` tool, gets findings back as JSON with a rule id, a severity and the fix, and revises before the human ever sees the draft. The skills library in the public repository tells the agent when to do this unprompted.`npx bolvrk check migration.sql`, no account, no config, exit code 1 on findings. This is the same rule set the MCP server runs, so a person double-checking an agent's work sees exactly what the agent saw.
The point of running the same corpus in all three places is that the verdict does not change as the migration moves from the agent's draft to the terminal to CI. The agent cannot talk its way past the Action, because the Action is not listening to arguments.

Looking at what agents actually produce, a handful of rules do most of the work.

`ADD COLUMN … NOT NULL` without a default.`CONCURRENTLY`, blocking writes for the whole build.` lock_timeout`, so one slow transaction turns into a queue behind it.` DROP COLUMN` while code that reads the column is still deployed.
Every one of these has a page that says what the rule catches, the SQL it fires on, and the safe pattern. That is deliberate: when an agent explains a finding to a person, or a person to an agent, the explanation should come from the same place and say the same thing.

A rule corpus does not know your buisness. It cannot tell you that the backfill will take four hours on your data, or that the column you are dropping is read by a report nobody has looked at since 2024. Live-schema context narrows that gap, a read-only connection lets the rules see the real table sizes and constraints, but judgement stays with the team. The corpus removes the category of mistakes that need no judgement at all, so the judgement can go where it is needed.

If you are letting agents write migrations, the question is not whether to review them. It is whether the review is something you can rely on the same way every time. Start with `npx bolvrk check` on the last migration an agent wrote for you.

*Originally published at [bolvrk.com](https://bolvrk.com/blog/deterministic-checks-for-ai-written-migrations). Bolvrk is a deterministic checker for database migrations and committed credentials: [free CLI, GitHub App and Action](https://bolvrk.com/?utm_source=crosspost&utm_medium=blog).*
