Visitar URL original
fix(review): a readable PR comment: diff-anchored inline comments, no reposts, grouped rules, size budget, new-only PII, no portability noise by RaghwendraSingh · Pull Request #1721 · AltimateAI/altimate-code · GitHub
Skip to content

fix(review): a readable PR comment: diff-anchored inline comments, no reposts, grouped rules, size budget, new-only PII, no portability noise - #1721

Open
RaghwendraSingh wants to merge 2 commits into
fix/review-ci-experiencefrom
fix/review-noise
Open

RaghwendraSingh wants to merge 2 commits into
fix/review-ci-experiencefrom
fix/review-noise

Conversation

@RaghwendraSingh

@RaghwendraSingh RaghwendraSingh commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #

Stacked on #1720, which is stacked on #1719; merge in that order.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

On a real dbt repo the review comment was mostly noise:

  • Reposts: nearly half of the bot's inline comments were the same comment re-posted on a later push.
  • Repeated rules: four generic rules made up most on-diff findings, emitted once per model.
  • Size: summaries ran to tens of thousands of characters.
  • Broken anchors: inline comments were anchored to compiled-SQL line numbers, so a single line outside the diff got a 422 and every inline comment was lost.

Fixes:

  • Anchors: a finding keeps its line only if that line is a head-side line inside the file's diff hunks. Engine lint on compiled SQL no longer sets raw-file lines. Anything unanchorable stays in the summary.
  • No reposts: each inline comment carries <!-- altimate-finding:<id> -->. The id is the existing fingerprint (rule, file, model, column — no line), so moving code doesn't change it. On rerun, findings already on the PR are skipped. A non-blocking rerun with nothing new only updates the summary, with no new "Reviewed with comments" entry. Blocking verdicts always post.
  • Grouping: the same rule (title minus model name, backticked names blanked) on 3+ models becomes one bullet listing the models.
  • Budget: the posted summary is capped at about 5K characters, most severe first. A count says what was left out; the envelope keeps everything. CLI and agent-tool output are uncapped.
  • PII: the fallback flags only columns the change newly exposes, as the docs already promise. Behavior change: a touched model that already exposed email no longer gets a critical (blocking in gate mode) finding. The test that asserted the old behavior is inverted, and four fixtures whose fake detector returned PII for any SQL are now SQL-aware.
  • Portability: cross-engine advice from the regex catalog ("isn't portable") is dropped once the engine is known from the manifest adapter or dialect. portability: true turns it back on for multi-adapter packages. Core's dialect-aware check (a function missing on your engine) is unchanged.

Follow-up after an adversarial review (commit 1c5dc3a):

  • Portability is now an explicit per-rule tag (portability: { worksOn? }) instead of matching the comment text. The text match was dropping BigQuery's legacy-SQL warning ("use Standard SQL") on BigQuery projects.
  • Advice is kept when the construct fails on your engine. With a known engine, portability advice is dropped only when that engine supports the construct. DISTINCT ON on Snowflake or GROUP BY alias on SQL Server stays, because it errors there.
  • Moved files are named in the comment, with a caveat that folder-level dbt_project.yml config (+schema, +materialized) can still change how they build.
  • No more "lint-only" label for runs without model files (yml-only changes, pure moves).
  • Tests: suppression is checked in both directions (Postgres drops DISTINCT ON advice, Snowflake keeps it), plus the legacy-SQL case and the move note.

How did you verify your code works?

  • New test/altimate/review-noise.test.ts (13 tests):
    • hunk line parsing (blank context lines, ++ SQL lines, headers)
    • anchoring and the compiled-SQL line drop
    • fingerprint markers and dedup
    • telemetry for a skipped review
    • grouping thresholds and backticked names
    • budget with a 200-finding envelope
    • portability suppression on a real catalog rule
  • 1,872 tests across the review files and release gates pass. bun typecheck is clean.

Cost side effects

Two extra GitHub API list calls per --post (review comments and reviews, paginated), plus one extra local PII detection per modified model on the fallback path. No model calls, warehouse queries or cloud resources.

Screenshots / recordings

N/A (CLI).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR
  • I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"

🤖 Generated with Claude Code


Summary by cubic

