Skip to content

alexbobkovv/db-migration-safe

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

db-migration-safe — zero-downtime SQL schema migrations, made safe

db-migration-safe

CI License: Apache 2.0 Python 3.8+ Dependencies: stdlib only Postgres-first Verified: squawk 2.58 · eugene 0.8.3 PRs welcome

Your coding agent is one ALTER TABLE away from a production outage. It will happily write CREATE INDEX and run it on a 10M-row table — and lock writes for minutes. db-migration-safe is the free guardrail that lets you let an agent near your database: it catches the lock hazard, rewrites the migration to zero-downtime, generates the rollback, and refuses to run anything risky without you (EXECUTE never auto-fires).

An open-source Claude/Agent Skill that makes SQL schema migrations safe. It detects locking and blocking hazards, rewrites unsafe DDL into zero-downtime multi-step migrations, auto-generates rollbacks, and gates execution behind a plan → validate → execute workflow.

  • Postgres-first — strong and deterministic (static lint → safe rewrite → real-lock trace → rollback → gated execute).
  • MySQL — an honest hybrid (InnoDB matrix heuristics + execute-online tooling), materially weaker, and labeled as such.
  • Engine-agnostic — works on hand-written .sql or any ORM's generated SQL.
  • Free / Apache-2.0 — it wraps existing open-source linters; it does not rebuild them.

Demo

analyze.py flags an unsafe CREATE INDEX, then the CONCURRENTLY rewrite re-lints clean

An agent writes a bare CREATE INDEX on a large table; analyze.py flags the write-blocking lock (squawk:require-concurrent-index-creation + eugene:E6/E9) and the CONCURRENTLY rewrite re-lints clean. PLAN/VALIDATE touch no database.

Why this exists

The migration-safety space splits into two halves that do not overlap:

  • Shallow pattern-skills that describe expand-contract in prose but do zero pre-flight lock analysis, no rollback generation, no tool integration.
  • Excellent standalone lock linters that no skill wrapssquawk, eugene, gh-ost/pt-osc.

The one serious competitor, Atlas Agent Skills, is engine-locked and paywalls the rules that matter: as of Atlas v0.38 (2025-10-28), every Postgres concurrency/lock analyzer (PG101–105, PG301–311) and the MySQL blocking rules (MY130–136) are Pro.

No open-source skill orchestrates squawk + eugene (or the MySQL equivalents) into a plan→validate→execute loop with size-aware lock reasoning and auto-generated rollbacks. This fills that gap.

Atlas Agent Skills squawk / eugene alone db-migration-safe
Lock-hazard analysis paywalled (Pro) yes, but unwrapped wraps squawk + eugene lint + trace, one verdict
Engine lock-in requires Atlas engine none none — raw .sql or any ORM
"100 rows vs 100k" trace only, manual trace on an ephemeral DB, orchestrated
Safe rewrite describes warns emits the concrete multi-step rewrite
Rollback generation up/down convention none generated from the DDL; flags irreversible ops
Cost paid lock rules free free / OSS

Does it actually work? Measured across Haiku 4.5 / Sonnet 4.6 / Opus 4.8 over three rounds — common operations, then subtle traps (FK NOT VALID, UNIQUE concurrently, volatile-default rewrite, direct SET NOT NULL), then partitioned indexes: with the skill, every model produced a machine-verified (0-error) safe rewrite plus a generated rollback on every task. Unaided, the frontier models already write safe DDL on the first two rounds — the skill's baseline catch there is concentrated on the cheaper, faster model (Haiku 1/3 and 2/4 → clean). Round 3 is the exception that reaches a frontier model: CREATE/DROP INDEX CONCURRENTLY is silently unsupported on a partitioned parent, so Sonnet (and Haiku) reach for it and fail at deploy while static linters wave it through — only Opus gets it right unaided, and the skill's is_partitioned.sql probe + catalog #12 lift Sonnet and Haiku to safe. Full method and tables in evals/eval.md.

Install

This is a Claude Code skill. Drop it where your skills live:

git clone https://github.com/alexbobkovv/db-migration-safe ~/.claude/skills/db-migration-safe

It works install-free: with no binaries present (or analyze.py --no-external), the Postgres path falls back to a stdlib heuristic over the cataloged ops so PLAN/CI still flag obviously-unsafe DDL — clearly labeled non-authoritative. Install the binaries it shells out to for authoritative analysis and the VALIDATE gate (only what you need) — see references/tool-setup.md:

