DVT COMPILE & DVT PARSE
Two small verbs that build nothing and land nothing. dvt compile renders your models into the exact SQL each target will receive and files it under target/; dvt parseloads the whole project and answers one question — is it healthy? — and it's the check every other graph command runs before doing anything. If you know these verbs from dbt, you already know them here: same flags, same behavior, same output shapes.
DVT COMPILE — SEE THE SQL BEFORE ANY ENGINE DOES
Take a model as you wrote it — refs, sources, Jinja and all:
-- models/marts/orders.sql
select
o.order_id,
o.customer_id,
sum(oi.amount) as order_total
from {{ ref('stg_orders') }} o
join {{ ref('stg_order_items') }} oi using (order_id)
group by 1, 2Compile it, and DVT prints what that model actually becomes — every {{ ref() }} resolved to a real relation, every macro expanded:
$ dvt compile --select orders
12:31:02 Found 34 models, 48 data tests, 6 sources, 519 macros
12:31:02
12:31:03 Concurrency: 4 threads (target='dev')
12:31:03
12:31:03 Compiled node 'orders' is:
select
o.order_id,
o.customer_id,
sum(oi.amount) as order_total
from "analytics"."public"."stg_orders" o
join "analytics"."public"."stg_order_items" oi using (order_id)
group by 1, 2Nothing ran and nothing landed — this is the fastest way to see what DVT would actually send. Drop the --select and dvt compile renders the whole project the same way, quietly, into files.
WHERE THE FILES LAND, AND WHY YOU'D LOOK
Every compiled model is written under target/compiled/, in a tree that mirrors your project exactly:
target/
compiled/
analytics/ # your project name
models/
staging/
stg_orders.sql # pure, runnable SELECT
stg_order_items.sql
marts/
orders.sql
manifest.json # the parsed project graphEach file is a pure, runnable select — no materialization wrapper — so you can paste it straight into a SQL console, put an EXPLAIN in front of it, or diff it in code review to see what a Jinja change really did. When a run later materializes the model, the same SQL wrapped in its DDL lands beside it under target/run/.
In a project that spans engines, this is also where dialect questions settle: each model compiles for the target it will actually run on, so what you read in target/compiled/ is what that engine receives — in its own dialect, not a lowest common denominator.
For an ad-hoc snippet, skip the file entirely — --inlinecompiles a string in your project's full context:
$ dvt compile --inline "select * from {{ ref('stg_orders') }} limit 10"
12:33:10 Found 34 models, 48 data tests, 6 sources, 519 macros
12:33:10
12:33:11 Concurrency: 4 threads (target='dev')
12:33:11
12:33:11 Compiled inline node is:
select * from "analytics"."public"."stg_orders" limit 10DVT PARSE — IS THE PROJECT HEALTHY?
dvt parse does the full project load — every yml validated, every ref and source resolved, every config checked — with zero warehouse connections. Healthy is quiet and exits 0:
$ dvt parse 12:29:40 Performance info: target/perf_info.json $ echo $? 0
A broken project prints the error — file, line, what went wrong — and exits 2:
$ dvt parse 12:30:07 Encountered an error: Compilation Error in model customer_ltv (models/marts/customer_ltv.sql) Model 'model.analytics.customer_ltv' (models/marts/customer_ltv.sql) depends on a node named 'stg_orderz' which was not found $ echo $? 2
Two exit codes, no third state: 0 means healthy, 2 means a handled parse failure — the exact contract you already script against, so a CI health check is one line. Alongside the verdict, parse writes target/manifest.json — the machine-readable picture of your whole project — and parse timing to target/perf_info.json.
THE GATE EVERY GRAPH COMMAND RUNS FIRST
You rarely need to type dvt parse yourself, because every graph-bearing verb — run, build, test, compile, seed, snapshot, ls, docs generate, retract — begins from a passing parse. A healthy project sails through silently: partial parsing means the re-check only re-reads what changed, and the gate keeps its own parse state under .dvt/parse (swept by dvt clean) so it never invalidates your compile artifacts. A broken project is refused before anything touches a warehouse, and the refusal is the parse error — same message, same exit 2 — never a second, vaguer complaint on top of it.
Parse is also where one DVT-specific mistake gets caught early: a model materialized ephemeral whose dependencies run federated. An ephemeral model is inlined as a CTE into whatever engine reads it — and that engine cannot see relations living on other connections — so the combination can never produce a correct query. Rather than fail confusingly at run time, parse refuses, names the model, and offers both remedies: pick a non-ephemeral materialization by hand, or run dvt config-checkup, whose ephemeral fixer lists the full legal set.
MANIFEST PROVENANCE — TRUST, VERIFIED
target/manifest.jsonis a contract: DVT, its app suite, and anything else you point at it all read that one file. So before trusting it, every DVT consumer asks one question — was this manifest written by the engine that's about to read it? A manifest left behind by a different engine version (someone ran a different binary in the project, or a stale artifact survived an upgrade) is refused with one loud, named error:
target/manifest.json was not written by the dbt-core that will read it (written by 1.9.4; this environment runs dbt-core 1.12.0) — run `dvt parse` to rebuild it.
The fix is always the same, and it's in the message: dvt parse rebuilds the manifest with the right engine, and everything downstream trusts it again.
IN PLAIN DBT
A manifest from the wrong version doesn't announce itself — it surfaces later, as a KeyError three stacks down in whatever tool read it, or as quietly wrong behavior in a --state comparison. DVT checks provenance at the door, refuses with the reason, and points at the one-command fix.
--WATCH — THE HEALTH CHECK THAT STAYS RUNNING
Editing a big project, you want the verdict continuously, not on demand. dvt parse --watch re-parses on every file change (debounced to 500ms) and prints one status line per cycle:
$ dvt parse --watch Watching /Users/you/analytics — re-parse on change (500ms debounce). Ctrl+C to stop. 14:02:11 ok 34 models [0.42s] 14:02:36 error models/marts/orders.sql:12 — Compilation Error in model orders (models/marts/orders.sql) [0.51s] 14:02:58 ok 34 models [0.38s]
Save a file, glance at the terminal, keep typing. The error line carries the file and line, so a slip is caught in the seconds after you make it — not minutes later when a run refuses to start.
REFERENCE — DVT COMPILE FLAGS
| FLAG | DEFAULT | WHAT IT DOES |
|---|---|---|
| -s, -m, --select | all | Which nodes to compile. Same selection syntax you already know: names, paths, graph operators (orders+, +orders), tags. |
| --exclude | none | Nodes to leave out of the selection. |
| --inline | none | Compile the SQL string given here — with full project context, refs and macros included — instead of a file. |
| --output | text | Output format for the compiled result: text or json. |
| --introspect / --no-introspect | introspect | Whether to run introspective queries while compiling. |
| --empty / --no-empty | off | Compile against zero rows — a schema-only dry run that is cheap on big tables. |
| -f, --full-refresh | off | Treat incremental models as full rebuilds, so you see the from-scratch SQL rather than the incremental increment. |
| -t, --target | profile default | Which target to compile for — dialect and relation names resolve against this output. |
| --vars | none | Supply project variables as a YAML string, e.g. '{my_variable: my_value}'. |
| --project-dir | . | Which directory to look in for the project. |
| --profiles-dir | standard resolution | Where to read profiles.yml from: this flag, then the project directory, then ~/.dbt. |
That's the compile-specific surface. Every engine-wide flag — selection, logging, state and deferral, threads — works too, with its exact spelling and meaning; dvt compile --help lists all of them.
REFERENCE — DVT PARSE FLAGS
| FLAG | DEFAULT | WHAT IT DOES |
|---|---|---|
| --project-dir | . | Which directory to parse. |
| --profiles-dir | standard resolution | Where to read profiles.yml from: this flag, then the project directory, then ~/.dbt. |
| -t, --target | profile default | Which target to load for the given profile. |
| --vars | none | Project variables as a YAML/JSON dict string. |
| --no-partial-parse | off | Ignore saved parse state and re-parse everything from scratch — the escape hatch when you suspect the cache. |
| --watch | off | Re-parse on file change (500ms debounce), one status line per cycle. Ctrl+C to stop. |
| -q, --quiet | off | Suppress all non-error output. Errors still print — quiet never hides a failure. |
Exit codes: 0 healthy, 2 on a handled parse failure — the same contract on every machine, which is what makes dvt parse a one-line CI gate.