Most deadly errors arise from obsolete assumptions. Frank Herbert, Children of DuneYour coding agent can write SQL. What it cannot see is what the SQL is supposed to mean: what one row is, which rows belong, how a number adds up. When that meaning lives only in someone’s head, or in the conversation that wrote the model, the next schema change breaks it silently. The query still runs. The numbers are just wrong. Dex Semantics keeps that meaning with your models, as code. Every model your agent creates or changes carries a declaration. Dex checks the built data against it, reads it back in the next session, and turns it into the semantic layer you already use. Three steps:
- Declare the rules. The declaration lives with your models.
- Check the results. Dex holds the built data to the declaration.
- Keep the context. The declaration flows into tests, semantic models and the next agent session.
The imperial accounts
An illustrated workflow on synthetic data. The figures are invented and checked, not taken from a real run. The Emperor asks his coding agent for production and revenue analytics on Arrakis. The agent builds the models and records three definitions:
Overnight, the source starts splitting each shipment into two lines. The join
still runs, so nothing errors. In the morning the report says production
doubled, from 100 to 200 tonnes, and revenue went from 140k to 280k credits.
The desert produced nothing extra: shipment S01 now arrives as two rows, and
each is counted.
With Dex, the shipment model declares one row per shipment. Production
counts harvests; revenue uses the agreed price. When the model is built, Dex
checks it: S01 and S02: expected 1 row, found 2. The duplicate shipments are
flagged before anyone reads the report, and the proposed fix (total the lines
per shipment first, then count harvests on their own) comes back as a diff for
the developer to review. With the fix, the report says 100 tonnes again.
The same story is told as a 60-second film,
The imperial accounts.
1. Declare the rules
A declaration travels beside the SQL in the payload oftransform plan (and of
semantic define|update|plan). It states what one row is (grain), which rows
are in (population), what each column is (role, unit, aggregation,
additivity, null_rule, references), and the decisions the model rests on
(assumptions, each with its evidence):
config.meta.dex:
You do not write declarations from nothing. Every plan carries
data.declarations.<model>: what Dex drafted from what it measured (the
grain a key proves, foreign keys from verified joins, running totals, unit
shapes, and what sibling models do with the same columns), what is
declared, and what is still missing. Measured facts are drafted with
their evidence; intent (the description, the population, the assumptions) is
never invented, only asked for.
An ambiguous choice does not stop the agent. It decides on the best evidence,
records the choice as an assumption with its tier (request, contract,
precedent, data or default), and continues. Every assumption comes back
under data.decisions, so the reviewer sees each decision the model rests on.
2. Check the results
A green build says dbt ran. It does not say the rows are right.transform build --verify and maintain verify hold each declaration to the
relation that was actually built, with aggregate-only statements (no row
leaves the warehouse):
A change to an existing model is also read against its declaration before it
is applied: a removed population filter, a dropped grain column or a
SUM of a
column declared semi_additive is named under contradictions on the plan,
with the side that looks stale.
Dex checks the meaning you recorded. It does not infer your business policy
from the data, and it never applies a fix on its own: every repair is a plan
you review.
3. Keep the context
The declaration is part of the repository, so it outlives the conversation that wrote it. Tests. The grain, NULL rule and foreign keys render as dbt tests, which every build runs. The next agent session.explore profile --use-project and
explore map --use-project read each model’s declaration back from the YAML
on every run and never from a cache, so a new session starts from the rule the
project records rather than one remembered from a chat. A declared
references is also a join, and a declared grain is compared with the keys Dex
measures (grain_status).
Drift. maintain snapshot fingerprints every declaration, and
maintain semantic reports a changed population, grain, unit or aggregation as
drift, even when the SQL did not change.
Semantic models. A declared mart (a model under transform.marts_paths,
default models/marts) gets the semantic definition its declaration already
states, in whichever layer the repository reads:
Three rules hold for every layer. Metrics are never derived: a measure is
what a column holds, a metric is a business definition, and it stays yours.
Nothing is guessed or renamed: a field a layer cannot carry is named under
not_carried, with why. A population is metadata, never a filter: the
mart’s SQL already enforces it, so a second copy in the layer would drift the
day the two disagree. It is carried in the layer’s metadata where the format
has a metadata key, and as the last sentence of the description everywhere.
Snowflake Semantic Views and Databricks Metric Views are written as files in
the repository. Dex deploys nothing to either platform. Which layers Dex can
also query is in the semantic layers table.
Adopting it on an existing project
Models written before declarations existed have none, and Dex never adds them unasked.transform declare proposes, in one capped plan, the measured facts
each selected model does not yet state (the grain, keys, foreign keys and
running totals), each with its evidence:
.dex/ cache
only, so it is free on every connector.
Further reading
- Declarations: what a model means: every field, what it renders into, and every check.
- Querying the semantic layer: the catalog and governed queries over the layer a declaration feeds.
- The command contract:
transform plan,transform declare,transform build --verifyandmaintain verifyin full. - ADE-bench results: the run behind the 25% figure.
references/dex-semantics.md (edit on main). This page is generated; change the source, not the site.