Inspiration

This started from a personal annoyance, not the hackathon brief. I've built a bunch of fullstack projects, and in the age of AI coding agents it's genuinely gotten easier to make them bigger and more complex. But that created a new tax: every time I reopened a project, I had to re-explain the codebase to the agent before it could safely make a change. That's tokens and time spent every single session just re-deriving context the agent already should have "known" from last time.

The same problem exists at company scale, just with higher stakes. Someone renames one column in the payments table. Four steps downstream, an ML model that decides which customers get a discount starts making bad calls, and nothing anywhere says the two are connected. That gap, between a small change and its real blast radius, is the same "re-explain everything, every time" problem, just wearing a data engineering costume.

So Steward maps the whole catalog once and puts two agents on top of it that tell me my actual dependencies: what a change touches and where, so I'm not searching for it by hand or re-briefing an agent from scratch each session.

Most catalog agents I looked at are also read only advisors: they inspect metadata and tell a human something, then forget it. Challenge #1 in this hackathon had a line that stuck with me: agents should write results back so the next person or agent inherits the knowledge. That's the same fix for the same root problem, so I built Steward around making that the measurable center of the system, not an afterthought.

What it does

Steward is a pair of agents that share one DataHub catalog and one tool set.

  • Blast Radius takes a proposed change and walks downstream to terminal consumers, reporting exactly what breaks and whose sign off is needed.
  • Root Cause takes a symptom and walks upstream, ranking candidate causes with the evidence that would confirm or eliminate each one.

Every conclusion gets written back into DataHub as a structured finding on the entity itself, not into a side database, and every run reads prior findings before investigating. That means the second question about a neighborhood of the graph is cheaper than the first: ask once and it's 13 entities inspected at 568 tokens, ask the same thing again and it drops to 1 entity, 0 tokens, because the answer is already sitting in the catalog.

The demo scenario is an ordinary analytics estate (Stripe and CRM raw tables, dbt models, an ML feature table, Looker dashboards, MLflow models) with one deliberately non obvious property: a raw payments column feeds a production churn model four hops downstream. Nobody reading the raw table would guess that. That's the whole point, blast radius is invisible exactly when it matters.

How I built it

  • Two agents, one adapter. src/steward/adapter.py is the only module that knows DataHub's API exists. Everything above it speaks in plain dataclasses against a Catalog protocol, so swapping transports (MCP Server, Agent Context Kit) means reimplementing one interface, not touching the agents or tests.
  • Findings as the unit of knowledge. Each finding carries a severity, evidence URNs (what the agent actually inspected), and a built_on list linking to prior findings it stands on. Findings are stored as institutionalMemory links on the subject entity and written read-modify-write, so a blind write can't wipe out human-added links, and writes are keyed by finding_id so a re-run updates instead of duplicating.
  • A fake catalog for speed. FakeCatalog implements the same protocol as the real DataHub adapter, built from the same scenario definition. The test suite runs in about 0.02s with no Docker, and steward parity checks that the fake hasn't drifted from the real thing.
  • Deployment. Dockerfile and render.yaml are included; on Render it just needs a GROQ_API_KEY set in the dashboard.

Challenges I ran into

  • GraphQL fragment merging. properties.name has different nullability on Dataset vs Dashboard, so requesting it across inline fragments throws a hard validation error unless every fragment's fields are aliased.
  • Degree filters are an enum, not an integer. searchAcrossLineage only accepts "1", "2", "3+". Passing "3" returns a 400.
  • ML lineage doesn't route the way you'd expect. MLModelProperties has no upstream-dataset field. Models connect to data through trainingJobs, so the demo scenario had to be modeled that way to match how DataHub, and reality, actually represents it.
  • The SDK is moving under me. DataHub 1.7's datahub.sdk package is explicitly marked experimental on import. I built on the stable trio every first party ingestion source uses instead (DataHubGraph, MetadataChangeProposalWrapper, generated aspect classes) plus GraphQL for traversal, to avoid building on ground that could shift mid hackathon.

Accomplishments that I'm proud of

  • Getting the compounding loop to actually show up in the numbers, not just claimed in a slide: 13 entities / 568 tokens on the first ask, 1 entity / 0 tokens on the repeat, because the second run reads a finding instead of re-deriving it.
  • Verifying against a live DataHub v1.7.0 instance, not just the fake catalog, including the full read-modify-write cycle and idempotent finding updates. It would have been easy to demo against the fake and call it done.
  • Keeping the adapter boundary honest. src/steward/adapter.py is genuinely the only file that knows DataHub's API exists, which I only confirmed by checking that swapping catalogs required zero changes to the agents or tests.
  • A test suite that runs in ~0.02s with no Docker or network, so I could iterate on agent logic without paying the DataHub startup cost every time.

What's next for Steward

  • Real write triggers. Right now findings are generated on demand. The natural next step is hooking Steward into a CI check or a PR bot, so a proposed schema change gets a blast radius comment before it merges, not after something breaks in production.
  • Multi-hop confidence scoring. Root Cause currently ranks candidates with evidence, but doesn't yet weight how many hops away or how stale the evidence is. Adding a decay/confidence score would make the ranking more trustworthy on larger, noisier catalogs.
  • Beyond DataHub. The Catalog protocol was built so the agents don't care about the backend. Testing that against the MCP Server or Agent Context Kit path would validate the adapter claim isn't just theoretical.
  • Scaling past the demo estate. The current scenario is 15 entities by design, small enough to reason about by hand. The real test is running Blast Radius and Root Cause against a catalog with thousands of entities and seeing whether the token savings from compounding knowledge hold up or shrink.
  • Applying it to my own codebases. The original itch was re-explaining project context to coding agents every session. Pointing Steward at a real fullstack repo's dependency graph, not just a data catalog, is the natural extension of the idea that started this.

Built With

  • anthropic-claude-api
  • datahub
  • docker
  • fastapi
  • github-actions
  • graphql
  • groq
  • llama-3.1
  • mcp
  • ollama
  • pytest
  • python
  • render
  • uv
Share this project:

Updates