Skip to main content
ClaudeWave

Catch breaking dbt schema changes before dbt run executes them: dropped columns, the downstream models and data tests they break, contract violations. Static analysis of compiled SQL, no warehouse connection. Like terraform plan, for dbt.

ToolsRegistry oficial2 estrellas14 forksPythonApache-2.0Actualizado today
ClaudeWave Trust Score
95/100
Verified
Passed
  • Open-source license (Apache-2.0)
  • Actively maintained (<30d)
  • Clear description
  • Topics declared
  • Documented (README)
Last scanned: 9/8/2026
Get started
Method: Clone
Terminal
git clone https://github.com/PresentJay/dbt-plan
1. Clone the repository.
2. Follow the README for installation and usage instructions.
Casos de uso

Resumen de Tools

# dbt-plan

Static analysis tool that warns about risky DDL changes before `dbt run`.

Like `terraform plan` for dbt, and used the same way: you run it **before** the thing
that changes your warehouse, not only in CI afterwards.

Runs on compiled SQL. It reads files and nothing else, so it works with any warehouse —
Snowflake, BigQuery, Redshift, Postgres, DuckDB — through one code path.

## What It Looks Like

```
$ dbt-plan check

dbt-plan -- 2 model(s) changed

DESTRUCTIVE  int_order_enriched (incremental, sync_all_columns)
  DROP COLUMN  shipping_info
  DROP COLUMN  billing_info
  ADD COLUMN   shipping_city
  Downstream: dim_customers, fct_orders (2 model(s))
  >> BROKEN_REF  fct_orders: references dropped column(s): shipping_info

SAFE  dim_customers (table)
  CREATE OR REPLACE TABLE

dbt-plan: 2 checked, 1 safe, 0 warning, 1 destructive, 1 cascade risk(s)
```

## What It Does

dbt-plan analyzes compiled SQL diffs to catch dangerous schema changes at PR time:

- **Column changes**: detects ADD/DROP COLUMN from SQL diff
- **Risk assessment**: judges safety based on materialization x on_schema_change rules
- **Cascade analysis**: finds downstream models broken by a dropped column — the ones that
  name it, resolved against the project's own schema rather than matched as text; the ones
  that select `*` and lose it without their own file changing; and the tests whose fixtures
  pin it down. Names the exposures whose owners need telling
- **Contracts**: reports a change an enforced contract will reject, in either direction
- **Config changes**: detects materialization or on_schema_change policy changes
- **Type changes**: compares explicit `CAST` types between revisions
- **`SELECT *` resolution**: reads the columns from the CTEs of the same statement, and follows a `ref()` into the referenced model's compiled SQL

It does NOT execute anything, connect to any warehouse, or simulate `dbt run`. It reads files, compares them, and warns you.

## Quick Start

```bash
pip install dbt-plan
dbt-plan run               # compile baseline → compile current → check
```

`dbt-plan run` does the whole thing in one command, and needs whatever credentials your
`dbt compile` normally needs.

### The loop it is built for

Once you have a baseline, the inner loop is a single sub-second command. Edit a model or a
macro, recompile, and see what `dbt run` would do — *before* running it:

```bash
dbt-plan snapshot          # once, on the revision you are changing from
                           # ... edit models, edit macros ...
dbt compile && dbt-plan check
```

Measured on a project of 3 models, median of 3 runs:

| step | time |
|---|---|
| `dbt compile` (Fusion) | 1.8 – 3.8 s |
| **`dbt-plan check`** | **0.11 s** |
| `dbt-plan snapshot` | 0.10 s |

200 models, every one of them changed: **0.48 s**. The compile is the cost, and you were
compiling anyway — dbt-plan itself is fast enough to sit in the edit loop rather than at
the end of it.

### Working with a coding agent

An agent editing models cannot eyeball a diff and hesitate. Give it the check and the
reasons behind it:

```bash
dbt-plan agent-setup       # writes dbt-plan guidance into your AGENTS.md
dbt-plan check --format json
```

