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, 2

Compile 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, 2

Nothing 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 graph

Each 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 10

DVT 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

FLAGDEFAULTWHAT IT DOES
-s, -m, --selectallWhich nodes to compile. Same selection syntax you already know: names, paths, graph operators (orders+, +orders), tags.
--excludenoneNodes to leave out of the selection.
--inlinenoneCompile the SQL string given here — with full project context, refs and macros included — instead of a file.
--outputtextOutput format for the compiled result: text or json.
--introspect / --no-introspectintrospectWhether to run introspective queries while compiling.
--empty / --no-emptyoffCompile against zero rows — a schema-only dry run that is cheap on big tables.
-f, --full-refreshoffTreat incremental models as full rebuilds, so you see the from-scratch SQL rather than the incremental increment.
-t, --targetprofile defaultWhich target to compile for — dialect and relation names resolve against this output.
--varsnoneSupply project variables as a YAML string, e.g. '{my_variable: my_value}'.
--project-dir.Which directory to look in for the project.
--profiles-dirstandard resolutionWhere 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

FLAGDEFAULTWHAT IT DOES
--project-dir.Which directory to parse.
--profiles-dirstandard resolutionWhere to read profiles.yml from: this flag, then the project directory, then ~/.dbt.
-t, --targetprofile defaultWhich target to load for the given profile.
--varsnoneProject variables as a YAML/JSON dict string.
--no-partial-parseoffIgnore saved parse state and re-parse everything from scratch — the escape hatch when you suspect the cache.
--watchoffRe-parse on file change (500ms debounce), one status line per cycle. Ctrl+C to stop.
-q, --quietoffSuppress 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.