dbt, dbt docs and an AI copilot: what's still missing
dbt solves the definition problem. The remaining gap is enforcement at the point where an answer is produced.
By Fanar

The stack I run into most often when I talk to data teams looks like this: dbt for transformations, dbt docs as the place definitions live, and a BI tool on top — Metabase, Power BI, Looker, Tableau.
Increasingly there's an AI assistant bolted onto the BI layer, sometimes an MCP server feeding the dbt docs into the model's context.
It's a good stack. I want to be precise about what it solves before I say anything about where it stops, because the version of this post where a vendor tells you dbt isn't enough is not a post worth reading.
What dbt actually gives you
One place where "net revenue" is written down as code, in version control, with a reviewer attached to every change. Tests that fail in CI before a broken model reaches anyone. Lineage, so when a number moves you can find out why. Marts that are DRY enough that a competent analyst writes ten lines of SQL instead of a hundred.
If your data team is three people and everyone who asks a question can also read a lineage graph, that may be the whole answer. I mean that literally. Some of the best-run stacks I've seen have no business case for buying anything else.
The gap isn't in the model. It's in the last mile between a defined metric and a person who needs a number.
1. A description is not a constraint
dbt docs are prose attached to models and columns. When you connect an LLM — through MCP, or through your BI tool's built-in copilot — those descriptions arrive in the model's context as text. The model reads your definition of net_revenue and then writes its own SQL.
That distinction does more damage than it looks like it should. Your definitions are governed at the point they're written and ungoverned at the point they're used. To the model, a definition is a hint.
Most of the time the hint is enough. The problem is the questions that don't map cleanly: a filter nobody anticipated, a time grain the mart doesn't carry, a segment that lives in a different model. The model doesn't stop at the edge of what's defined. It composes something. That's what it's built to do.
The failure mode is quiet. The same question, phrased two ways, returns two different numbers, and both of them look reasonable. Nothing errors. Nobody flags it. The number ends up in a board deck.
2. Your dashboards answer yesterday's questions
A dashboard encodes the questions the data team already anticipated. It covers the recurring ones well and the follow-ups not at all.
"Why is DACH margin down three points versus Q1?" isn't on any dashboard. It becomes a Slack message, then a ticket, then two days of an analyst's week.
dbt made that analyst much faster at building the model. It did nothing about the queue in front of them.
3. Correction arrives after the number has travelled
The most sophisticated version of this stack I've seen in the wild: every BI card is select * from gold_mart. A scheduled job scans for new cards, reads the SQL the user or the LLM generated, generalizes it into a reusable mart, and replaces the card's query with a select against it.
Ad-hoc query in, reviewed definition out. That's a genuinely good pattern, and it's roughly the right shape. It's also being run by hand.
Two things strain it as you scale. It only catches what becomes a card, and a large share of ad-hoc answers get pasted into Slack and never saved anywhere. And it corrects after the fact: by the time the scan runs, the number has already been quoted in a meeting.
With ten business users, the review queue is an afternoon a week. With two hundred, it's somebody's job.
What we built instead
Fanar sits on top of your existing models. It doesn't replace dbt, and if you don't have defined metrics it has nothing to work with. The difference is where enforcement happens.
A question gets resolved against the defined semantic model before any SQL exists. If the answer can be composed from defined metrics, it's composed from them. If it can't, the loop doesn't quietly fall back to freestyle SQL.
Depending on how you've configured it, one of two things happens: the question is refused with a clear statement of what's missing, or a second agent proposes a new definition — and that proposal goes back to the data team as something reviewable, not as a one-off query buried in a chat log.
So the claim isn't that we write better SQL than your copilot. It's that:
- The governed definition is enforced where the answer is produced, not only where it's written.
- Coverage gaps surface as a queue of proposed definitions instead of arriving later as silent drift.
- Because the loop resolves against known definitions, the same question asked two ways is the same query.
Around that sit the two unglamorous parts: context management, so the model knows which entities relate to which and doesn't have to infer it from column names, and evals, so you can measure whether the loop is holding instead of assuming it.
Your data team still gets a copilot for the deep work — the questions that genuinely require writing new SQL. It just stops being the only path to a number for everyone else.
When you shouldn't buy this
Your marts are DRY, your users are technical, and ad-hoc volume is low. Your stack is fine. Adding a layer buys you nothing.
You enjoy building this. Plenty of teams running dbt, MCP and a scan-and-promote loop will build the rest of it themselves, and do it well.
Your real problem is access, not definitions. Who can see which rows is a Lake Formation or RLS question. Different problem, different tool.
The teams this is actually for look like this: the definitions exist and are good, the data team is a bottleneck for a business that keeps asking, and somebody has already noticed the same question producing two different answers.
If that's recognisable, I'd like to hear how you're handling it — whether or not it ends up involving us. We're taking on a small number of design partners right now, and the teams that have already built half of this themselves are the ones we learn the most from.