Skip to main content
By default, sqb dbt build and sqb dbt run rebuild everything in the selection, exactly like dbt. With --changes-only, SQLBuild instead plans which dbt models actually need to run: models whose SQL has not changed and whose inputs are current are pruned from the dbt command, so a second build skips them entirely. This works on a plain dbt project, with no SQLBuild models and no changes to your dbt files. SQLBuild reads the dbt manifest, compares it against state it stores in your warehouse, and decides per model whether dbt needs to run it. The fingerprints below are recorded on every build, so change-aware pruning is ready the moment you pass --changes-only.

How dbt models are tracked

SQLBuild stores a fingerprint for every dbt model it builds, in a _sqlbuild_fingerprints table in your target schema. Each fingerprint is keyed with node_type: "dbt" and the model’s dbt unique_id, and records the model’s version identity. The version identity comes from dbt’s own checksum for the model node in the manifest. On each plan, SQLBuild compares the checksum in the current manifest against the fingerprint it last stored: After dbt runs, SQLBuild writes an updated fingerprint for each dbt model that executed, so the next plan sees them as current.

Enabling change-aware pruning with --changes-only

The table above only takes effect under --changes-only. Without it, every selected dbt model is planned and run regardless of whether its fingerprint matches; with it, current models are pruned from the dbt command.
Use the default full build when you want to rebuild despite a clean plan, for example after an out-of-band change to the warehouse that SQLBuild’s fingerprints cannot see. Fingerprints are recorded on every build regardless of the flag, so switching between full and change-aware runs never loses state. It is distinct from --full-refresh: --changes-only decides whether a model runs, while --full-refresh decides how (a full rebuild rather than an incremental run). This mirrors native sqb build --changes-only. See Flags for how flags route across dbt and SQLBuild.

What SQLBuild creates in your warehouse

All state is append-only and lives in your target schema, in the same warehouse dbt already uses. There is no external state store, no manifest comparison server, and no requirement to log in anywhere. SQLBuild creates two small tables: These are the only objects SQLBuild adds. Your dbt models, schemas, and tables are otherwise untouched. The janitor can prune old rows, keeping only the latest per identity.

Cascade propagation

Change detection runs over the combined dbt and SQLBuild graph, so a change in one dbt model correctly forces the work that depends on it, in both directions:
  • Upstream changed. A dbt model that is otherwise current is rerun when any of its upstream dbt models is running. The plan reason is upstream_changed.
  • Downstream into SQLBuild. When a dbt model runs, SQLBuild marks the SQLBuild models that read from it (through __dbt_ref) as stale, so they rebuild against the new data.
This means a single changed staging model propagates a rebuild signal through the rest of the dbt DAG and across the boundary into your SQLBuild models, without you having to select them by hand.

Source freshness

SQLBuild translates the sources declared in the dbt manifest into its own source freshness model and observes them as part of planning. Observations are stored in the _sqlbuild_source_freshness table in your target schema.
  • Source changed. A current dbt model whose upstream source has new data is promoted to run, with reason source_freshness_changed.
  • Source stale past its age policy. If an upstream source is older than its configured age tolerance, the dbt models downstream of it are blocked rather than built on stale inputs, with reason source_freshness_error. SQLBuild models downstream of a blocked dbt model are blocked too.
Source freshness records are persisted after a successful build, so the next plan can detect new arrivals. See Sources for freshness strategies and age policies.

Pruning and steady state

Under --changes-only, only the dbt models classified as run are passed to the underlying dbt build/dbt run command. Current models are removed from the dbt selection, dbt tests and seeds for pruned models are dropped from the command, and dbt never sees the models it does not need to rebuild. When every dbt model in the selection is current, SQLBuild does not invoke dbt at all. The dbt section of the plan reports the skip and lists the current models:
With --changes-only, a first build runs the changed models; a second build with no changes skips the entire dbt run and only re-evaluates SQLBuild models, which are themselves current. This is the same opt-in skip-unchanged behavior SQLBuild applies to its own models, extended over your existing dbt project.

Replay on change

When a changed incremental dbt model is rerun, replay_on_change controls whether it runs incrementally or rebuilds in full. It is a single project-wide policy in the [dbt] config block, applied to every changed model in the run (not per model):
Unlike SQLBuild’s own per-model replay_on_change, the dbt setting is all-or-nothing across the run and does not support bounded windows. A bounded-<duration> value is rejected for dbt.

What is never touched

  • dbt source files are never modified. SQLBuild reads the manifest and drives the dbt CLI; it does not patch, rewrite, or revert your dbt project.
  • dbt internals are never monkey-patched. Selection and execution go through the documented dbt CLI surface (dbt ls, dbt compile, dbt build/dbt run).
  • State lives in your warehouse, not in a separate database or service.