The guidance leads with what an agent most often gets wrong: adding a model to
`ignore_models`, or downgrading `on_schema_change` from `sync_all_columns` to `ignore`,
silences a real finding without making the change safe.

Or give it the check as an MCP tool:

```bash
pip install 'dbt-plan[mcp]'
dbt-plan-mcp                # stdio MCP server exposing `plan` and `snapshot`
```

`plan` returns the verdict, the per-model operations, and — separately — a `refusals`
list naming everything dbt-plan declined to judge. That separation is the point: a person
reading "safe" may still glance at the diff, an agent reading it proceeds, so a
non-empty `refusals` must never be collapsed into the verdict.

Both tools accept `target_dir` when dbt writes artifacts outside the default `target/`,
for example `plan(project_dir=".", target_dir="build")`.

The server is a separate package from the analysis core. The core is offline and
synchronous by design and `tests/test_invariants.py` fails the build on an `asyncio` or
network import anywhere inside it; an MCP server is both, so keeping them apart is what
keeps that guarantee provable.

Registry entry — the line below is how the MCP registry verifies that whoever publishes
the entry also owns this PyPI package, so it has to stay in the README that ships:

```
mcp-name: io.github.PresentJay/dbt-plan
```

### More commands

```bash
dbt-plan init              # Generate .dbt-plan.yml config + update .gitignore
dbt-plan stats             # Analyze project readiness
dbt-plan ci-setup          # Generate GitHub Actions workflow
dbt-plan check --format github   # GitHub markdown output
dbt-plan check --format json     # JSON for CI pipelines
dbt-plan run --against main           # compare with where this branch left main
dbt-plan check --select fct_orders    # one model
dbt-plan check --select fct_orders+   # it and everything downstream
```



## Scope

dbt-plan is a **static analysis warning tool**, not a runtime simulator.

| In scope | Out of scope |
|----------|-------------|
| Column ADD/DROP detection from compiled SQL | `dbt run` simulation |
| materialization × on_schema_change risk rules | Warehouse connection |
| Cascade: broken refs, build failures, inherited column loss | `seed` / `source` change detection |
| Config change detection (materialization, osc) | `pre_hook` / `post_hook` DDL analysis |
| Unit test fixtures and exposure owners downstream | `seed` / `source` fixtures dbt-plan cannot read |
| Enforced-contract violations: names, and types by family | Contract types compared more finely than family |
| Explicit `CAST` type changes | Type changes on uncast columns |
| `SELECT *` resolved through CTEs and `ref()` | `SELECT *` over a source or a raw table |
| CI exit codes + structured output | `full_refresh` mode judgment |

**Design principle**: false warnings are OK, false safe is never OK.

## When to use it

dbt-plan answers a narrower question than the warehouse-connected tools (Recce,
SQLMesh, data-diff) and costs nothing to run, so it works as the cheap gate in
front of them — and on the Fusion engine, which compiles without a warehouse
connection, that includes fork pull requests where they cannot run at all.
See [use cases](docs/use-cases.md) for the comparison, real timings, and what it
gets wrong.

## Deliberately Not Planned

Ideas that look useful but contradict what this tool is:

| Idea | Why not |
|------|---------|
| INFORMATION_SCHEMA query | Requires a warehouse connection. dbt-plan reads files and nothing else, which is what lets it run wherever its input exists — including a fork's pull request, once the project compiles on Fusion. |
| Type changes on columns with no explicit `CAST` | The type is whatever the warehouse assigned, so seeing a change would mean asking it. Columns that *are* cast explicitly on both sides are compared — see below. |

## DDL Prediction Rules

