-
-
The incident banner the agent appended to the asset description, with the detection time.
-
DataHub after the run: the broken table tagged hindsight-degraded by the agent.
-
The postmortem saved as a DataHub document — this is what the next investigation retrieves.
-
The investigation: resolved asset with the ambiguity it called out, 29 ranked consumers, 14 owners to page.
-
GIF
A captured run replaying in the real UI: evidence timeline, blast radius, approval gate, write-back.
-
The human gate: three mutations and a postmortem as a dry run, with the reason for each. Nothing written yet.
Plenty of agents in this gallery walk DataHub lineage to a root cause. This one is the one that gets cheaper the second time.
Hindsight files its postmortem inside DataHub, and the next investigation retrieves it before it walks a single edge: 20 tool calls cold, 15 warm — both runs on record, replayable offline, and the example states the cost as well as the gain.
Then it re-reads every change it wrote through the GMS GraphQL API — not the MCP tools that wrote
it. That is how it caught a real defect:
verified 5/6,
a second run silently overwriting the first one's incident banner. Both runs had reported success.
The failing file ships unaltered.
And none of it is locked in this codebase: npx skills add gmassello/hindsight runs the same
procedure in your own CLI with no Python in the loop, and handed the same incident it reaches
the same fourteen owners, set for set
— proposed upstream to the official
DataHub skills repo.
The memory loop, measured
The learn phase saves the postmortem with save_document, inside DataHub. The next investigation's
recall finds it before it starts walking lineage, and turns it into hints that steer
root_cause. That is the 20-vs-15 above: the warm run did not re-derive what the cold run had
already concluded.
The gain is not free, and the example says so out loud: the warm run swept 6 consumers against the
cold run's 29. Memory bought speed and cost coverage. Both runs ship with their raw event streams, so
hindsight replay examples/02-cold-vs-warm/warm reproduces either one with no DataHub and no API
key.
The memory also outlives the agent. It lives in the catalog, so the next on-call inherits it whether or not they ever run this thing.
An agent that reports its own success proves nothing
So hindsight verify re-reads every mutation in a run's audit log straight from DataHub through the
GMS GraphQL API — a different channel from the MCP tools that wrote it — and exits non-zero if
anything is missing.
It immediately found something the agent had not: two runs against the same table silently overwrote
each other's incident banner, because update_description replaces rather than appends. Both runs
had reported success. The failing verify.txt ships as it came out, the failure included.
None of it is locked in this codebase
The same procedure also ships as a portable Agent Skill. npx skills add gmassello/hindsight
installs it into whatever CLI you already use — Claude Code, Cursor, Codex, Gemini CLI and seven more
— with no Hindsight code in the loop and nothing cloned.
Handed the same incident as the reference implementation, it reached the same 14 owner URNs, set
for set, in 9 investigation tool calls, applied three approved mutations, and saved a postmortem
that a re-search retrieved. It is proposed upstream to datahub-project/datahub-skills.
What it does
Give it a free-text alert — "orders in order_entry_db is showing NULL values in customer_id since 03:00 UTC, downstream order reports look wrong" — and it runs eight phases against DataHub:
- intake — parses the report into symptom + mentioned assets
- resolve — turns names into URNs, and states the ambiguity it resolved (three tables are
called
orders; it says which one it picked and why) - recall — searches past postmortems stored as DataHub documents, before investigating
- impact — walks downstream lineage and computes the blast radius with a deterministic formula: 29 consumers ranked, 14 deduplicated owners to page
- root_cause — walks upstream and ranks competing hypotheses, each citing the URNs it rests on
- propose — an action plan as a dry run, with a reason per mutation
- → human approval gate ←
- commit — applies
add_tags,update_description,add_ownersover MCP - learn — saves the postmortem with
save_document, inside DataHub
Inspiration
Data on-call is a job of re-derivation. A dashboard is wrong since 03:00, and whoever is paged opens a lineage graph and starts clicking: which table actually broke, who consumes it, who do I wake up, what changed upstream. Two weeks later the same thing happens and none of that work exists anymore — the conclusion died in a Slack thread while the catalog, the one place everybody looks, still says the table is fine.
That is the gap Hindsight is built for. Not "an LLM that answers questions about your metadata", but an agent that finishes the job the way a human on-call has to finish it: with the catalog updated and a postmortem someone else can find.
How we built it
- A deterministic phase state machine with an LLM inside each phase, not a free ReAct loop. Each
phase gets a whitelisted subset of MCP tools and a terminal
submit_*tool whose schema is a Pydantic model. Phases are marked critical or not: a non-critical failure emits an error event and the run continues with partial state. - The math is not in the model.
impacthas the LLM report raw consumers; the score is computed in Python. The same graph always produces the same ranking. - 13 DataHub MCP tools, 8 read and 5 mutation, with tool schemas discovered at runtime — the mutation prompts are built from the live schemas rather than hardcoded shapes. Mutations that fail over MCP retry through GMS GraphQL.
- Python 3.13 + FastAPI with SSE, a React frontend with zero runtime dependencies beyond React
(native
EventSource, plain CSS), Gemini as the default provider, and a hosted demo on GitHub Pages that replays captured runs with no backend at all.
Challenges we ran into
datahub datapack loadreturns before the lineage graph exists. The ingestion reports success in a second while the index fills for minutes. A run started too early produces a real-looking investigation with a blast radius that is quietly wrong — no error, just fewer consumers. It cost a full re-capture of every recorded run.- The negative control we could not honestly build. We wanted a run where the agent exonerates
the pipeline. Four attempts, none exonerated — and the diagnosis was not the prompt: the demo
catalog has no freshness, no row counts, no job history, so "the pipeline is healthy" is not a
claim that metadata can support. We cut the scenario and kept what stands on its own: a
three-state verdict where
exoneratedforces an empty plan in code, and a grounded-hypothesis rule — every hypothesis must cite a URN a tool actually returned. - Gemini rejects
$ref/$defsin function declarations, and a property legitimately namedtitlehas to survive the same pass that dropstitleas schema metadata.
Accomplishments that we're proud of
- Five captured runs across four directories, each replayable offline with
hindsight replay <dir>— no DataHub, no API key. - The clean-clone test. The repo cloned from GitHub into a fresh directory, following only the
README:
uv sync,ruff,pytest(30 passed),hindsight serveanswering on:8000,npm ci+npm run build. It surfaced three real defects, all fixed. - Verifying the Skill surfaced three defects in the Skill, all fixed before it was proposed upstream. Proving portability was not a formality; it was a bug hunt.
What we learned
Verification through a second channel is worth more than any amount of self-reported success — and the corollary is that honest limits are cheaper to ship than to hide. Every caveat we ended up writing down (what memory costs, the negative control we could not build, the mutation that never fired in a captured run) turned out to be a stronger claim than the version that pretended it did not exist.
What's next for Hindsight
Append-only incident banners, freshness and volume signals so the agent can genuinely exonerate a healthy pipeline, and raising a formal DataHub incident as part of the commit phase.
Built With
- datahub
- docker
- fastapi
- gemini
- github-actions
- graphql
- mcp
- pydantic
- python
- react
- server-sent-events
- typescript
- vite
Log in or sign up for Devpost to join the conversation.