Inspiration
A dropped or renamed column in a dbt PR silently breaks dashboards and models downstream. Every tool in this space (SQLMesh, Recce, Datafold, Gable) will warn you in the PR. Then a human has to open the catalog, figure out what truly broke, file the incident, and message the owners. That last mile is where breaks slip through.
What it does
One GitHub Action runs the whole loop on a PR that drops or renames a column, or deletes a whole model:
- Detect: diffs the dbt manifest.json (base vs PR) for dropped/renamed columns and tables across models, sources, seeds and snapshots, mapping each to a DataHub schemaField URN.
- Trace and classify: walks DataHub's own column-level lineage (scroll-paginated to exhaustion) to the assets that TRULY reference the column, separating them from pass-through assets that are downstream of the table but never touch the column. It understands DataHub's sibling model, so the changed table's own dbt/warehouse twin is never flagged as its own victim and downstream sibling pairs count once. On the demo PR (dropping order_items.unit_price): 12 truly-broken (a Snowflake mart at degree 1 plus Looker/Power BI/Tableau assets at degree 2) vs 17 pass-through, with 5 more surfaced at degree 3+ to monitor. A naive tool flags all 29.
- Write back to DataHub, and only when something actually breaks: raises an Incident on the critical asset assigned to its real DataHub owners (idempotent across re-runs), deprecates the dropped column itself at the schemaField level, and tags every truly-broken downstream backstop:needs-review.
- Draft: claude-sonnet-5 writes the migration SQL (a compatibility view) and a specific message per owner, addressed by name.
- Block the merge: posts a red DO NOT MERGE check naming the broken assets, their owners, and the incident links. Any internal error fails closed, so a broken analysis is never a green light.
That write-back is the whole point. Every competitor stops at a warning.
How I built it
Python, strict TDD, 71 tests. dbt manifest diff into DataHub GraphQL (scrollAcrossLineage, raiseIncident, updateDeprecation, batchAddTags), claude-sonnet-5 for drafting, and a Docker-based GitHub Action that posts the check. Verified end-to-end against a live DataHub OSS instance with the showcase-ecommerce datapack: real cross-platform lineage and owners, never fabricated.
Challenges I ran into
Two bugs that only appear under real GitHub runner conditions and that a local-only test never sees: the runner forces the container working directory to /github/workspace (so python -m src.main could not resolve the package), and it exposes inputs with hyphens preserved as INPUT_DATAHUB-GMS-URL (so every input silently fell back to its default). Both were caught by running the built image under simulated runner conditions.
The subtler one was honesty about impact. DataHub pairs a dbt model with its warehouse table via the siblings aspect, so the changed table's own twin appears at degree 1 in its own lineage. Counting it meant a false DO NOT MERGE on every real column.
What I learned
Column-level lineage is only useful if you are honest about what it returns. The hard part is not finding downstreams, it is confidently NOT flagging the ones that do not break.
What's next
Prompt-caching the drafts, a Slack post for owner comms, and a compiled-SQL fallback for undocumented models. The assess_blast_radius Skill and a sample ingestion recipe are queued as upstream contributions to DataHub.
Built With
- datahub
- python
Log in or sign up for Devpost to join the conversation.