Fixes the dbt PR review comment so it is readable over noisy: inline comments now land on lines that actually exist in the diff, findings already on the PR are not re-posted on later pushes, repeated rules are grouped, and the posted summary respects a ~5,000-character budget (local CLI and agent output stay complete).

Behavior changes

  • PII fallback now flags only columns the change newly exposes; a model that already exposed PII in the base no longer gets a critical (gate-blocking) finding.
  • Cross-engine portability advice is an explicit catalog-rule tag, hidden once a single engine is known from the manifest adapter or dialect — but kept when it would fail on the project's own engine (e.g. DISTINCT ON on Snowflake). Set portability: true in review.yml for multi-adapter packages.
  • Files moved without edits are called out in the comment, and yml-only or pure-move runs are no longer labeled lint-only.

Written for commit 1c5dc3a. Summary will update on new commits.

View guided diff Turn on auto-fix

… reposts, grouped rules, size budget, new-only PII, no portability noise

- Inline comments only on head-side lines inside the file's diff hunks;
  engine findings computed on compiled SQL no longer carry raw-file lines.
  Anything else moves to the summary instead of 422-ing the whole batch.
- Each inline comment carries `<!-- altimate-finding:<id> -->` (the
  line-independent fingerprint); a rerun skips findings already on the PR,
  and a non-blocking rerun with nothing new only updates the summary.
- The same rule on 3+ models collapses into one bullet listing the models.
- The posted summary has a ~5K-character budget, most severe first, with a
  count of what was left out (the envelope keeps everything); CLI and agent
  tool output stay complete.
- The PII fallback flags only columns the change newly exposes, not columns
  the base already surfaced.
