A curated collection of Agent Skills for working with dbt, to help AI agents understand and execute dbt workflows more effectively.
91
91%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Core principle: In a mesh project, upstream data comes through ref(), not source(). Every cross-project reference requires the project name. When in doubt, read dependencies.yml first.
stg_ models)dependencies.ymlDo NOT use for:
using-dbt-for-analytics-engineering skill)adding-dbt-unit-test skill)building-dbt-semantic-layer skill)Before writing or modifying any SQL in a project that uses dbt Mesh, follow these steps:
dependencies.ymlThis file at the project root tells you which upstream projects exist:
# dependencies.yml
projects:
- name: core_platform
- name: marketing_platformIf this file has a projects: key, you are in a multi-project mesh setup. Every model you reference from those upstream projects must use cross-project ref().
In a mesh setup, upstream project models replace what would alternatively be sources:
| Alternative | Mesh multi-project |
|---|---|
{{ source('stripe', 'payments') }} | {{ ref('core_platform', 'stg_payments') }} |
| Data comes from raw database tables | Data comes from another dbt project's public models |
Defined in sources.yml | Declared in dependencies.yml |
The upstream project has already staged and transformed the raw data. Your project builds on top of their public models, not their raw sources.
When multiple upstream projects have models with the same name (e.g. stg_customers in both core_platform and marketing_platform), you must use the two-argument ref():
-- Correct: explicit project name, no ambiguity
select * from {{ ref('core_platform', 'stg_customers') }}
select * from {{ ref('marketing_platform', 'stg_customers') }}
-- WRONG: dbt cannot determine which project's stg_customers you mean
select * from {{ ref('stg_customers') }}Before writing new SQL:
ref() calls to see which upstream projects and models are already in useaccess: public models — only these are referenceable cross-projectref() must exactly match the name field in the upstream project's dbt_project.yml (case-sensitive)| Upstream model access | Can you ref() it cross-project? |
|---|---|
access: public | Yes |
access: protected (default) | No — only within the same project |
access: private | No — only within the same group |
If you need a model that isn't public, coordinate with the upstream team to widen its access.
Cross-project ref() and the projects: key in dependencies.yml are only available on dbt Cloud Enterprise or Enterprise+ plans. Before setting up any cross-project collaboration, verify plan eligibility:
dependencies.yml already has a projects: key and the project is actively using cross-project refs — Enterprise is already in place. Proceed.projects: to dependencies.yml or writing new two-argument ref() calls.If the user cannot confirm the plan level, or confirms they are on a plan below Enterprise, do not set up cross-project refs. Explain that this feature requires upgrading to Enterprise or Enterprise+ and suggest they use the intra-project governance features (groups, access modifiers, contracts) instead.
ref() Syntax-- Reference an upstream model (latest version)
select * from {{ ref('upstream_project', 'model_name') }}
-- Reference a specific version
select * from {{ ref('upstream_project', 'model_name', v=2) }}For full cross-project setup details (dependencies.yml, prerequisites, orchestration), see references/cross-project-collaboration.md.
dbt Mesh includes four governance features. These work independently and can be adopted incrementally:
| Feature | Purpose | Key Config | Reference |
|---|---|---|---|
| Model Contracts | Guarantee column names, types, and constraints at build time | contract: {enforced: true} | references/model-contracts.md |
| Groups | Organize models by team/domain ownership | group: finance | references/groups-and-access.md |
| Access Modifiers | Control which models can ref yours | access: public / protected / private | references/groups-and-access.md |
| Model Versions | Manage breaking changes with migration windows | versions: with latest_version: | references/model-versions.md |
In model property YAML files, access, group, and contract are configs and must always be nested under the config: key — never placed as top-level model properties. Placing them at the top level may appear to work in dbt Core but causes parse errors in dbt's Fusion engine.
# ✅ CORRECT — all governance configs under `config:`
models:
- name: fct_orders
config:
group: finance
access: public
contract:
enforced: true
columns:
- name: order_id
data_type: int
# ❌ WRONG — governance configs as top-level properties (breaks Fusion)
models:
- name: fct_orders
access: public # WRONG — not under config:
group: finance # WRONG — not under config:
contract: # WRONG — not under config:
enforced: true
columns:
- name: order_id
data_type: intThis applies to property YAML files only. In dbt_project.yml, use the + prefix for directory-level assignment (e.g. +group: finance, +access: private). In SQL files, use {{ config(access='public', group='finance') }}.
1. Groups & Access → 2. Contracts → 3. Versions → 4. Cross-Project Refs
(organize teams) (lock shapes) (manage changes) (split projects)| Contracts | Data Tests | |
|---|---|---|
| When | Build-time (pre-flight) | Post-build (post-flight) |
| What | Column names, data types, constraints | Data quality, business rules |
| Failure | Model does not materialize | Model exists but test fails |
| Use for | Shape guarantees for downstream consumers | Content validation and anomaly detection |
Contracts are enforced before tests run. If a contract fails, the model is not built, and no tests execute.
Use a contract when:
access: public (especially if referenced cross-project)Do NOT add a contract when:
stg_*) — these are internal implementation details, not consumer-facing APIspivot(), unpivot(), or dynamically generate columns are poor candidates because the column list isn't fixed and the contract will break whenever the dynamic values changeIf the user asks for a contract on a model that matches the "do NOT add" criteria above, advise against it and explain why. Do not simply comply — the user may not realize the contract is inappropriate. Suggest alternatives (e.g., data tests for staging models, waiting for schema stability, or switching materialization for ephemeral models).
Version a model when:
Do NOT version a model:
Is it referenced cross-project?
└─ Yes → public (with contract recommended)
└─ No
Is it referenced outside its group?
└─ Yes → protected (default)
└─ No
Is it internal to a small team?
└─ Yes → private
└─ No → protected (default)Best practice: Default new models to private and widen access only when needed. The default protected is permissive — be intentional.
| Mistake | Why It's Wrong | Fix |
|---|---|---|
Using single-argument ref() in multi-project setups | Ambiguous — dbt may not resolve to the intended project | Always use ref('project_name', 'model_name') for cross-project refs |
Using source() for upstream project data | In mesh, upstream data comes through public models, not raw sources | Use ref('upstream_project', 'model_name') instead |
Not reading dependencies.yml first | You won't know which upstream projects exist or what they're called | Always read dependencies.yml before writing cross-project SQL |
Making all models public | Exposes internal implementation details cross-project | Only mark models public that are intentional APIs for other teams |
| Skipping contracts on public models | Downstream consumers can break silently when schema changes | Always enforce contracts on access: public models |
| Versioning for non-breaking changes | Creates unnecessary maintenance burden and warehouse cost | Only version for breaking changes (column removal, type change, rename) |
Forgetting dependencies.yml | Cross-project refs fail without declaring the upstream project | Add upstream project to dependencies.yml before using two-argument ref() |
| Referencing non-public models cross-project | Only public models are available to other projects | Set access: public on models intended for cross-project consumption |
Placing access, group, or contract as top-level model properties in YAML | Breaks Fusion engine parsing; top-level placement is not valid config | Always nest under config: — e.g. config: { access: public } |
| Adding contracts to staging models | Staging models are internal — contracts add friction without protecting external consumers | Advise against it; suggest data tests instead |
| Adding contracts to models with dynamic/pivot columns | Column list changes with data, breaking the contract | Advise against it; explain why the column list isn't fixed |
| Adding contracts without establishing external consumers | Contracts protect a schema boundary — no consumers means no boundary to protect | Ask who depends on this model before adding a contract |
Making a model private that is already referenced outside its group | Existing refs break with a DbtReferenceError | Widen access to protected or refactor callers into the same group first |
| Setting up cross-project refs without confirming dbt Cloud Enterprise | Cross-project ref() is unavailable on lower plan tiers | Confirm the plan level before adding projects: to dependencies.yml or writing two-argument ref() calls |
Adding dependencies.yml without a successful upstream production job | dbt Cloud resolves cross-project refs via the upstream manifest.json — no job run means no manifest | Run at least one successful production deployment in the upstream project first |
evals
scenarios
dbt-docs-arguments
dbt-docs-unit-test-fixtures
dbt-job-failure
dbt-unit-test-format-choice
example-yaml-error
fusion-migration-triage-basic
fusion-migration-triage-blocked
fusion-triage-cat-a-static-analysis
fusion-triage-cat-b-dict-meta-get
fusion-triage-cat-b-unexpected-config
fusion-triage-cat-b-unused-schema
fusion-triage-cat-b-yaml-syntax
fusion-triage-cat-c-hardcoded-fqn
src
tests
scripts
skills
dbt
skills
adding-dbt-unit-test
references
answering-natural-language-questions-with-dbt
building-dbt-semantic-layer
configuring-dbt-mcp-server
fetching-dbt-docs
scripts
running-dbt-commands
troubleshooting-dbt-job-errors
references
using-dbt-for-analytics-engineering
working-with-dbt-mesh
dbt-extras
skills
creating-mermaid-dbt-dag
dbt-migration
skills
migrating-dbt-core-to-fusion
migrating-dbt-project-across-platforms