Skip to content

Commit 3572f1c

Browse files
authored
fix: prepare dbt review action for v0.8.5 (#900)
* fix: prepare dbt review action for v0.8.4 * fix: keep review action assets self-contained * fix: degrade safely on manifest probe errors * fix: guard optional manifest availability hook * test: make review e2e assertion platform-stable * test: isolate action review e2e process * fix: bound action release lookup
1 parent 49fd9be commit 3572f1c

17 files changed

Lines changed: 494 additions & 12 deletions

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.8.5] - Unreleased
9+
10+
### Fixed
11+
12+
- **The `github/review` composite action can be downloaded by GitHub Actions again.** The release tree contained three VS Code image symlinks whose removed targets caused GitHub's action downloader to reject the entire archive before the review step started. The images are now self-contained files and a release-critical test prevents dangling links from returning.
13+
- **A valid dbt manifest is no longer mislabeled as a lint-only run.** Manifest availability is now checked independently from changed-model lookup, so new models and other valid manifests receive the correct full-run status.
14+
15+
### Added
16+
17+
- **Direct GitHub onboarding and a live dbt review demo.** The GitHub App installer now opens GitHub's repository-selection screen directly, and the README/docs link to the public `dbt-pr-review-demo` pull requests.
18+
819
## [0.8.4] - 2026-06-05
920

