dbt-col-lineage
Know what breaks before you change it. Column-level lineage and change-impact analysis for dbt — answer "what breaks if I change this column?" in seconds, without ever running your warehouse.
📖 Documentation · 🚀 Live Demo · 🐛 Report Bug · 💡 Request Feature
"I already have lineage in dbt docs."
You have model-level lineage. When you rename, retype, or drop a single column, the dbt DAG can't tell you which downstream columns, transformations, or dashboards actually break — it only knows model A feeds model B. That's the gap this closes.
| dbt docs / DAG | dbt-col-lineage | |
|---|---|---|
| Lineage granularity | Model → model | Column → column |
"What breaks if I change orders.amount?" |
Guess from the model graph | Exact affected columns, models & exposures |
| Pass-through vs. real logic | ❌ | ✅ flags columns whose SQL actually recomputes the value |
| Needs a warehouse connection | dbt docs serve |
No — reads artifacts, runs anywhere |
| Blast-radius check in CI | ❌ | ✅ sticky PR comment + severity gate |
| Machine-readable for AI agents | ❌ | ✅ one JSON document, built for automation |
It reads only your dbt artifacts (manifest.json + catalog.json) and parses the
compiled SQL statically with sqlglot.
It never connects to your warehouse and never runs dbt models.
Comment the blast radius on every dbt PR
Point it at CI and it posts a sticky comment on the pull request showing exactly what a change breaks — the downstream models, columns, and business-facing exposures (dashboards, apps) it touches — so every reviewer sees the impact before they approve. Optionally fail the check when a change is too risky.
Add it as a GitHub Action:
# .github/workflows/impact.yml
permissions:
pull-requests: write # so the check can post its comment
# ...build base- and head-branch dbt artifacts, then:
- uses: Fszta/dbt-column-lineage@v0
with:
manifest: artifacts/head/manifest.json
catalog: artifacts/head/catalog.json
base-manifest: artifacts/base/manifest.json
base-catalog: artifacts/base/catalog.json
fail-on: none # start non-blocking; flip to exposures|critical once trusted
How the loop works: a PR opens → CI builds dbt artifacts for the base and PR branches → the action diffs them, traces every affected column, model, and exposure, and posts one sticky comment (found-and-updated via a hidden marker, so re-runs edit the same comment instead of spamming the thread).
The severity gate (fail-on) decides when an impactful change should block the PR:
fail-on |
Blocks the PR when… |
|---|---|
none |
never — comment only (default, the safe on-ramp) |
tests |
a change provably breaks a dbt test (removes/renames a column a not_null/unique/relationships test still targets) — the objective, false-positive-free level to block on (needs base-manifest) |
exposures |
a change reaches a business-facing exposure (dashboard / app) |
critical |
a downstream column recomputes derived logic (not just a pass-through) |
any |
any downstream column is affected at all |
The action also emits step outputs for your own gating/reporting: affected_models,
affected_columns, affected_exposures, provable_breaks, verdict (safe/review/block),
and tripped_level.
Pin @v0 for updates within the current major (like actions/checkout@v4), or an
exact release — @v0.13.0 — for reproducible builds. The action installs the CLI
bundled at whichever ref you pin, so the tool always matches the tag. A complete
runnable workflow lives at
docs/examples/impact-pr-check.yml.
Quick start (local)
pip install dbt-col-lineage
Generate your dbt artifacts once — this is the only step that touches dbt, and it still never connects to your warehouse:
dbt compile # produces target/manifest.json
dbt docs generate # produces target/catalog.json (column metadata)
Then explore your column lineage in the browser — no flags needed, it reads target/
by default:
dbt-col-lineage --explore
Open http://127.0.0.1:8000, pick a column, and click Analyze Impact to see the
columns that need review, the pass-through columns, and the affected models and
exposures. Try the live demo → — no
install required.
Works even when
manifest.jsonhas no embeddedcompiled_code(e.g. fromdbt parse), as long astarget/compiled/**exists — it falls back to the compiled SQL on disk.
Machine-readable output (built for agents & automation)
Emit any column's lineage and downstream impact as a single JSON document — a stable contract you can pipe into an LLM tool call, a CI script, or your own tooling:
dbt-col-lineage --select stg_accounts.account_id+ --format json \
--manifest target/manifest.json --catalog target/catalog.json
Selector grammar (works for text, json, and dot output):
| Selector | Meaning |
|---|---|
+model.col |
upstream only (where the value comes from) |
model.col+ |
downstream only (what it feeds) |
model.col |
both directions |
The JSON splits upstream/downstream into models, sources, direct_refs, and
exposures, plus an impact block summarising the affected models, columns, and
exposures. Use --format dot for Graphviz.
Run the impact report locally
The impact command derives the set of changed columns for a branch and reports one
consolidated blast radius, ranked by severity:
removed > type_changed > logic_changed > added.
# Reliable two-manifest diff (base branch vs. current)
dbt-col-lineage impact \
--manifest target/manifest.json --catalog target/catalog.json \
--base-manifest base/manifest.json --base-catalog base/catalog.json
# Git-diff fallback when only one manifest is available
dbt-col-lineage impact --git-base main
It defaults to a human-readable Markdown summary (exposures first, then a blast-radius
table); add --format json for the machine-readable report. Add --ci to post the
sticky PR comment and apply the --fail-on gate.
Beyond the blast radius: a decision engine
The impact report is the foundation; on top of it the tool now turns a PR into a decision, on the principle "diff cheaply, rebuild selectively." All of this is additive — skip the flags and the tool behaves exactly as before.
-
Semantic categorization — every changed column is labelled breaking vs provably cosmetic, so a refactor that doesn't change any value doesn't get flagged.
-
A metadata-agnostic policy gate — you author rules (
predicate → block/warn/build/test/notify) over any dbtmeta, the change kind, the semantic signal, and the lineage reach. The tool ships the engine; you ship the rules.critical/piiare example configs, never built-ins.dbt-col-lineage impact --base-manifest base/manifest.json --base-catalog base/catalog.json \ --policy policy.yml --fail-on policy
-
Cross-boundary (Metabase) impact — a separate credentialed
metabase-extractstep snapshots Metabase intometabase_lineage.json; the offline gate then answers "will this column change break that dashboard?" by folding dashboards into the same reach the policy engine scans.
Full guides: Decision Engine docs.
Compatibility
Works with any sqlglot dialect via --adapter
(auto-detected from your manifest by default). Verified against Snowflake,
DuckDB, SQLite, and MS SQL Server / TSQL; on BigQuery, Redshift, Postgres,
etc., pass --adapter <dialect> if auto-detection needs a nudge.
Limitations
- Python models are not supported.
- Some SQL functions/syntax can't be parsed and cause the affected model to be skipped.
Documentation
Full CLI reference — every flag (--scope-git, --github-token/--repo/--pr-number,
the complete impact surface), output formats, and CI recipes — lives at
fszta.github.io/dbt-column-lineage.
License
MIT — see LICENSE.
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 dbt_col_lineage-0.16.0.tar.gz.
File metadata
- Download URL: dbt_col_lineage-0.16.0.tar.gz
- Upload date:
- Size: 298.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.1.3 CPython/3.10.21 Linux/6.17.0-1022-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d0c4acc7d3459dea224d05e5251b9db04cb83a308615502b858699403a22aa3
|
|
| MD5 |
3b95b315402c59fad4f205bb468aeb0c
|
|
| BLAKE2b-256 |
9b6f69ce1979eb3a20f5f4bcf61764ed5a017e5a2626c528dc1c5dc4396a6dda
|
File details
Details for the file dbt_col_lineage-0.16.0-py3-none-any.whl.
File metadata
- Download URL: dbt_col_lineage-0.16.0-py3-none-any.whl
- Upload date:
- Size: 327.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.1.3 CPython/3.10.21 Linux/6.17.0-1022-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19d3bc2693b595adcb12e149f8dd135b6b5e2490bd34b33d9dfcb14b653d962f
|
|
| MD5 |
783870264012248d3ae79a3bb30ff41d
|
|
| BLAKE2b-256 |
e2ba291442f48631e7b76f8d14a540ca6d534ec33f1e5d882621104c59622dc8
|