Inspiration

On a hospitality payroll run, a tip-reconciliation total came out wrong, and payroll couldn't ship until someone knew why. The catalog couldn't explain it, so I pulled the raw POS check data and reconciled it against what the code should have produced, line by line. It took days.

The root cause turned out to be a small, explainable data defect — exactly the kind of problem a machine could pinpoint in seconds if it had both the business context and the lineage.

Incident Copilot is built to do that investigation automatically. The planted incident in this repo is a genericized version of that class of real-world failure.

What it does

When a DataHub assertion fails, the agent wakes up automatically through DataHub Actions — no polling.

Collect → Verify → Observe → Diagnose → Write back
  • Collect — gathers deterministic SQL evidence from the warehouse.
  • Verify — checks business invariants before the LLM is allowed to reason about the incident.
  • Observe — reads DataHub lineage, ownership, tags, and glossary context through GraphQL.
  • Diagnose — Claude explains the verified evidence, identifies the root cause and mechanism, and calculates the blast radius.
  • Write back — files the incident in DataHub with the diagnosis, evidence, and affected assets.

The important part is the verification firewall. If the deterministic checks cannot prove a business-rule violation, the LLM does not get to invent a root cause, and this behaviour will be enhanced in the future.

The human's role is a verdict, not a permission slip. The agent files the incident autonomously. When a human acknowledges it, the confirmed business rules are promoted into native DataHub custom assertions, so the same failure can be caught deterministically next time — without the LLM.

The catalog learns.

The entire investigation is visible in the agent's live console: lineage is drawn as an illuminated track, the blast radius lights up as it is discovered, and each pipeline stage carries its underlying evidence.

How DataHub is load-bearing (not decorative)

DataHub is fundamental to the agent in three places:

  • Trigger — DataHub Actions emits the assertion-failure event that wakes the agent.
  • Context — GraphQL provides lineage, owners, tags, and glossary terms used to determine the affected assets and blast radius.
  • Memory — the diagnosis is written back as a DataHub incident and tags; confirmed invariants become durable native assertions.

Take DataHub away and the project becomes a local diagnostic tool.

I initially tried using the self-hosted DataHub MCP server, but it didn't support the complete workflow I needed, particularly for incident and custom-assertion writes. I switched to the GraphQL API, which gave me one interface for both the context reads and the writes required by the agent.

How we built it

The agent is written in TypeScript and uses a deterministic pipeline:

collect → verify → observe → diagnose → write-back

The reasoning stage is a single structured Anthropic SDK call over the verified evidence package.

The warehouse is dbt + DuckDB, populated with synthetic hospitality data modeled after a POS system. There is no real company data. Each incident is a reproducible benchmark containing the scenario, failing assertion, business playbook, and expected diagnosis. The current evaluation passes 5/5.

The UI is a zero-build HTML/CSS/JS console served directly by the agent and driven by SSE. It is generic across incidents rather than hard-coded to the demo scenario.

I also built a judge kit so the project can go from a clean machine to the live demo with one command:

node scripts/demo.js all

The setup handles the DataHub environment, dependencies, access-token creation, catalog publishing, trigger arming, and the agent console. Claude Code or Codex can also run the guided demo through the repository's demo skill and AGENTS.md.

Challenges we ran into

The biggest challenge was making autonomous action trustworthy.

An agent that can file incidents without asking for permission needs a strong boundary around what it is allowed to claim. That led to the verification firewall: deterministic business invariants run before the LLM, and the model is only allowed to explain evidence that has already been verified.

I also wanted the judge experience to be as reliable as the agent itself. Testing the setup on a genuinely cold machine exposed several failure modes around Docker, WSL, trigger processes, and first-run setup.

The result is a judge kit that verifies its own steps, detects when the trigger process dies, retries where appropriate, and fails loudly with an actionable error instead of showing a misleading success state.

Accomplishments that we're proud of

The complete incident-response loop runs end-to-end on DataHub OSS:

assertion failure → autonomous investigation → verified diagnosis
→ incident write-back → human verdict → durable assertion

There is no human intervention between the initial failure and the filed incident. The learning loop turns confirmed business rules into permanent catalog checks.

The examples, artifacts, and README screenshots come from real executions of the system, not staged output.

What I learned

The biggest lesson is that trust in an agent comes from the determinism around the model, not from the model itself.

Putting a cheap, deterministic verification firewall in front of the LLM changed the architecture. The model isn't responsible for discovering whether a business rule was violated; SQL and deterministic checks establish that first. The LLM's job is to explain the verified evidence. That made autonomous action much easier to trust.

I also learned that "autonomous" needs a precise human boundary. My early design had a vague "human approval" step. The final design is much clearer:

AI investigates → evidence proves → AI files → human ratifies → catalog remembers

The agent does not wait for permission to investigate or file the incident. The human decision happens afterward: acknowledge or dismiss. Acknowledge is the promotion gate that turns a confirmed business rule into a durable DataHub assertion.

Finally, write-back is what makes an agent compound. An agent that only reads metadata helps once. An agent that writes its diagnosis back into the catalog leaves knowledge behind. When confirmed invariants also become durable assertions, each investigation makes the next recurrence cheaper and more deterministic.

I also learned that the first-run experience is a product surface. A green checkmark that lies is worse than an explicit error. Every setup step now verifies its own outcome, because a judge's ten minutes deserve the same rigor as the agent's evidence.

What's next for DataHub Incident Copilot

  • On-ingest auto-validation — trigger the loop when new data lands, removing the manual rebuild/ingest step.
  • More incident scenarios — add reopened_check, silent_filter, and orphan_dimension. The agent and console are already generic, so new scenarios require no agent/UI changes.
  • Learning firewall — let the agent propose brand-new invariants for a human to review and ratify.

Built With

Share this project:

Updates