Skip to main content
Outcome: a reviewed project change, built on dev, with evidence that its rows match the request. You need a connected warehouse and a dbt project with a dev target. Start with Connect your data if either is missing.

1. State what the model should mean

Tell the agent the grain and population you want, not just the model name:
The agent should profile the source relations and verify the joins before writing the SQL. It records the model’s meaning in a declaration: grain, population, column roles, units, and any assumptions it had to make.
After connecting and exploring, use dex transform init analytics to create a project named analytics. This command creates the project files and records the connection in configuration, returning the changes as diffs. It refuses to overwrite an existing project. Existing projects are discovered from the repository; dbt_project_dir can select one when the repository holds several.The generated dev profile still needs a valid target. For DuckDB, a new dev file that reads sources must be populated before the build can succeed. Follow the DuckDB dev-target guidance.

2. Review the plan

The agent submits the edits and declarations together:
edits.json is the agent’s prepared change, not a file Dex creates from this command alone. The result includes a plan ID, diffs, warnings, and declaration feedback. Resolve missing intent and contradictions, then plan again before applying. For an existing model, also review row attribution: a refactor should preserve its results. When the plan rewrites a model that already exists, it carries data.refactor_comparison_offer. If the change is meant to keep that model’s results (a refactor, an extracted intermediate, a cleanup), compare before applying, because applying discards the baseline:
equivalent holds for the relations named in input_scope at compared_at. different means the rewrite changed the rows or the schema: fix it, or state an intended rename with --map or type change with --accept-type. not_checked is not a pass: its reason_code names what to repair, such as a parent whose last build failed. The offer’s static_attribution says when this comparison is the only evidence a refactor kept its results, for example when the model’s SQL calls a macro. A change meant to alter results needs no comparison. The verdict is recorded on the plan, and transform apply reports it. When the plan adds a cast or a lower, upper or trim to an identifier, such as a try_cast(product_id as integer) is not null that makes a broken join compile, it reports the conversion under data.identifier_conversions, with counts and their denominator. The counts show how many identifiers do not convert, how many collapse into one key (7 and 007), and how many the prior model held that the authored one does not, even when the row count is unchanged. A text identifier may be a second legitimate scheme rather than bad data, and Dex does not decide which. An undeclared conversion needs a decision: keep the other scheme (compare as text, normalize, use a bridge or a namespace), or record the exclusion as an assumption naming its column. On DuckDB the counts are free. Elsewhere they need --attribute-rows and are priced with row attribution. Read the command contract when you need to construct the edits payload yourself.

3. Apply the reviewed plan

Replace PLAN_ID with the ID returned by planning:
This writes project files. If a file changed after planning, re-plan against its current content so the diff you review is the one you apply.

4. Build and verify on dev

On a metered connector, review the estimate and follow the cost confirmation flow. A successful dbt build is only part of the result. Read data.completion first. It is the first key of the payload and is held to 4 KiB, so it survives a cut-off read even when hundreds of nodes follow it. With --for-plan, its deliverables say which of the nodes the plan changed were built, failed, skipped, missing or stale, and blocked names what stopped each one. A skipped report is not done, even if dev still holds a table from an earlier run. assurance.build_only lists what built with nothing checking its rows. Without --for-plan, coverage is unknown. data.verification.scope lists the models this run built successfully; scope_not_built names the other reported models. In the evidence block, coverage.covered includes failed and skipped nodes present in run results. Use coverage.built and coverage.not_built to distinguish successful execution. When something upstream stopped the build, data.prerequisites names the failed node’s owner (your project or an installed package), whether the plan could have caused it, and the repairs dbt supports for it. An edit under dbt_packages/ is not one of them, because the next dbt deps replaces it. Add --cold-deps to reinstall packages into an isolated copy and build from there, which proves the repair survives and lists anything your installed tree held that a reinstall does not reproduce. It may reach the network, so a normal build never does it. Each prerequisite’s previous_run gives its earlier status and run context when available. This history survives pricing and confirmation retries without an extra build. It may describe a different selection or source tree, so it does not establish whether the plan caused the failure or change plan_relation. The command contract gives every field. Then read data.outcome (only validated means every required node ran and passed) and data.verification.ran, its findings, and suppressed checks. Missing parents, multiplied rows, or an entirely NULL column need investigation before the change is finished. data.verification.column_contract says which built models had their columns compared with schema.yml, which declare none, and which could not be checked and why.
Use --full-refresh when the requested change requires rebuilding the selected incremental relation, rather than processing its next batch:
A full refresh drops and rebuilds selected incremental relations on dev. It can scan substantially more data. Review the full-rebuild estimate before approving it; this flag is not a routine fix for a failed build.A model configured with full_refresh: false is not rebuilt: check data.full_refresh.not_refreshed before treating a selected relation as rebuilt. --verify does not run its row-population checks on incremental models, so a green verification does not show the rebuilt history is right. Check the rebuilt rows yourself before accepting the change, for example with dex explore profile <relation> --target dev --refresh.
By default, Dex writes dbt artifacts under target/dex/<dbt-version>/ rather than mixing them with another runtime’s artifacts. The build result’s data.artifact_directory names the directory used. Follow that path when investigating a failure instead of assuming the project’s previous manifest describes this run. See the build command contract.

Keep it correct

Once the change is verified, establish a maintenance baseline. For refactoring tools, test scaffolding, mutation testing, and every build flag, see dex transform.