feat(review): [R20 S1] grain-key not_null completeness detector (+11 findings on 5-PR corpus) - #1029
Conversation
There was a problem hiding this comment.
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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
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. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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>
| * | ||
| * Returns one gap per uncovered column per grain declaration. | ||
| */ | ||
| export interface GrainKeyGap { |
There was a problem hiding this comment.
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.
| 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)) { |
There was a problem hiding this comment.
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.
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (2 files)
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
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (2 files)
Fix these issues in Kilo Cloud Previous review (commit 488a4c1)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (2 files)
Fix these issues in Kilo Cloud Previous review (commit b9a8c99)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (2 files)
Fix these issues in Kilo Cloud Previous review (commit 59fe1e4)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Fix these issues in Kilo Cloud Files Reviewed (2 files)
Previous review (commit 5ad0e85)Status: No Issues Found | Recommendation: Merge Incremental review of changes since Files Reviewed (10 files)
Previous review (commit 385ecdc)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (2 files)
Reviewed by glm-5.2 · Input: 80.1K · Output: 8.8K · Cached: 927.6K Review guidance: REVIEW.md from base branch |
Validation update — this detector actually catches +23 grain-key gaps, not +11The 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:
Notable expansion on PR C (18 grain-key gaps, up from the 10 originally reported): the hardened rule correctly refuses to accept No regressions: full altimate review suite still 3797 / 0 pass / fail. |
dev-punia-altimate
left a comment
There was a problem hiding this comment.
🤖 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:
- Here in
testName, it will return"name"instead of"not_null", causing false positives by missing validnot_nullcoverage. - In the grain test loop below (
for (const k of Object.keys(entry))), keys like"name"and"combination_of_columns"are evaluated, causingisGrainTestNameto miss theunique_combination_of_columnstest 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
}
| const type = (cn as Record<string, unknown>).type | ||
| if (typeof type === "string" && type.toLowerCase() === "not_null") { | ||
| coveredByConstraint.add(key) | ||
| } |
There was a problem hiding this comment.
[🟠 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") { | |
| 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) | |
| } |
| 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 | ||
| } |
There was a problem hiding this comment.
[🟠 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:
- Here in
testName, it will return"name"instead of"not_null", causing false positives by missing validnot_nullcoverage. - In the grain test loop below (
for (const k of Object.keys(entry))), keys like"name"and"combination_of_columns"are evaluated, causingisGrainTestNameto miss theunique_combination_of_columnstest 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 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 | |
| } |
🤖 Code Review — OpenCodeReview (Gemini) — No Issues FoundNo supported files changed. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
2 similar comments
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Consensus Code Review — PR #1029Title: feat(review): [S1] grain-key Panel: multi-model review. Verdict: REQUEST CHANGES — 1 MAJOR (small, well-scoped fix), 5 MINOR, 4 NITAll 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 MAJOR1. Model-level &
|
| 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.
8eac0d9 to
4544cf8
Compare
385ecdc to
5ad0e85
Compare
…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 @@ | |||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
5ad0e85 to
b3e93cb
Compare
…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
… 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).
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
4622059 to
59fe1e4
Compare
| // changed and every gap surfaces. | ||
| if (!isDeletedFile && newDoc !== undefined) { | ||
| const changedEntities = grainDeclChangedEntities(oldDoc, newDoc) | ||
| grainGaps = extractGrainKeyGaps(newDoc).filter((g) => changedEntities.has(g.model)) |
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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.
…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
… 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).
59fe1e4 to
b9a8c99
Compare
…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
… 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).
b9a8c99 to
488a4c1
Compare
…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
… 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).
488a4c1 to
b8aa713
Compare
…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
… 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).
9435bc6 to
9bdd6aa
Compare
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.
b8aa713 to
0c48d4e
Compare
dev-punia-altimate
left a comment
There was a problem hiding this comment.
approving as requested
Summary
New detector in
detectSchemaYmlPatterns: for every column named in adbt_utils.unique_combination_of_columnstest'scombination_of_columns, requirenot_nullcoverage 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 whencontract.enforced == true. On views / non-contracted models,constraints:is documentation-only, not enforced by the DB.data_tests: [not_null]/tests: [not_null]— always counted; dbt's test runner enforces regardless of contract state.Recommendation flips between
constraints:(contracted model) anddata_tests:(view / non-contracted).Supported YAML shapes:
unique_combination_of_columnsanddbt_utils.unique_combination_of_columns(noendsWith— avoids false positives onnot_unique_combination_of_columns).arguments:nesting.config.contract:and top-levelcontract:for enforcement flag.Precision guards:
status === 'deleted'files.Validation
Deterministic detector run against a set of merged dbt PRs used for offline validation:
--pure --no-ai=true)--pure, LLM lane on)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
review-dbt-patterns.test.ts(58 pre-existing + 13 new tests)New test fixtures (all green):
data_tests:recommendationtests:(dbt <1.8 alias) countschange_time) missingnot_nullflaggedunique_combination_of_columns(nodbt_utils.prefix)arguments:nesting recognisedconstraints: [{type: not_null}]doesn't count as coveragecontract: {enforced: true}(not just nested underconfig:) recognisednot_unique_combination_of_columnsetc.)WORKSPACE_ID↔workspace_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).