Skip to main content

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.

Release files for novyx-blast-radius 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for novyx-blast-radius 0.1.0
File Size Uploaded
novyx_blast_radius-0.1.0.tar.gz 31.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for novyx-blast-radius 0.1.0
File Interpreter ABI Platform
novyx_blast_radius-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size:60.0 kB

Release files / novyx_blast_radius-0.1.0.tar.gz

Download URL novyx_blast_radius-0.1.0.tar.gz
Size 31.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2e8088241ff4965b7fa1a428b873edde452e21481632b632881c56f2ae4e3471
BLAKE2b-256 checksum
How to use checksums
a1e93f9ebd2ad674eeb9eff58f2f9be08327069b79b5a8ec2efc699c6c377e8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release files / novyx_blast_radius-0.1.0-py3-none-any.whl

Download URL novyx_blast_radius-0.1.0-py3-none-any.whl
Size 28.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5107a7725238310bde6e98a29b1dac80e76a394bc8da95ed3e3e9c099ab7d43b
BLAKE2b-256 checksum
How to use checksums
a60a62f1a5e97a8d2d83488f271ad6fab8d979969529833fd3bd41171a542e2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page