feat(review): consumers outside dbt, engine-aware integer division, opt-in contract and column-list checks

Draft
RA
2d ago
+955-22

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.ts is 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 (and count(…) / 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 exposures or an enforced contract produces a finding that names those consumers (new modelConsumers runner method).
  • Contract vs SELECT, opt-in (checkContractColumns). A new column missing from an enforced contract is reported. It's off by default because dbt build already 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 given inputs are all rows: []. 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 found warnings. 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 complete catalog.json schema 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, modelConsumers and the partial-schema filter, using the engine's real response shape.
  • Suites: 495 tests across the review files pass, bun typecheck is 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_dashboard and the contract; 0.12.10 reports nothing for it.
  • Replay of 7 real PRs from a private dbt monorepo, CI-equivalent (dbt parse only, 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 (and count(…) / 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 exposures or an enforced contract now names those consumers (new modelConsumers runner method).
  • checkContractColumns (opt-in, never blocks): a new column missing from an enforced contract is reported; dbt build already 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 given inputs are all rows: [] 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 found validation warnings; these are kept when a complete catalog.json schema is loaded.
  • An unmapped adapter (community adapter) no longer counts as a known dialect, so generic advice isn't suppressed on a guess.