Inspiration
Monday 9:04 a.m. - finance and the executive dashboard disagree on revenue by 12%. Three definitions looked reasonable. None were governed.
I wasn't on the analytics team. I was on the platform team - the person who keeps dbt, the warehouse, and the catalog running. When numbers disagree, everyone looks at whoever touched the pipeline last.
Finance used recognized_revenue from a legacy report. Looker used a calculated field from two years ago. Our dbt Semantic Layer stub had revenue_usd - a column that does not exist - because an analyst asked ChatGPT to draft YAML and the PR looked professional.
I spent that week reading SQL people actually run. On Thursday I opened DataHub query history on orders - dozens of real SELECT statements. I didn't need a model to invent revenue. I needed a pipeline that reads institutional SQL, validates every column against the catalog, compiles dbt Semantic Layer YAML, and writes back so the next agent inherits an official definition.
QueryMint is that pipeline.

What it does
QueryMint turns catalog-grounded query history into validated, mergeable dbt Semantic Layer artifacts.
The pipeline:
- Mine - institutional SQL from DataHub via
get_dataset_queries - Extract - sqlglot parses SELECT, GROUP BY, aggregations, CTEs, UNIONs (deterministic - no LLM on compile path)
- Cluster - structural hash deduplicates recurring patterns
- Compile - Jinja templates emit
semantic_models/,metrics/, staging SQL - Validate - fail-closed schema gate;
dbt parsein CI - Write-back - PENDING → read-back → VERIFIED

Dual-dataset proof (orders revenue + customers LTV/signups):
| Dataset | Queries | Clusters | Validation | VERIFIED |
|---|---|---|---|---|
| orders | 20 | 10 | 10/10 passed | 10/10 |
| customers | 12 | 8 | 8/8 passed | 8/8 |
| Combined | 32 | 18 | 18/18 | 18/18 |
Measured eval (pipeline-derived grounded vs ungrounded A/B):
| Check | Result |
|---|---|
| Column picks grounded | 18 / 18 |
| Ungrounded baseline | 0 / 18 |
| Hallucinated columns in sealed run | 0 |
| pytest + coverage | 176 tests, 92% |
| Transports | Fixture · GraphQL · MCP stdio |

Why DataHub is essential
| Step | DataHub capability |
|---|---|
| Mine | get_dataset_queries |
| Validate | schema field allow-list |
| Write-back | document + tags → next agent inherits answer |
Without query history I am guessing. Without schema metadata I am back to revenue_usd. Without write-back the next agent still hallucinates.
What makes QueryMint different
QueryMint is a batch compiler, not a chat agent:
| QueryMint | |
|---|---|
| Input | Institutional SQL from get_dataset_queries |
| Output | dbt Semantic Layer YAML + staging SQL - merge-ready artifacts |
| Proof | Fail-closed schema gate · dbt parse in CI · PENDING → VERIFIED read-back |
| Scope | Compiles what your organization already runs into governed metrics |

Next-agent flip
| Question | Without write-back | With QueryMint VERIFIED |
|---|---|---|
| Official revenue on orders? | "Use revenue_usd" (unverified) |
Cites validated sum_amount |
| Official LTV on customers? | "Use ltv_usd" (not in schema) |
Cites sum_lifetime_value |
Try it in 60 seconds:
git clone https://github.com/HawaleShailesh004/querymint-app.git
cd querymint-app && pip install -e ".[dev]"
querymint demo && querymint verify

Interactive: War Room - switch orders ↔ customers in the top bar.
How I built it
Python 3.11 · Typer · sqlglot · Jinja2 · Pydantic · acryl-datahub · pytest · GitHub Actions
| Module | Responsibility |
|---|---|
extract/parser.py + normalizer.py |
sqlglot intent (CTE/UNION/subquery) |
cluster/hasher.py |
Structural deduplication |
compile/compiler.py |
dbt Semantic Layer YAML |
validate/schema_gate.py |
Fail-closed catalog allow-list |
writeback/protocol.py |
PENDING → VERIFIED state machine |
eval/runner.py |
Pipeline-derived A/B eval |
datahub_client_mcp.py |
MCP stdio transport (QUERYMINT_TRANSPORT=mcp) |
Design choices:
- Deterministic compile path - sqlglot + templates, not LLM generation
- Fail-closed gate - column not in catalog → artifact does not ship
- Two-phase write-back - emit ≠ landed; read-back before VERIFIED
- Fixture mode - identical proof offline; judges not blocked by Docker
- Sealed bundle -
examples/+ SHA256SUMS;querymint verifyrecomputes integrity
Upstream skill contribution prepared for datahub-skills: contributions/datahub-skills/
Accomplishments
I built the pipeline I wished existed that Monday. QueryMint reads SQL your organization already runs and ships compiled dbt Semantic Layer YAML - not just a conflict report.
Dual-dataset proof. Orders (revenue) and customers (LTV/signups) both pass schema gate and VERIFIED write-back - not a one-table demo.
Reproducible by design. querymint demo && querymint verify → same numbers I report: 18/18 eval, 18/18 VERIFIED, 176 tests, 92% coverage.
I closed the agent loop. Write-back means the next agent answering "what is revenue?" reads catalog metadata instead of inventing revenue_usd.
Judge-friendly packaging. Offline fixture mode, War Room, docs/JUDGES.md, sealed examples/HEADLINE.json.
Contributed to DataHub
- datahub-skills PR #140 - adds the
datahub-query-to-semanticskill: institutional SQL fromget_dataset_queries→ validated dbt Semantic Layer YAML → optional catalog write-back.
Reference implementation: QueryMint
Challenges
Trust was harder than parsing. Every column ref checked against catalog schema or it does not ship.
Real SQL is messy. Structural clustering tuned so near-duplicates collapse without hiding real differences.
YAML is unforgiving. COUNT(*) → expr: * broke dbt parse; fixed templates + CI gate.
Write-back ≠ read-back. Rebuilt as two-step protocol after emit succeeded but UI showed nothing.
Windows + DataHub locally was painful. Port conflicts, empty query history, datapack path bugs - fixture mode ensures judges get identical proof without my laptop.
Knowing when to stop. LLM column names hallucinated revenue_usd in early experiments. QueryMint's value is boring and catalog-grounded.
What's next
- CI on schedule: query history shifts → PR with updated semantic YAML
- Steward approval queue before promoting
semantic-layer-candidate→official - Multi-dataset semantic models via lineage-proven joins
- Continuous drift detection when analysts change SQL patterns
Built With
- acryl-datahub
- apache-2.0
- datahub
- datahub-skills
- dbt
- dbt-semantic-layer
- github-actions
- graphql
- mcp
- mcp-server-datahub
- pydantic
- pytest
- python
- sqlglot
- typer


Log in or sign up for Devpost to join the conversation.