1021
A trace-durability patch. Open `/traces` mid-session and you'd see a rich waterfall — then the moment the agent finished its turn the view collapsed to a single "system-prompt" span, the Summary tab's *"What was asked"* showed *"No prompt recorded"*, and the Chat tab dropped every user turn but the last. The data was genuinely gone from disk, not just hidden in the viewer. This release stops the on-disk trace from being overwritten after each turn and makes the file authoritative across worker restarts. A five-persona pre-release review drove a follow-up wording fix so a reconstructed trace isn't misread as a failed run.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ into CI pipelines and orchestration DAGs. Precision data tooling for any LLM.
1717
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
1818
[![Slack](https://img.shields.io/badge/Slack-Join%20Community-4A154B?logo=slack)](https://altimate.studio/join-agentic-data-engineering-slack)
1919
[![Docs](https://img.shields.io/badge/docs-docs.altimate.sh-blue)](https://docs.altimate.sh)
20+
[![Install GitHub App](https://img.shields.io/badge/GitHub-Install%20App-24292f?logo=github)](https://github.com/apps/altimate-code-agent/installations/new)
21+
[![Live dbt review](https://img.shields.io/badge/dbt-Live%20PR%20Demo-ff694b?logo=dbt)](https://github.com/AltimateAI/dbt-pr-review-demo/pulls)
2022

2123
</div>
2224

@@ -36,6 +38,12 @@ curl -fsSL https://www.altimate.sh/install | bash
3638

3739
The curl install drops a single self-contained binary named `altimate`. The npm install exposes both `altimate` and `altimate-code` on PATH; the curl install only exposes `altimate`. Alpine Linux (musl) and Windows on ARM64 are not currently supported by the standalone binary — use `apk add gcompat` on Alpine, or use WSL on Windows-on-ARM.
3840

41+
For GitHub, [install the Altimate Code App](https://github.com/apps/altimate-code-agent/installations/new)
42+
to select repositories for interactive agent tasks. Automatic dbt pull-request
43+
reviews use the deterministic GitHub Action documented below; see the
44+
[public demo PRs](https://github.com/AltimateAI/dbt-pr-review-demo/pulls) before
45+
installing it in your own repository.
46+
3947
Then — in order:
4048

4149
**Step 1: Configure your LLM provider** (required before anything works):

docs/docs/usage/dbt-pr-review.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# dbt PR Review
22

3+
[**See live review PRs**](https://github.com/AltimateAI/dbt-pr-review-demo/pulls)
4+
·
5+
[**Install the GitHub App**](https://github.com/apps/altimate-code-agent/installations/new)
6+
7+
The public demo is a zero-secret DuckDB project with open PRs for broken joins,
8+
removed tests, PII exposure, `SELECT *`, unsafe incremental models, and a safe
9+
refactor. The GitHub App handles interactive repository tasks; the automatic
10+
review on every pull request is installed with the Action below.
11+
312
AI code review specialized for dbt/SQL. `dbt-pr-review` produces a single,
413
**signed** verdict on a pull request — `APPROVE`, `COMMENT`, or `REQUEST_CHANGES`
514
— where every **blocking** finding is backed by a deterministic engine call, not
@@ -172,7 +181,7 @@ jobs:
172181
with: { fetch-depth: 0 }
173182
# Produce target/manifest.json for the full verdict (adapter-specific).
174183
- run: pip install dbt-core dbt-bigquery && dbt deps && dbt compile
175-
- uses: AltimateAI/altimate-code/github/review@v1
184+
- uses: AltimateAI/altimate-code/github/review@v0.8.5
176185
with:
177186
mode: comment # `gate` to block merges
178187
manifest_path: target/manifest.json
@@ -270,7 +279,7 @@ In GitHub Actions, supply the connection from a secret — both sides of the dif
270279
run against the **same** warehouse (base-compiled vs head-compiled SQL):
271280

272281
```yaml
273-
- uses: AltimateAI/altimate-code/github/review@v1
282+
- uses: AltimateAI/altimate-code/github/review@v0.8.5
274283
with:
275284
mode: comment
276285
manifest_path: target/manifest.json

github/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ This will walk you through installing the GitHub app, creating the workflow, and
6060

6161
### Manual Setup
6262

63-
1. Install the GitHub app https://github.com/apps/altimate-code-agent. Make sure it is installed on the target repository.
63+
1. [Install the GitHub app](https://github.com/apps/altimate-code-agent/installations/new) and select the target repository.
6464
2. Add the following workflow file to `.github/workflows/altimate-code.yml` in your repo. Set the appropriate `model` and required API keys in `env`.
6565

6666
```yml

github/review/action.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,20 @@ runs:
5757
- name: Get altimate-code version
5858
id: version
5959
shell: bash
60+
env:
61+
ACTION_REF: ${{ github.action_ref }}
6062
run: |
61-
VERSION=$(curl -sf https://api.github.com/repos/AltimateAI/altimate-code/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
62-
VERSION="${VERSION#v}" # the installer's --version expects no leading 'v'
63+
set -euo pipefail
64+
if [[ "${ACTION_REF:-}" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
65+
VERSION="${BASH_REMATCH[1]}"
66+
else
67+
VERSION=$(
68+
curl -sf --connect-timeout 5 --max-time 15 \
69+
https://api.github.com/repos/AltimateAI/altimate-code/releases/latest \
70+
| grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4 || true
71+
)
72+
VERSION="${VERSION#v}" # the installer's --version expects no leading 'v'
73+
fi
6374
echo "version=${VERSION:-latest}" >> $GITHUB_OUTPUT
6475
6576
- name: Cache altimate-code
@@ -107,6 +118,7 @@ runs:
107118
echo "::error::altimate_api_key is set but altimate_instance is empty — provide the tenant/instance name."
108119
exit 1
109120
fi
121+
umask 077
110122
mkdir -p "$HOME/.altimate"
111123
jq -nc \
112124
--arg url "${IN_ALT_URL:-https://api.myaltimate.com}" \

github/review/examples/altimate-ingestion.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
- name: altimate dbt PR review
6565
# Pin to the altimate-code release that ships `altimate review`
6666
# (the first release cut after the dbt-pr-review feature merges).
67-
uses: AltimateAI/altimate-code/github/review@v0.8.0
67+
uses: AltimateAI/altimate-code/github/review@v0.8.5
6868
with:
6969
mode: comment # start non-blocking; switch to `gate` once trusted
7070
manifest_path: target/manifest.json

packages/opencode/src/altimate/review/orchestrate.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ export interface CheckResult {
112112

113113
/** High-level engine surface the orchestrator depends on. */
114114
export interface ReviewRunner {
115+
/** True when the configured dbt manifest loaded, independent of model lookup. */
116+
manifestAvailable?(): Promise<boolean>
115117
impact(model: string): Promise<ImpactResult>
116118
grade(sql: string, dialect: string): Promise<GradeResult>
117119
check(sql: string, dialect: string, baseSql?: string): Promise<CheckResult>
@@ -1002,6 +1004,9 @@ export async function runReview(input: OrchestrateInput): Promise<VerdictEnvelop
10021004
const modelFiles = reviewable.filter((f) => f.kind === "model_sql" || f.kind === "python_model")
10031005
const ctxByPath = new Map<string, ModelContext>()
10041006
let anyManifest = false
1007+
if (input.runner.manifestAvailable) {
1008+
anyManifest = await input.runner.manifestAvailable().catch(() => false)
1009+
}
10051010
await Promise.all(
10061011
modelFiles.map(async (file) => {
10071012
const model = modelNameFromPath(file.path)

packages/opencode/src/altimate/review/runner.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Dispatcher } from "../native"
2+
import { parseManifest } from "../native/dbt/manifest"
23
import type { CheckResult, EquivalenceResult, GradeResult, ImpactResult, ReviewRunner } from "./orchestrate"
34
import { buildReviewSchemaContext, type SchemaContext } from "./schema-context"
45

@@ -116,7 +117,10 @@ export function createDispatcherRunner(opts: DispatcherRunnerOptions): ReviewRun
116117
if (!manifestPromise) {
117118
manifestPromise = (async () => {
118119
try {
119-
const res = await Dispatcher.call("dbt.manifest", { path: opts.manifestPath })
120+
// Manifest parsing is pure TypeScript. Keep it independent from the
121+
// native dispatcher registration path so a core-loading failure
122+
// cannot incorrectly downgrade a valid dbt run to lint-only.
123+
const res = await parseManifest({ path: opts.manifestPath })
120124
const models = new Map<string, ManifestModel>()
121125
const byName = new Map<string, ManifestModel>()
122126
const children = new Map<string, string[]>()
@@ -239,6 +243,10 @@ export function createDispatcherRunner(opts: DispatcherRunnerOptions): ReviewRun
239243
}
240244

241245
return {
246+
async manifestAvailable(): Promise<boolean> {
247+
return (await loadManifest()).ok
248+
},
249+
242250
async impact(model: string): Promise<ImpactResult> {
243251
const mf = await loadManifest()
244252
if (!mf.ok) {

packages/opencode/src/cli/cmd/github.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ type IssueQueryResponse = {
141141
const AGENT_USERNAME = "altimate-code-agent[bot]"
142142
const AGENT_REACTION = "eyes"
143143
const WORKFLOW_FILE = ".github/workflows/altimate-code.yml"
144+
export const GITHUB_APP_INSTALL_URL = "https://github.com/apps/altimate-code-agent/installations/new"
144145
// altimate_change end
145146

146147
// Event categories for routing
@@ -333,7 +334,7 @@ export const GithubInstallCommand = cmd({
333334

334335
// Open browser
335336
// altimate_change start — upstream_fix: GitHub App slug is altimate-code-agent
336-
const url = "https://github.com/apps/altimate-code-agent"
337+
const url = GITHUB_APP_INSTALL_URL
337338
// altimate_change end
338339
const command =
339340
process.platform === "darwin"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, expect, test } from "bun:test"
2+
import { writeFileSync } from "node:fs"
3+
import path from "node:path"
4+
import { createDispatcherRunner } from "../../src/altimate/review/runner"
5+
import { tmpdir } from "../fixture/fixture"
6+
7+
describe("review manifest loading", () => {
8+
test("loads a valid manifest without initializing the native dispatcher", async () => {
9+
await using tmp = await tmpdir()
10+
const manifestPath = path.join(tmp.path, "manifest.json")
11+
writeFileSync(
12+
manifestPath,
13+
JSON.stringify({
14+
metadata: { adapter_type: "duckdb" },
15+
nodes: {
16+
"model.demo.orders": {
17+
resource_type: "model",
18+
name: "orders",
19+
original_file_path: "models/orders.sql",
20+
config: { materialized: "table" },
21+
depends_on: { nodes: [] },
22+
columns: {},
23+
},
24+
},
25+
sources: {},
26+
}),
27+
)
28+
29+
const runner = createDispatcherRunner({ manifestPath })
30+
expect(await runner.manifestAvailable?.()).toBe(true)
31+
expect(await runner.impact("orders")).toEqual({
32+
hasManifest: true,
33+
severity: "SAFE",
34+
directCount: 0,
35+
transitiveCount: 0,
36+
testCount: 0,
37+
})
38+
})
39+
})

0 commit comments

Comments
 (0)