Visitar URL original
fix(review): failures are never clean: engine errors reported, per-file isolation, PII abstention, gate dismissal by RaghwendraSingh · Pull Request #1723 · AltimateAI/altimate-code · GitHub
Skip to content

fix(review): failures are never clean: engine errors reported, per-file isolation, PII abstention, gate dismissal - #1723

Open
RaghwendraSingh wants to merge 2 commits into
fix/review-verdict-trustfrom
fix/review-failures-gate
Open

RaghwendraSingh wants to merge 2 commits into
fix/review-verdict-trustfrom
fix/review-failures-gate

Conversation

@RaghwendraSingh

@RaghwendraSingh RaghwendraSingh commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #

Stacked on #1722 (← #1721 ← #1720 ← #1719); merge in that order.

Type of change

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

What does this PR do?

A broken review could look like a clean one:

  • Silent engine failures: every ReviewRunner method caught its errors and returned empty. A failed native core read as "no findings" and could produce APPROVE.
  • One file aborts everything: the per-model precompute and the lane Promise.all had no error handling, so one file throwing rejected the whole review.
  • PII "no columns" ambiguity: an empty PII result looked the same whether the model was clean or core couldn't parse it.
  • Gate never clears: a blocking review stayed blocking after a clean rerun.

Fixes:

  • Engine errors: the dispatcher runner records each throwing call as method, first reason and count (13 call sites), and still returns its empty result. These are signed into the envelope as engineErrors, named in the comment, and counted in review_run.engine_errors. An empty review with engine errors is COMMENT, never APPROVE.
  • Per-file isolation: a failure preparing one file, or a lane throwing for one file, marks that file unverified with the reason; the rest is reviewed. The PR-level lanes (sibling consistency, topology equivalence) and the core-ran check are guarded too. A test caught the core-ran check aborting the review after a lane failure.
  • PII abstention: detectPii returns abstained when core couldn't analyse the SQL (check didn't run, or pii.parse_error). The model is listed as not fully verified.
  • Gate lifecycle: when a rerun's event is non-blocking, earlier altimate CHANGES_REQUESTED reviews are dismissed with a pointer to the new verdict. postGitHubReview takes an optional client, so dedup, dismissal and skip are tested without the network.

Not in this PR: wiring the break-glass override (applyOverride) behind maintainer authorization. That needs a decision on who may override and where the audit record lives.

Follow-up after an adversarial review (commit 6736c7c):

  • A failed run never clears a block. A stale CHANGES_REQUESTED review is dismissed only when the new run completed (no engineErrors, no file that failed). Before, an engine crash on the next push made the findings disappear, and the block was dismissed.
  • Shallow CI checkouts work. In CI, a missing target branch is fetched from origin, and a shallow history is deepened (50, 200, 1000) until the merge-base exists. The default actions/checkout therefore works without fetch-depth: 0. It's still an error when that fails.
  • Upgrade note for gate users in the docs. Pattern-rule findings now comment instead of blocking, unless they're listed in rubric.blockingRules.
  • Tests: a crashed run and a failed file each leave the block in place; resolving the base from a real git clone --depth 1 with only GITHUB_BASE_REF set.

How did you verify your code works?

  • New test/altimate/review-failures-gate.test.ts (9 tests):
    • engine failure → COMMENT with a note; engineErrors signed and tamper-evident; an empty clean run still approves
    • unreadable content and a throwing lane each degrade only their file
    • PII abstention reported
    • with a fake GitHub client: a clean rerun dismisses only the bot's blocking review; a still-blocking rerun doesn't dismiss; already-posted findings aren't re-posted and no repeat review is created
  • 1,892 tests across the review files and release gates pass. bun typecheck is clean.

Cost side effects

None. At most one dismissReview call per stale blocking review on a non-blocking rerun.

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 review so engine failures, per-file crashes, and PII parse errors can no longer read as a clean pass, a stale blocking review is cleared only by a completed non-blocking rerun, and a shallow CI checkout can fetch the base it needs.

Bug Fixes

  • Dispatcher runner records each throwing engine call (method, first reason, count) instead of silently returning empty; errors are signed into the envelope as engineErrors, shown in the comment, and counted in telemetry, and an empty review with engine errors is COMMENT, never APPROVE.
  • A failure preparing one file, or a lane throwing for one file, marks that file unverified; the rest of the PR is still reviewed, and PR-level lanes and the core-ran check are guarded the same way.
  • detectPii reports abstained when core couldn't analyse the SQL (check didn't run or its PII parser errored), listing the model as not fully verified instead of PII-clean.
  • A non-blocking rerun dismisses earlier blocking altimate reviews only when the new run completed — a crash no longer clears a gate (pull-requests: write required); the GitHub client is injectable so dedup, dismissal, and skip rules are tested without the network.
  • In CI, a missing target branch is fetched from origin and history deepened until the merge-base exists, so the default shallow actions/checkout works without fetch-depth: 0; if that fails, the review errors instead of diffing another branch.
  • Docs: pattern-rule findings no longer block gate setups unless listed in rubric.blockingRules.

Written for commit 6736c7c. Summary will update on new commits.

View guided diff Turn on auto-fix

@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: 64935f3f-148e-4401-829f-8d3091914be7

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.

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

10 issues found across 10 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/format.ts">

<violation number="1" location="packages/opencode/src/altimate/review/format.ts:210">
P3: Grouping by the full reason text fragments per-file error messages into one note each, so a widespread failure produces many notes. Group by a normalized reason category, or cap the number of distinct notes, so the comment stays compact.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/format.ts:220">
P3: `e.reason` is not sanitized here, and PR-level lane errors can contain newlines. That breaks the `> ℹ️` blockquote in the posted comment. Collapse whitespace before interpolating the reason.</violation>
</file>

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

<violation number="1" location="packages/opencode/src/altimate/review/runner.ts:269">
P1: A failed `altimate_core.check` call is treated as a successful run with no PII. `runCheck` never checks `res.success`, so it sets `ran: true` and `piiAbstained: false`, and `detectPii` reports the file as clean. Check `res.success === false` (or a missing `data`) and return `ran: false` with `piiAbstained: true`.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/runner.ts:330">
P1: Most engine failures never reach `fail()`. The `altimate_core.*` handlers return `{ success: false }` rather than throwing, so `engineErrors`, the comment, and telemetry stay empty. Record a failure whenever `res.success === false` (for example, a helper that wraps `Dispatcher.call`) in each method, not only in `catch`.</violation>

<violation number="3" location="packages/opencode/src/altimate/review/runner.ts:572">
P2: The base-side PII comparison ignores `detectPii(...).abstained`, so an unparseable base is treated as having no PII and can create a false new-exposure critical finding. Propagate the base abstention as unverified and skip the new-only comparison until the base can be analyzed.</violation>
</file>

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

<violation number="1" location="packages/opencode/src/altimate/review/orchestrate.ts:1349">
P2: Per-file isolation is incomplete: the new `tasks` catch does not cover the synchronous lanes (`lineageBreakageLane`, `detectModelPatterns`) or the schema.yml detector. A throw from any of them still rejects `runReview` and aborts the whole review. Wrap each per-file lane call in the same try/catch that `tasks` uses, so the failure goes to `failedFile`.</violation>
</file>

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

<violation number="1" location="packages/opencode/src/altimate/review/post-github.ts:123">
P1: Do not identify reviews for dismissal solely by the headline text. A human or another integration can match this regex, causing a COMMENT rerun to dismiss an unrelated blocking review; require an immutable Altimate marker and/or verify the review author before adding its ID to `staleBlocking`.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/post-github.ts:146">
P2: A failed dismissal must not fall through to the no-new-review skip path. Track failed IDs and only skip after every stale blocking review is dismissed; otherwise surface the incomplete cleanup and retry or leave an explicit failure signal instead of claiming no review was needed.</violation>
</file>

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

<violation number="1" location="packages/opencode/src/altimate/review/verdict.ts:256">
P2: Unverified file and PII-abstention failures can still produce a clean `APPROVE`. Include `scope.unverified` in the empty-review `COMMENT` gate so an incomplete review cannot be mistaken for a clean approval.</violation>
</file>

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

<violation number="1" location="docs/docs/usage/dbt-pr-review.md:317">
P2: The docs overstate the engine-error downgrade: a gate-mode run with an engine error and another blocking finding can still return `REQUEST_CHANGES`. Limit this statement to otherwise empty reviews, which are downgraded from APPROVE to COMMENT.</violation>
</file>

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

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

return { grade: data.overall_grade ?? data.grade }
} catch {
} catch (err) {
fail("grade", err)

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: Most engine failures never reach fail(). The altimate_core.* handlers return { success: false } rather than throwing, so engineErrors, the comment, and telemetry stay empty. Record a failure whenever res.success === false (for example, a helper that wraps Dispatcher.call) in each method, not only in catch.

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/runner.ts, line 330:

<comment>Most engine failures never reach `fail()`. The `altimate_core.*` handlers return `{ success: false }` rather than throwing, so `engineErrors`, the comment, and telemetry stay empty. Record a failure whenever `res.success === false` (for example, a helper that wraps `Dispatcher.call`) in each method, not only in `catch`.</comment>

<file context>
@@ -310,7 +326,8 @@ export function createDispatcherRunner(opts: DispatcherRunnerOptions): ReviewRun
         return { grade: data.overall_grade ?? data.grade }
-      } catch {
+      } catch (err) {
+        fail("grade", err)
         return {}
       }
</file context>

issues,
piiColumns: [...new Set(piiColumns)],
ran: true,
piiAbstained: !!(data.pii as any)?.parse_error,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: A failed altimate_core.check call is treated as a successful run with no PII. runCheck never checks res.success, so it sets ran: true and piiAbstained: false, and detectPii reports the file as clean. Check res.success === false (or a missing data) and return ran: false with piiAbstained: true.

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/runner.ts, line 269:

<comment>A failed `altimate_core.check` call is treated as a successful run with no PII. `runCheck` never checks `res.success`, so it sets `ran: true` and `piiAbstained: false`, and `detectPii` reports the file as clean. Check `res.success === false` (or a missing `data`) and return `ran: false` with `piiAbstained: true`.</comment>

<file context>
@@ -252,8 +262,14 @@ export function createDispatcherRunner(opts: DispatcherRunnerOptions): ReviewRun
+        issues,
+        piiColumns: [...new Set(piiColumns)],
+        ran: true,
+        piiAbstained: !!(data.pii as any)?.parse_error,
+      }
+    } catch (err) {
</file context>

priorBotReview = reviews.some((r) => BOT_REVIEW_RE.test(r.body ?? ""))
const botReviews = reviews.filter((r) => BOT_REVIEW_RE.test(r.body ?? ""))
priorBotReview = botReviews.length > 0
staleBlocking = botReviews.filter((r) => r.state === "CHANGES_REQUESTED").map((r) => r.id)

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: Do not identify reviews for dismissal solely by the headline text. A human or another integration can match this regex, causing a COMMENT rerun to dismiss an unrelated blocking review; require an immutable Altimate marker and/or verify the review author before adding its ID to staleBlocking.

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 123:

<comment>Do not identify reviews for dismissal solely by the headline text. A human or another integration can match this regex, causing a COMMENT rerun to dismiss an unrelated blocking review; require an immutable Altimate marker and/or verify the review author before adding its ID to `staleBlocking`.</comment>

<file context>
@@ -99,20 +111,45 @@ export async function postGitHubReview(env: VerdictEnvelope, target: GitHubTarge
-    priorBotReview = reviews.some((r) => BOT_REVIEW_RE.test(r.body ?? ""))
+    const botReviews = reviews.filter((r) => BOT_REVIEW_RE.test(r.body ?? ""))
+    priorBotReview = botReviews.length > 0
+    staleBlocking = botReviews.filter((r) => r.state === "CHANGES_REQUESTED").map((r) => r.id)
   } catch {
     /* listing failed: post everything rather than nothing */
</file context>

// A lane that throws drops only its own findings for this file, recorded
// as unverified — never the whole review.
let laneFailed = false
const settled = await Promise.all(

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: Per-file isolation is incomplete: the new tasks catch does not cover the synchronous lanes (lineageBreakageLane, detectModelPatterns) or the schema.yml detector. A throw from any of them still rejects runReview and aborts the whole review. Wrap each per-file lane call in the same try/catch that tasks uses, so the failure goes to failedFile.

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 1349:

<comment>Per-file isolation is incomplete: the new `tasks` catch does not cover the synchronous lanes (`lineageBreakageLane`, `detectModelPatterns`) or the schema.yml detector. A throw from any of them still rejects `runReview` and aborts the whole review. Wrap each per-file lane call in the same try/catch that `tasks` uses, so the failure goes to `failedFile`.</comment>

<file context>
@@ -1318,20 +1343,32 @@ export async function runReview(input: OrchestrateInput): Promise<VerdictEnvelop
+    // A lane that throws drops only its own findings for this file, recorded
+    // as unverified — never the whole review.
+    let laneFailed = false
+    const settled = await Promise.all(
+      tasks.map((t) =>
+        t.catch((err) => {
</file context>

// An engine failure means some checks did not run: "no findings" is then
// unknown, not clean, so the verdict cannot be APPROVE.
const computed = computeIdealVerdict(input.findings, rubric)
const ideal = computed === "APPROVE" && input.engineErrors?.length ? "COMMENT" : computed

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: Unverified file and PII-abstention failures can still produce a clean APPROVE. Include scope.unverified in the empty-review COMMENT gate so an incomplete review cannot be mistaken for a clean approval.

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/verdict.ts, line 256:

<comment>Unverified file and PII-abstention failures can still produce a clean `APPROVE`. Include `scope.unverified` in the empty-review `COMMENT` gate so an incomplete review cannot be mistaken for a clean approval.</comment>

<file context>
@@ -244,7 +250,10 @@ function summarize(findings: Finding[], degraded: boolean): VerdictEnvelope["sum
+  // An engine failure means some checks did not run: "no findings" is then
+  // unknown, not clean, so the verdict cannot be APPROVE.
+  const computed = computeIdealVerdict(input.findings, rubric)
+  const ideal = computed === "APPROVE" && input.engineErrors?.length ? "COMMENT" : computed
   const verdict = applyMode(ideal, input.mode)
   const degraded = input.degraded ?? input.findings.some((f) => f.degraded)
</file context>
Suggested change
const ideal = computed === "APPROVE" && input.engineErrors?.length ? "COMMENT" : computed
const ideal = computed === "APPROVE" && (input.engineErrors?.length || input.scope?.unverified?.length) ? "COMMENT" : computed

// "no PII": say so.
const result = await runCheck(sql)
return { columns: result.piiColumns ?? [] }
return { columns: result.piiColumns ?? [], abstained: !result.ran || result.piiAbstained === true }

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: The base-side PII comparison ignores detectPii(...).abstained, so an unparseable base is treated as having no PII and can create a false new-exposure critical finding. Propagate the base abstention as unverified and skip the new-only comparison until the base can be analyzed.

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/runner.ts, line 572:

<comment>The base-side PII comparison ignores `detectPii(...).abstained`, so an unparseable base is treated as having no PII and can create a false new-exposure critical finding. Propagate the base abstention as unverified and skip the new-only comparison until the base can be analyzed.</comment>

<file context>
@@ -529,16 +556,24 @@ export function createDispatcherRunner(opts: DispatcherRunnerOptions): ReviewRun
+      // "no PII": say so.
       const result = await runCheck(sql)
-      return { columns: result.piiColumns ?? [] }
+      return { columns: result.piiColumns ?? [], abstained: !result.ran || result.piiAbstained === true }
+    },
+
</file context>

columns the base already surfaced. A model the PII check could not analyse is
listed as not fully verified.
- **Failures are never a clean pass:** if an engine check throws, the comment
names it and the verdict is at most COMMENT, never APPROVE (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.

P2: The docs overstate the engine-error downgrade: a gate-mode run with an engine error and another blocking finding can still return REQUEST_CHANGES. Limit this statement to otherwise empty reviews, which are downgraded from APPROVE to COMMENT.

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 317:

<comment>The docs overstate the engine-error downgrade: a gate-mode run with an engine error and another blocking finding can still return `REQUEST_CHANGES`. Limit this statement to otherwise empty reviews, which are downgraded from APPROVE to COMMENT.</comment>

<file context>
@@ -311,7 +311,14 @@ rubric:
+  columns the base already surfaced. A model the PII check could not analyse is
+  listed as not fully verified.
+- **Failures are never a clean pass:** if an engine check throws, the comment
+  names it and the verdict is at most COMMENT, never APPROVE (envelope
+  `engineErrors`). A failure on one file marks that file unverified and the rest
+  of the PR is still reviewed.
</file context>
Suggested change
names it and the verdict is at most COMMENT, never APPROVE (envelope
names it; an otherwise empty review is COMMENT rather than APPROVE (envelope

"Run `dbt compile` (and compile the base into `target-base/`) for equivalence and lineage checks.",
)
const byReason = new Map<string, string[]>()
for (const u of unverified) byReason.set(u.reason, [...(byReason.get(u.reason) ?? []), u.path])

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: Grouping by the full reason text fragments per-file error messages into one note each, so a widespread failure produces many notes. Group by a normalized reason category, or cap the number of distinct notes, so the comment stays compact.

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 210:

<comment>Grouping by the full reason text fragments per-file error messages into one note each, so a widespread failure produces many notes. Group by a normalized reason category, or cap the number of distinct notes, so the comment stays compact.</comment>

<file context>
@@ -206,13 +206,19 @@ export function coverageNotes(env: VerdictEnvelope): string[] {
-        "Run `dbt compile` (and compile the base into `target-base/`) for equivalence and lineage checks.",
-    )
+  const byReason = new Map<string, string[]>()
+  for (const u of unverified) byReason.set(u.reason, [...(byReason.get(u.reason) ?? []), u.path])
+  for (const [reason, paths] of byReason) {
+    const head = paths.slice(0, 5).map((p) => `\`${p}\``).join(", ")
</file context>

notes.push(`${paths.length} model(s) not fully verified — ${reason}: ${head}${more}.${hint}`)
}
if (env.engineErrors?.length) {
const list = env.engineErrors.map((e) => `${e.method} ×${e.count} (${e.reason})`).join("; ")

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: e.reason is not sanitized here, and PR-level lane errors can contain newlines. That breaks the > ℹ️ blockquote in the posted comment. Collapse whitespace before interpolating the reason.

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 220:

<comment>`e.reason` is not sanitized here, and PR-level lane errors can contain newlines. That breaks the `> ℹ️` blockquote in the posted comment. Collapse whitespace before interpolating the reason.</comment>

<file context>
@@ -206,13 +206,19 @@ export function coverageNotes(env: VerdictEnvelope): string[] {
+    notes.push(`${paths.length} model(s) not fully verified — ${reason}: ${head}${more}.${hint}`)
+  }
+  if (env.engineErrors?.length) {
+    const list = env.engineErrors.map((e) => `${e.method} ×${e.count} (${e.reason})`).join("; ")
+    notes.push(`Some engine checks failed and did not run: ${list}. This verdict is not a clean approval.`)
   }
</file context>

const piiRes = engineNewSql ? await input.runner.detectPii(engineNewSql, dialect) : { columns: [] }
if (piiRes.abstained) unverified.push({ path: file.path, reason: "the PII check could not analyse this model" })
const pii = piiRes.columns
const complex = engineNewSql && input.runner.isComplex ? await input.runner.isComplex(engineNewSql) : false

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: The per-file try is too coarse — a failure in a supplementary call discards the whole file.

ctxByPath.set is the last statement of the block (line 1190), so a throw from input.runner.impact(...) (1184), detectPii(...) (1186) or isComplex(...) (1189) leaves the file out of ctxByPath entirely. isComplex in particular is only a tiering hint, yet its failure means the file gets no quality / semantic-change / dbt-patterns lanes, no lineage lane, and no blast-radius / PII / complexity signal for tiering (blastRadiusOf / touchesPiiOf / isComplexOf at 1227-1231) — zero review even though its SQL was read successfully.

Consider per-call fallbacks (impact → zeroed result, pii: [], complex: false) and always calling ctxByPath.set once the content has been fetched, so an enrichment failure degrades the file instead of skipping it.

const impact = await input.runner.impact(model)
if (impact.hasManifest) anyManifest = true
const piiRes = engineNewSql ? await input.runner.detectPii(engineNewSql, dialect) : { columns: [] }
if (piiRes.abstained) unverified.push({ path: file.path, reason: "the PII check could not analyse this model" })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: An abstained PII result is reported but then treated as "no PII" for tiering. pii is assigned piiRes.columns, which is empty, so touchesPiiOf (line 1230) returns false and the FULL-tier "PII column touched" hard floor cannot fire. On trivial/lite tiers pii_exposure is not in TIER_LANES, so piiLane never runs for this file either, leaving only the coarse regex twins. Abstention means unknown, not clean — consider promoting the file (or keeping the broad fallback) when abstained is true.

scope: ReviewScope.optional(),
/** Engine calls that failed during the review. Their checks did not run, so
* the verdict is never a clean APPROVE when this is present. */
engineErrors: z

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: An empty engineErrors array satisfies this schema, and buildEnvelope (line 256) tests input.engineErrors?.length, so engineErrors: [] still produces APPROVE — contradicting the field doc above ("the verdict is never a clean APPROVE when this is present"). runReview normalises empty to undefined, but buildEnvelope is exported and called directly (including from tests). Add .min(1) or normalise empty to undefined in buildEnvelope.

@kilo-code-bot

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

Copy link
Copy Markdown

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
packages/opencode/src/altimate/review/orchestrate.ts 1189 Per-file try is too coarse: a throw from impact/detectPii/isComplex (a tiering hint only) leaves the file out of ctxByPath, so it gets no lanes and no tiering signals despite its SQL being readable. Move ctxByPath.set before the enrichment calls and give each call a fallback.

SUGGESTION

File Line Issue
packages/opencode/src/altimate/review/orchestrate.ts 1187 abstained PII is recorded but stored as pii = [], so touchesPiiOf is false and the FULL-tier PII hard floor cannot fire; on trivial/lite tiers piiLane never runs either. Unknown PII should promote the file, not read as clean.
packages/opencode/src/altimate/review/verdict.ts 159 engineErrors: [] passes the schema and .length is falsy, so buildEnvelope({ engineErrors: [] }) still yields APPROVE, contradicting the field doc. Add .min(1) or normalise empty to undefined.
Files Reviewed (10 files)
  • docs/docs/usage/dbt-pr-review.md — no new issues
  • packages/opencode/src/altimate/review/format.ts — no new issues
  • packages/opencode/src/altimate/review/orchestrate.ts — 2 issues
  • packages/opencode/src/altimate/review/post-github.ts — no new issues (existing review comments cover the dismissal gating and bot-identification concerns)
  • packages/opencode/src/altimate/review/runner.ts — no new issues (existing review comments cover success: false handling and base-side PII abstention)
  • packages/opencode/src/altimate/review/telemetry.ts — no new issues
  • packages/opencode/src/altimate/review/verdict.ts — 1 issue
  • packages/opencode/src/altimate/telemetry/index.ts — no new issues
  • packages/opencode/src/cli/cmd/review.ts — no new issues
  • packages/opencode/test/altimate/review-failures-gate.test.ts — no new issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash-1017 · Input: 94.6K · Output: 23.2K · Cached: 1.4M

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

RaghwendraSingh and others added 2 commits November 13, 2026 11:08
…le isolation, PII abstention, gate dismissal

- The dispatcher runner records every engine call that throws (method,
  first reason, count) instead of silently returning empty. They are signed
  into the envelope (`engineErrors`), named in the comment, counted in
  `review_run.engine_errors`, and an empty review with engine errors is
  COMMENT, never APPROVE.
- A failure while preparing one file, or a lane throwing for one file, marks
  that file unverified with the reason; the rest of the PR is still reviewed.
  PR-level lanes and the core-ran check are guarded the same way.
- `detectPii` reports `abstained` when core could not analyse the SQL (check
  did not run, or its PII parser reported a parse error); the model is listed
  as not fully verified instead of passing as PII-clean.
- When a rerun no longer blocks, earlier blocking altimate reviews are
  dismissed so the gate does not stay closed. The GitHub client is injectable
  so dedup, dismissal and skip rules are tested without the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fetch the base; upgrade note for gate users

- a stale `CHANGES_REQUESTED` review is dismissed only when the new run completed (no `engineErrors`, no file that failed); an engine crash no longer unblocks a PR by making its findings disappear
- in CI, a missing target branch is fetched from `origin` and a shallow history deepened (50/200/1000) until the merge-base exists, so the default `actions/checkout` works without `fetch-depth: 0`; still an error when that fails
- docs: what changes for existing `gate` setups (pattern rules no longer block unless listed in `rubric.blockingRules`) and the new checkout behavior

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

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

3 issues found across 5 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/src/altimate/review/post-github.ts">

<violation number="1" location="packages/opencode/src/altimate/review/post-github.ts:67">
P3: The new `runCompleted` function was inserted between the existing `/** Headline of a review this bot posted */` doc comment and `BOT_REVIEW_RE`, so that comment now documents `runCompleted`. Move `runCompleted` above the headline comment, or move the headline comment back to `BOT_REVIEW_RE`.</violation>
</file>

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

<violation number="1" location="packages/opencode/src/altimate/review/git.ts:212">
P2: The deepen fetch adds `headSha` as a raw SHA request, which requires the remote to allow reachable SHA wants and can make otherwise-valid CI remotes reject the fetch. Remove this extra argument; the target-branch refspec is sufficient for deepening the shallow checkout.</violation>

<violation number="2" location="packages/opencode/src/altimate/review/git.ts:267">
P3: The result of `find()` inside `fetchTargetHistory` is thrown away and `find()` runs again here. Have `fetchTargetHistory` return the resolved base (or undefined) so the caller uses it directly, which also removes the boolean-plus-callback pattern.</violation>
</file>

Tip: Get a Slack DM when cubic finishes reviewing your PRs. Connect Slack

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

try {
if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return false
const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)

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: The deepen fetch adds headSha as a raw SHA request, which requires the remote to allow reachable SHA wants and can make otherwise-valid CI remotes reject the fetch. Remove this extra argument; the target-branch refspec is sufficient for deepening the shallow checkout.

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/git.ts, line 212:

<comment>The deepen fetch adds `headSha` as a raw SHA request, which requires the remote to allow reachable SHA wants and can make otherwise-valid CI remotes reject the fetch. Remove this extra argument; the target-branch refspec is sufficient for deepening the shallow checkout.</comment>

<file context>
@@ -191,6 +191,33 @@ async function mergeBase(a: string, b: string, cwd: string): Promise<string | un
+    try {
+      if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return false
+      const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
+      await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
+    } catch {
+      return false
</file context>
Suggested change
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], cwd)

}

/** Headline of a review this bot posted (see verdictHeadline). */
/** True when no engine call and no file failed, so missing findings mean "fixed", not "not checked". */

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 new runCompleted function was inserted between the existing /** Headline of a review this bot posted */ doc comment and BOT_REVIEW_RE, so that comment now documents runCompleted. Move runCompleted above the headline comment, or move the headline comment back to BOT_REVIEW_RE.

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 67:

<comment>The new `runCompleted` function was inserted between the existing `/** Headline of a review this bot posted */` doc comment and `BOT_REVIEW_RE`, so that comment now documents `runCompleted`. Move `runCompleted` above the headline comment, or move the headline comment back to `BOT_REVIEW_RE`.</comment>

<file context>
@@ -64,6 +64,12 @@ export interface PostResult {
 }
 
 /** Headline of a review this bot posted (see verdictHeadline). */
+/** True when no engine call and no file failed, so missing findings mean "fixed", not "not checked". */
+export function runCompleted(env: VerdictEnvelope): boolean {
+  if (env.engineErrors?.length) return false
</file context>

}
return undefined
}
const found = (await find()) ?? ((await fetchTargetHistory(target.branch, opts.cwd, find)) ? await find() : undefined)

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 result of find() inside fetchTargetHistory is thrown away and find() runs again here. Have fetchTargetHistory return the resolved base (or undefined) so the caller uses it directly, which also removes the boolean-plus-callback pattern.

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/git.ts, line 267:

<comment>The result of `find()` inside `fetchTargetHistory` is thrown away and `find()` runs again here. Have `fetchTargetHistory` return the resolved base (or undefined) so the caller uses it directly, which also removes the boolean-plus-callback pattern.</comment>

<file context>
@@ -230,10 +257,15 @@ export async function resolveReviewBase(opts: ResolveBaseOptions): Promise<Resol
+      }
+      return undefined
     }
+    const found = (await find()) ?? ((await fetchTargetHistory(target.branch, opts.cwd, find)) ? await find() : undefined)
+    if (found) return found
     throw new Error(
</file context>

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

  • 6 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/format.ts (L209-L210)

[🟠 MEDIUM] Using the array spread operator within a loop to accumulate items creates a new array on each iteration. This can lead to O(N²) time complexity and unnecessary memory allocations when there are many items. It is more efficient to mutate the existing array by pushing new elements.

Suggested change:

  const byReason = new Map<string, string[]>()
  for (const u of unverified) {
    const paths = byReason.get(u.reason)
    if (paths) paths.push(u.path)
    else byReason.set(u.reason, [u.path])
  }

2. packages/opencode/src/altimate/review/git.ts (L194-L219)

[🟠 MEDIUM] By making fetchTargetHistory return a boolean, the caller is forced to execute await find() an extra time when the fetch succeeds. Since find() invokes an external git merge-base process, this results in redundant and potentially expensive operations. It is recommended to use a generic type so the function can return the found result directly, avoiding double evaluation.

Suggested change:

/**
 * A default CI checkout is shallow (`actions/checkout` fetches one commit) and
 * lacks the target branch. Fetch the branch, then deepen the history until a
 * merge-base exists, so the review works without `fetch-depth: 0`. Returns
 * the resolved value once `found` succeeds; undefined when it never does (no remote, no access).
 */
async function fetchTargetHistory<T>(branch: string, cwd: string, found: () => Promise<T | undefined>): Promise<T | undefined> {
  if (branch.startsWith("-")) return undefined
  try {
    await git(["fetch", "--no-tags", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], cwd)
  } catch {
    return undefined
  }
  let result = await found()
  if (result) return result
  for (const depth of [50, 200, 1000]) {
    try {
      if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return undefined
      const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
      await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
    } catch {
      return undefined
    }
    result = await found()
    if (result) return result
  }
  return undefined
}

3. packages/opencode/src/altimate/review/git.ts (L267-L268)

[🔵 LOW] Following the suggested refactoring of fetchTargetHistory, you can significantly simplify this caller logic. This effectively removes the complex ternary expression and avoids the redundant invocation of find().

Suggested change:

    const found = (await find()) ?? (await fetchTargetHistory(target.branch, opts.cwd, find))
    if (found) return found

4. packages/opencode/src/altimate/review/runner.ts (L269)

[🔵 LOW] Using any bypasses TypeScript's type checking and goes against best practices. Since data is already typed as Record<string, any>, data.pii is implicitly any, so the explicit as any cast is redundant. However, it's better to provide a more specific type structure, such as !!(data.pii as { parse_error?: boolean })?.parse_error, or properly type the pii property in the data interface to ensure type safety.

Suggested change:

        piiAbstained: !!(data.pii as { parse_error?: boolean })?.parse_error,

5. packages/opencode/src/altimate/review/orchestrate.ts (L1433-L1436)

[🔴 HIGH] High Severity: Risk of synchronous exceptions bypassing .catch()

The fault-tolerance logic heavily relies on chaining .catch() directly to the promise returned by input.runner.check(...). However, check is an interface method. If the underlying implementation is not a strict async function and throws a synchronous exception (e.g., during argument validation before returning a Promise), the exception will bypass the .catch() and crash the Promise.all block.

To safely handle both synchronous exceptions and asynchronous rejections, it's recommended to wrap the await within a try/catch block.

Suggested change:

        let cr: CheckResult = { ran: false }
        try {
          cr = await input.runner.check(ctx.engineNewSql, dialect, ctx.engineOldSql)
        } catch {
          // ignore failures
        }
        if (cr.ran) coreRanFiles.add(ctx.file.path)

6. packages/opencode/src/altimate/review/orchestrate.ts (L1156-L1166)

[🔵 LOW] Low Severity: Error object stringification obscuring details & Duplication of unverified files

  1. Object Stringification: Using String(err) on plain object errors (e.g., { code: 500, message: "failed" }) will evaluate to "[object Object]", making debugging difficult. We should serialize plain objects using JSON.stringify().
  2. Duplication in unverified: A single file might be pushed to unverified multiple times (e.g., once for PII abstained, and again here if a lane crashes). The downstream consumer or UI may not expect duplicate paths. It's recommended to deduplicate or aggregate reasons for the same path.

You can create a helper to format errors and ensure duplicate entries for the same file path are merged or ignored.

Suggested change:

  const formatErr = (err: unknown) => err instanceof Error ? err.message : (typeof err === "object" && err !== null ? JSON.stringify(err) : String(err))
  const laneErrors: Array<{ method: string; reason: string; count: number }> = []
  
  // Consider taking a function `() => Promise<Finding[]>` and using try/catch if lanes can throw synchronously
  const prLevel = (method: string, lane: Promise<Finding[]>) =>
    lane.catch((err) => {
      laneErrors.push({ method, reason: formatErr(err).slice(0, 200), count: 1 })
      return [] as Finding[]
    })
  const failedFile = (file: { path: string }, err: unknown) => {
    const reason = `review failed for this file: ${formatErr(err).replace(/\s+/g, " ").slice(0, 160)}`
    const existing = unverified.find((u) => u.path === file.path)
    if (existing) {
      if (!existing.reason.includes(reason)) existing.reason += `; ${reason}`
    } else {
      unverified.push({ path: file.path, reason })
    }
  }

Comment on lines +209 to +210
const byReason = new Map<string, string[]>()
for (const u of unverified) byReason.set(u.reason, [...(byReason.get(u.reason) ?? []), u.path])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🟠 MEDIUM] Using the array spread operator within a loop to accumulate items creates a new array on each iteration. This can lead to O(N²) time complexity and unnecessary memory allocations when there are many items. It is more efficient to mutate the existing array by pushing new elements.

Suggested change:

Suggested change
const byReason = new Map<string, string[]>()
for (const u of unverified) byReason.set(u.reason, [...(byReason.get(u.reason) ?? []), u.path])
const byReason = new Map<string, string[]>()
for (const u of unverified) {
const paths = byReason.get(u.reason)
if (paths) paths.push(u.path)
else byReason.set(u.reason, [u.path])
}

Comment on lines +194 to +219
/**
* A default CI checkout is shallow (`actions/checkout` fetches one commit) and
* lacks the target branch. Fetch the branch, then deepen the history until a
* merge-base exists, so the review works without `fetch-depth: 0`. Returns
* true once `found` resolves; false when it never does (no remote, no access).
*/
async function fetchTargetHistory(branch: string, cwd: string, found: () => Promise<unknown>): Promise<boolean> {
if (branch.startsWith("-")) return false
try {
await git(["fetch", "--no-tags", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], cwd)
} catch {
return false
}
if (await found()) return true
for (const depth of [50, 200, 1000]) {
try {
if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return false
const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
} catch {
return false
}
if (await found()) return true
}
return false
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🟠 MEDIUM] By making fetchTargetHistory return a boolean, the caller is forced to execute await find() an extra time when the fetch succeeds. Since find() invokes an external git merge-base process, this results in redundant and potentially expensive operations. It is recommended to use a generic type so the function can return the found result directly, avoiding double evaluation.

Suggested change:

Suggested change
/**
* A default CI checkout is shallow (`actions/checkout` fetches one commit) and
* lacks the target branch. Fetch the branch, then deepen the history until a
* merge-base exists, so the review works without `fetch-depth: 0`. Returns
* true once `found` resolves; false when it never does (no remote, no access).
*/
async function fetchTargetHistory(branch: string, cwd: string, found: () => Promise<unknown>): Promise<boolean> {
if (branch.startsWith("-")) return false
try {
await git(["fetch", "--no-tags", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], cwd)
} catch {
return false
}
if (await found()) return true
for (const depth of [50, 200, 1000]) {
try {
if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return false
const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
} catch {
return false
}
if (await found()) return true
}
return false
}
/**
* A default CI checkout is shallow (`actions/checkout` fetches one commit) and
* lacks the target branch. Fetch the branch, then deepen the history until a
* merge-base exists, so the review works without `fetch-depth: 0`. Returns
* the resolved value once `found` succeeds; undefined when it never does (no remote, no access).
*/
async function fetchTargetHistory<T>(branch: string, cwd: string, found: () => Promise<T | undefined>): Promise<T | undefined> {
if (branch.startsWith("-")) return undefined
try {
await git(["fetch", "--no-tags", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`], cwd)
} catch {
return undefined
}
let result = await found()
if (result) return result
for (const depth of [50, 200, 1000]) {
try {
if ((await git(["rev-parse", "--is-shallow-repository"], cwd)).trim() !== "true") return undefined
const headSha = (await git(["rev-parse", "HEAD"], cwd)).trim()
await git(["fetch", "--no-tags", `--deepen=${depth}`, "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`, headSha], cwd)
} catch {
return undefined
}
result = await found()
if (result) return result
}
return undefined
}

Comment on lines +267 to +268
const found = (await find()) ?? ((await fetchTargetHistory(target.branch, opts.cwd, find)) ? await find() : undefined)
if (found) return found

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🔵 LOW] Following the suggested refactoring of fetchTargetHistory, you can significantly simplify this caller logic. This effectively removes the complex ternary expression and avoids the redundant invocation of find().

Suggested change:

Suggested change
const found = (await find()) ?? ((await fetchTargetHistory(target.branch, opts.cwd, find)) ? await find() : undefined)
if (found) return found
const found = (await find()) ?? (await fetchTargetHistory(target.branch, opts.cwd, find))
if (found) return found

issues,
piiColumns: [...new Set(piiColumns)],
ran: true,
piiAbstained: !!(data.pii as any)?.parse_error,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🔵 LOW] Using any bypasses TypeScript's type checking and goes against best practices. Since data is already typed as Record<string, any>, data.pii is implicitly any, so the explicit as any cast is redundant. However, it's better to provide a more specific type structure, such as !!(data.pii as { parse_error?: boolean })?.parse_error, or properly type the pii property in the data interface to ensure type safety.

Suggested change:

Suggested change
piiAbstained: !!(data.pii as any)?.parse_error,
piiAbstained: !!(data.pii as { parse_error?: boolean })?.parse_error,

Comment on lines +1433 to 1436
const cr = await input.runner
.check(ctx.engineNewSql, dialect, ctx.engineOldSql)
.catch(() => ({ ran: false }) as CheckResult)
if (cr.ran) coreRanFiles.add(ctx.file.path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🔴 HIGH] High Severity: Risk of synchronous exceptions bypassing .catch()

The fault-tolerance logic heavily relies on chaining .catch() directly to the promise returned by input.runner.check(...). However, check is an interface method. If the underlying implementation is not a strict async function and throws a synchronous exception (e.g., during argument validation before returning a Promise), the exception will bypass the .catch() and crash the Promise.all block.

To safely handle both synchronous exceptions and asynchronous rejections, it's recommended to wrap the await within a try/catch block.

Suggested change:

Suggested change
const cr = await input.runner
.check(ctx.engineNewSql, dialect, ctx.engineOldSql)
.catch(() => ({ ran: false }) as CheckResult)
if (cr.ran) coreRanFiles.add(ctx.file.path)
let cr: CheckResult = { ran: false }
try {
cr = await input.runner.check(ctx.engineNewSql, dialect, ctx.engineOldSql)
} catch {
// ignore failures
}
if (cr.ran) coreRanFiles.add(ctx.file.path)

Comment on lines +1156 to +1166
const laneErrors: Array<{ method: string; reason: string; count: number }> = []
const prLevel = (method: string, lane: Promise<Finding[]>) =>
lane.catch((err) => {
laneErrors.push({ method, reason: (err instanceof Error ? err.message : String(err)).slice(0, 200), count: 1 })
return [] as Finding[]
})
const failedFile = (file: { path: string }, err: unknown) =>
unverified.push({
path: file.path,
reason: `review failed for this file: ${(err instanceof Error ? err.message : String(err)).replace(/\s+/g, " ").slice(0, 160)}`,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[🔵 LOW] Low Severity: Error object stringification obscuring details & Duplication of unverified files

  1. Object Stringification: Using String(err) on plain object errors (e.g., { code: 500, message: "failed" }) will evaluate to "[object Object]", making debugging difficult. We should serialize plain objects using JSON.stringify().
  2. Duplication in unverified: A single file might be pushed to unverified multiple times (e.g., once for PII abstained, and again here if a lane crashes). The downstream consumer or UI may not expect duplicate paths. It's recommended to deduplicate or aggregate reasons for the same path.

You can create a helper to format errors and ensure duplicate entries for the same file path are merged or ignored.

Suggested change:

Suggested change
const laneErrors: Array<{ method: string; reason: string; count: number }> = []
const prLevel = (method: string, lane: Promise<Finding[]>) =>
lane.catch((err) => {
laneErrors.push({ method, reason: (err instanceof Error ? err.message : String(err)).slice(0, 200), count: 1 })
return [] as Finding[]
})
const failedFile = (file: { path: string }, err: unknown) =>
unverified.push({
path: file.path,
reason: `review failed for this file: ${(err instanceof Error ? err.message : String(err)).replace(/\s+/g, " ").slice(0, 160)}`,
})
const formatErr = (err: unknown) => err instanceof Error ? err.message : (typeof err === "object" && err !== null ? JSON.stringify(err) : String(err))
const laneErrors: Array<{ method: string; reason: string; count: number }> = []
// Consider taking a function `() => Promise<Finding[]>` and using try/catch if lanes can throw synchronously
const prLevel = (method: string, lane: Promise<Finding[]>) =>
lane.catch((err) => {
laneErrors.push({ method, reason: formatErr(err).slice(0, 200), count: 1 })
return [] as Finding[]
})
const failedFile = (file: { path: string }, err: unknown) => {
const reason = `review failed for this file: ${formatErr(err).replace(/\s+/g, " ").slice(0, 160)}`
const existing = unverified.find((u) => u.path === file.path)
if (existing) {
if (!existing.reason.includes(reason)) existing.reason += `; ${reason}`
} else {
unverified.push({ path: file.path, reason })
}
}

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

@dev-punia-altimate

Copy link
Copy Markdown
Contributor

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

No supported files changed.

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.

2 participants