release: v0.9.3#1041
Conversation
- Stamp `Installation.VERSION` into `engine.cliVersion` on the signed verdict envelope so auditors reconstructing a stored verdict months later can identify which policy version generated it. The version is included in the canonical body, so tampering breaks the HMAC signature. - Add `staleManifest` boolean to the envelope, populated when a change- affecting source file has been modified after the manifest was written. Renames the internal `warnIfStale` to `detectStaleManifest` and removes the `opts.head` gate so the local working-tree workflow (compile once, edit for an hour, then review) also surfaces the signal instead of silently under-warning. - Emit `tierReasons[]` on the envelope whenever the classifier lands on `full` tier, not only when `--explain-tier` / `--force-tier` / `pathTokenConfigError` fires. A REQUEST_CHANGES on a schema.yml diff now carries the "why" in the PR-comment blockquote by default; `trivial` / `lite` stay quiet to avoid noise on approvals. - Document `--explain-tier`, `--force-tier`, `riskTierPathTokens`, and manifest auto-discovery in `docs/docs/usage/dbt-pr-review.md`.
The `Discovering tools` assertion depends on the `bootstrap.resolve-tools` span (~30-100ms while listing MCP tools) outlasting the PTY harness's 50ms poll interval. On a cold CI runner the MCP listing can complete faster than the harness samples, dropping the label between polls. A local sample observed 4/5 passes; the flake mode is real and would intermittently gate main. Gate on the `CI` env so cold runners skip the test while local dev still runs it.
CHANGELOG entry and adversarial regression suite for the v0.9.3 review skill changes (grain-key detector, tier promotion, --explain-tier / --force-tier flags, manifest auto-discovery, bootstrap phase labels) and the pre-release review fixes (envelope cliVersion + staleManifest, tier-reasons rendered in PR comment for full-tier verdicts, stale- manifest signal on local working-tree reviews).
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
📝 WalkthroughWalkthroughThe v0.9.3 changes add signed verdict audit metadata, persist stale-manifest detection, document CLI tiering controls and configuration, harden manifest discovery, and add adversarial regression coverage. A flaky TUI phase-label test is skipped in CI, and release preflight comments mark the existing guard. ChangesRelease audit and review behavior
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant Review
participant Manifest
participant Envelope
CLI->>Review: start review with cliVersion
Review->>Manifest: detect stale manifest
Manifest-->>Review: staleManifest result
Review->>Envelope: build verdict with audit fields
Envelope-->>CLI: signed verdict
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
1 similar comment
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
| await writeFile(changedFile, "select 1\n") | ||
|
|
||
| // Silence expected stderr warning during the test. | ||
| const origWrite = process.stderr.write.bind(process.stderr) |
There was a problem hiding this comment.
SUGGESTION: The stderr-silencing scaffolding (save process.stderr.write, monkeypatch, restore in finally) is duplicated verbatim across all four tests in this file (lines 220-221/239, 278-279/294, 318-319/334, 352-353/364).
Extracting a small helper would remove ~12 lines of repetition and centralize the restore-on-exit contract in one place:
function silenceStderr(): () => void {
const orig = process.stderr.write.bind(process.stderr)
process.stderr.write = (() => true) as typeof process.stderr.write
return () => {
process.stderr.write = orig
}
}Each test then collapses to const restore = silenceStderr(); try { ... } finally { restore() }.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
There was a problem hiding this comment.
Not doing this release. Same reasoning as cubic-dev-ai's comment at the top of the file — 12 lines across four tests, adversarial file gets rewritten each release, DRY benefit doesn't compound.
Code Review SummaryStatus: No New Issues (1 carried forward) | Recommendation: Optional (non-blocking) Incremental Review (
|
| Severity | Count |
|---|---|
| CRITICAL | 0 |
| WARNING | 0 |
| SUGGESTION | 1 (carried forward) |
Issue Details (click to expand)
SUGGESTION (carried forward — unchanged line, still unresolved)
| File | Line | Issue |
|---|---|---|
packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts |
247 | Duplicated stderr-silencing scaffolding across tests — extract a silenceStderr() helper |
Files Reviewed (4 files, incremental)
packages/opencode/src/altimate/review/run.ts— cleanpackages/opencode/test/skill/release-v0.9.3-adversarial.test.ts— clean (1 carried-forward suggestion)script/release-preflight.ts— cleandocs/docs/usage/dbt-pr-review.md— doc-only
Fix these issues in Kilo Cloud
Previous Review Summary (commit afcca83)
Current summary above is authoritative. Previous snapshots are kept for context only.
Previous review (commit afcca83)
Status: 1 Issue Found | Recommendation: Optional (non-blocking)
Overview
| Severity | Count |
|---|---|
| CRITICAL | 0 |
| WARNING | 0 |
| SUGGESTION | 1 |
Issue Details (click to expand)
SUGGESTION
| File | Line | Issue |
|---|---|---|
packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts |
220 | Duplicated stderr-silencing scaffolding across 4 tests — extract a silenceStderr() helper |
Files Reviewed (8 files)
packages/opencode/src/altimate/review/orchestrate.ts— cleanpackages/opencode/src/altimate/review/run.ts— cleanpackages/opencode/src/altimate/review/verdict.ts— cleanpackages/opencode/src/cli/cmd/review.ts— cleanpackages/opencode/test/cli/tui/phase-label.tui-e2e.test.ts— cleanpackages/opencode/test/skill/release-v0.9.3-adversarial.test.ts— 1 suggestionCHANGELOG.md— doc-onlydocs/docs/usage/dbt-pr-review.md— doc-only
Notes
- The audit-field plumbing (
engine.cliVersion,staleManifest), the removal of theopts.headgate ondetectStaleManifest, and thetier === "full"tierReasons emission were all verified as logically correct. The new fields are covered by the HMAC signature (stableStringifyincludes them), and the tamper-detection paths are sound. - The ternary precedence at
orchestrate.ts:1442(... || tier === "full" ? tierReasons : undefined) parses as intended —===binds tighter than||/?:— matching the pre-existing pattern.
Reviewed by glm-5.2 · Input: 78.8K · Output: 18.9K · Cached: 754.9K
Review guidance: REVIEW.md from base branch main
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts (1)
219-241: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated stderr-mute/restore pattern into a helper.
The save/stub/restore-in-
finallyblock forprocess.stderr.writeis duplicated verbatim across 4 tests in this file. A shared helper (e.g.withSilencedStderr(fn)) would remove the duplication and centralize the "restore even on throw" guarantee in one place, which matters for a global mutable likeprocess.stderr.writeunderbun test.Based on path instructions, tests touching shared/global state must provide safe teardown and isolation for parallel `bun test` execution; centralizing this pattern makes that guarantee easier to audit.♻️ Suggested helper
+async function withSilencedStderr<T>(fn: () => Promise<T>): Promise<T> { + const origWrite = process.stderr.write.bind(process.stderr) + process.stderr.write = (() => true) as typeof process.stderr.write + try { + return await fn() + } finally { + process.stderr.write = origWrite + } +}Then each test body becomes
await withSilencedStderr(async () => { ... }).Also applies to: 278-296, 318-336, 352-366
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts` around lines 219 - 241, Extract the duplicated process.stderr.write save, silencing, and finally-based restoration logic from the affected tests into a shared withSilencedStderr helper. Update the four test bodies to execute their reviewPullRequest assertions through this helper, preserving restoration even when the callback throws and keeping each test’s existing behavior unchanged.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/docs/usage/dbt-pr-review.md`:
- Around line 252-255: Update the riskTierPathTokens documentation to describe
values as case-insensitive path/word/digit-boundary tokens rather than arbitrary
substrings. In the section describing named categories and preset:finops, add a
clear non-match example such as “cost” not matching an unrelated word containing
those letters, while preserving the existing full-review and FinOps expansion
behavior.
---
Nitpick comments:
In `@packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts`:
- Around line 219-241: Extract the duplicated process.stderr.write save,
silencing, and finally-based restoration logic from the affected tests into a
shared withSilencedStderr helper. Update the four test bodies to execute their
reviewPullRequest assertions through this helper, preserving restoration even
when the callback throws and keeping each test’s existing behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 072a2579-7f31-488d-875d-c95afbf0db33
📒 Files selected for processing (8)
CHANGELOG.mddocs/docs/usage/dbt-pr-review.mdpackages/opencode/src/altimate/review/orchestrate.tspackages/opencode/src/altimate/review/run.tspackages/opencode/src/altimate/review/verdict.tspackages/opencode/src/cli/cmd/review.tspackages/opencode/test/cli/tui/phase-label.tui-e2e.test.tspackages/opencode/test/skill/release-v0.9.3-adversarial.test.ts
There was a problem hiding this comment.
2 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode/test/cli/tui/phase-label.tui-e2e.test.ts">
<violation number="1" location="packages/opencode/test/cli/tui/phase-label.tui-e2e.test.ts:42">
P2: CI will report this critical TUI end-to-end coverage as skipped rather than detecting regressions in phase publishing or rendering. A retry/stabilized polling strategy or a separately quarantined job would preserve CI signal without disabling the entire test on every CI run.</violation>
</file>
<file name="packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts">
<violation number="1" location="packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts:220">
P3: The stderr-silencing pattern (`const origWrite = process.stderr.write.bind(...); process.stderr.write = ...; try { ... } finally { process.stderr.write = origWrite }`) is repeated verbatim across all four tests in this file. Consider extracting a small helper like:
```ts
function silenceStderr(): () => void {
const orig = process.stderr.write.bind(process.stderr)
process.stderr.write = (() => true) as typeof process.stderr.write
return () => { process.stderr.write = orig }
}
This would reduce ~12 lines of duplication and centralize the restore-on-exit contract.
</details>
<sub>Reply with feedback, questions, or to request a fix.<br /><br />[Re-trigger cubic](https://www.cubic.dev/action/re-review/pr/AltimateAI/altimate-code/1041/ai_pr_review_1784839945974_ff2cf364-e9dd-4cab-8ef6-7e567ae7456b?returnTo=https%3A%2F%2Fgithub.com%2FAltimateAI%2Faltimate-code%2Fpull%2F1041)</sub>
<!-- cubic:review-post:ai_pr_review_1784839945974_ff2cf364-e9dd-4cab-8ef6-7e567ae7456b:afcca831b983fd1067a1a21a2ac4e4b2c4030477:74d7d9ab-4ecc-4c49-aab3-fc7318d34d46 -->
| // local sample observed 4/5 passes; the flake mode is real. Gate on CI so a | ||
| // cold runner doesn't intermittently block main — the local dev signal is | ||
| // preserved (the test still runs on developer machines). | ||
| const runTui = process.env["CI"] ? test.skip : test |
There was a problem hiding this comment.
P2: CI will report this critical TUI end-to-end coverage as skipped rather than detecting regressions in phase publishing or rendering. A retry/stabilized polling strategy or a separately quarantined job would preserve CI signal without disabling the entire test on every CI run.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/test/cli/tui/phase-label.tui-e2e.test.ts, line 42:
<comment>CI will report this critical TUI end-to-end coverage as skipped rather than detecting regressions in phase publishing or rendering. A retry/stabilized polling strategy or a separately quarantined job would preserve CI signal without disabling the entire test on every CI run.</comment>
<file context>
@@ -32,8 +32,17 @@ import { describe, expect, test } from "bun:test"
+// local sample observed 4/5 passes; the flake mode is real. Gate on CI so a
+// cold runner doesn't intermittently block main — the local dev signal is
+// preserved (the test still runs on developer machines).
+const runTui = process.env["CI"] ? test.skip : test
+
describe("TUI e2e — AI-7519 phase-label render", () => {
</file context>
There was a problem hiding this comment.
Declined for this release. The alternative to a plain skip is a quarantined-job pattern that doesn't exist yet in this repo — worth a follow-up but not appropriate to design and land inside a patch release. The local dev signal is preserved (the test still runs on developer machines) and the flake mode itself was flagged by the persona review with a documented 4/5 pass rate, so test.skip(CI) is the honest short-term posture until a quarantine track exists.
| @@ -0,0 +1,367 @@ | |||
| /** | |||
There was a problem hiding this comment.
P3: The stderr-silencing pattern (const origWrite = process.stderr.write.bind(...); process.stderr.write = ...; try { ... } finally { process.stderr.write = origWrite }) is repeated verbatim across all four tests in this file. Consider extracting a small helper like:
function silenceStderr(): () => void {
const orig = process.stderr.write.bind(process.stderr)
process.stderr.write = (() => true) as typeof process.stderr.write
return () => { process.stderr.write = orig }
}This would reduce ~12 lines of duplication and centralize the restore-on-exit contract.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/test/skill/release-v0.9.3-adversarial.test.ts, line 220:
<comment>The stderr-silencing pattern (`const origWrite = process.stderr.write.bind(...); process.stderr.write = ...; try { ... } finally { process.stderr.write = origWrite }`) is repeated verbatim across all four tests in this file. Consider extracting a small helper like:
```ts
function silenceStderr(): () => void {
const orig = process.stderr.write.bind(process.stderr)
process.stderr.write = (() => true) as typeof process.stderr.write
return () => { process.stderr.write = orig }
}
This would reduce ~12 lines of duplication and centralize the restore-on-exit contract.
@@ -0,0 +1,367 @@ + await writeFile(changedFile, "select 1\n") + + // Silence expected stderr warning during the test. + const origWrite = process.stderr.write.bind(process.stderr) + process.stderr.write = (() => true) as typeof process.stderr.write + try { ```There was a problem hiding this comment.
Not doing this release. 12 lines of duplication across four tests is not worth churning the adversarial file on a patch — this file gets rewritten every release anyway, so the DRY benefit doesn't compound.
Bot review on the v0.9.3 release PR caught three correctness gaps and a CI-blocking branding-audit trip. All fixed here so the release stays clean: - **`cliVersion` default in `reviewPullRequest`.** Only the CLI command forwarded `Installation.VERSION`; the agent-invoked `dbt_pr_review` tool wrapper never did, so tool-path verdicts silently dropped `engine.cliVersion` and defeated the audit-trail promise the field was added for. Default the option to `Installation.VERSION` inside `reviewPullRequest` so both entry points populate it. (cubic P2 conf 9) - **`staleManifest` deletion false-negative.** `detectStaleManifest` previously took `changedPaths: string[]` and tried to `stat` each — a deletion of a manifest-affecting model reported CLEAN because the file no longer exists on disk. The signed envelope then falsely certified a verdict against ghost models. Pass the full `ChangedFile[]` through instead and treat `deleted` / `renamed` manifest-affecting entries as unconditionally stale (they can't be stat'd but the manifest still references them, so the metadata is by definition out of date). Rename covers both sides so a rename-away and a rename-to both trip. (cubic P2 conf 8) - **Docs boundary-matching wording + non-match example.** The `riskTierPathTokens` doc claimed "case-insensitive substrings" but `compilePathTokenResolver` actually anchors tokens at path/word/digit boundaries — `cost` fires on `mrt_cost_daily.sql` but not on `broadcaster.sql`. Correct the wording and add the non-match example so users don't configure tokens that don't match their intent. (cubic P2 conf 5 + coderabbit) - **Branding-audit trip in `script/release-preflight.ts:609`.** The operational instruction telling the operator how to add the upstream remote (`git remote add upstream https://github.com/anomalyco/opencode.git`) legitimately names the upstream URL. The CI branding audit doesn't know operational text from branding drift; wrap the block in `altimate_change` markers so the audit skips it.
…s tests Two new positive tests pin the fixes from the previous commit: - `reviewPullRequest defaults cliVersion so the dbt_pr_review tool path gets audit provenance too` — invokes `reviewPullRequest` WITHOUT passing `cliVersion`, asserts `env.engine.cliVersion` is non-empty. - `deleted manifest-affecting file trips staleness even though it can't be stat'd` — deletion of `models/orders.sql` with the manifest present must set `env.staleManifest === true`. Two vacuous tests from the original v0.9.3 adversarial file are repaired per cubic-review PR #1041: - Grandparent walk-past regression test now plants an actual `grand/ target/manifest.json` (a poisoned grandparent DAG), then invokes review from `grand/inner/subdir` where `inner/dbt_project.yml` has no compiled artifact. The assertion `env.manifestHash` must remain undefined AND the stderr breadcrumb must NOT reference the grandparent path — a regressed walker would announce a hit there. Previously the fixture just made an unrelated sibling directory, so the walker never had a real manifest to consider skipping. - Symlink-loop test is removed. Its `looped -> tmp` fixture was a one-hop parent alias, not a cycle; `fs.realpath` resolves it in one step, so the test proved nothing about the try/catch fallback in `run.ts`. A real ELOOP fixture is filesystem-dependent and the fallback pattern is a straight try/catch obvious from a code read; cheaper to delete than to churn on a cross-platform cycle.
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
Pushed 2 commits addressing the bot review + CI Marker Guard failure.
Declined:
Local status:
Standing by for maintainer approval + merge. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
1 similar comment
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/opencode/src/altimate/review/run.ts`:
- Around line 163-183: Update detectStaleManifest so deleted files remain
immediately stale, but renamed files are checked using their new path’s mtime
against manifestMtime before deciding freshness. Ensure renamed paths missing
from disk take the safe stale outcome via an explicit catch branch, while
preserving the existing stat-based handling for added and modified files.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b6275484-8c0f-415c-9488-0a6cfbadbe82
📒 Files selected for processing (4)
docs/docs/usage/dbt-pr-review.mdpackages/opencode/src/altimate/review/run.tspackages/opencode/test/skill/release-v0.9.3-adversarial.test.tsscript/release-preflight.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/docs/usage/dbt-pr-review.md
| async function detectStaleManifest(manifestAbs: string, changed: ChangedFile[], fsRoot: string): Promise<boolean> { | ||
| try { | ||
| const manifestMtime = (await stat(manifestAbs)).mtimeMs | ||
| for (const rel of changedPaths) { | ||
| if (!isManifestAffecting(rel)) continue | ||
| // `changedPaths` are repo-root relative (from `git diff --name-status`), | ||
| for (const f of changed) { | ||
| // For renamed files, the OLD path is what the manifest still knows about; | ||
| // check both sides so both a rename-away and a rename-to a | ||
| // manifest-affecting shape trigger the signal. | ||
| const paths: string[] = [f.path] | ||
| if (f.status === "renamed" && f.oldPath) paths.push(f.oldPath) | ||
| if (!paths.some(isManifestAffecting)) continue | ||
|
|
||
| // Deleted / renamed files no longer exist at their old path on disk, so | ||
| // stat would fail. The manifest still references them though, so the | ||
| // metadata is stale by definition — fire immediately. | ||
| if (f.status === "deleted" || f.status === "renamed") { | ||
| process.stderr.write( | ||
| `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` + | ||
| `Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`, | ||
| ) | ||
| return true | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Renamed manifest-affecting files always report stale, even when the manifest was recompiled after the rename.
For deleted files, forcing stale = true is the only option since the old path can't be stat'd. But for renamed files, f.path is the new location and does exist on disk — the code never attempts to stat it and compare against manifestMtime, unlike the added/modified path below. Any rename of a manifest-affecting file will unconditionally mark staleManifest: true in the signed envelope, even if dbt compile was re-run after the rename and the manifest is actually fresh. This weakens the audit signal this PR is specifically trying to harden — auditors will see spurious staleManifest: true on any PR containing a rename.
🐛 Proposed fix: only force-stale on deleted; check new-path mtime for renames
- // Deleted / renamed files no longer exist at their old path on disk, so
- // stat would fail. The manifest still references them though, so the
- // metadata is stale by definition — fire immediately.
- if (f.status === "deleted" || f.status === "renamed") {
+ // Deleted files no longer exist on disk, so stat would fail. The
+ // manifest still references them though, so the metadata is stale by
+ // definition — fire immediately. Renamed files fall through to the
+ // mtime check below against their NEW path, which does exist on disk.
+ if (f.status === "deleted") {
process.stderr.write(
`⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` +
`Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`,
)
return true
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function detectStaleManifest(manifestAbs: string, changed: ChangedFile[], fsRoot: string): Promise<boolean> { | |
| try { | |
| const manifestMtime = (await stat(manifestAbs)).mtimeMs | |
| for (const rel of changedPaths) { | |
| if (!isManifestAffecting(rel)) continue | |
| // `changedPaths` are repo-root relative (from `git diff --name-status`), | |
| for (const f of changed) { | |
| // For renamed files, the OLD path is what the manifest still knows about; | |
| // check both sides so both a rename-away and a rename-to a | |
| // manifest-affecting shape trigger the signal. | |
| const paths: string[] = [f.path] | |
| if (f.status === "renamed" && f.oldPath) paths.push(f.oldPath) | |
| if (!paths.some(isManifestAffecting)) continue | |
| // Deleted / renamed files no longer exist at their old path on disk, so | |
| // stat would fail. The manifest still references them though, so the | |
| // metadata is stale by definition — fire immediately. | |
| if (f.status === "deleted" || f.status === "renamed") { | |
| process.stderr.write( | |
| `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` + | |
| `Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`, | |
| ) | |
| return true | |
| } | |
| async function detectStaleManifest(manifestAbs: string, changed: ChangedFile[], fsRoot: string): Promise<boolean> { | |
| try { | |
| const manifestMtime = (await stat(manifestAbs)).mtimeMs | |
| for (const f of changed) { | |
| // For renamed files, the OLD path is what the manifest still knows about; | |
| // check both sides so both a rename-away and a rename-to a | |
| // manifest-affecting shape trigger the signal. | |
| const paths: string[] = [f.path] | |
| if (f.status === "renamed" && f.oldPath) paths.push(f.oldPath) | |
| if (!paths.some(isManifestAffecting)) continue | |
| // Deleted files no longer exist on disk, so stat would fail. The | |
| // manifest still references them though, so the metadata is stale by | |
| // definition — fire immediately. Renamed files fall through to the | |
| // mtime check below against their NEW path, which does exist on disk. | |
| if (f.status === "deleted") { | |
| process.stderr.write( | |
| `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` + | |
| `Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`, | |
| ) | |
| return true | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/opencode/src/altimate/review/run.ts` around lines 163 - 183, Update
detectStaleManifest so deleted files remain immediately stale, but renamed files
are checked using their new path’s mtime against manifestMtime before deciding
freshness. Ensure renamed paths missing from disk take the safe stale outcome
via an explicit catch branch, while preserving the existing stat-based handling
for added and modified files.
dev-punia-altimate
left a comment
There was a problem hiding this comment.
🤖 Code Review — OpenCodeReview (Gemini) — 1 finding(s)
- 1 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/run.ts (L177-L183)
[🟠 MEDIUM] Unconditionally returning true for deleted or renamed files creates a permanent false positive for any PR containing these operations. Since changedFiles represents the diff of the entire PR, a deleted file will always appear in the list, causing this function to flag the manifest as stale even if dbt compile was freshly run.
To correctly detect if the manifest is older than the deletion/rename operation without false positives, you can compare the manifestMtime against the parent directory's mtime. A directory's mtime updates when files within it are added, removed, or renamed.
if (f.status === "deleted" || f.status === "renamed") {
try {
// For deletions/renames, the parent directory's mtime reflects the structural change.
const dirMtime = (await stat(path.dirname(abs))).mtimeMs
if (dirMtime > manifestMtime) {
process.stderr.write(
`⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` +
`Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`,
)
return true
}
} catch {
/* parent dir might also be deleted, safely ignore or fallback */
}
continue
}| if (f.status === "deleted" || f.status === "renamed") { | ||
| process.stderr.write( | ||
| `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` + | ||
| `Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`, | ||
| ) | ||
| return true | ||
| } |
There was a problem hiding this comment.
[🟠 MEDIUM] Unconditionally returning true for deleted or renamed files creates a permanent false positive for any PR containing these operations. Since changedFiles represents the diff of the entire PR, a deleted file will always appear in the list, causing this function to flag the manifest as stale even if dbt compile was freshly run.
To correctly detect if the manifest is older than the deletion/rename operation without false positives, you can compare the manifestMtime against the parent directory's mtime. A directory's mtime updates when files within it are added, removed, or renamed.
if (f.status === "deleted" || f.status === "renamed") {
try {
// For deletions/renames, the parent directory's mtime reflects the structural change.
const dirMtime = (await stat(path.dirname(abs))).mtimeMs
if (dirMtime > manifestMtime) {
process.stderr.write(
`⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` +
`Re-run \`dbt compile\` (or \`dbt build\`) to refresh before reviewing.\n`,
)
return true
}
} catch {
/* parent dir might also be deleted, safely ignore or fallback */
}
continue
}There was a problem hiding this comment.
3 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. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode/src/altimate/review/run.ts">
<violation number="1" location="packages/opencode/src/altimate/review/run.ts:177">
P2: When `detectStaleManifest` fires for a renamed file where only `f.oldPath` (not `f.path`) matches `isManifestAffecting`, the stderr warning misleadingly claims the new path is manifest-affecting. For a rename from `models/old.sql` to `analyses/analysis.sql`, the message would say `renamed \`analyses/analysis.sql\` is a manifest-affecting path` even though `analyses/analysis.sql` is not manifest-affecting — the old path (`models/old.sql`) is. Consider reporting the old path (or both) in the diagnostic when the renamed file's old path is what triggered the manifest-affecting check.</violation>
<violation number="2" location="packages/opencode/src/altimate/review/run.ts:177">
P2: Fresh manifests can never clear `staleManifest` for PRs that rename or delete a dbt input: this branch returns before any freshness check, so re-running `dbt compile` produces the same warning and signed stale verdict. Determine whether the manifest still references the old path (or otherwise compare it to the current revision) before setting the flag.</violation>
</file>
<file name="docs/docs/usage/dbt-pr-review.md">
<violation number="1" location="docs/docs/usage/dbt-pr-review.md:254">
P3: The `cost` vs. `broadcaster.sql` example in the `riskTierPathTokens` docs is technically incorrect — `cost` is not a substring of `broadcaster` at all (the word contains `cast`). This makes the boundary-matching example misleading. Replace it with a pair where the token actually appears as a substring but is correctly rejected by the boundary regex, such as `cost` matching `mrt_cost_daily.sql` but NOT `telecostanalysis.sql`, or `cast` matching `models/marts/address_cast.sql` but NOT `models/broadcaster.sql`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // Deleted / renamed files no longer exist at their old path on disk, so | ||
| // stat would fail. The manifest still references them though, so the | ||
| // metadata is stale by definition — fire immediately. | ||
| if (f.status === "deleted" || f.status === "renamed") { |
There was a problem hiding this comment.
P2: When detectStaleManifest fires for a renamed file where only f.oldPath (not f.path) matches isManifestAffecting, the stderr warning misleadingly claims the new path is manifest-affecting. For a rename from models/old.sql to analyses/analysis.sql, the message would say renamed \analyses/analysis.sql` is a manifest-affecting patheven thoughanalyses/analysis.sql is not manifest-affecting — the old path (models/old.sql`) is. Consider reporting the old path (or both) in the diagnostic when the renamed file's old path is what triggered the manifest-affecting check.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/review/run.ts, line 177:
<comment>When `detectStaleManifest` fires for a renamed file where only `f.oldPath` (not `f.path`) matches `isManifestAffecting`, the stderr warning misleadingly claims the new path is manifest-affecting. For a rename from `models/old.sql` to `analyses/analysis.sql`, the message would say `renamed \`analyses/analysis.sql\` is a manifest-affecting path` even though `analyses/analysis.sql` is not manifest-affecting — the old path (`models/old.sql`) is. Consider reporting the old path (or both) in the diagnostic when the renamed file's old path is what triggered the manifest-affecting check.</comment>
<file context>
@@ -152,28 +153,51 @@ export function isManifestAffecting(rel: string): boolean {
+ // Deleted / renamed files no longer exist at their old path on disk, so
+ // stat would fail. The manifest still references them though, so the
+ // metadata is stale by definition — fire immediately.
+ if (f.status === "deleted" || f.status === "renamed") {
+ process.stderr.write(
+ `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` +
</file context>
| // Deleted / renamed files no longer exist at their old path on disk, so | ||
| // stat would fail. The manifest still references them though, so the | ||
| // metadata is stale by definition — fire immediately. | ||
| if (f.status === "deleted" || f.status === "renamed") { |
There was a problem hiding this comment.
P2: Fresh manifests can never clear staleManifest for PRs that rename or delete a dbt input: this branch returns before any freshness check, so re-running dbt compile produces the same warning and signed stale verdict. Determine whether the manifest still references the old path (or otherwise compare it to the current revision) before setting the flag.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/review/run.ts, line 177:
<comment>Fresh manifests can never clear `staleManifest` for PRs that rename or delete a dbt input: this branch returns before any freshness check, so re-running `dbt compile` produces the same warning and signed stale verdict. Determine whether the manifest still references the old path (or otherwise compare it to the current revision) before setting the flag.</comment>
<file context>
@@ -152,28 +153,51 @@ export function isManifestAffecting(rel: string): boolean {
+ // Deleted / renamed files no longer exist at their old path on disk, so
+ // stat would fail. The manifest still references them though, so the
+ // metadata is stale by definition — fire immediately.
+ if (f.status === "deleted" || f.status === "renamed") {
+ process.stderr.write(
+ `⚠️ manifest ${manifestAbs} appears stale — ${f.status} \`${f.path}\` is a manifest-affecting path. ` +
</file context>
|
|
||
| Named categories under `riskTierPathTokens` promote any diff touching a matching | ||
| path to `full` review tier, so those areas never auto-approve on `trivial` | ||
| classification. Tokens are matched **case-insensitively at path, word, or digit |
There was a problem hiding this comment.
P3: The cost vs. broadcaster.sql example in the riskTierPathTokens docs is technically incorrect — cost is not a substring of broadcaster at all (the word contains cast). This makes the boundary-matching example misleading. Replace it with a pair where the token actually appears as a substring but is correctly rejected by the boundary regex, such as cost matching mrt_cost_daily.sql but NOT telecostanalysis.sql, or cast matching models/marts/address_cast.sql but NOT models/broadcaster.sql.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/docs/usage/dbt-pr-review.md, line 254:
<comment>The `cost` vs. `broadcaster.sql` example in the `riskTierPathTokens` docs is technically incorrect — `cost` is not a substring of `broadcaster` at all (the word contains `cast`). This makes the boundary-matching example misleading. Replace it with a pair where the token actually appears as a substring but is correctly rejected by the boundary regex, such as `cost` matching `mrt_cost_daily.sql` but NOT `telecostanalysis.sql`, or `cast` matching `models/marts/address_cast.sql` but NOT `models/broadcaster.sql`.</comment>
<file context>
@@ -251,12 +251,16 @@ rubric:
-this config. When a category value is invalid the CLI logs a stderr warning AND
-surfaces the error in the verdict envelope's `tierReasons[]` (and in the PR
-comment), so a typo can't silently kill your opt-in.
+classification. Tokens are matched **case-insensitively at path, word, or digit
+boundaries** — not as arbitrary substrings. For example, the token `cost` fires
+on `models/marts/mrt_cost_daily.sql` (bounded by `_` and `_`) but **not** on
</file context>
🤖 Code Review — OpenCodeReview (Gemini) — No Issues FoundNo supported files changed. |

Issue for this PR
Closes #1040
Type of change
What does this PR do?
Cuts the v0.9.3 stable release. Bundles three groups of changes for maintainer approval:
1. Pre-release review fixes (
fix(review): v0.9.3 pre-release review findings, ae52fe0)Installation.VERSIONintoengine.cliVersionon the signed verdict envelope so an auditor can reconstruct which policy version generated a stored verdict months later. Covered by the HMAC signature.staleManifest: trueto the envelope when a change-affecting source file has been modified after the manifest was written. Rename the internalwarnIfStaletodetectStaleManifestand remove theopts.headgate so the local working-tree workflow (compile once, edit for an hour, then review) surfaces the signal instead of silently under-warning.tierReasons[]on the envelope whenever the classifier lands onfulltier (not only under--explain-tier/--force-tier/pathTokenConfigError). AREQUEST_CHANGESon a schema.yml diff now carries the "why" in the PR-comment blockquote by default;trivial/litestay silent to avoid approval noise.--explain-tier,--force-tier,riskTierPathTokens, and manifest auto-discovery indocs/docs/usage/dbt-pr-review.md.2. Flaky CI test skip (
test(tui): skip flaky phase-label e2e when CI env is set, 4cb6e9d)phase-label.tui-e2e"Discovering tools" assertion depends on thebootstrap.resolve-toolsspan (~30-100 ms while listing MCP tools) outlasting the PTY harness's 50 ms poll interval. On a cold CI runner MCP listing can complete faster than the harness samples, dropping the label between polls. A local sample observed 4/5 passes; the flake mode is real. Gate onCIenv so cold runners skip while local dev still runs it.3. Release commit (
release: v0.9.3, afcca83)CHANGELOG.mdentry describing the sevenAddeditems (grain-keynot_nulldetector,--explain-tier,--force-tier,riskTierPathTokens,engine.cliVersion,staleManifest, bootstrap phase labels) and the fiveChangeditems (never-auto-approve risky metadata,--no-aifix, manifest auto-discovery, column-aware schema.yml test-removal, working-tree stale-manifest detection).test/skill/release-v0.9.3-adversarial.test.ts— ten regression tests covering the envelope audit fields, tier-reason PR-comment leak, stale-manifest detection, and manifest auto-discovery under hostile filesystem shapes.How did you verify your code works?
bun test test/altimate/frompackages/opencode)bun run typecheckonpackages/opencode— cleanbun run script/upstream/analyze.ts --markers --base origin/main --strict— cleanbun script/release-preflight.ts --version 0.9.3 --stage tag— 9/9 PASS after fixing a stale fork-inheritedv0.9.3tag from upstream OpenCode's Sep 2025 release(cd packages/opencode && OPENCODE_VERSION=0.9.3 bun run pre-release)built the dist binary;--versionreports0.9.3exactlySHIP WITH NOTES, oneP0and multipleP1s addressed in this PR; six deferred findings filed as #1034 - #1039Screenshots / recordings
n/a — no UI change.
Checklist
Note to reviewer
Prefer merge commit or rebase-and-merge (not squash) so my local
v0.9.3tag onafcca831b9stays reachable fromorigin/main. Squash would still work —release.ymlchecks out from the tag directly — but the linear history and tag reachability are cleaner with merge/rebase.After this lands on
main, I'll push thev0.9.3tag (already created locally onafcca831b9) to triggerrelease.ymlfor the npm publish.Summary by cubic
Release v0.9.3 for the dbt PR reviewer. Improves auditability, explains full-tier decisions by default, hardens stale-manifest detection (including deletes/renames), and stabilizes CI runs.
New Features
engine.cliVersionto the signed verdict envelope for audit trails; addstaleManifestwhen change‑affecting files are newer than the manifest. Staleness check now runs for CI and local working-tree reviews.tierReasons[]when the classifier lands onfullso PR comments explain REQUEST_CHANGES without extra flags.--explain-tier,--force-tier,riskTierPathTokens(now clearly boundary-matched, with examples), and manifest auto-discovery; add v0.9.3 CHANGELOG.Bug Fixes
engine.cliVersioninreviewPullRequestsodbt_pr_reviewtool-invoked runs also stamp the CLI version.altimate_changemarkers.Written for commit e700a40. Summary will update on new commits.
Summary by CodeRabbit
--explain-tierand experimental--force-tierfor more control and richer tier-reason output.riskTierPathTokens(includingpreset:finopsandpiitokens) with case-insensitive, boundary-aware matching and improved invalid-token reporting.target/manifest.jsonby searching upward fordbt_project.yml; better stale detection (including local changes and deletes/renames).--no-aito truly disable the AI review lane.