The Problem
A dbt model gets a column renamed. The PR looks clean — tests pass, lint is green, CI is green. It merges. Three days later, the executive churn dashboard shows null. The ML team's feature pipeline has been ingesting wrong values since the merge. Nobody connects the cause to the change because three days passed.
CI catches code errors. DataHub knows the lineage. But nobody puts the lineage graph in the merge path automatically. Levee does.
What Levee Does
Levee intercepts GitHub pull requests that modify dbt models, traces their
full downstream impact through DataHub's lineage graph via
mcp-server-datahub, and issues a verdict before the PR can merge:
- BLOCK — a PROD-tagged asset or production ML model is downstream. The PR is blocked. Levee opens a companion fix PR with a safe migration already committed.
- WARN — downstream dependents exist but none are production-critical. Levee posts an impact report and lets the engineer decide.
- APPROVE — no downstream dependents. Merge safely.
Every verdict is written back to DataHub — the blocked asset is tagged
urn:li:tag:levee-blocked and an Incident aspect is created with the
full impact report, so the next agent or engineer who touches the asset
inherits the context.
The Companion Fix PR
When Levee blocks a PR, it doesn't just post a comment. It creates a new branch with the generated safe migration already applied, opens a PR against the original branch, and links it in the block comment. The engineer merges the fix PR first, then their original PR passes.
Zero manual SQL copying. The fix is a mergeable artifact, not a suggestion in a text box.
How We Built It
- GitHub OAuth App: real browser-redirect OAuth flow. Any user clicks "Connect Your Repo," authenticates with GitHub, selects their repo, and Levee registers the webhook automatically. Personal workspaces are isolated per connected repo.
- Webhook receiver: HMAC SHA256 signature verification on every incoming event. Unverified payloads rejected with 401.
- Diff parser: extracts column renames, drops, and type changes from real git patch diffs into structured change manifests
- Lineage tracer:
mcp-server-datahubdownstream traversal up to 6 hops, classifying each asset as CRITICAL / HIGH / MEDIUM / LOW - Impact scorer: BLOCK / WARN / APPROVE verdict engine with fails-closed behavior — DataHub unreachable means BLOCK, never silent APPROVE
- Migration generator: produces safe dbt SQL for rename and drop scenarios — COALESCE aliases, backfill queries, ref() update lists
- DataHub write-back:
acryl-datahubSDK for tagging assets and writing Incident aspects - Frontend: Next.js 16.2 PR review queue with live polling, personal workspace per connected repo, in-UI fix PR merge action
The Real User Flow
- Engineer opens a dbt PR renaming
billing_amount→amount - GitHub sends webhook to Levee
- Levee traces lineage via DataHub MCP, finds
billing_risk_model [PRODUCTION ML]three hops downstream - Levee posts BLOCK comment on the PR with consequence list and fix PR
- Levee opens
levee/fix-pr-104with the safe COALESCE migration - Engineer merges the fix PR
- Engineer re-opens their original PR — it now passes
- Levee posts APPROVE, tags the asset
levee-approvedin DataHub
Challenges
- GitHub OAuth state validation had to handle the case where either the cookie or the query parameter is missing — not just when they disagree. Both must be present and match.
- Vercel's serverless functions cannot run long-lived Python MCP subprocesses or write durable file-based state. The architecture requires a persistent Python backend; for the demo this runs locally with the Next.js frontend deployed separately.
- Column-level lineage in DataHub is sparse in practice. Levee uses dataset-level lineage with asset classification rather than relying on column-to-column edges, and documents this accurately rather than overstating the precision of the impact detection.
- The companion fix PR flow required careful sequencing: create branch, commit migration, open PR, post comment on original PR, write back to DataHub — all in order, all with explicit failure handling at each step so a missing token produces a clear error rather than a fabricated URL.
Built With
DataHub · mcp-server-datahub · acryl-datahub SDK · GitHub OAuth Apps · GitHub Webhooks API · Next.js 16.2 · TypeScript · Tailwind CSS · Python 3.11 · Claude (claude-cli)
Built With
- claude
- css
- datahub
- dbt
- github
- mcp-server-datahub
- next.js
- oauth
- python
- tailwind
- typescript
- webhooks
Log in or sign up for Devpost to join the conversation.