npm install -g squawk-cli          # or pip install squawk-cli
mise use ubi:kaaveland/eugene@latest

The skill shells out to squawk/eugene/gh-ost as separate processes — it does not bundle them, which keeps it lightweight and license-clean.

Quickstart

# 1. PLAN — static analysis (no DB)
python3 scripts/analyze.py migration.sql --dialect postgres

# 2. rewrite the flagged statements per references/postgres-catalog.md → migration.safe.sql

# 3. VALIDATE — re-lint until zero error-level findings
python3 scripts/analyze.py migration.safe.sql --dialect postgres

# 4. ROLLBACK — generate the reverse migration
python3 scripts/gen_rollback.py migration.safe.sql > migration.rollback.sql

# 5. TRACE (optional) — observe real locks on an ephemeral Postgres
python3 scripts/trace.py migration.safe.sql

# 6. EXECUTE — user-triggered only; apply with lock_timeout + CONCURRENTLY (see SKILL.md)

Workflow

Phase What it does Touches your DB?
PLAN squawk + eugene lint → merged verdict; size probe; optional trace no
VALIDATE re-lint the rewrite; gate on zero error-level findings no
EXECUTE apply with lock_timeout, CONCURRENTLY, batched backfill yes — user-triggered only

PLAN/VALIDATE are analysis only and safe to auto-run. EXECUTE never auto-fires — it runs only when you explicitly ask to apply the migration (see the Safety contract in SKILL.md).

CI gate

analyze.py exits nonzero on any error-level finding, so it drops straight into CI. The easiest path is the bundled GitHub Action — it analyzes the migrations changed in a PR and uploads SARIF so findings annotate the diff inline (and land in the Security tab):

# .github/workflows/migrations.yml
name: migration-safety
on: pull_request
permissions:
  contents: read
  security-events: write          # required to upload SARIF to code scanning
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0            # so the Action can diff against the PR base
      - uses: alexbobkovv/db-migration-safe@v1
        with:
          dialect: postgres         # or mysql
          # files: db/migrate/*.sql # optional: override the changed-files default

It installs squawk for authoritative analysis and falls back to the stdlib heuristic if that install fails, so the gate never silently no-ops. Prefer to wire it by hand? The scripts are a plain CLI:

      - run: pip install squawk-cli      # optional; heuristic fallback works without it
      - run: |
          for f in $(git diff --name-only origin/${{ github.base_ref }}... -- '*.sql'); do
            python3 scripts/migrate_safe.py analyze "$f" --dialect postgres || exit 1
          done

pre-commit hook

Block unsafe DDL before it's even committed. Add to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/alexbobkovv/db-migration-safe
    rev: v0.2.0
    hooks:
      - id: db-migration-safe          # runs on changed *.sql; postgres by default
        # args: [--dialect, mysql]

It runs install-free via the heuristic, and uses squawk/eugene automatically when they are on your PATH.

Versioning & upgrading

The installed version is in VERSION, and every script reports it:

python3 scripts/analyze.py --version

It is a skill you install by cloning, so you upgrade by pulling:

git -C ~/.claude/skills/db-migration-safe pull

CHANGELOG.md records what changed. The project follows semver for its public surface — the scripts/* flags and exit codes, and the rule ids and rewrite shapes under references/. A change to any of those is breaking (major); new safe behavior is a minor bump; fixes are patches.

Layout

SKILL.md            entry point (workflow + safety contract)
references/         postgres-catalog · mysql-catalog · squawk-rules · eugene-hints · tool-setup
scripts/           migrate_safe.py (dispatcher) · analyze.py · trace.py · gen_rollback.py · table_size.sql · is_partitioned.sql  (stdlib only)
evals/             baseline cases + methodology
CHANGELOG.md        what changed per version
CONTRIBUTING.md     dev setup · eval workflow · invariants

Contributing

Issues and PRs welcome — especially "the verdict was wrong" reports (a false positive, or a hazard the tool missed), which is how the catalogs improve. The scripts are standard-library-only by design; please keep new code dependency-free. See CONTRIBUTING.md for the dev setup, how to run the evals, and the invariants to preserve.

License

Apache-2.0. The skill invokes third-party binaries (squawk, eugene, gh-ost, …) as separate processes; it does not bundle or statically link them, so their licenses are independent. Install them yourself per references/tool-setup.md.

About

Zero-downtime SQL migration safety as an Agent Skill: detect lock hazards, rewrite unsafe DDL, auto-generate rollbacks, gate execution (Postgres-first, wraps squawk + eugene).

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages