Inspiration
A data engineer drops a column. The diff looks clean, CI is green, two people approve it. Three days later a finance dashboard is quietly wrong.
The code review wasn't the problem — nobody in that review knew what depended on that column. That knowledge existed the whole time, sitting in the catalog, and no one asked it.
What it does
lineage-guard reviews schema changes the way a senior data engineer would: by asking DataHub what actually depends on the thing you're changing.
On every pull request it parses the schema diff, resolves the changed columns to DataHub URNs, walks column-level lineage across platforms, and posts a verdict with the blast radius drawn out:
⛔ Blocked —
order_detailsdropsorder_total34 downstream assets across dbt, looker, powerbi, snowflake, tableau — 4 hops deep. Blast radius: 16 column dependencies · 19 tables · 15 charts/dashboards
Then it fails the CI check, so the merge is blocked. An additive migration gets ✅ approved instead — it isn't a blunt instrument.
Finally it writes the finding back into DataHub: a tag on the asset, a tracked incident, and a Context Document holding the full reasoning, linked to every affected asset. The next engineer — or the next agent — inherits the analysis instead of recomputing it.
How we built it
Python, sqlglot for the DDL/diff parsing, DataHub's GraphQL API for lineage traversal (searchAcrossLineage, get_lineage_paths_between) and the upstreamLineage aspect for the fineGrainedLineages column-level edges. Write-back goes through raiseIncident, createDocument and addTag. Ships as a CLI and a GitHub Action.
Challenges we ran into
Everyone else points the arrow at generation. The obvious build is "agent writes your dbt model." The problem is that a reviewer then has to trust SQL an LLM produced, with no way to verify it without a warehouse. We pointed the arrow the other way: lineage-guard doesn't write your migration, it tells you what your migration breaks — and every claim is backed by a lineage edge that already exists in the catalog. There is nothing to hallucinate.
The docs said incidents were Cloud-only. DataHub's own datahub-quality skill lists raiseIncident under "Cloud-only capabilities (Acryl SaaS)". We tested it against a stock datahub docker quickstart and it works fine on OSS Core v1.5.0.6 — incident created, persisted ACTIVE, readable back. We filed a documentation correction upstream: https://github.com/datahub-project/datahub-skills/pull/60
URN resolution is genuinely ambiguous. Catalog URNs are namespaced and dbt↔Snowflake siblings mean one logical table appears under two platforms. A fuzzy matcher would confidently produce the wrong blast radius, so we ship an explicit mapping.yml and report unresolved tables honestly instead of guessing.
Judges shouldn't need 8GB of Docker. DataHub's quickstart is 7 containers. So the repo ships a recorded lineage fixture: the agent runs with no DataHub instance at all, and replay output matches the live instance exactly. That's also what the GitHub Action replays, so the comments on the demo PRs are genuinely generated rather than checked in.
Accomplishments that we're proud of
Two real pull requests that anyone can open and read in 15 seconds — one blocked with a red CI check and a full blast radius, one approved green. No setup, no install, no video required to believe it.
What we learned
The strongest thing an agent can do with a catalog isn't read from it — it's put something back. The rubric says it outright ("contributes back to the graph"), and most agents stop at reading.
What's next for lineage-guard
Reverse lineage for deletions, dbt manifest ingestion so mapping.yml becomes optional, and suggesting the fix (the downstream patch) alongside the block.
Log in or sign up for Devpost to join the conversation.