| Materialization | on_schema_change | Predicted DDL | Safety |
|-----------------|------------------|---------------|--------|
| table | any | `CREATE OR REPLACE TABLE` | SAFE |
| view | any | `CREATE OR REPLACE VIEW` | SAFE |
| ephemeral | any | (no physical object) | SAFE |
| snapshot | any | `REVIEW REQUIRED` | WARNING |
| incremental | ignore | no DDL | SAFE |
| incremental | fail | build failure | WARNING |
| incremental | append_new_columns | `ADD COLUMN` only | SAFE |
| incremental | sync_all_columns | `ADD + DROP COLUMN` | DESTRUCTIVE if columns removed |
| any | (model removed) | `MODEL REMOVED` | DESTRUCTIVE |
| any | (unknown osc) | `UNKNOWN on_schema_change` | WARNING |
| materialized_view / custom | (not set by you) | `UNKNOWN materialization` | WARNING |
| materialized_view / custom | (you set one) | follows the incremental rules | per osc |
| any | (`contract: {enforced: true}`) | `CONTRACT VIOLATION` | WARNING |

Under a contract, a column's declared `data_type` is compared with its explicit `CAST`,
by family -- text against number against date/time against boolean. `varchar` and `text`
are the same family and not a finding; `varchar` and `integer` are a build failure.
Comparing more finely means a per-adapter type table, and a wrong answer about a type is
worse than no answer.

"Not set by you" means the author wrote no `on_schema_change`, in the model or in
`dbt_project.yml`. dbt resolves one for every model regardless, so the resolved value
asserts nothing; an explicit setting is a claim about how that materialization behaves
and is honoured. dbt-plan reads `unrendered_config` to tell them apart.

An enforced contract inverts the rules above: dbt requires every column to be declared,
so a column added to the SQL fails the build just as a removed one does. Names only —
dbt compares its declared `data_type` against the warehouse, which dbt-plan does not read.

## CI Integration (GitHub Actions)

```yaml
name: dbt-plan
on:
  pull_request:

jobs:
  plan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    env:
      # Whatever your profiles.yml reads. `dbt compile` connects; dbt-plan does not.
      SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
      SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
      SNOWFLAKE_PRIVATE_KEY: ${{ secrets.SNOWFLAKE_PRIVATE_KEY }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0          # the base revision has to be in the clone
          persist-credentials: false
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - run: pip install uv && uv sync

      - uses: PresentJay/dbt-plan@v1
```

Keep the `pull_request` trigger. Never switch it to `pull_request_target` — `dbt compile`
runs Jinja and macros written in the pull request, so that would hand your warehouse
credentials to 
analytics-engineeringbigquerybreaking-changescici-cddata-contractsdata-engineeringdata-qualitydatabricksdbtdbt-coredeveloper-toolsduckdbgithub-actionspostgresredshiftschema-migrationsnowflakesqlglotstatic-analysis

Lo que la gente pregunta sobre dbt-plan

¿Qué es PresentJay/dbt-plan?

+

PresentJay/dbt-plan es tools para el ecosistema de Claude AI. Catch breaking dbt schema changes before dbt run executes them: dropped columns, the downstream models and data tests they break, contract violations. Static analysis of compiled SQL, no warehouse connection. Like terraform plan, for dbt. Tiene 2 estrellas en GitHub y su última actualización registrada es del 2026-09-08.

¿Cómo se instala dbt-plan?

+

Puedes instalar dbt-plan clonando el repositorio (https://github.com/PresentJay/dbt-plan) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar PresentJay/dbt-plan?

+

Nuestro agente de seguridad ha analizado PresentJay/dbt-plan y le ha asignado un Trust Score de 95/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene PresentJay/dbt-plan?

+

PresentJay/dbt-plan es mantenido por PresentJay. La última actividad registrada en GitHub es del 2026-09-08, con 61 issues abiertos.

¿Hay alternativas a dbt-plan?

+

Sí. En ClaudeWave puedes explorar tools similares en /categories/tools, ordenados por popularidad o actividad reciente.

Despliega dbt-plan en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: PresentJay/dbt-plan
[![Featured on ClaudeWave](https://claudewave.com/api/badge/presentjay-dbt-plan)](https://claudewave.com/repo/presentjay-dbt-plan)
<a href="https://claudewave.com/repo/presentjay-dbt-plan"><img src="https://claudewave.com/api/badge/presentjay-dbt-plan" alt="Featured on ClaudeWave: PresentJay/dbt-plan" width="320" height="64" /></a>

Más Tools

Alternativas a dbt-plan