Inspiration
AI agents are increasingly writing production data pipeline code, but an LLM doesn't actually know your real tables, it pattern-matches plausible-sounding column names from training data. That code often looks correct, passes a quick review, and then breaks in production because a column is actually called email, not customer_email. DataHub already knows the real schema, lineage, and business context. We wanted an agent that treats that as ground truth instead of guessing, checking DataHub before it writes anything, and checking its own work again afterward.
What it does
PR-Ready is a LangGraph agent that takes a plain-English data request and:
1 - Searches DataHub via its MCP Server to find the real tables, exact columns, and lineage involved. 2 - Writes a dbt SQL model using only what it actually found, never inventing a column. 3 - Validates its own generated SQL against that same schema, and retries itself if it catches a mistake. 4 - Opens a real GitHub pull request with the generated model. 5 - Writes a note back onto the DataHub table(s) it used, linking to the PR, so the catalog itself shows "a model was generated from this table," closing the loop instead of only reading from it
In testing against a live DataHub instance loaded with a realistic e-commerce schema, the agent has consistently caught real, non-trivial issues on its own, for example, correctly identifying that a pre-joined analytics table would double-count revenue for multi-line orders, and steering the generated SQL toward the correct grain instead.
How we built it
The core is a LangGraph state graph with five nodes: schema_agent → codegen → validator → pr_writer → write_back. The schema_agent is a ReAct-style sub-agent bound to DataHub's MCP tools (search, get_entities, list_schema_fields, get_lineage), so the LLM decides which lookups it needs rather than us hardcoding a fixed sequence. The validator is a second LLM call that re-reads the generated SQL against the retrieved schema and reports any column that doesn't actually exist, which routes back to codegen for a retry (max 3 attempts) if it finds a problem. pr_writer uses PyGithub to create a branch, commit the file, and open a PR. write_back uses the DataHub Python SDK to emit an Institutional Memory link back onto each table that was used.
The whole pipeline is provider-agnostic, swapping between Google Gemini, and DeepSeek is a single environment variable, since every node calls one shared _llm() function. We also built a mock-schema mode so the full pipeline could be developed and demoed before Docker/DataHub was even running locally, then flipped a single flag to point at the real thing.
A Streamlit UI streams each step of the LangGraph run live, so the schema lookup, code generation, validation (including any retry), PR link, and write-back confirmation are all visible as they happen rather than only as a final answer.
Challenges we ran into
Getting the real DataHub MCP Server wired in took real debugging. it ships as a Python package launched via uv/uvx, not an npm package as we first assumed. We also hit a subtle bug where our code was trying to read tool call arguments under the wrong key names (e.g. expecting urns when the real tool used urn, and expecting a tool named get_entity when it's actually get_entities), which silently produced an empty result instead of an error. we only caught it by directly querying DataHub's backend aspect data (datahub get --aspect institutionalMemory) and confirming it was empty, rather than trusting the UI or our own success message. Free-tier LLM rate limits also forced us to add retry-with-backoff around the calls we control directly and pick a less-throttled model.
Accomplishments that we're proud of
Getting the validator loop to be genuinely meaningful rather than decorative. it's caught real column-hallucination attempts and forced a retry before anything reached a PR. We're also proud that the write-back step is independently verifiable three separate ways (raw backend aspect via CLI, the DataHub UI, and our own diagnostics output), because we wanted to be sure it was actually true and not just claimed.
What we learned
That "read real context before generating" is only half the story, a second pass that checks the output against that same context is what actually makes generated code trustworthy, since the first pass can still make reasoning mistakes even with perfect schema access. We also learned how much silent-failure risk exists in tool-calling pipelines: a single wrong argument key name produced no error at all, just a quietly empty result, and the only way to catch that was verifying against the real system of record instead of trusting our own code's return value.
What's next for PR-Ready
Supporting Airflow DAG generation alongside dbt models, letting the write-back step also apply a DataHub tag (e.g. "agent-generated") for easier auditing at scale, and extending the validator to check business-logic assertions from DataHub documentation (like the grain-mismatch warning it already reads) rather than only column existence.
Log in or sign up for Devpost to join the conversation.