doctor
dbt-forge doctor scans an existing dbt project and validates it against common best
practices. Use it to catch issues before they cause production failures.
Command
Section titled “Command”dbt-forge doctor [--check NAME] [--fix] [--ci] [--format FORMAT]What it does
Section titled “What it does”The command runs 11 health checks on the current dbt project and displays a summary table with pass/fail status and actionable remediation suggestions for each check.
All checks are file-based and do not require a warehouse connection.
Options
Section titled “Options”--check, -c
Section titled “--check, -c”Run a specific check only instead of all checks.
dbt-forge doctor --check naming-conventionsdbt-forge doctor --check test-coverageValid check names: naming-conventions, schema-coverage, test-coverage,
hardcoded-refs, packages-pinned, source-freshness, orphaned-yml,
sqlfluff-config, gitignore, disabled-models, contract-enforcement.
Auto-fix issues where possible. Currently supports two auto-fixes:
- Schema coverage — generates missing schema YAML stubs for undocumented models.
For each model without a YAML entry,
--fixcreates a file atmodels/<layer>/_<model_name>__models.ymlwith a model name and description placeholder. - Contract enforcement — injects
config: {contract: {enforced: true}}into mart model YAML entries that are missing contract configuration.
dbt-forge doctor --fixExample output:
Created models/marts/finance/_fct_revenue__models.yml Created models/staging/stripe/_stg_stripe__orders__models.yml Added contract enforcement to orders in _orders__models.yml
9 passed, 0 failedOther failing checks include specific remediation hints but require manual intervention.
Non-interactive mode for CI pipelines. Exits with code 1 if any check fails, code 0 if all checks pass.
dbt-forge doctor --ci--format
Section titled “--format”Output format: table (default) or json. JSON output includes pass/fail counts
and full results for each check, suitable for CI/CD integrations and dashboards.
dbt-forge doctor --format jsonExample JSON output:
{ "passed": false, "pass_count": 9, "fail_count": 2, "results": [ { "name": "naming-conventions", "passed": true, "message": "All models follow naming conventions.", "fix_hint": null } ]}Checks
Section titled “Checks”| Check | What it validates |
|---|---|
naming-conventions | Staging models prefixed stg_, intermediates prefixed int_ |
schema-coverage | Every SQL model has a corresponding YAML entry |
test-coverage | Every model has at least one test defined |
hardcoded-refs | No hardcoded database.schema.table patterns in model SQL |
packages-pinned | All entries in packages.yml have version ranges |
source-freshness | Sources have freshness config defined |
orphaned-yml | No YAML model entries referencing non-existent SQL files |
sqlfluff-config | .sqlfluff config file exists |
gitignore | .gitignore includes target/, dbt_packages/, logs/ |
disabled-models | No models with enabled: false (tech debt indicator) |
contract-enforcement | Mart models have contract: { enforced: true } config |
Check details
Section titled “Check details”naming-conventions — Models in models/staging/ must start with stg_. Models
in models/intermediate/ must start with int_. Models in other directories are
not checked.
schema-coverage — Every .sql file in models/ should have a matching entry in
a models: section of a YAML file. Intermediate models (int_ prefix) are excluded
from this check since they are typically ephemeral and do not need schema documentation.
test-coverage — Every model should have at least one test (column-level or data test)
defined in a YAML file. The check looks for data_tests: or tests: entries under
model columns.
hardcoded-refs — Scans SQL files for patterns like database.schema.table that
bypass ref() and source(). These break cross-environment portability.
packages-pinned — Checks that all packages in packages.yml use version ranges
(version:) rather than unpinned git references.
source-freshness — Every source in *sources*.yml files should define a
freshness: block with warn_after and/or error_after.
orphaned-yml — YAML model entries that reference a SQL file that does not exist. These accumulate when models are renamed or deleted without updating the YAML.
disabled-models — Models with config(enabled=false) or enabled: false in YAML.
These are tech debt — either remove the model or re-enable it.
contract-enforcement — Mart-layer models (in models/marts/) should have
contract: { enforced: true } in their YAML config. Data contracts ensure that
downstream consumers can rely on a stable schema. Use --fix to auto-inject the
contract config, or run dbt-forge contracts generate for full contract generation
with column types.
Output
Section titled “Output”The command displays a Rich table with pass/fail status per check:
dbt-forge doctor Status Check Details PASS naming-conventions All models follow naming conventions. PASS schema-coverage All models have YAML documentation. FAIL test-coverage 2 model(s) have no tests: models/marts/finance/fct_revenue.sql Use dbt-forge add test <model> to generate test stubs. PASS packages-pinned All packages have version ranges.
9 passed, 2 failedEach failing check includes the affected file paths and a specific remediation
suggestion (e.g., the exact dbt-forge command to run to fix the issue).
CI integration
Section titled “CI integration”Use --ci in CI pipelines to fail the build when checks don’t pass:
# GitHub Actions example- name: dbt-forge doctor run: dbt-forge doctor --ci# GitLab CI exampledoctor: script: - dbt-forge doctor --ciBehavior and limits
Section titled “Behavior and limits”- Must run from inside an existing dbt project (walks up from the current directory to find
dbt_project.yml). - Intermediate models (
int_prefix) are excluded from schema coverage checks since they are typically ephemeral. - The
--fixflag generates missing schema stubs and injects contract enforcement config. Other fixes require manual intervention. - Checks are fast and do not require a warehouse connection — all data comes from local files.
- When using
--check, only the specified check runs. The exit code in--cimode reflects only that check.