Skip to content

feat(review): [R20 S1] grain-key not_null completeness detector (+11 findings on 5-PR corpus) - #1029

Merged
sahrizvi merged 5 commits into
mainfrom
feat/review-r20-s1-grain-key-not-null
Jul 23, 2026
Merged

feat(review): [R20 S1] grain-key not_null completeness detector (+11 findings on 5-PR corpus)#1029
sahrizvi merged 5 commits into
mainfrom
feat/review-r20-s1-grain-key-not-null

Conversation

@sahrizvi

@sahrizvi sahrizvi commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

New detector in detectSchemaYmlPatterns: for every column named in a dbt_utils.unique_combination_of_columns test's combination_of_columns, require not_null coverage on the same model. Otherwise a NULL grain-key value silently passes the uniqueness test — the guardrail is toothless.

Design

Coverage sources (from code review feedback):

  • constraints: [{type: not_null}] — counted ONLY when contract.enforced == true. On views / non-contracted models, constraints: is documentation-only, not enforced by the DB.
  • Column-level data_tests: [not_null] / tests: [not_null] — always counted; dbt's test runner enforces regardless of contract state.

Recommendation flips between constraints: (contracted model) and data_tests: (view / non-contracted).

Supported YAML shapes:

  • Exact-match on unique_combination_of_columns and dbt_utils.unique_combination_of_columns (no endsWith — avoids false positives on not_unique_combination_of_columns).
  • Both pre-1.9 flat args + dbt 1.9+ arguments: nesting.
  • Both nested config.contract: and top-level contract: for enforcement flag.

Precision guards:

  • Column-name comparison is case-folded (Snowflake identifiers fold uppercase).
  • Skipped on status === 'deleted' files.
  • Only runs on files in the PR diff.

Validation

Deterministic detector run against a set of merged dbt PRs used for offline validation:

Variant Baseline With detector Delta
A1 (--pure --no-ai=true) 118 findings 129 findings +11 grain-key gaps
A2 (--pure, LLM lane on) 143 findings 150 findings +7 net (LLM run-to-run noise partially masks the deterministic gain)

Each new finding is a real grain-column gap and cites the exact test + column + model. A subset of new findings correlates to human review comments on the same models (grain-integrity gaps reviewers had flagged specifically — carrier-identity columns and mart-grain temporal columns).

Tests

  • 71 pass / 0 fail in review-dbt-patterns.test.ts (58 pre-existing + 13 new tests)
  • 3797 pass / 640 skip / 0 fail in the full altimate review suite (132 files)

New test fixtures (all green):

  • Basic contracted-mart grain gap
  • View / non-contracted → data_tests: recommendation
  • Coverage via tests: (dbt <1.8 alias) counts
  • SCD2-style temporal grain (change_time) missing not_null flagged
  • No false positive when every grain col has coverage
  • Bare unique_combination_of_columns (no dbt_utils. prefix)
  • No fire when there's no grain test
  • No fire on deleted schema.yml
  • dbt 1.9+ arguments: nesting recognised
  • Non-contracted constraints: [{type: not_null}] doesn't count as coverage
  • Top-level contract: {enforced: true} (not just nested under config:) recognised
  • Exact test-name match (rejects not_unique_combination_of_columns etc.)
  • Adapter case-folding (WORKSPACE_IDworkspace_id)

Independent code review

Reviewed independently before push. All 3 highs addressed: exact test-name match, column-name case folding, constraint coverage gated on contract enforcement.

Depends on

Stacks on top of #1028 (feat/review-r20-s4-triage-promotion), which stacks on #1027 (feat/review-r18-observability-recall).

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d440c49-4c7c-4a64-a6f5-f19ba26b381a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/review-r20-s1-grain-key-not-null

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/review/dbt-patterns.ts">

