feat(review): consumers outside dbt, engine-aware integer division, opt-in contract and column-list checks
Draft
RA
2d ago+955+955+012345678901234567890123456789-22-22-01234567890123456789
Issue for this PR
Closes #
Stacked on #1723 (← #1722 ← #1721 ← #1720 ← #1719); merge in that order. Draft: the new rules are not yet measured on a public-repo benchmark.
Type of change
- Bug fix
- New feature
- Refactor / code improvement
- Documentation
What does this PR do?
Deterministic checks that work on any dbt project, from the manifest and the diff, plus three fixes found by an end-to-end run on a real dbt project.
New checks
- Engine-aware integer division.
adapter-capabilities.tsis a per-adapter table, with each entry citing the engine's docs. Rules whose correctness depends on the engine read the table instead of assuming one engine.int / int(andcount(…) / count(…)) is a correctness finding on Postgres, Redshift, Trino/Athena and SQL Server.- On Snowflake, BigQuery, Databricks and DuckDB it is only portability advice, shown for multi-adapter packages.
- Text inside string literals is ignored.
- Consumers outside the DAG. A column removed from a model with
exposuresor an enforced contract produces a finding that names those consumers (newmodelConsumersrunner method). - Contract vs
SELECT, opt-in (checkContractColumns). A new column missing from an enforced contract is reported. It's off by default becausedbt buildalready fails on it, and it never blocks. - Explicit column lists outside dbt, opt-in (
consumerColumnLists). For export or reverse-ETL scripts that copy a model's columns. - Unit tests whose
giveninputs are allrows: []. The model's logic over its inputs is never exercised.
Fixes found by the end-to-end run
- Column lineage returned nothing against a manifest-only schema. The manifest only types documented models, so lineage dropped columns from undocumented upstream tables. That silently disabled column-breakage checks on every project without
catalog.json. It now retries without the schema. - False
validate: Table '<x>' not foundwarnings. A manifest-only schema produced this engine warning for undocumented models; 50 new models meant 50 warnings and a blocked gate. It's still reported when a completecatalog.jsonschema is loaded. - Unmapped adapters guessed as known. An adapter the review doesn't map (a community adapter) no longer counts as a known dialect, so generic advice isn't suppressed on a guess.
How did you verify your code works?
- New tests:
test/altimate/review-contracts-dialect.test.ts: engine table per dialect, string literals, exposures and contracts, the opt-ins, unit-test fixtures, and dialect packs.- Runner tests for the lineage fallback,
modelConsumersand the partial-schema filter, using the engine's real response shape.
- Suites: 495 tests across the review files pass,
bun typecheckis clean, and the real-world corpus is unchanged (15/15 caught, 0/5 false positives). - Local end-to-end run. A real dbt 1.10 DuckDB project, 11 scenario branches, the released 0.12.10 binary vs this stack. Removing a column under an exposure and a contract now names
revenue_dashboardand the contract; 0.12.10 reports nothing for it. - Replay of 7 real PRs from a private dbt monorepo, CI-equivalent (
dbt parseonly, base from the GitHub event). Off-diff findings went from 219–427 per PR to 0 (e.g. 431 findings → 1 on a 2-file PR), and no earlier catch was lost. That's mostly from the earlier PRs in the stack; this PR's fixes keep the count at 0.
Cost side effects
None. No model calls or warehouse queries. Column lineage may run a second time, without the schema, when the first call returns nothing (in-process, bounded by the number of changed models).
Screenshots / recordings
N/A (CLI).
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
- I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"
🤖 Generated with Claude Code
Summary by cubic
Adds deterministic review checks that run on any dbt project from the manifest and the diff, plus three fixes from an end-to-end run on a real dbt project.
New checks
int / int(andcount(…) / count(…)) is a correctness finding on Postgres, Redshift, Trino/Athena and SQL Server; on Snowflake, BigQuery, Databricks and DuckDB it is portability advice for multi-adapter packages only, and text inside string literals is ignored.- Removing a column from a model with
exposuresor an enforced contract now names those consumers (newmodelConsumersrunner method). checkContractColumns(opt-in, never blocks): a new column missing from an enforced contract is reported;dbt buildalready fails on it, so it's off by default.consumerColumnLists(opt-in): named files outside dbt that copy a model's columns must follow added and removed columns.- Unit tests whose
giveninputs are allrows: []are reported, since they never exercise the model's logic.
Fixes
- Column lineage retries without the schema when a manifest-only schema returns nothing, restoring column-breakage checks on projects without
catalog.json. - A manifest-only schema no longer emits false
Table '<x>' not foundvalidation warnings; these are kept when a completecatalog.jsonschema is loaded. - An unmapped adapter (community adapter) no longer counts as a known dialect, so generic advice isn't suppressed on a guess.