Skip to main content

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_approval

Impact: 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, owner platform-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=2300000 or --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-spec defaults to installing the checker from the Action's own pinned source, so @v1 works with no PyPI dependency. Once the package is on PyPI you can set version-spec: novyx-blast-radius==0.1.0 to 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

novyx_blast_radius-0.1.0.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

novyx_blast_radius-0.1.0-py3-none-any.whl (28.8 kB view details)

Uploaded Python 3

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

Hashes for novyx_blast_radius-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2e8088241ff4965b7fa1a428b873edde452e21481632b632881c56f2ae4e3471
MD5 7f9fbb1a9ff00b1dbbb6001b7c6aa2b7
BLAKE2b-256 a1e93f9ebd2ad674eeb9eff58f2f9be08327069b79b5a8ec2efc699c6c377e8b

See more details on using hashes here.

File details

Details for the file novyx_blast_radius-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for novyx_blast_radius-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5107a7725238310bde6e98a29b1dac80e76a394bc8da95ed3e3e9c099ab7d43b
MD5 f61c330c8564d39d066e7899f1ecab69
BLAKE2b-256 a60a62f1a5e97a8d2d83488f271ad6fab8d979969529833fd3bd41171a542e2e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page