A developer renames a column in a dbt model. It merges. Forty hours later three dashboards are blank and a churn model has been scoring on nulls all week. Everything needed to prevent that already sat in DataHub — nobody looked, because looking meant leaving the pull request.
Every column below is a real field on order_details, and every
edge is real lineage exported from a DataHub instance. Twenty-five assets sit downstream —
but a given column feeds only some of them. That difference is the whole point.
One downstream model in the demo repository reads
order_status. Faultline grounds the fix in that model's real SQL and the
schema DataHub holds, then checks its own work.
select o.order_id, o.customer_id, - o.order_status, + o.order_state as order_status, o.order_total, current_timestamp() as as_of_date from {{ ref('order_details') }} as o
Verified — re-parsed with the same SQL parser that found the break, confirming it no longer reads a removed column.
It reads the renamed upstream column and aliases it back to the old name, so this model's own output contract — and everything downstream of it — is unchanged. A patch that still read a removed column would be reported unverified rather than shipped.
| Change | Column | Read downstream by |
|---|---|---|
| dropped | cust_last_name | 15 assets |
| renamed | cust_email → customer_email_address | 15 assets |
| renamed | order_status → order_state | 17 assets |
retyped DOUBLE → DECIMAL(10, 2) | order_total | 21 assets |
The assessment is written into DataHub as a document on the model, and all 21 affected assets are tagged — so the next person who opens one sees the pending risk without knowing this tool exists.
Lineage, ownership, PII tags and consumer SQL all come from DataHub through the MCP Server. The severity is deterministic and shows its reasoning; only the migration is generated.