Review AI-authored SQL migrations and post a self-explaining Change Ticket that gates only the irreversible, high-blast-radius changes.
Project description
novyx-blast-radius
A GitHub Action that reviews AI-authored SQL migrations and posts a self-explaining Change Ticket on the pull request — gating only the irreversible changes that actually destroy data.
Coding agents open PRs with database migrations now. A blanket "a human must approve all migrations" rule trains people to rubber-stamp; a regex content policy can't tell DROP COLUMN on an empty table from DROP COLUMN on 2.3M rows. novyx-blast-radius grades each statement on two axes — reversibility (structural) and blast radius (live row impact) — and only stops the statements that are both irreversible and material. Everything additive passes silently.
The output is a Change Ticket: one readable, hash-verifiable comment that says what the change does, what it would destroy, and what an approver must check.
Quick start (GitHub Action)
Add .github/workflows/novyx-change-ticket.yml:
name: Novyx Change Ticket
on:
pull_request:
paths:
- "**/*.sql"
permissions:
contents: read
pull-requests: write
jobs:
change-ticket:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: novyxlabs/novyx-blast-radius@v1
That's it. On every PR that touches a .sql file, the Action posts (and keeps updated) a Change Ticket comment, and fails the check only when a statement is irreversible and destroys data (configurable — see Options).
What the comment looks like
For a migration that adds one column and drops another:
ALTER TABLE users ADD COLUMN nickname text;
ALTER TABLE users DROP COLUMN legacy_token;
Verdict:
requires_approvalImpact: statement 2 of 2 drops column
users.legacy_token, which holds 2,300,000 rows — irreversible; 2,300,000 rows destroyed. The other 1 are additive.Operator Review — decision
approve_or_revise, ownerplatform-lead
- approval reason cites the concrete blast radius
- backup or rollback reference
- dry-run output or live row-count evidence
Safer alternative: replace statement 2 with a nullable deprecation marker or a staged backfill before dropping
users.legacy_token.
The additive ADD COLUMN is never flagged. Only the destructive drop gates.
How the verdict is decided
Every statement lands in one of three buckets:
| Verdict | Meaning | Default check result |
|---|---|---|
auto_allow |
Additive/reversible, or irreversible with zero data impact | ✅ pass |
requires_approval |
Irreversible and destroys data below the block threshold | ✅ pass, flagged in ticket |
block |
Irreversible and destroys ≥ 10M rows (catastrophic) | ❌ fail |
Anything the parser doesn't recognize fails closed to requires_approval — a gate that treated unknown SQL as safe would be worse than no gate.
Where the row counts come from
By default the Action runs with no database connection, so it knows structure but not live row counts — every destructive statement fails closed to approval. To get precise blast radius (and let genuinely-empty drops auto-allow), give it live facts one of two ways:
- Connect a read-only Postgres probe (
pip install "novyx-blast-radius[postgres]") and point it at a replica. - Seed counts for no-DB analysis:
--column users.legacy_token=2300000or--table events=50000000.
Options
- uses: novyxlabs/novyx-blast-radius@v1
with:
fail-on: block # block (default) | approval | never
comment: "true" # post/update the PR comment
python-version: "3.11"
version-spec: "" # default: install from the action's pinned source
version-specdefaults to installing the checker from the Action's own pinned source, so@v1works with no PyPI dependency. Once the package is on PyPI you can setversion-spec: novyx-blast-radius==0.1.0to pin a release.
fail-on: block— fail only on catastrophic (≥10M row) drops. Default.fail-on: approval— fail on anything that isn't auto-allowed (strict).fail-on: never— never fail the check; just post the ticket (advisory mode).
CLI
The same engine runs locally:
pip install novyx-blast-radius
# One ticket across changed SQL, GitHub workflow, and Terraform plan files:
novyx-pr-check db/migrations/0007_drop_legacy.sql --format markdown
# Single file, JSON, with a seeded row count:
novyx-change-ticket migration.sql --column users.legacy_token=2300000 --format json --out ticket.json
# Verify a ticket's hash is self-consistent:
novyx-change-ticket --verify-ticket ticket.json
Exit codes: 0 auto-allow, 2 requires approval, 3 block.
Beyond SQL
The same Change Ticket also classifies two other high-blast-radius change types when they appear in a PR:
- GitHub Actions workflows — production deploys reachable from PRs, write-token escalation, secret exposure, unpinned third-party actions.
- Terraform plans (
terraform show -json) — admin IAM grants, public sensitive ingress, public bucket exposure, sensitive-infra destroys.
Point the Action at those paths and they fold into the same ticket. SQL migrations are the sharp edge; the rest is coverage.
Trust boundary (read this)
ticket_hash is an unsigned SHA-256 checksum. It detects a ticket whose body was altered without recomputing the hash — it is not an authenticity signature. A caller that controls the ticket can recompute a matching hash. Today the gate is an advisory, self-explaining review aid, not an adversarial-safe enforcement boundary. Cryptographically-signed tickets are on the roadmap. Use it to make dangerous migrations visible and reviewable, not to stop a motivated insider.
Development
pip install -e ".[dev]"
pytest
ruff check .
License
MIT — see LICENSE.
Built by Novyx Labs. Questions: blake@novyxlabs.com.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file novyx_blast_radius-0.1.0.tar.gz.
File metadata
- Download URL: novyx_blast_radius-0.1.0.tar.gz
- Upload date:
- Size: 31.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e8088241ff4965b7fa1a428b873edde452e21481632b632881c56f2ae4e3471
|
|
| MD5 |
7f9fbb1a9ff00b1dbbb6001b7c6aa2b7
|
|
| BLAKE2b-256 |
a1e93f9ebd2ad674eeb9eff58f2f9be08327069b79b5a8ec2efc699c6c377e8b
|
File details
Details for the file novyx_blast_radius-0.1.0-py3-none-any.whl.
File metadata
- Download URL: novyx_blast_radius-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5107a7725238310bde6e98a29b1dac80e76a394bc8da95ed3e3e9c099ab7d43b
|
|
| MD5 |
f61c330c8564d39d066e7899f1ecab69
|
|
| BLAKE2b-256 |
a60a62f1a5e97a8d2d83488f271ad6fab8d979969529833fd3bd41171a542e2e
|