- Cross-engine portability advice from the regex catalog is dropped once the
  engine is known (`portability: true` in review.yml re-enables it for
  multi-adapter packages); core's dialect-aware check is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 12, 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

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7f2d6f27-e3fa-4ac9-a8aa-5c361c084ebc

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
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@github-actions

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

}
const out: Array<{ text: string; count: number }> = []
for (const g of groups.values()) {
if (g.length >= GROUP_MIN) {

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: Grouping threshold counts findings, not models

g.length >= GROUP_MIN collapses on the number of findings, but the documented contract (the GROUP_MIN comment at line 42 and the new docs/docs/usage/dbt-pr-review.md text) is "the same rule on three or more models". Because ruleTitle() blanks backticked names, three findings for different columns of one model (m: column \a` has no description, `b`, `c`) collapse into one bullet reading 3 findings across 1 model(s): `m`that keeps onlyg[0]'s body. These findings carry no startLine (qualityLane`/AI are the only line producers), so the summary is their only surface — the other two columns vanish from the PR comment entirely.

Gate the collapse on distinct models (names.length >= GROUP_MIN) and keep per-finding bullets within a single model, or list each merged finding's column/title in the grouped bullet. There is also no test for the one-model case (review-noise.test.ts only covers 3-4 distinct models).

}
if (hidden) {
lines.push(
`> … ${hidden} more finding(s) not shown to keep this comment short — the full list is in the verdict envelope ` +

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 --output artifact this points at does not exist for the bundled action

github/review/action.yml builds its args from --mode/--manifest/--severity/--cwd/--base/--head/--post only — it never passes --json or --output, so an action user who hits the 5K budget has no --output artifact to open, and renderSummary is the only rendering they see. The uncapped list does reach the step log (the CLI writes renderSummary(env, { maxChars: Infinity }) to stdout), and the envelope is written only for an explicit CLI --output. Point readers at the job log or at altimate review --json --output <file> instead of an artifact the action never creates.

suppressPortability?: boolean
}

const PORTABILITY_RE = /portab|dialect[ -]specific|non-?standard|n't standard|not standard|standard sql/i

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: Duplicate portability regex

This literal is byte-identical to the isPortability regex inside the twin-dedup pass at line 1467, and the two already differ in what they test (${f.title} ${f.body} here vs f.body there). A future tweak to one (new catalog phrasings) will silently leave the other behind, so isCatalogPortability would drop a finding the dedup pass no longer recognises as portability — or vice versa. Hoist a single module-level PORTABILITY_RE and reuse it in both places.

* whole batch of inline comments.
*/
export function anchorToDiff(findings: Finding[], files: ChangedFile[]): Finding[] {
const lines = new Map(files.map((f) => [f.path, commentableLines(f.diff)]))

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: Parses every changed file's diff even when nothing needs a line

commentableLines is run over all ChangedFile.diff values up front, but only findings that already carry a startLine need their file parsed — and the only producers are qualityLane (non-Jinja SQL) and the AI lane. On a wide PR this is an extra full pass over every hunk of every changed file in memory. Build the set lazily for the files that actually have an anchored finding:

const lines = new Map(
  [...new Set(findings.filter((f) => typeof f.startLine === "number").map((f) => f.file))].map((p) => [
    p,
    commentableLines(files.find((f) => f.path === p)?.diff),
  ]),
)

@kilo-code-bot

kilo-code-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
packages/opencode/src/altimate/review/format.ts 175 Grouping collapses on finding count, not distinct models — same-rule findings on different columns of one model merge into one bullet that keeps only the first body, and these findings have no line so the summary is their only surface.
packages/opencode/src/altimate/review/format.ts 148 "the action's --output artifact" does not exist: github/review/action.yml never passes --json/--output, so users who hit the 5K budget have no artifact to open (the full list is only in the step log).

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/orchestrate.ts 213 PORTABILITY_RE duplicates the identical regex at line 1467, already diverging in scope (title+body vs body).
packages/opencode/src/altimate/review/orchestrate.ts 253 anchorToDiff parses the diff of every changed file instead of only files with an anchored finding.
Files Reviewed (11 files)
  • docs/docs/usage/dbt-pr-review.md - docs match the new behavior, no issues
  • packages/opencode/src/altimate/review/config.ts - optional portability flag, no issues
  • packages/opencode/src/altimate/review/format.ts - 2 issues
  • packages/opencode/src/altimate/review/orchestrate.ts - 2 issues
  • packages/opencode/src/altimate/review/post-github.ts - no issues (dedup/skip logic checked against HEAD; reviewSkipped only affects the COMMENT event)
  • packages/opencode/src/altimate/review/run.ts - no issues (dialectKnown preserves prior dialect resolution)
  • packages/opencode/src/altimate/review/telemetry.ts - no issues
  • packages/opencode/src/altimate/tools/dbt-pr-review.ts - uncapped output, correct
  • packages/opencode/src/cli/cmd/review.ts - uncapped output, correct
  • packages/opencode/test/altimate/review-noise.test.ts - no issues; the one-model grouping case is untested (see format.ts:175)
  • packages/opencode/test/altimate/review.test.ts - PII behavior inversion matches the documented change

Verified against 10e47c53a2dee859400b4433bc4aed2addb96937. The diff-anchoring, fingerprint dedup, and PII base-side comparison are consistent with their producers: the only startLine writers are qualityLane (now gated on raw-vs-compiled identity) and the AI lane, and runner.detectPii returns core's pii_columns output aliases, so comparing head against base output columns is a like-for-like check. No unbounded cost added (two paginated GitHub list calls per post plus one local core check per modified model).

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash-1017 · Input: 119.7K · Output: 36.8K · Cached: 2.7M

Review guidance: REVIEW.md from base branch fix/review-ci-experience (truncated)

@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.

9 issues found across 11 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/review/post-github.ts">

<violation number="1" location="packages/opencode/src/altimate/review/post-github.ts:65">
P1: `BOT_REVIEW_RE` classifies `Changes requested` as non-blocking. After a gate run requests changes, a clean rerun can hit this guard and return without resolving the old blocking review; track review state and dismiss or resolve that review before skipping.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/post-github.ts:108">
P2: `priorBotReview` accepts any review whose free text resembles the headline; it never verifies the author or a bot marker. A human or another automation can therefore suppress this run's non-blocking review when no new inline comments exist; use an authenticated reviewer identity or unique marker.</violation>
</file>

<file name="packages/opencode/test/altimate/review-noise.test.ts">

<violation number="1" location="packages/opencode/test/altimate/review-noise.test.ts:175">
P3: This test does not verify the claimed severity ordering: `toContain` passes regardless of where `m0` appears. Assert that a critical finding appears before a suggestion (and, ideally, that all three critical findings are shown) so a renderer regression cannot pass.</violation>
</file>

<file name="docs/docs/usage/dbt-pr-review.md">

<violation number="1" location="docs/docs/usage/dbt-pr-review.md:297">
P2: This bullet overstates the new-only PII guarantee when the base lacks compiled SQL. The fallback emits all head-side PII because `engineOldSql` is unavailable, so an existing PII column can still be flagged; either make that path abstain/compare a safe base representation, or qualify the documentation for unverifiable bases.</violation>
</file>

<file name="packages/opencode/src/altimate/review/format.ts">

<violation number="1" location="packages/opencode/src/altimate/review/format.ts:149">
P3: The bundled action does not pass `--output`, so this artifact is never created; point readers to the job log or explicit `altimate review --json --output <file>` usage.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/format.ts:160">
P2: This normalization misses findings whose producer puts the model or filename outside a `model: ` prefix, so the promised cross-model grouping fails for rules such as removed-test findings. Group by a stable rule identity or normalize those producer-specific location fields before building the key.</violation>

<violation number="3" location="packages/opencode/src/altimate/review/format.ts:175">
P2: This groups three findings from one model and renders only the first body as “across 1 model(s)”. Use the distinct model/file count for the `GROUP_MIN` threshold so repeated issues within one model stay separate.</violation>
</file>

<file name="packages/opencode/src/altimate/review/run.ts">

<violation number="1" location="packages/opencode/src/altimate/review/run.ts:322">
P2: Unrecognized manifest adapters are treated as known and suppress all catalog portability findings by default. Restrict automatic suppression to dialects recognized by `ADAPTER_DIALECT`, while keeping explicit `dialect` overrides authoritative.</violation>
</file>

<file name="packages/opencode/src/altimate/review/orchestrate.ts">

<violation number="1" location="packages/opencode/src/altimate/review/orchestrate.ts:253">
P3: Build `commentableLines` sets only for files with findings carrying `startLine`; this eagerly parses every changed diff even when no inline comment can be emitted.</violation>
</file>

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

View guided diff | Turn on auto-fix | Re-trigger cubic

}

/** Headline of a review this bot posted (see verdictHeadline). */
const BOT_REVIEW_RE = /(Approved|Reviewed with comments|Changes requested)\b.*\((trivial|lite|full) tier/

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: BOT_REVIEW_RE classifies Changes requested as non-blocking. After a gate run requests changes, a clean rerun can hit this guard and return without resolving the old blocking review; track review state and dismiss or resolve that review before skipping.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/post-github.ts, line 65:

<comment>`BOT_REVIEW_RE` classifies `Changes requested` as non-blocking. After a gate run requests changes, a clean rerun can hit this guard and return without resolving the old blocking review; track review state and dismiss or resolve that review before skipping.</comment>

<file context>
@@ -54,8 +54,16 @@ export interface PostResult {
 }
 
+/** Headline of a review this bot posted (see verdictHeadline). */
+const BOT_REVIEW_RE = /(Approved|Reviewed with comments|Changes requested)\b.*\((trivial|lite|full) tier/
+
 export async function postGitHubReview(env: VerdictEnvelope, target: GitHubTarget): Promise<PostResult> {
</file context>

octo.paginate(octo.rest.pulls.listReviews, { owner, repo, pull_number: prNumber, per_page: 100 }),
])
posted = postedFindingIds(reviewComments.map((c) => c.body))
priorBotReview = reviews.some((r) => BOT_REVIEW_RE.test(r.body ?? ""))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: priorBotReview accepts any review whose free text resembles the headline; it never verifies the author or a bot marker. A human or another automation can therefore suppress this run's non-blocking review when no new inline comments exist; use an authenticated reviewer identity or unique marker.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/post-github.ts, line 108:

<comment>`priorBotReview` accepts any review whose free text resembles the headline; it never verifies the author or a bot marker. A human or another automation can therefore suppress this run's non-blocking review when no new inline comments exist; use an authenticated reviewer identity or unique marker.</comment>

<file context>
@@ -85,9 +93,34 @@ export async function postGitHubReview(env: VerdictEnvelope, target: GitHubTarge
+      octo.paginate(octo.rest.pulls.listReviews, { owner, repo, pull_number: prNumber, per_page: 100 }),
+    ])
+    posted = postedFindingIds(reviewComments.map((c) => c.body))
+    priorBotReview = reviews.some((r) => BOT_REVIEW_RE.test(r.body ?? ""))
+  } catch {
+    /* listing failed: post everything rather than nothing */
</file context>

- **No re-posting on reruns:** each inline comment carries the finding's
fingerprint, so a push does not repeat findings already on the PR, and a
non-blocking rerun with nothing new only updates the summary.
- **PII:** flagged only for PII columns the change newly exposes, not for

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This bullet overstates the new-only PII guarantee when the base lacks compiled SQL. The fallback emits all head-side PII because engineOldSql is unavailable, so an existing PII column can still be flagged; either make that path abstain/compare a safe base representation, or qualify the documentation for unverifiable bases.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/docs/usage/dbt-pr-review.md, line 297:

<comment>This bullet overstates the new-only PII guarantee when the base lacks compiled SQL. The fallback emits all head-side PII because `engineOldSql` is unavailable, so an existing PII column can still be flagged; either make that path abstain/compare a safe base representation, or qualify the documentation for unverifiable bases.</comment>

<file context>
@@ -279,6 +281,22 @@ rubric:
+- **No re-posting on reruns:** each inline comment carries the finding's
+  fingerprint, so a push does not repeat findings already on the PR, and a
+  non-blocking rerun with nothing new only updates the summary.
+- **PII:** flagged only for PII columns the change newly exposes, not for
+  columns the base already surfaced.
+
</file context>

* blanked, so the same rule on different models/columns shares a key. */
function ruleTitle(f: Finding): string {
const t = f.model && f.title.startsWith(`${f.model}: `) ? f.title.slice(f.model.length + 2) : f.title
return t.replace(/`[^`]*`/g, "`…`")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This normalization misses findings whose producer puts the model or filename outside a model: prefix, so the promised cross-model grouping fails for rules such as removed-test findings. Group by a stable rule identity or normalize those producer-specific location fields before building the key.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/format.ts, line 160:

<comment>This normalization misses findings whose producer puts the model or filename outside a `model: ` prefix, so the promised cross-model grouping fails for rules such as removed-test findings. Group by a stable rule identity or normalize those producer-specific location fields before building the key.</comment>

<file context>
@@ -115,7 +109,90 @@ export function renderSummary(env: VerdictEnvelope): string {
+ *  blanked, so the same rule on different models/columns shares a key. */
+function ruleTitle(f: Finding): string {
+  const t = f.model && f.title.startsWith(`${f.model}: `) ? f.title.slice(f.model.length + 2) : f.title
+  return t.replace(/`[^`]*`/g, "`…`")
+}
+
</file context>

}
const out: Array<{ text: string; count: number }> = []
for (const g of groups.values()) {
if (g.length >= GROUP_MIN) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This groups three findings from one model and renders only the first body as “across 1 model(s)”. Use the distinct model/file count for the GROUP_MIN threshold so repeated issues within one model stay separate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/format.ts, line 175:

<comment>This groups three findings from one model and renders only the first body as “across 1 model(s)”. Use the distinct model/file count for the `GROUP_MIN` threshold so repeated issues within one model stay separate.</comment>

<file context>
@@ -115,7 +109,90 @@ export function renderSummary(env: VerdictEnvelope): string {
+  }
+  const out: Array<{ text: string; count: number }> = []
+  for (const g of groups.values()) {
+    if (g.length >= GROUP_MIN) {
+      const names = [...new Set(g.map((f) => f.model ?? f.file))]
+      const shown = names.slice(0, 8).map((n) => `\`${n}\``).join(", ")
</file context>
Suggested change
if (g.length >= GROUP_MIN) {
if (new Set(g.map((f) => f.model ?? f.file)).size >= GROUP_MIN) {

// analyzed as the snowflake default — wrong-dialect portability suppression).
if (!config.dialect) config.dialect = (await detectDialect(manifestAbs)) ?? "snowflake"
const detectedDialect = config.dialect ? undefined : await detectDialect(manifestAbs)
const dialectKnown = !!config.dialect || !!detectedDialect

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Unrecognized manifest adapters are treated as known and suppress all catalog portability findings by default. Restrict automatic suppression to dialects recognized by ADAPTER_DIALECT, while keeping explicit dialect overrides authoritative.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/run.ts, line 322:

<comment>Unrecognized manifest adapters are treated as known and suppress all catalog portability findings by default. Restrict automatic suppression to dialects recognized by `ADAPTER_DIALECT`, while keeping explicit `dialect` overrides authoritative.</comment>

<file context>
@@ -318,7 +318,9 @@ export async function reviewPullRequest(opts: ReviewPullRequestOptions): Promise
   // analyzed as the snowflake default — wrong-dialect portability suppression).
-  if (!config.dialect) config.dialect = (await detectDialect(manifestAbs)) ?? "snowflake"
+  const detectedDialect = config.dialect ? undefined : await detectDialect(manifestAbs)
+  const dialectKnown = !!config.dialect || !!detectedDialect
+  if (!config.dialect) config.dialect = detectedDialect ?? "snowflake"
 
</file context>
Suggested change
const dialectKnown = !!config.dialect || !!detectedDialect
const dialectKnown = !!config.dialect || (!!detectedDialect && Object.values(ADAPTER_DIALECT).includes(detectedDialect))

const env = buildEnvelope({ findings: many, tier: "full", mode: "comment" })
const md = renderSummary(env)
expect(md.length).toBeLessThanOrEqual(SUMMARY_BUDGET_CHARS)
expect(md).toContain("m0: distinct issue number 0")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This test does not verify the claimed severity ordering: toContain passes regardless of where m0 appears. Assert that a critical finding appears before a suggestion (and, ideally, that all three critical findings are shown) so a renderer regression cannot pass.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/test/altimate/review-noise.test.ts, line 175:

<comment>This test does not verify the claimed severity ordering: `toContain` passes regardless of where `m0` appears. Assert that a critical finding appears before a suggestion (and, ideally, that all three critical findings are shown) so a renderer regression cannot pass.</comment>

<file context>
@@ -0,0 +1,235 @@
+    const env = buildEnvelope({ findings: many, tier: "full", mode: "comment" })
+    const md = renderSummary(env)
+    expect(md.length).toBeLessThanOrEqual(SUMMARY_BUDGET_CHARS)
+    expect(md).toContain("m0: distinct issue number 0")
+    expect(md).toMatch(/… \d+ more finding\(s\) not shown/)
+    // Local output is complete.
</file context>

if (hidden) {
lines.push(
`> … ${hidden} more finding(s) not shown to keep this comment short — the full list is in the verdict envelope ` +
"(`altimate review --json`, or the action's `--output` artifact).",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The bundled action does not pass --output, so this artifact is never created; point readers to the job log or explicit altimate review --json --output <file> usage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/format.ts, line 149:

<comment>The bundled action does not pass `--output`, so this artifact is never created; point readers to the job log or explicit `altimate review --json --output <file>` usage.</comment>

<file context>
@@ -115,7 +109,90 @@ export function renderSummary(env: VerdictEnvelope): string {
+  if (hidden) {
+    lines.push(
+      `> … ${hidden} more finding(s) not shown to keep this comment short — the full list is in the verdict envelope ` +
+        "(`altimate review --json`, or the action's `--output` artifact).",
+      "",
+    )
</file context>
Suggested change
"(`altimate review --json`, or the action's `--output` artifact).",
"(`altimate review --json`, or the job log; use `--output <file>` when running the CLI directly).",

* whole batch of inline comments.
*/
export function anchorToDiff(findings: Finding[], files: ChangedFile[]): Finding[] {
const lines = new Map(files.map((f) => [f.path, commentableLines(f.diff)]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Build commentableLines sets only for files with findings carrying startLine; this eagerly parses every changed diff even when no inline comment can be emitted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/orchestrate.ts, line 253:

<comment>Build `commentableLines` sets only for files with findings carrying `startLine`; this eagerly parses every changed diff even when no inline comment can be emitted.</comment>

<file context>
@@ -206,6 +206,57 @@ export interface OrchestrateInput {
+ * whole batch of inline comments.
+ */
+export function anchorToDiff(findings: Finding[], files: ChangedFile[]): Finding[] {
+  const lines = new Map(files.map((f) => [f.path, commentableLines(f.diff)]))
+  return findings.map((f) => {
+    if (typeof f.startLine !== "number") return f
</file context>
Suggested change
const lines = new Map(files.map((f) => [f.path, commentableLines(f.diff)]))
const filesNeedingLines = new Set(findings.filter((f) => typeof f.startLine === "number").map((f) => f.file))
const lines = new Map(
files.filter((f) => filesNeedingLines.has(f.path)).map((f) => [f.path, commentableLines(f.diff)]),
)

…s on the project's own engine; moved files are named

- catalog rules carry `portability: { worksOn? }` instead of the comment text being matched: `bq-legacy-sql` ("use Standard SQL") was being dropped on BigQuery projects
- with a known engine, portability advice is dropped only when that engine supports the construct; `DISTINCT ON` on Snowflake or `GROUP BY alias` on SQL Server stays, because it errors there
- files moved without edits are named in the comment, with the caveat that folder-level `dbt_project.yml` config can still change how they build
- a run with no model files (yml-only, pure moves) is no longer labeled lint-only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kilo-code-bot

kilo-code-bot Bot commented Nov 13, 2026

Copy link
Copy Markdown

Kilo Code Review could not run — your account is out of credits.

Add credits or switch to a free model to enable reviews on this change.

@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.

4 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/test/altimate/review-noise.test.ts">

<violation number="1" location="packages/opencode/test/altimate/review-noise.test.ts:262">
P3: This test does not exercise the “not a lint-only run” behavior it names. Build the envelope through the moved-only review path, or add an assertion backed by a fixture that would fail if that path sets `degraded` or renders the lint-only banner.</violation>
</file>

<file name="packages/opencode/src/altimate/review/rule-catalog.ts">

<violation number="1" location="packages/opencode/src/altimate/review/rule-catalog.ts:1715">
P2: `portability: {}` suppresses the PIVOT/UNPIVOT warning for every known engine, even though this rule provides no verified support set. Populate `worksOn` with verified engines or retain portability findings when support is unknown so engine-specific failures are not hidden.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/rule-catalog.ts:1888">
P2: Add DuckDB to `worksOn`; otherwise the new single-engine suppression still reports `FETCH FIRST` as non-portable on a dialect that supports it.</violation>
</file>

<file name="packages/opencode/src/altimate/review/format.ts">

<violation number="1" location="packages/opencode/src/altimate/review/format.ts:221">
P2: This note incorrectly claims every unchanged move has SQL that was not re-reviewed. `movedUnchangedFiles` also counts unchanged renames of YAML and other files, so the summary is misleading for those moves; describe unchanged content generically or restrict the counter to model files.</violation>
</file>

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

View guided diff | Turn on auto-fix | Re-trigger cubic

"PIVOT/UNPIVOT is dialect-specific",
"`PIVOT`/`UNPIVOT` syntax varies by warehouse and is hard to test; consider conditional aggregation.",
{ added: /\b(pivot|unpivot)\s*\(/i },
{ added: /\b(pivot|unpivot)\s*\(/i, portability: {} },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: portability: {} suppresses the PIVOT/UNPIVOT warning for every known engine, even though this rule provides no verified support set. Populate worksOn with verified engines or retain portability findings when support is unknown so engine-specific failures are not hidden.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/rule-catalog.ts, line 1715:

<comment>`portability: {}` suppresses the PIVOT/UNPIVOT warning for every known engine, even though this rule provides no verified support set. Populate `worksOn` with verified engines or retain portability findings when support is unknown so engine-specific failures are not hidden.</comment>

<file context>
@@ -1703,7 +1712,7 @@ const STYLE: Rule[] = [
     "PIVOT/UNPIVOT is dialect-specific",
     "`PIVOT`/`UNPIVOT` syntax varies by warehouse and is hard to test; consider conditional aggregation.",
-    { added: /\b(pivot|unpivot)\s*\(/i },
+    { added: /\b(pivot|unpivot)\s*\(/i, portability: {} },
     { add: "from t pivot(sum(x) for k in ('a'))" },
     { add: "sum(case when k='a' then x end)" },
</file context>

Comment on lines +221 to +222
`${env.scope.movedUnchangedFiles} file(s) moved without edits; their SQL was not re-reviewed. ` +
"Folder-level config in `dbt_project.yml` (`+schema`, `+materialized`, `+tags`) can still change where and how a moved model builds.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This note incorrectly claims every unchanged move has SQL that was not re-reviewed. movedUnchangedFiles also counts unchanged renames of YAML and other files, so the summary is misleading for those moves; describe unchanged content generically or restrict the counter to model files.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/format.ts, line 221:

<comment>This note incorrectly claims every unchanged move has SQL that was not re-reviewed. `movedUnchangedFiles` also counts unchanged renames of YAML and other files, so the summary is misleading for those moves; describe unchanged content generically or restrict the counter to model files.</comment>

<file context>
@@ -216,6 +216,11 @@ export function coverageNotes(env: VerdictEnvelope): string[] {
     notes.push(`${env.scope.outOfScopeFiles} changed file(s) outside the dbt project were not reviewed.`)
+  if (env.scope?.movedUnchangedFiles)
+    notes.push(
+      `${env.scope.movedUnchangedFiles} file(s) moved without edits; their SQL was not re-reviewed. ` +
+        "Folder-level config in `dbt_project.yml` (`+schema`, `+materialized`, `+tags`) can still change where and how a moved model builds.",
+    )
</file context>
Suggested change
`${env.scope.movedUnchangedFiles} file(s) moved without edits; their SQL was not re-reviewed. ` +
"Folder-level config in `dbt_project.yml` (`+schema`, `+materialized`, `+tags`) can still change where and how a moved model builds.",
`${env.scope.movedUnchangedFiles} file(s) moved without edits; their unchanged contents were not re-reviewed. ` +
"For moved models, folder-level config in `dbt_project.yml` (`+schema`, `+materialized`, `+tags`) can still change where and how they build.",

"FETCH FIRST portability",
"`FETCH FIRST n ROWS` isn't supported everywhere; `LIMIT` (with ORDER BY) is more portable.",
{ added: /fetch\s+first\s+\d+/i },
{ added: /fetch\s+first\s+\d+/i, portability: { worksOn: ["postgres", "snowflake", "oracle", "trino"] } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Add DuckDB to worksOn; otherwise the new single-engine suppression still reports FETCH FIRST as non-portable on a dialect that supports it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/src/altimate/review/rule-catalog.ts, line 1888:

<comment>Add DuckDB to `worksOn`; otherwise the new single-engine suppression still reports `FETCH FIRST` as non-portable on a dialect that supports it.</comment>

<file context>
@@ -1876,7 +1885,7 @@ const MISC: Rule[] = [
     "FETCH FIRST portability",
     "`FETCH FIRST n ROWS` isn't supported everywhere; `LIMIT` (with ORDER BY) is more portable.",
-    { added: /fetch\s+first\s+\d+/i },
+    { added: /fetch\s+first\s+\d+/i, portability: { worksOn: ["postgres", "snowflake", "oracle", "trino"] } },
     { add: "fetch first 10 rows only" },
     { add: "limit 10" },
</file context>
Suggested change
{ added: /fetch\s+first\s+\d+/i, portability: { worksOn: ["postgres", "snowflake", "oracle", "trino"] } },
{ added: /fetch\s+first\s+\d+/i, portability: { worksOn: ["postgres", "snowflake", "oracle", "trino", "duckdb"] } },


describe("moved files", () => {
test("moves are named in the comment with the folder-config caveat, and are not a lint-only run", async () => {
const env = buildEnvelope({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This test does not exercise the “not a lint-only run” behavior it names. Build the envelope through the moved-only review path, or add an assertion backed by a fixture that would fail if that path sets degraded or renders the lint-only banner.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/opencode/test/altimate/review-noise.test.ts, line 262:

<comment>This test does not exercise the “not a lint-only run” behavior it names. Build the envelope through the moved-only review path, or add an assertion backed by a fixture that would fail if that path sets `degraded` or renders the lint-only banner.</comment>

<file context>
@@ -227,9 +248,24 @@ describe("portability noise", () => {
+
+describe("moved files", () => {
+  test("moves are named in the comment with the folder-config caveat, and are not a lint-only run", async () => {
+    const env = buildEnvelope({
+      findings: [],
+      tier: "trivial",
</file context>

This branch has not been deployed

No deployments
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.

1 participant