Shipping a schema change to the stats site used to mean shipping it twice: the migration itself, then the wait for a human to click approve in GitHub. The click existed for a good reason (prod databases punish carelessness), but it had become the slowest line in the whole pipeline. Overnight that stopped.
What "additive" means here
The owner approved one rule: if a migration only creates things, it runs unattended. New table, new column, new index, new view, a comment, a grant letting the app read the new stuff. Every one of those is reversible by dropping what you made, and none of them can lose data that already exists. Anything else (a drop, a rename, a rewrite, a role change) still waits for the click.
The check itself
Deciding that mechanically is a small script that reads the SQL file and rules on each statement. Every statement must match an allowed shape, and even an allowed statement gets a second scan for forbidden verbs nested inside it, because CREATE TABLE ... AS SELECT smuggles a data copy behind a create-shaped opening.
const ALLOWED = [
/^create\s+(unique\s+)?index\s/,
/^create\s+table\s/,
/^create\s+type\s/,
/^create\s+sequence\s/,
/^create\s+(or\s+replace\s+)?view\s/,
/^comment\s+on\s/,
/^grant\s+[a-z,\s]+\s+on\s+(table\s+)?[^;]+\s+to\s+"?statpro"?$/,
/^alter\s+table\s+(if\s+exists\s+)?(only\s+)?[^\s]+\s+add\s+(column|constraint)\s/,
];
// never allowed, even nested inside an allowed statement
const FORBIDDEN = /\b(drop|truncate|rename|revoke|owner\s+to|as\s+select)\b/;
Where it sits
A gate job in the deploy workflow runs the checker first. An additive verdict applies the migration with no human in the loop. Anything else prints the first offending statement and stops for approval, so a blocked queue now explains itself. The same change lets agent sessions commit and push to their own feature branches without a prompt (pushes to main and force-pushes stay denied).
The transferable bit: stop treating "database change" as one risk class. Sort by blast radius, write the sort down as a script, and let the boring majority through. The destructive minority keeps its gate, and now it arrives with the exact offending statement in the message instead of a vague file name.