<violation number="1" location="packages/opencode/src/altimate/review/dbt-patterns.ts:1000">
P1: A disabled column `not_null` test is treated as coverage, so a NULL grain key can still bypass the uniqueness guard without a finding. Inspect the test entry’s `config.enabled` before adding it to `coveredByTest`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const tests = c[testsKey]
if (!Array.isArray(tests)) continue
for (const t of tests) {
if (testName(t) === "not_null") coveredByTest.add(key)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A disabled column not_null test is treated as coverage, so a NULL grain key can still bypass the uniqueness guard without a finding. Inspect the test entry’s config.enabled before adding it to coveredByTest.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/review/dbt-patterns.ts, line 1000:

<comment>A disabled column `not_null` test is treated as coverage, so a NULL grain key can still bypass the uniqueness guard without a finding. Inspect the test entry’s `config.enabled` before adding it to `coveredByTest`.</comment>

<file context>
@@ -896,6 +896,162 @@ function extractTestOccurrences(doc: unknown): Set<string> {
+          const tests = c[testsKey]
+          if (!Array.isArray(tests)) continue
+          for (const t of tests) {
+            if (testName(t) === "not_null") coveredByTest.add(key)
+          }
+        }
</file context>

Comment thread packages/opencode/src/altimate/review/dbt-patterns.ts Outdated
*
* Returns one gap per uncovered column per grain declaration.
*/
export interface GrainKeyGap {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: GrainKeyGap is exported but never imported outside this file.

extractGrainKeyGaps (the only producer/consumer) is itself not exported, so this type has no external consumer. Dropping export keeps the public surface of dbt-patterns.ts minimal — the test file imports detectSchemaYmlPatterns and DEFAULT_RUBRIC, not this type.

Suggested change
export interface GrainKeyGap {
interface GrainKeyGap {

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

// since dbt's test runner enforces it independent of contract state.
const coveredByConstraint = new Set<string>()
const coveredByTest = new Set<string>()
if (Array.isArray(mm.columns)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Model-level constraints: declarations are not counted as coverage (false-positive risk).

dbt allows declaring not_null at the model level with a column: field (e.g. constraints: [{type: not_null, column: id}]) in addition to column-level c.constraints. A contracted model that uses the model-level form for a grain column would still surface a false-positive gap here, because only column-level constraints are walked into coveredByConstraint. Consider also iterating mm.constraints and adding entries whose column: matches a grain key (case-folded) to coveredByConstraint.

@kilo-code-bot

kilo-code-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1396 Change-scoping keys only on the grain declaration; a coverage-only edit (e.g. dropping a constraints: [{type: not_null}] from a grain column) slips past both the grain detector and the removals detector.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1030 arguments:-nested combination_of_columns extraction is duplicated in extractGrainDeclarations and extractGrainKeyGaps; extract a shared helper.
Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts - 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts

Fix these issues in Kilo Cloud

Previous Review Summaries (6 snapshots, latest commit b8aa713)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit b8aa713)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1396 Change-scoping keys only on the grain declaration; a coverage-only edit (e.g. dropping a constraints: [{type: not_null}] from a grain column) slips past both the grain detector and the removals detector.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1030 arguments:-nested combination_of_columns extraction is duplicated in extractGrainDeclarations and extractGrainKeyGaps; extract a shared helper.
Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts - 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts

Fix these issues in Kilo Cloud

Previous review (commit 488a4c1)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1396 Change-scoping keys only on the grain declaration; a coverage-only edit (e.g. dropping a constraints: [{type: not_null}] from a grain column) slips past both the grain detector and the removals detector.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1030 arguments:-nested combination_of_columns extraction is duplicated in extractGrainDeclarations and extractGrainKeyGaps; extract a shared helper.
Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts - 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts

Fix these issues in Kilo Cloud

Previous review (commit b9a8c99)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1396 Change-scoping keys only on the grain declaration; a coverage-only edit (e.g. dropping a constraints: [{type: not_null}] from a grain column) slips past both the grain detector and the removals detector.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1030 arguments:-nested combination_of_columns extraction is duplicated in extractGrainDeclarations and extractGrainKeyGaps; extract a shared helper.
Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts - 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts

Fix these issues in Kilo Cloud

Previous review (commit 59fe1e4)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1396 Change-scoping keys only on the grain declaration; a coverage-only edit (e.g. dropping a constraints: [{type: not_null}] from a grain column) slips past both the grain detector and the removals detector.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 1030 arguments:-nested combination_of_columns extraction is duplicated in extractGrainDeclarations and extractGrainKeyGaps; extract a shared helper.

Fix these issues in Kilo Cloud

Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts - 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts

Previous review (commit 5ad0e85)

Status: No Issues Found | Recommendation: Merge

Incremental review of changes since 385ecdc (subdir/monorepo content resolution, block-scalar-aware risk scanning, primary_key/model-level constraint coverage, envelope tierForced/tierClassified invariant, and relationships dedupe discriminator).

Files Reviewed (10 files)
  • packages/opencode/src/altimate/review/compiled.ts
  • packages/opencode/src/altimate/review/dbt-patterns.ts
  • packages/opencode/src/altimate/review/git.ts
  • packages/opencode/src/altimate/review/risk-tier.ts
  • packages/opencode/src/altimate/review/run.ts
  • packages/opencode/src/altimate/review/verdict.ts
  • packages/opencode/test/altimate/review-ci.test.ts
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts
  • packages/opencode/test/altimate/review-subdir-invocation.test.ts
  • packages/opencode/test/altimate/review.test.ts

Previous review (commit 385ecdc)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/dbt-patterns.ts 917 GrainKeyGap exported but never imported outside the file
packages/opencode/src/altimate/review/dbt-patterns.ts 979 Model-level constraints: not counted as coverage (false-positive risk on contracted models using model-level not_null)
Files Reviewed (2 files)
  • packages/opencode/src/altimate/review/dbt-patterns.ts — 2 issues
  • packages/opencode/test/altimate/review-dbt-patterns.test.ts — clean (13 new tests cover the documented YAML shapes: contracted vs view, pre-1.9 vs 1.9+ arguments:, exact-name match, case-folding, deleted-file skip)

Fix these issues in Kilo Cloud


Reviewed by glm-5.2 · Input: 80.1K · Output: 8.8K · Cached: 927.6K

Review guidance: REVIEW.md from base branch feat/review-r20-s4-triage-promotion

@sahrizvi

sahrizvi commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Validation update — this detector actually catches +23 grain-key gaps, not +11

The PR body reported +11 grain-key findings; the accurate post-hardening number is +23. The +11 baseline compared against was a stale pre-hardening run captured before the code-review-driven tightening (constraints-only-count-when-contract-enforced + case-folding + exact-name match). After a clean rerun against the parent PR's CLI baseline:

PR Parent baseline With detector Delta Grain-key gaps
A 25 29 +4 4
B 62 63 +1 1
C 23 41 +18 18
D 0 0 0 0 (fixes already in HEAD)
E 8 8 0 0
TOTAL 118 141 +23 23

Notable expansion on PR C (18 grain-key gaps, up from the 10 originally reported): the hardened rule correctly refuses to accept constraints: [{type: not_null}] as coverage on non-contracted models, which flushes previously-hidden gaps. Several of the caught gaps directly match human review comments on the corresponding files.

No regressions: full altimate review suite still 3797 / 0 pass / fail.

@dev-punia-altimate dev-punia-altimate left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Code Review — OpenCodeReview (Gemini) — 2 finding(s)

  • 2 anchored to a line (posted inline when the comment stream is on)
  • 0 without a line anchor
All findings (full text)

1. packages/opencode/src/altimate/review/dbt-patterns.ts (L989-L992)

[🟠 MEDIUM] The constraint extraction logic only checks for type: not_null. However, in dbt, a constraint of type: primary_key also implicitly enforces not_null at the database level. Models correctly utilizing type: primary_key for their grain columns will be falsely flagged as lacking not_null coverage, resulting in false positives. You should treat primary_key as a valid equivalent to not_null.

Suggested change:

              const type = (cn as Record<string, unknown>).type
              if (typeof type === "string" && (type.toLowerCase() === "not_null" || type.toLowerCase() === "primary_key")) {
                coveredByConstraint.add(key)
              }

2. packages/opencode/src/altimate/review/dbt-patterns.ts (L936-L943)

[🟠 MEDIUM] The logic to extract test names does not support the dbt 1.8+ syntax where generic tests can be explicitly named via the name: property (e.g., - name: not_null instead of - not_null:).

When a test is defined with name:, Object.keys(t)[0] may evaluate to "name" instead of the actual test name. This leads to two issues:

  1. Here in testName, it will return "name" instead of "not_null", causing false positives by missing valid not_null coverage.
  2. In the grain test loop below (for (const k of Object.keys(entry))), keys like "name" and "combination_of_columns" are evaluated, causing isGrainTestName to miss the unique_combination_of_columns test entirely.

Consider checking for t.name explicitly if it's a string, or updating the extraction logic to accommodate the dbt 1.8+ - name: test_name syntax.

Suggested change:

  const testName = (t: unknown): string | undefined => {
    if (typeof t === "string") return t.toLowerCase()
    if (t && typeof t === "object") {
      const obj = t as Record<string, unknown>
      if (typeof obj.name === "string") return obj.name.toLowerCase()
      const k = Object.keys(obj)[0]
      return k ? k.toLowerCase() : undefined
    }
    return undefined
  }

Comment on lines +989 to +992
const type = (cn as Record<string, unknown>).type
if (typeof type === "string" && type.toLowerCase() === "not_null") {
coveredByConstraint.add(key)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[🟠 MEDIUM] The constraint extraction logic only checks for type: not_null. However, in dbt, a constraint of type: primary_key also implicitly enforces not_null at the database level. Models correctly utilizing type: primary_key for their grain columns will be falsely flagged as lacking not_null coverage, resulting in false positives. You should treat primary_key as a valid equivalent to not_null.

Suggested change:

Suggested change
const type = (cn as Record<string, unknown>).type
if (typeof type === "string" && type.toLowerCase() === "not_null") {
coveredByConstraint.add(key)
}
const type = (cn as Record<string, unknown>).type
if (typeof type === "string" && (type.toLowerCase() === "not_null" || type.toLowerCase() === "primary_key")) {
coveredByConstraint.add(key)
}

Comment on lines +936 to +943
const testName = (t: unknown): string | undefined => {
if (typeof t === "string") return t.toLowerCase()
if (t && typeof t === "object") {
const k = Object.keys(t as Record<string, unknown>)[0]
return k ? k.toLowerCase() : undefined
}
return undefined
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[🟠 MEDIUM] The logic to extract test names does not support the dbt 1.8+ syntax where generic tests can be explicitly named via the name: property (e.g., - name: not_null instead of - not_null:).

When a test is defined with name:, Object.keys(t)[0] may evaluate to "name" instead of the actual test name. This leads to two issues:

  1. Here in testName, it will return "name" instead of "not_null", causing false positives by missing valid not_null coverage.
  2. In the grain test loop below (for (const k of Object.keys(entry))), keys like "name" and "combination_of_columns" are evaluated, causing isGrainTestName to miss the unique_combination_of_columns test entirely.

Consider checking for t.name explicitly if it's a string, or updating the extraction logic to accommodate the dbt 1.8+ - name: test_name syntax.

Suggested change:

Suggested change
const testName = (t: unknown): string | undefined => {
if (typeof t === "string") return t.toLowerCase()
if (t && typeof t === "object") {
const k = Object.keys(t as Record<string, unknown>)[0]
return k ? k.toLowerCase() : undefined
}
return undefined
}
const testName = (t: unknown): string | undefined => {
if (typeof t === "string") return t.toLowerCase()
if (t && typeof t === "object") {
const obj = t as Record<string, unknown>
if (typeof obj.name === "string") return obj.name.toLowerCase()
const k = Object.keys(obj)[0]
return k ? k.toLowerCase() : undefined
}
return undefined
}

@dev-punia-altimate

Copy link
Copy Markdown
Contributor

🤖 Code Review — OpenCodeReview (Gemini) — No Issues Found

No supported files changed.

@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

2 similar comments
@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@sahrizvi

sahrizvi commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Consensus Code Review — PR #1029

Title: feat(review): [S1] grain-key not_null completeness detector
Branch: feat/review-r20-s1-grain-key-not-nullfeat/review-r20-s4-triage-promotion (stacks on #1028#1027) · Repo: AltimateAI/altimate-code
Size: 2 files, +542 / −1 (dbt-patterns.ts +213, review-dbt-patterns.test.ts +330) · Reviewed: 2026-07-22

Panel: multi-model review.


Verdict: REQUEST CHANGES — 1 MAJOR (small, well-scoped fix), 5 MINOR, 4 NIT

All 8 reviewers independently flagged the same headline false positive. The detector's core parsing is solid and corpus-validated (+11 deterministic findings, several matching critical human blockers); the blocker is a coverage blind spot that misfires on exactly the well-designed contracted models the rule is meant to reward.

Counts: Critical 0 · Major 1 · Minor 5 · Nit 4


MAJOR

1. Model-level & primary_key constraints are not counted as not_null coverage → false positives on contracted models

Category: False positive / coverage detection · Location: dbt-patterns.ts extractGrainKeyGaps
The detector inspects only COLUMN-level constraints: [{type: not_null}] and column-level tests. dbt (1.5+) also supports model-level constraints — constraints: [{type: primary_key, columns: [a,b]}] and {type: not_null, columns: [...]} — and a primary_key constraint inherently enforces NOT NULL on Postgres/Snowflake/BigQuery/Databricks. Grain columns are frequently the primary key, so a contracted mart that declares its grain via a model-level PK constraint gets every grain column falsely flagged as missing not_null — a false positive on precisely the well-designed models this rule intends to encourage.
Fix: when contractEnforced, also scan mm.constraints; for entries of type not_null or primary_key, add each name in their columns: array to coveredByConstraint. Also recognise column-level constraints: [{type: primary_key}].
8-way unanimous: multiple reviewers CRITICAL, multiple reviewers/a reviewer MAJOR, Claude MINOR. Convergence settled MAJOR (multiple reviewers all agree: systematic false positive on standard syntax is a classic MAJOR; CRITICAL is reserved for execution failure / state corruption, which this is not).


MINOR

2. Fires on PRE-EXISTING grain gaps (whole NEW-side file), not just PR-introduced ones → PR noise

A one-line edit to a shared _models.yml surfaces warnings on untouched legacy models. This is deliberate, documented in-code, and unit-tested (steady-state test), and it drives the strong corpus recall (+11). a reviewer rated it MAJOR for the shared-file noise case; multiple reviewers/A reviewer hold MINOR as a deliberate precision/recall tradeoff. Consider diffing old vs new gaps to emit only newly-introduced ones, or marking pre-existing gaps as such. Kept MINOR; 's MAJOR dissent noted.

3. Contract-precedence bug: config.contract without enforced masks a top-level contract: {enforced: true}

Category: Logic · The ternary selects cfg.contract whenever it's an object (e.g. config: {contract: {alias: ...}}), short-circuiting mm.contract.enforced === true. Enforced constraint coverage is then wrongly treated as inert → false positive. Fix: evaluate enforced === true across both locations independently and OR them. Narrow trigger (needs both blocks in the same model). A reviewer.

4. dbt.not_null namespaced test alias not recognised (low-confidence)

In dbt 1.8+, - dbt.not_null may be valid; testName() returns "dbt.not_null" which fails === "not_null", so covered columns would be flagged. Strip a leading dbt. prefix in testName(). Gemini; A reviewer flagged this as low-confidence pending validation against dbt's parser (official docs emphasise bare not_null).

5. Contract enforcement inferred only from schema.yml

Misses contract set in dbt_project.yml or the model's SQL config(...), so enforced constraints are treated as inert → false positive + wrong remediation text. Resolve contract state from manifest/project context when available, or soften the finding wording when it can't be resolved from schema.yml alone. A reviewer.

6. {name:, test_name: not_null} alternative test format not recognised (added in convergence)

dbt's documented alternative data-test object form is {name: my_test, test_name: not_null, ...}. testName() reads only the first object key (Object.keys(t)[0]), so a not_null test written in this form is missed — both as column coverage (false positive gap) and as a grain-test declaration (false negative). Read test_name when present before falling back to the first key. A reviewer.


NIT

  1. norm() is defined inside the per-model loop; hoist it. A reviewer.
  2. Duplicate GrainKeyGap for a column listed twice / multiple grain tests — collapses downstream via the per-column fingerprint, but cleaner to dedupe inside extractGrainKeyGaps. A reviewer.
  3. Scanning model-level data_tests: for the grain test is slightly non-standard (model-level tests go under tests:); harmless (no false match) but signals a schema-shape mismatch. A reviewer.
  4. Grain detection runs only on the structural NEW-side parse; the diff-only fallback silently skips it (acceptable — needs real YAML — but undocumented). A reviewer.

Rejected findings (for transparency)

  • "ruleKey ${model}.${column} collides across files" ( MINOR) — false: the dedupe fingerprint includes file, model, and column independently of ruleKey (finding.ts:108). A reviewer flagged this in convergence; lead confirmed. Removed.
  • "Column-level grain test missed" ( MAJOR) — unique_combination_of_columns is inherently multi-column and declared at model level; a column-level form isn't a standard dbt shape.
  • "clampSeverity(..., "high") looks wrong" ( NIT) — "high" is the confidence argument, consistent with the same call pattern elsewhere in the file.
  • "unique test should count as coverage" () — a unique test does not imply not_null; not a coverage source.

Positive observations (consensus)

  • Contract-gated constraint coverage is the right call — on non-contracted models constraints: is documentation-only, so requiring contract.enforced avoids missing real gaps on views. Tested both ways.
  • Exact grain-test-name match (avoids not_unique_combination_of_columns), adapter case-folding, and pre-1.9 flat vs 1.9+ arguments: nesting — all handled and tested.
  • Deleted-file suppression, structural-only gating with clean fallback, and per-column ruleKey for dedupe.
  • Corpus validation is strong: +11 deterministic findings, several matching critical human blockers (a PR <column> ×3, <column>).

Missing tests

  • Model-level constraints: [{type: primary_key/not_null, columns:[...]}] covering the grain → should NOT flag (guards MAJOR no. 1). · config.contract without enforced + top-level contract: {enforced: true} (guards MINOR no. 3). · {name:, test_name: not_null} object form counts as coverage (guards MINOR no. 6). · data_tests: [dbt.not_null] prefix (guards MINOR no. 4).

Convergence

Reviewer Verdict on synthesis Key point
a reviewer APPROVE MAJOR no. 1 correct (not CRITICAL); MINOR no. 2 correct; rejected list valid.
a reviewer APPROVE MAJOR no. 1 correct; MINOR no. 2 correct ('s MAJOR overweights the inconvenience).
a reviewer CHANGES NEEDED → incorporated Drop no. 6 (fingerprint includes file); add the {name:, test_name:} format miss; keep no. 4 low-confidence; MAJOR no. 1 correct.
Reviewer (author) Agreed; incorporated 's corrections after shell-verifying finding.ts:108.

Applied in convergence: dropped the "ruleKey cross-file collision" finding (shell-verified incorrect); added the test_name: alternative-format blind spot (MINOR no. 6); marked dbt.not_null (no. 4) low-confidence. no. 1 settled MAJOR (unanimous), no. 2 kept MINOR. Converged in 1 round.

Finding attribution

# Finding Origin Type
1 Model-level / primary_key constraints not counted as coverage all 8 reviewers Consensus (8-way)
2 Fires on pre-existing gaps → PR noise (MAJOR), multiple reviewers (MINOR), multiple reviewers (tradeoff) Consensus
3 Contract-precedence ternary bug multiple reviewers Unique
4 dbt.not_null alias not recognised (low-confidence) Reviewer Unique
5 Contract not resolved from project/SQL config Reviewer Unique
6 {name:, test_name:} format not recognised Reviewer Convergence add
7–10 NITs (norm() hoist, dup gaps, model-level data_tests, fallback skip) multiple reviewers Nit
ruleKey cross-file collision → rebuttal Rejected (shell-verified)
Contract-gated coverage; corpus recall +11 all 8 Consensus (positive)

Reviewed by a multi-model panel.

@sahrizvi
sahrizvi force-pushed the feat/review-r20-s4-triage-promotion branch from 8eac0d9 to 4544cf8 Compare July 22, 2026 13:33
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from 385ecdc to 5ad0e85 Compare July 22, 2026 13:50
sahrizvi pushed a commit that referenced this pull request Jul 22, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
@@ -1100,7 +1313,7 @@

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grain detector is not change-scoped: extractGrainKeyGaps(newDoc) runs against the entire new-side YAML — every model in the file, whether or not it was touched in this PR. A housekeeping PR that modifies a schema.yml for an unrelated reason (adding a column description, bumping a meta tag) will surface ALL pre-existing grain-key gaps in every model in that file.

The removal detector is intrinsically change-aware (it diffs old vs new occurrence sets); the grain detector here is not. The PR description acknowledges this as intentional ("a broken grain-guard is a real risk either way"), but there is a real precision cost: a reviewer who sees grain findings on a PR that only touched descriptions will ignore them and suppress the rule — losing coverage on real future gaps.

Consider filtering to models whose grain config actually changed between oldDoc and newDoc (compare the combination_of_columns sets on each model), or at least documenting the intentional breadth in the JSDoc so callers can set expectations with users.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 462205900 — grain detector is now change-scoped. New helper grainDeclChangedEntities(oldDoc, newDoc) diffs the combination_of_columns set per entity; extractGrainKeyGaps(newDoc) results are filtered to only those entities. Added file (oldDoc === undefined) unconditionally treats every entity as changed so newly-shipped grain declarations are still guarded.

Tests: rewrote the earlier steady-state → still fires case to the newly-added case; added steady-state grain gap on unchanged model is NOT re-surfaced to lock in the precision guarantee, and grain declaration changed (column added to combination_of_columns) does fire gap to keep the regression case covered.

// Normalise column names for coverage comparison. dbt YAML often uses
// adapter-cased column names (Snowflake folds unquoted identifiers to
// uppercase; other adapters differ). Lowercase both sides so `WORKSPACE_ID`
// in `combination_of_columns` matches `workspace_id` in `columns:`. Hoisted

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractGrainKeyGaps only iterates d.models, silently skipping snapshots, sources, and seeds. The existing extractTestOccurrences covers all four entity types. unique_combination_of_columns can appear on snapshots (SCD-2 grain declarations are a real use case), so snapshot grain gaps would go undetected here.

Either expand the loop to cover all four sections — mirroring extractTestOccurrences — or add a JSDoc note explicitly stating that model-only scope is intentional, so a future maintainer does not add snapshot support thinking it was simply forgotten.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 462205900 — new helper iterateGrainEntities(d) walks models, snapshots, sources.tables[], and seeds (mirrors extractTestOccurrences). Sources descend one level to iterate their tables[]. Three new tests: grain detector also covers snapshots, ... source tables, ... seeds.

Follow-up: cubic-dev-ai then flagged source-table conflation across different sources (two tables: named orders under different sources: collapsed to one entity). Fixed in 59fe1e412 — source tables are now qualified as ${sourceName}.${tableName} so raw.orders and legacy.orders don't merge.

@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from 5ad0e85 to b3e93cb Compare July 23, 2026 07:01
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/review/dbt-patterns.ts Outdated
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from 4622059 to 59fe1e4 Compare July 23, 2026 07:47
saravmajestic
saravmajestic previously approved these changes Jul 23, 2026
// changed and every gap surfaces.
if (!isDeletedFile && newDoc !== undefined) {
const changedEntities = grainDeclChangedEntities(oldDoc, newDoc)
grainGaps = extractGrainKeyGaps(newDoc).filter((g) => changedEntities.has(g.model))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Change-scoping keys only on the combination_of_columns declaration, so a coverage-only edit on a grain column slips through both detectors.

grainDeclChangedEntities diffs the grain-declaration set; when the declaration is unchanged this .filter(...) drops the gap even if the column's not_null coverage was just removed. The removals detector won't catch it either — extractTestOccurrences (line ~866) tracks tests/data_tests only, never constraints:. So dropping constraints: [{type: not_null}] from a grain column on a contracted model (the exact remediation this rule recommends) produces no finding while the column silently becomes NULL-able and can bypass the uniqueness test. Consider also flagging an entity as changed when its coverage set differs old→new, or extending the removals path to constraint drops.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

argsObj.arguments && typeof argsObj.arguments === "object"
? (argsObj.arguments as Record<string, unknown>)
: undefined
const combo =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The arguments:-nested combination_of_columns extraction is duplicated here and in extractGrainKeyGaps (~line 1216).

Extracting a shared helper keeps the two grain-test parsers in sync. They already diverge slightly on name matching (extractGrainDeclarations strips dbt_utils. and compares bare; extractGrainKeyGaps uses isGrainTestName with two exact forms), and a future dbt arg-shape change would need the same edit in both places.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from 59fe1e4 to b9a8c99 Compare July 23, 2026 09:17
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from b9a8c99 to 488a4c1 Compare July 23, 2026 09:50
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from 488a4c1 to b8aa713 Compare July 23, 2026 10:56
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
sahrizvi pushed a commit that referenced this pull request Jul 23, 2026
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s4-triage-promotion branch from 9435bc6 to 9bdd6aa Compare July 23, 2026 12:05
Haider and others added 5 commits July 23, 2026 18:48
For every column named in a `dbt_utils.unique_combination_of_columns`
test's `combination_of_columns`, require `not_null` coverage on the
same model. Otherwise a NULL grain-key value silently passes the
uniqueness test — the guardrail is toothless. Cited as a hard rule in
`docs/DBT_GUIDELINES.md` in the corpus repo; kilo catches it, we didn't.

Coverage sources:
- `constraints: [{type: not_null}]` — only counted when
  `contract.enforced == true` (per codex R20 S1 high #3). On views /
  non-contracted models, `constraints:` is documentation-only, not
  enforced, so we don't miss a real gap.
- Column-level `data_tests: [not_null]` / `tests: [not_null]` — always
  counted; dbt's test runner enforces regardless of contract state.

Recommendation flips between `constraints:` (contracted model) and
`data_tests:` (view / non-contracted) based on the model's contract
state — matches the adapter-semantics discussion in the corpus study
(PR D×2 + PR A×2).

Supported YAML shapes:
- `unique_combination_of_columns` and `dbt_utils.unique_combination_of_columns`
  (exact match per codex high #1; `endsWith` would over-match).
- pre-1.9 flat args + dbt 1.9+ `arguments:` nesting.
- top-level `contract:` and nested `config.contract:` for enforcement flag.

False-positive guards:
- Column-name comparison is case-folded (Snowflake identifiers, per
  codex high #2) so `WORKSPACE_ID` in `combination_of_columns` matches
  `workspace_id` in `columns:`.
- Skipped on `status === "deleted"` files (no current grain to guard).
- Only runs on files in the PR diff, not repo-wide.

### Validation on 5-PR internal corpus (recall improvement)

vs S4 baseline (the tier-promotion PR this branch stacks on):
| Variant | S4 baseline | S1 result | Delta |
|---|---:|---:|---:|
| A1 (`--pure --no-ai=true`) | 118 | 129 | **+11 grain-key gaps** |
| A2 (`--pure`, LLM lane) | 143 | 150 | +7 (net; LLM run-to-run noise) |

Grain-key gaps caught:
- PR B (#1111) ×1: `mrt_all_purpose_auto_tune_recommendation.rec_type`
- PR C (#862) ×10: workspace_id x3, job_id x2, period_start_time x2, period_end_time x2, task_key x1
- PR A, D, E: 0 (fixes already landed in HEAD state per corpus study)

Strong matches to human blocker findings on PR C:
- `int_job_run_billing.workspace_id`, `int_job_run_cost_carrier.workspace_id`,
  `int_job_task_run_cost_carrier.workspace_id` → PR C human F11 (critical:
  "workspace_id missing from carrier identity")
- `mrt_job_run_timeline.period_end_time`, `mrt_job_task_run_timeline.period_end_time`
  → PR C human F10 (critical: "dbt mart grain includes period_end_time")

### Tests

- 71 pass / 0 fail in `review-dbt-patterns.test.ts` (58 pre-existing + 13 new S1 tests).
- 3797 pass / 640 skip / 0 fail in the full altimate review suite (132 files).
- Codex-reviewed diff, 3 highs addressed:
  - endsWith → exact-name match
  - column-name case-folding
  - constraints only count when contract enforced
- Codex minor #4 addressed (test fixture for top-level `contract:`)
- Codex minor #5 addressed (test fixture for non-contracted constraints ≠ coverage)

Depends on PR #1028 (feat/review-r20-s4-triage-promotion), which in turn
stacks on PR #1027 (feat/review-r18-observability-recall).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
…detector

Bundle addresses PR #1029's consensus review:

- MAJOR #1 — model-level and column-level primary_key / not_null
  constraints now count as coverage. dbt 1.5+ supports model-level
  constraints via `constraints: [{type: primary_key, columns: [a, b]}]`,
  and a primary_key inherently enforces NOT NULL on Postgres/Snowflake/
  BigQuery/Databricks. Grain columns declared via a model-level PK were
  previously falsely flagged as missing not_null. Fix scans both
  `mm.constraints` (model-level, honouring the `columns:` list) and
  column-level `constraints: [{type: primary_key}]`.

- MINOR #3 — contract-precedence bug. Earlier ternary short-circuited
  when `cfg.contract` was any object (e.g. `config: {contract:
  {alias: X}}` with no `enforced` key), masking a top-level
  `contract: {enforced: true}`. Now evaluated independently at both
  locations and OR'd.

- MINOR #4 — `dbt.` prefix on namespaced test names (`dbt.not_null` in
  dbt 1.8+) is stripped in `testName()` before matching, so it counts
  as coverage.

- MINOR #6 — `{name: <alias>, test_name: not_null}` alternative object
  form is recognised. Reading `Object.keys(t)[0]` returned `name`
  (the alias) rather than the underlying test type. Now `test_name`
  wins when present, falling back to the first key.

- NIT #7 — `norm()` hoisted from per-model to function scope.

Consensus items NOT addressed this round:
- NIT #8 (dedup GrainKeyGap for a column listed twice / multiple
  grain tests) — collapses downstream via the global finding
  fingerprint; cosmetic rather than correctness.
- NIT #9 (model-level `data_tests:` scanned as a grain-test source)
  — reviewer noted "harmless (no false match)"; no action.
- NIT #10 (documentation of fallback-skip) — no code change needed.
- MINOR #5 (contract resolved from dbt_project.yml or SQL config)
  — cross-file / cross-context, deferred.

Regression tests (all pass; 82 pass / 0 fail in review-dbt-patterns
suite, 3828 pass / 0 fail in full altimate suite — up from 3785):

- Model-level primary_key constraint covers grain columns
- Model-level not_null constraint with `columns:` list covers named cols
- Column-level primary_key counts as coverage
- Precision guard: PK missing cols still flagged
- Model-level constraints on non-contracted model don't count
- `config.contract` without `enforced` does NOT mask top-level
  `contract: {enforced: true}` (MINOR #3)
- `dbt.not_null` covers (MINOR #4)
- `{name:, test_name: not_null}` alternative form covers (MINOR #6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
…ns comments

Companion cleanup to PR #1028's FinOps strip. The grain-key detector's
docstrings named a specific internal column (`workspace_id`) as the
Snowflake case-folding example and referenced internal corpus PR labels
(`PR D×2, PR A×2`). Neither carries product meaning outside the
altimate-ingestion codebase.

- `dbt-patterns.ts:962` — replace `WORKSPACE_ID` / `workspace_id` example
  with `ORDER_ID` / `order_id`. Same illustrative point, no internal name.
- `dbt-patterns.ts:1443` — replace `(PR D×2, PR A×2)` corpus-label
  citation with `(four instances across the sample)`. Same count, no
  internal reference.

Tests: 250/250 green.
… grain-key detector

Two substantive findings on PR #1029:

1. `dbt-patterns.ts:1099` — grain detector was not change-scoped.
   `extractGrainKeyGaps(newDoc)` ran against every entity in the file,
   so a housekeeping edit (description bump, meta tag) surfaced all
   pre-existing grain gaps on unrelated models. Real precision cost:
   reviewers seeing findings on a PR they didn't intend suppress the
   whole rule.

   Fix: compare the `combination_of_columns` set per entity between
   `oldDoc` and `newDoc`; only emit gaps for entities whose grain
   declaration actually changed (added, removed, or column set diff).
   Added entities on the new side count as changed; the "added file"
   case (no oldDoc) unconditionally treats every entity as changed
   so newly-shipped grain declarations are still guarded.

   New helpers: `extractGrainDeclarations` returns
   `Map<entityName, Set<column>>` and `grainDeclChangedEntities` diffs
   two docs into a set of entity names whose declaration moved.
   Existing test that pinned the old steady-state-fires behavior
   (`R20 S1: unique_combination_of_columns with grain col missing
   not_null → warning finding`) rewritten to test the newly-added
   case; added a companion test that pins the new precision
   guarantee (`steady-state grain gap on unchanged model is NOT
   re-surfaced`), plus a `grain declaration changed (column added to
   combination_of_columns) does fire gap` test that keeps the
   regression case covered.

2. `dbt-patterns.ts:963` — `extractGrainKeyGaps` iterated only
   `d.models`, silently skipping `snapshots`, `sources`, and `seeds`.
   `unique_combination_of_columns` on a snapshot is a real SCD-2
   grain declaration; sources declare per-table `columns:` + tests;
   seeds carry the model shape.

   Fix: new `iterateGrainEntities(d)` helper walks all four sections
   (mirrors `extractTestOccurrences`). Sources descend one level to
   iterate their `tables[]` entries which carry the model shape.
   Three new tests: `grain detector also covers snapshots`,
   `... source tables`, `... seeds`.

Tests: 255/255 green (8 review-* files, +6 new grain-key tests).
…<table>` (cubic-review P2)

Two sources both containing a table named `orders` (e.g. `raw.orders`
and `legacy.orders`) previously conflated in `iterateGrainEntities`:
- Grain-change detection collapsed them into a single map entry, so a
  change in one source's grain declaration could surface or suppress
  gaps for the other.
- Finding fingerprint uses the entity name; two distinct source tables
  with the same table name would dedupe to a single finding.

Fix: `iterateGrainEntities` now returns `{name, body}` pairs. Source
tables are qualified as `${sourceName}.${tableName}` while models,
snapshots, and seeds retain their unqualified name (they live in a
flat namespace already).

Tests: `grain detector also covers source tables` updated to assert
the qualified name; new test `same source-table name in two sources
does NOT conflate` locks in the fix.

257/257 review-* tests pass.
@sahrizvi
sahrizvi force-pushed the feat/review-r20-s1-grain-key-not-null branch from b8aa713 to 0c48d4e Compare July 23, 2026 13:18
@sahrizvi
sahrizvi changed the base branch from feat/review-r20-s4-triage-promotion to main July 23, 2026 13:20
@sahrizvi
sahrizvi dismissed saravmajestic’s stale review July 23, 2026 13:20

The base branch was changed.

@dev-punia-altimate dev-punia-altimate left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving as requested

@sahrizvi
sahrizvi merged commit 39d7d94 into main Jul 23, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants