Skip to content

feat: greenfield spec-test synthesis for dbt-pr-review (P0–P2)#991

Open
anandgupta42 wants to merge 9 commits into
mainfrom
feat/greenfield-spec-test-execution
Open

feat: greenfield spec-test synthesis for dbt-pr-review (P0–P2)#991
anandgupta42 wants to merge 9 commits into
mainfrom
feat/greenfield-spec-test-execution

Conversation

@anandgupta42

@anandgupta42 anandgupta42 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

PINEAPPLE

Summary

Greenfield spec-test synthesis for dbt-pr-review — the full feature (P0→P2). Closes #989. Supersedes #990 (the propose-only P0 slice, folded in here with the review-comment fixes).

For a brand-new (git-added) dbt model — which has no base to diff against, so the equivalence/data-diff lanes can't verify it — an auxiliary lane generates tests from the model's declared intent and (optionally) executes them, raising the no-reference verification ceiling that missingGrainTestLane previously only flagged. Design: packages/opencode/specs/greenfield-spec-test-synthesis.md.

Two strictly separated tracks:

  • Track A (deterministic, block-eligible): materialize dbt constraints the author declared but did not enforce (not_null/unique/accepted_values/relationships, contract types). The assertion is built from the parsed constraint — no LLM in the blocking path. Executed against the warehouse behind specTests.execute; an executed failure is a real contract_violation.
  • Track B (LLM, advisory): propose new tests from soft intent (descriptions, ref() edges, PR text). Non-blocking; optionally executed under a strict SQL sandbox and surfaced as candidate findings that can never block.

Defense in depth: verdict.ts specTestMayBlock lets a spec-test finding block only with evidence {executed:true, origin:"declared_constraint"}; advisory tools are excluded from the warning-count.

Also included:

  • Track-B SQL sandbox (spec-test-sandbox.ts): single-SELECT-only state-machine scan, no DDL/DML/multi-statement, CTE-aware relation allowlist, bounded count(*).
  • P2 read-path corrective app-memory: config-seeded suppress/bias rules; can only remove advisory findings or bias generation — never escalate a verdict.
  • Track-B prompt moved toward the compiled core (review_spec_test_prompt) with an inline fallback (Rust const in specs/core-spec-test-prompt.md; addon rebuild activates it, no regression before then).

Test Plan

  • 162 tests pass across 7 review suites: propose-only behavior, verdict enforcement (5 cases), track-A execution (block / pass / no-warehouse / execute-off), the SQL sandbox (rejects DDL/DML/multi-statement/unknown-relation, accepts bounded SELECT), track-B candidate non-blocking, and corrective-memory retrieval + suppression safety.
  • Safety invariants verified by inspection: only track-A executed+declared can block; track-B never blocks; the sandbox rejects dangerous SQL before execution; memory never suppresses a critical or the executed track-A path.
  • bun run script/upstream/analyze.ts --markers --base main --strict — clean. TypeScript CI was green on feat: greenfield spec-test synthesis for dbt-pr-review (P0: propose-only) #990.

Note: local turbo typecheck reports 3 errors in untouched packages/drivers/src/sqlserver.ts — they surface only when the optional @azure/identity peer is installed locally; CI omits it and passed.

Checklist

  • Tests added/updated
  • Documentation updated (design specs under packages/opencode/specs/)
  • CHANGELOG updated (if user-facing) — N/A: opt-in advisory lane, no default behavior change

Summary by cubic

Adds greenfield spec-test synthesis for dbt-pr-review to validate brand-new dbt models by generating tests from declared intent and, optionally, executing deterministic constraints. Tightens blocking rules and hardens the SQL sandbox, with better dedupe and relationship validation. Closes #989.

  • New Features

    • Track A (deterministic): materializes declared‑but‑unenforced constraints (not_null, unique, accepted_values, relationships, contract types). When specTests.execute: true and a warehouse is available, executes and reports contract_violation with evidence { executed: true, origin: "declared_constraint" }. Relationship targets are sandbox‑validated; source('s','t') targets resolve via the manifest (skip when unresolvable).
    • Track B (advisory): proposes tests from soft intent (descriptions, refs, PR text), executed in a strict SQL sandbox; surfaced as candidate findings that never block and are excluded from warning counts.
    • Verdict gate: only executed, declared‑constraint findings may block; now requires tool altimate.spec_test.executed with { executed: true, origin: "declared_constraint" }. Advisory/unexecuted outputs cannot block.
    • SQL sandbox: single‑SELECT only, no DDL/DML, CTE‑aware relation allowlist, bounded count(*); validates built SQL for track‑B dbtTests and track‑A relationships; requires fully qualified allowlist matches, handles comma‑separated FROM factors, and preserves quoted identifiers while stripping comments/strings.
    • Corrective memory (read‑only): config‑seeded memory.entries to bias generation and suppress advisory‑only findings; never suppresses critical or Track A paths.
    • Prompt sourcing: moves Track B system prompt toward compiled core with an inline fallback; registers altimate_core.review_spec_test_prompt in the dispatcher.
    • Reporting: summary shows spec coverage (executed/passed/failed); sidebar groups proposed tests. Risk tiers: new spec_tests lane in lite and full reviews.
    • Correctness: column_type declared constraints now surface as a single non‑blocking advisory; model primary_key emits not_null per key column; improved constraint dedupe with normalized args for accepted_values/relationships; composite/model‑level unique renders dbt_utils.unique_combination_of_columns YAML (single‑column uses core unique); stricter source‑kind matching; dropped unsupported range; stable timeout handling; memory uses explicit project with lastSeen tiebreak.
  • Migration

    • No behavior change by default; the new lane is advisory unless enabled.
    • To execute deterministic constraints, set specTests.execute: true in .altimate/review.yml and ensure a warehouse is configured; without a warehouse, proposals are shown but not run.
    • Optionally seed memory.entries to suppress known‑noisy advisory proposals.
    • Rebuild the core addon to activate the compiled prompt; the inline fallback works until then.

Written for commit 73e93fb. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added greenfield spec-test synthesis with proposed advisory tests and optional execution of declared constraints.
    • Introduced corrective, attribute-keyed memory (deterministic retrieval + suppression) and added specTests/memory to review configuration.
    • Added a native handler to resolve the spec-test system prompt from core with a safe fallback for older addons.
  • Bug Fixes
    • Improved verdict/blocking gating for spec-test evidence and expanded summaries to include enforced-constraint coverage and “Proposed tests”.
    • Added SQL sandboxing for generated assertions.
  • Documentation
    • Added/update spec documents for greenfield synthesis and corrective app-memory.
  • Tests
    • Expanded Bun test coverage for synthesis, execution gating, sandboxing, memory behavior, and verdict rules.

anandgupta42 and others added 6 commits July 7, 2026 16:54
…nly)

Adds an auxiliary lane that generates candidate tests for greenfield
(git-added) dbt models from their declared intent, raising the no-reference
verification ceiling that `missingGrainTestLane` only flagged. Two strictly
separated tracks:

- Track A (deterministic): materialize declared-but-unenforced dbt constraints.
- Track B (LLM, advisory): propose new tests from soft intent.

P0 is propose-only: every finding is `suggestion` + `confidence: unknown` under
the `altimate.spec_test.proposed` evidence tool, which is excluded from the
verdict warning-count — it can never block a PR. Emits a committable
`schema.yml` patch as authoring help.

- `spec-test-gen.ts`: types, `filterToSpecDerived` anti-fabrication guard,
  `isBlockEligible`, `runSpecTestGen` transport (inline prompt for P0).
- `orchestrate.ts`: `specTestSynthesisLane` gated on added models + injected
  `generateSpecTests` (pure, like `aiReview`).
- `risk-tier.ts`: `spec_tests` in lite/full tiers.
- `verdict.ts`: exclude `altimate.spec_test.proposed` from warning accumulation.
- `config.ts`: `specTests.execute` shape (P1). `format.ts`: "Proposed tests".
- Design specs under `packages/opencode/specs/`.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A spec-test finding may only reach a blocking verdict when it was actually
EXECUTED against a constraint the author DECLARED (evidence executed:true +
origin:declared_constraint). Advisory/unexecuted/inferred spec-test findings
can never force REQUEST_CHANGES, regardless of the severity the lane assigned —
defense in depth so the verdict never trusts the lane. Non-spec-test findings
are unaffected.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…straints

Adds the deterministic blocking path for greenfield spec-test synthesis:

- runner: declaredConstraints(model) reads the manifest for declared
  not_null/unique/accepted_values/relationships + contract types with
  provenance and hasEnforcingTest; runGeneratedTests(tests, warehouse?) executes
  bounded violating-row checks via the dispatcher, id-keyed, null when no warehouse.
- orchestrate: materialize only declared-but-UNENFORCED constraints (no LLM) into
  GeneratedTests; when config.specTests.execute is set and a warehouse is present,
  execute them. An executed failure emits a critical contract_violation carrying
  evidence { executed: true, origin: declared_constraint }, the only shape
  specTestMayBlock lets block. No warehouse / execute off → non-blocking proposals.
- Track B (LLM proposals) stays propose-only and is NEVER executed.
- verdict: summary.enforcedConstraints (executed/passed/failed); format: Spec coverage.

145 review tests pass; the sole blocking path is deterministic end to end.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…execution

Review findings (#990):
- Include test identity (ref:kind:column) in spec-test ruleKey so distinct
  proposals from one source no longer collapse in dedupe().
- filterToSpecDerived replaces derivedFrom with the trusted extracted source
  (matched by ref), so a forged origin/kind can't survive.
- Guard requires dbtTest.test === kind (drop test_mismatch), blocking arbitrary
  macros from reaching the proposed patch.
- Build the committable schema.yml patch from evidence.result.yaml, not by
  reparsing the LLM-influenced finding body.
- Don't turn already-enforcing schema.yml tests into proposal sources.
- Track A (deterministic) now runs even when AI is disabled: the lane is gated on
  (generateSpecTests || runner.declaredConstraints), not the LLM injection.

Track-B execution + sandbox:
- New spec-test-sandbox.ts sanitizeAssertionSql: single SELECT only (state-machine
  scan), no DDL/DML/side-effects, CTE-aware relation allowlist (model + declared
  upstreams), wrapped as a bounded count(*). Rejected SQL is dropped, never run.
- Executed track-B failures emit warning/unknown under altimate.spec_test.candidate
  (category test_coverage) — never critical, never blocking (also excluded from the
  verdict warning-count). Only track-A executed+declared can block.

156 review tests pass; verdict gate specTestMayBlock unchanged.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…line fallback

Moves the greenfield spec-test track-B generation prompt toward the IP-in-binary
pattern used by the AI reviewer:
- native/types.ts + native/altimate-core.ts: register
  altimate_core.review_spec_test_prompt, calling core.reviewSpecTestSystemPrompt()
  via a defensive cast so it typechecks before the addon regenerates the binding.
- spec-test-gen.ts resolveSystemPrompt(): fetch the prompt from core, falling back
  to the inline buildSystemPrompt() when the running addon predates the method —
  no runtime regression until the core ships it.
- specs/core-spec-test-prompt.md: the Rust const + napi export to apply on a clean
  altimate-core-internal branch (the local core checkout had unrelated WIP), plus
  rebuild/activation notes.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Structured, attribute-keyed corrective memory (corrective-app-memory.md), read-path only:
- corrective-memory.ts: MemoryScope/MemoryEntry + pure getMemory() retrieval
  (wildcard match, most-specific-match-wins, project as hard isolation boundary).
- config.ts: seeded memory.entries loaded from .altimate/review.yml.
- Two consumers: (1) bias track-B generation via SpecTestGenInput.priors and
  deterministically drop suppressed track-B proposal kinds; (2) reviewer post-filter
  drops advisory findings matched by a suppress entry.
- SAFETY (enforced): memoryMaySuppressFinding never suppresses a critical or the
  altimate.spec_test.executed (block-eligible) path; memory can only REMOVE advisory
  findings / bias generation, never add or escalate — so it cannot change a verdict
  toward REQUEST_CHANGES. Track A (declared constraints) is never memory-suppressed.

162 review tests pass. Write-path/decay deferred to a later phase.

Refs #989

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

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

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.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds greenfield spec-test generation and execution for dbt reviews, with core prompt bridging, corrective-memory suppression, verdict gating, summary rendering, and tests covering the new flows.

Changes

Spec-test synthesis and corrective memory

Layer / File(s) Summary
Specification documents
packages/opencode/specs/greenfield-spec-test-synthesis.md, packages/opencode/specs/corrective-app-memory.md, packages/opencode/specs/core-spec-test-prompt.md
Defines the synthesis split, corrective memory model, and prompt migration steps.
Config, native bridge, and lane routing
packages/opencode/src/altimate/review/config.ts, packages/opencode/src/altimate/native/types.ts, packages/opencode/src/altimate/native/altimate-core.ts, packages/opencode/src/altimate/review/index.ts, packages/opencode/src/altimate/review/risk-tier.ts, packages/opencode/src/altimate/review/run.ts, packages/opencode/test/altimate/altimate-core-native.test.ts
Adds specTests and memory config sections, registers the native prompt bridge, routes spec_tests, and updates native registration coverage.
Spec-test generation and sandbox
packages/opencode/src/altimate/review/spec-test-gen.ts, packages/opencode/src/altimate/review/spec-test-sandbox.ts, packages/opencode/src/altimate/review/stable-json.ts
Implements Track B generation, prompt fallback, anti-fabrication guards, deterministic IDs, and SQL assertion sanitization.
Corrective memory model
packages/opencode/src/altimate/review/corrective-memory.ts, packages/opencode/test/altimate/review-corrective-memory.test.ts
Adds scoped memory schemas, matching/ranking, read-only retrieval, and suppression coverage.
Runner manifest and execution
packages/opencode/src/altimate/review/runner.ts, packages/opencode/test/altimate/review-runner.test.ts
Enriches manifest loading, derives declared constraints, and executes generated tests with timeout handling and sandbox checks.
Spec-test synthesis orchestration
packages/opencode/src/altimate/review/orchestrate.ts, packages/opencode/test/altimate/review-spec-test-gen.test.ts, packages/opencode/test/altimate/review-spec-test-exec.test.ts
Adds Track A/Track B orchestration, memory suppression, enforced-constraint metrics, and end-to-end synthesis/execution coverage.
Verdict and summary output
packages/opencode/src/altimate/review/verdict.ts, packages/opencode/src/altimate/review/format.ts, packages/opencode/test/altimate/review-spec-test-verdict.test.ts, packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
Restricts blocking to executed declared constraints, carries enforced-constraint counts, and renders proposed tests in the summary.
Estimated code review effort: 5 (Critical) ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant runReview
  participant runSpecTestGen
  participant altimate_core_review_spec_test_prompt
  participant LLM
  runReview->>runSpecTestGen: generateSpecTests(specSources)
  runSpecTestGen->>altimate_core_review_spec_test_prompt: resolveSystemPrompt()
  altimate_core_review_spec_test_prompt-->>runSpecTestGen: prompt or fallback
  runSpecTestGen->>LLM: stream request
  LLM-->>runSpecTestGen: JSON proposed tests
  runSpecTestGen->>runSpecTestGen: filterToSpecDerived(tests)
  runSpecTestGen-->>runReview: GeneratedTest[]
Loading
sequenceDiagram
  participant specTestSynthesisLane
  participant ReviewRunner
  participant sanitizeAssertionSql
  participant getMemory
  participant buildEnvelope
  specTestSynthesisLane->>ReviewRunner: declaredConstraints(model)
  ReviewRunner-->>specTestSynthesisLane: DeclaredConstraint[]
  specTestSynthesisLane->>ReviewRunner: runGeneratedTests(tests, warehouse, options)
  ReviewRunner-->>specTestSynthesisLane: pass/fail results
  specTestSynthesisLane->>sanitizeAssertionSql: validate assertionSql
  sanitizeAssertionSql-->>specTestSynthesisLane: sanitized SQL or reject
  specTestSynthesisLane->>getMemory: getMemory(entries, query)
  getMemory-->>specTestSynthesisLane: suppression entries
  specTestSynthesisLane->>buildEnvelope: findings + enforcedConstraints
  buildEnvelope-->>specTestSynthesisLane: VerdictEnvelope
Loading

Possibly related PRs

Suggested reviewers: sahrizvi

Poem

A rabbit hops through schema.yml’s glade,
Finding constraints the author forgot, unmade.
Track A stands firm, Track B hums low,
Memory prunes what shouldn’t grow.
🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description does not follow the required template; it omits the Issue, Type of change, Screenshots, and Checklist sections. Rewrite it using the repository template and fill in the missing sections: issue link, change type, verification, screenshots if relevant, and checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes align with #989 by adding Track A/Track B synthesis, sandboxing, verdict gating, memory, and core prompt wiring.
Out of Scope Changes check ✅ Passed The patch stays focused on spec-test synthesis and supporting docs/tests, with no clear unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed Title concisely matches the main change: greenfield spec-test synthesis for dbt-pr-review across P0–P2.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/greenfield-spec-test-execution

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

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

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

Actionable comments posted: 8

🧹 Nitpick comments (1)
packages/opencode/test/altimate/review-spec-test-sandbox.test.ts (1)

4-32: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add CTE/WITH and comment-stripping coverage for the SQL sandbox.

The four tests cover the core rejection paths (side-effect, multi-statement, unknown relation) and a basic acceptance case. However, the upstream sanitizeAssertionSql explicitly handles CTE/WITH queries (/^\s*with\b[\s\S]*\bselect\b/i) and strips strings/comments via withoutStringsAndComments before side-effect detection — neither path is exercised by these tests. Since this is a security-critical function guarding Track B execution, adding coverage for these cases would strengthen confidence.

🧪 Suggested additional sandbox tests
   test("accepts and bounds a select on allowlisted relations", () => {
     const sanitized = sanitizeAssertionSql("select order_id from fct_orders where order_id is null;", ["fct_orders"])
     expect(sanitized.ok).toBe(true)
     if (sanitized.ok) {
       expect(sanitized.sql).toBe(
         "select count(*) as n from ( select order_id from fct_orders where order_id is null ) _s",
       )
     }
   })
+
+  test("accepts a WITH/CTE query referencing allowlisted relations", () => {
+    const sanitized = sanitizeAssertionSql(
+      "with cte as (select order_id from fct_orders) select * from cte where order_id is null",
+      ["fct_orders"],
+    )
+    expect(sanitized.ok).toBe(true)
+    if (sanitized.ok) {
+      expect(sanitized.sql).toContain("count(*) as n")
+    }
+  })
+
+  test("does not flag side-effect keywords inside string literals", () => {
+    const sanitized = sanitizeAssertionSql(
+      "select 'drop table evil' as msg from fct_orders",
+      ["fct_orders"],
+    )
+    expect(sanitized.ok).toBe(true)
+  })
+
+  test("does not flag side-effect keywords inside comments", () => {
+    const sanitized = sanitizeAssertionSql(
+      "select order_id from fct_orders /* drop table evil */",
+      ["fct_orders"],
+    )
+    expect(sanitized.ok).toBe(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/test/altimate/review-spec-test-sandbox.test.ts` around
lines 4 - 32, Add coverage in sanitizeAssertionSql for the missing CTE/WITH and
comment-stripping paths. Extend the review-spec-test-sandbox tests to verify
that a WITH query containing a SELECT is accepted and sanitized correctly, and
that SQL comments are ignored by the side-effect detection in
withoutStringsAndComments. Keep the existing sanitizeAssertionSql assertions
pattern and place the new cases alongside the current rejection/acceptance
tests.
🤖 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/orchestrate.ts`:
- Around line 1106-1123: materializeDeclaredConstraints is currently emitting
DeclaredConstraintKind.column_type as an executable dbt test, but the
runner-side SQL generation path does not support that kind and drops the
resulting error in execute mode. Update materializeDeclaredConstraints (and the
corresponding generated-test SQL/rendering path it feeds) to either add explicit
column_type support end-to-end or skip materializing column_type into
GeneratedTest until it can be represented safely. Make sure the handling is
consistent with the other declared constraint kinds so unsupported tests are not
silently lost.
- Around line 249-253: The project selection in memoryProjectForInput is
incorrectly falling back to the only configured project even when input.project
is explicitly set to a different repo. Update memoryProjectForInput so that when
input.project exists it always returns that value as the query key, and only use
the single configured project fallback when input.project is absent; keep the
existing configuredMemoryEntries and scope.project checks to locate the affected
logic.

In `@packages/opencode/src/altimate/review/runner.ts`:
- Around line 660-669: The declaredConstraints()/buildGeneratedTestSql() flow is
emitting `column_type` as an executable constraint even though
`buildGeneratedTestSql()` does not support it, which causes contract-type
violations to be dropped instead of enforced. Update the `runner.ts` handling
around `declaredConstraints()` and `buildGeneratedTestSql()` so `column_type` is
either fully implemented as an executable SQL test or explicitly excluded from
the set of constraints that are treated as executable. Make sure the
`DeclaredConstraint["kind"]` branch for `column_type` stays aligned with
whatever `buildGeneratedTestSql()` can actually generate.
- Around line 701-716: The constraint normalization in runner.ts currently
treats table-level primary_key the same as unique, so null key values can still
slip through. Update the constraint handling loop in the constraint collection
logic to detect primary_key separately from unique, and when a primary_key is
found, add the existing unique declaration plus not_null checks for each column
in the key. Use the existing helpers around add(), constraintKey(), and
sourceRef() so the new not_null entries are generated alongside the primary_key
constraint.
- Around line 305-309: The withTimeout helper leaves the fallback setTimeout
running even when the raced promise settles first, which can keep the CLI alive
unnecessarily. Update withTimeout in runner.ts to retain the timer handle, and
clear it as soon as the input promise settles (success or failure) while
preserving the existing timeout error behavior from the Promise.race path.
- Around line 270-280: The relationships branch in runner.ts is building
executable SQL directly from dbtTest args["to"], which can bypass
sanitizeAssertionSql and allow unsafe generated relation targets to reach
sql.execute when specTests.execute is enabled. Update the logic in relationships
handling and relationshipTarget usage so the target relation is validated and
sanitized against the manifest before being interpolated, rejecting anything
outside the expected relation format or any quoted/multi-statement input. Keep
the fix localized to the test SQL construction path in runner.ts, especially the
relationships test branch and relationshipTarget resolution.

In `@packages/opencode/src/altimate/review/spec-test-sandbox.ts`:
- Around line 211-212: The relation allowlist check in
relationAliases/relationAllows currently permits basename fallback, so qualified
references can match too loosely. Update the sandbox validation in
spec-test-sandbox.ts so exact allowlist matching is required for qualified
relation references, while still allowing unqualified basenames only when the
reference itself is unqualified. Use the existing relationAliases and
allowed-set check to distinguish fully qualified names from basename aliases and
reject cases like other_schema.fct_orders unless that exact qualified form is
explicitly allowlisted.

In `@packages/opencode/src/altimate/review/verdict.ts`:
- Around line 60-65: The blocking gate in specTestMayBlock should only accept
findings from the exact executed spec-test tool, not any altimate.spec_test*
prefix. Update the check in specTestMayBlock to require the tool name to be
altimate.spec_test.executed explicitly before allowing the executed/origin
result fields to block, so malformed proposed or candidate findings cannot spoof
the result object. Keep the existing result validation in place, but tighten the
tool identity check used by the Finding evidence.

---

Nitpick comments:
In `@packages/opencode/test/altimate/review-spec-test-sandbox.test.ts`:
- Around line 4-32: Add coverage in sanitizeAssertionSql for the missing
CTE/WITH and comment-stripping paths. Extend the review-spec-test-sandbox tests
to verify that a WITH query containing a SELECT is accepted and sanitized
correctly, and that SQL comments are ignored by the side-effect detection in
withoutStringsAndComments. Keep the existing sanitizeAssertionSql assertions
pattern and place the new cases alongside the current rejection/acceptance
tests.
🪄 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

Run ID: 19e062d7-f949-4fe0-8624-6539080daf60

📥 Commits

Reviewing files that changed from the base of the PR and between f376fef and 416bab7.

📒 Files selected for processing (21)
  • packages/opencode/specs/core-spec-test-prompt.md
  • packages/opencode/specs/corrective-app-memory.md
  • packages/opencode/specs/greenfield-spec-test-synthesis.md
  • packages/opencode/src/altimate/native/altimate-core.ts
  • packages/opencode/src/altimate/native/types.ts
  • packages/opencode/src/altimate/review/config.ts
  • packages/opencode/src/altimate/review/corrective-memory.ts
  • packages/opencode/src/altimate/review/format.ts
  • packages/opencode/src/altimate/review/index.ts
  • packages/opencode/src/altimate/review/orchestrate.ts
  • packages/opencode/src/altimate/review/risk-tier.ts
  • packages/opencode/src/altimate/review/run.ts
  • packages/opencode/src/altimate/review/runner.ts
  • packages/opencode/src/altimate/review/spec-test-gen.ts
  • packages/opencode/src/altimate/review/spec-test-sandbox.ts
  • packages/opencode/src/altimate/review/verdict.ts
  • packages/opencode/test/altimate/review-corrective-memory.test.ts
  • packages/opencode/test/altimate/review-spec-test-exec.test.ts
  • packages/opencode/test/altimate/review-spec-test-gen.test.ts
  • packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
  • packages/opencode/test/altimate/review-spec-test-verdict.test.ts

Comment thread packages/opencode/src/altimate/review/orchestrate.ts
Comment thread packages/opencode/src/altimate/review/orchestrate.ts
Comment thread packages/opencode/src/altimate/review/runner.ts Outdated
Comment thread packages/opencode/src/altimate/review/runner.ts Outdated
Comment on lines +660 to +669
if (dataType) {
const kind: DeclaredConstraint["kind"] = "column_type"
add({
kind,
column,
args: { data_type: dataType },
hasEnforcingTest: enforcing.has(constraintKey(kind, column)),
uniqueId: target.unique_id,
sourceRef: sourceRef(target.name, kind, column),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Implement column_type execution or exclude it from executable constraints.

declaredConstraints() emits column_type, but buildGeneratedTestSql() always returns unsupported. Executed contract-type violations will become errors and get dropped instead of enforcing Track A.

Also applies to: 284-284

🤖 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/runner.ts` around lines 660 - 669, The
declaredConstraints()/buildGeneratedTestSql() flow is emitting `column_type` as
an executable constraint even though `buildGeneratedTestSql()` does not support
it, which causes contract-type violations to be dropped instead of enforced.
Update the `runner.ts` handling around `declaredConstraints()` and
`buildGeneratedTestSql()` so `column_type` is either fully implemented as an
executable SQL test or explicitly excluded from the set of constraints that are
treated as executable. Make sure the `DeclaredConstraint["kind"]` branch for
`column_type` stays aligned with whatever `buildGeneratedTestSql()` can actually
generate.

Comment thread packages/opencode/src/altimate/review/runner.ts
Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts Outdated
Comment thread packages/opencode/src/altimate/review/verdict.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 416bab7b8b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (test.kind === "relationships") {
if (!column) return { detail: "relationships test is missing column" }
const args = test.dbtTest?.args ?? {}
const target = relationshipTarget(args["to"], manifest)

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 Badge Validate relationship targets before execution

When specTests.execute is enabled, a Track-B LLM proposal can return a relationships dbtTest with args.to set to any relation; this line turns that untrusted value into a JOIN target without checking options.allowedRelations (unlike the assertionSql sandbox). A malformed or prompt-injected proposal such as to: "sensitive_schema.users" would make the review warehouse query an arbitrary relation instead of only the changed model/upstreams.

Useful? React with 👍 / 👎.

Comment on lines +305 to +309
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) => setTimeout(() => reject(new Error(`spec-test SQL timed out after ${ms}ms`)), ms)),
])

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 Badge Clear the SQL timeout after successful execution

For fast sql.execute calls, the timeout created here is never cleared after Promise.race resolves, so every generated test leaves a live 30s timer behind. In the specTests.execute path this keeps the CLI/CI process alive until the last stale timer fires even though the review work is complete; store the timer id and clear it in a finally/settled handler.

Useful? React with 👍 / 👎.

Comment on lines +661 to +666
const kind: DeclaredConstraint["kind"] = "column_type"
add({
kind,
column,
args: { data_type: dataType },
hasEnforcingTest: enforcing.has(constraintKey(kind, column)),

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 Badge Do not materialize enforced contract types as tests

For a new model with contract.enforced: true, this records every declared data_type as an unenforced column_type constraint, but the later materialization emits a dbt test named column_type and runGeneratedTests does not support executing it. With the default non-executing mode the summary proposes invalid YAML for every typed column; with execution enabled these checks are all dropped as errors, so contract type coverage is noisy rather than enforceable.

Useful? React with 👍 / 👎.

Comment on lines +706 to +710
if (normalized !== "primary_key" && normalized !== "unique") continue
const columns = asArray(rawConstraint?.columns).map(String).filter(Boolean)
if (!columns.length) continue
const kind: DeclaredConstraint["kind"] = "unique"
add({

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 Badge Add not_null checks for model-level primary keys

When a dbt contract declares a model-level primary_key, this branch collapses it to only a unique generated test. The generated unique SQL filters out null key values, so with specTests.execute a new model can contain nulls in a declared primary key and still be counted as passed instead of producing the block-eligible contract violation.

Useful? React with 👍 / 👎.

const out: DeclaredConstraint[] = [...testConstraints]
const seen = new Set(out.map((c) => `${c.kind}:${c.column ?? ""}:${c.uniqueId ?? c.sourceRef}`))
const add = (c: DeclaredConstraint) => {
const key = `${c.kind}:${c.column ?? ""}:${c.uniqueId ?? c.sourceRef}`

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 Badge Include columns when deduping model-level constraints

When a model declares more than one model-level unique/primary_key constraint, the constraints added below all have no column and reuse the model's uniqueId, so this dedupe key treats distinct column sets as the same constraint and drops every one after the first. In that schema, only one declared constraint is proposed/executed and the rest are silently skipped.

Useful? React with 👍 / 👎.

function relationAliases(name: string): string[] {
const normalized = normalizeRelation(name)
const parts = normalized.split(".").filter(Boolean)
return [...new Set([normalized, parts.at(-1) ?? ""].filter(Boolean))]

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 Badge Match sandboxed relations by full name

For Track-B assertionSql, the sandbox allowlist accepts any relation whose final segment matches an allowed model name because relationAliases() adds parts.at(-1). In a multi-schema warehouse, an LLM-proposed assertion like select * from prod_sensitive.fct_orders passes when only fct_orders was allowlisted, so the review can read an unintended relation that merely shares the table basename.

Useful? React with 👍 / 👎.

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

14 issues found across 21 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts
Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts Outdated
Comment thread packages/opencode/src/altimate/review/runner.ts Outdated
Comment thread packages/opencode/src/altimate/review/runner.ts
Comment thread packages/opencode/src/altimate/review/orchestrate.ts Outdated
Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts
Comment thread packages/opencode/src/altimate/review/verdict.ts
Comment thread packages/opencode/src/altimate/review/runner.ts Outdated
Comment thread packages/opencode/specs/corrective-app-memory.md
Comment thread packages/opencode/src/altimate/review/spec-test-gen.ts Outdated
The registration-completeness test hardcodes ALL_METHODS; add the new
altimate_core.review_spec_test_prompt dispatcher method so registered-count
matches (was the sole CI test failure on #991).

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

2 similar comments
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

…ness)

Security:
- Track-B dbtTest execution (relationships/accepted_values) now validates the
  built SQL through sanitizeAssertionSql against the allowlist — LLM args.to can
  no longer make the warehouse join an arbitrary relation.
- Sandbox: qualified relation refs require a full-qualified allowlist match (no
  basename fallback for other_schema.x); allowlist-check comma-separated FROM
  factors; fix trailing-semicolon-plus-comment and leading-comment-before-WITH
  edge cases.
- verdict.ts specTestMayBlock now requires tool === altimate.spec_test.executed
  exactly, so a proposed/candidate finding can't spoof {executed,origin} to block.

Correctness:
- column_type declared constraints are no longer materialized as unrunnable dbt
  tests (excluded from the executable/proposed set; TODO INFORMATION_SCHEMA).
- Model-level primary_key emits not_null per key column (nulls no longer pass).
- Model-level constraint dedupe keys on columns so distinct sets aren't collapsed.
- Memory uses the explicit input.project; single-project fallback only when absent.
- filterToSpecDerived rejects a declared-constraint proposal whose source kind
  differs from the test kind.
- Clear the raced sql.execute timeout in finally; memory tie-break by lastSeen.

Docs/cleanup: mark memory pin/forbid as future scope; dedup stableJson.

Review blast radius 264 tests green.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

2 similar comments
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/opencode/test/altimate/review-runner.test.ts (1)

166-180: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use await using with tmpdir() instead of manual disposal.

Both runnerWithNodes and runnerWithManifest use const tmp = await tmpdir() and rely on manual await tmp[Symbol.asyncDispose]?.() at the end of each test. If an assertion fails before that line, the temp directory leaks. Per coding guidelines, await using syntax is required for automatic cleanup.

The helpers can be restructured to spread tmp into the returned object (preserving [Symbol.asyncDispose]), then use await using at call sites:

♻️ Proposed refactor for helpers and test call sites
   async function runnerWithNodes(nodes: Record<string, any>) {
-    const tmp = await tmpdir()
+    const tmp = await tmpdir()
     const manifestPath = path.join(tmp.path, "manifest.json")
     writeFileSync(
       manifestPath,
       JSON.stringify({
         metadata: { adapter_type: "duckdb" },
         nodes,
         sources: {},
       }),
     )
-    return { tmp, runner: createDispatcherRunner({ manifestPath }) }
+    return { ...tmp, runner: createDispatcherRunner({ manifestPath }) }
   }
   async function runnerWithManifest() {
-    const tmp = await tmpdir()
+    const tmp = await tmpdir()
     const manifestPath = path.join(tmp.path, "manifest.json")
     writeFileSync(
       manifestPath,
       JSON.stringify({
         metadata: { adapter_type: "duckdb" },
         nodes: { ... },
         sources: {},
       }),
     )
-    return { tmp, runner: createDispatcherRunner({ manifestPath }) }
+    return { ...tmp, runner: createDispatcherRunner({ manifestPath }) }
   }

Then update each test to use await using and remove manual disposal:

   test("model-level primary_key emits composite unique plus not_null per key column", async () => {
-    const { tmp, runner } = await runnerWithNodes({
+    await using ctx = await runnerWithNodes({
       "model.demo.fct_orders": { ... },
     })
+    const { runner } = ctx
     ...
-    await tmp[Symbol.asyncDispose]?.()
   })
   test("rejects track-B relationships when the target relation is not allowlisted", async () => {
-    const { tmp, runner } = await runnerWithManifest()
+    await using ctx = await runnerWithManifest()
+    const { runner } = ctx
     ...
-    await tmp[Symbol.asyncDispose]?.()
   })

Apply the same pattern to all four tests in both suites.

Based on learnings, for brand-new test files under packages/opencode/test/altimate/, use await using tmp = await tmpdir() with per-test scoping. As per coding guidelines, always use await using syntax with tmpdir() for automatic cleanup when the variable goes out of scope.

Also applies to: 238-270

🤖 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/altimate/review-runner.test.ts` around lines 166 -
180, The test helpers in review-runner.test.ts still manage tmpdir cleanup
manually, which can leak temp directories if a test fails early. Update the
runnerWithNodes and runnerWithManifest helpers to work with await using by
preserving the async disposable tmp object when returning it, then change each
test that calls tmpdir() to declare the temp directory with await using at the
call site and remove the explicit Symbol.asyncDispose cleanup. Apply the same
pattern consistently across the review runner test suites so tmpdir usage is
always automatically cleaned up.

Sources: Coding guidelines, Learnings

🤖 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/spec-test-sandbox.ts`:
- Around line 224-226: The relation extraction in referencedRelations(trimmed)
is picking up FROM/JOIN tokens inside comments and string literals, which can
incorrectly trigger unknown_relation. Update the parsing path in the sandbox
assertion check to scan a masked SQL form that blanks comments and string
literals while preserving quoted identifiers, then run relation extraction
against that sanitized text. Keep the ctes and relationAllowed checks in the
same flow so only real table references are validated.

---

Nitpick comments:
In `@packages/opencode/test/altimate/review-runner.test.ts`:
- Around line 166-180: The test helpers in review-runner.test.ts still manage
tmpdir cleanup manually, which can leak temp directories if a test fails early.
Update the runnerWithNodes and runnerWithManifest helpers to work with await
using by preserving the async disposable tmp object when returning it, then
change each test that calls tmpdir() to declare the temp directory with await
using at the call site and remove the explicit Symbol.asyncDispose cleanup.
Apply the same pattern consistently across the review runner test suites so
tmpdir usage is always automatically cleaned up.
🪄 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

Run ID: 43fc890e-1b65-4caf-8903-b121a4ef5ef3

📥 Commits

Reviewing files that changed from the base of the PR and between 0086cb3 and 122b316.

📒 Files selected for processing (14)
  • packages/opencode/specs/corrective-app-memory.md
  • packages/opencode/src/altimate/review/corrective-memory.ts
  • packages/opencode/src/altimate/review/orchestrate.ts
  • packages/opencode/src/altimate/review/runner.ts
  • packages/opencode/src/altimate/review/spec-test-gen.ts
  • packages/opencode/src/altimate/review/spec-test-sandbox.ts
  • packages/opencode/src/altimate/review/stable-json.ts
  • packages/opencode/src/altimate/review/verdict.ts
  • packages/opencode/test/altimate/review-corrective-memory.test.ts
  • packages/opencode/test/altimate/review-runner.test.ts
  • packages/opencode/test/altimate/review-spec-test-exec.test.ts
  • packages/opencode/test/altimate/review-spec-test-gen.test.ts
  • packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
  • packages/opencode/test/altimate/review-spec-test-verdict.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/opencode/specs/corrective-app-memory.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
  • packages/opencode/test/altimate/review-spec-test-verdict.test.ts
  • packages/opencode/test/altimate/review-corrective-memory.test.ts
  • packages/opencode/src/altimate/review/corrective-memory.ts
  • packages/opencode/src/altimate/review/verdict.ts
  • packages/opencode/test/altimate/review-spec-test-exec.test.ts
  • packages/opencode/src/altimate/review/spec-test-gen.ts
  • packages/opencode/test/altimate/review-spec-test-gen.test.ts
  • packages/opencode/src/altimate/review/runner.ts
  • packages/opencode/src/altimate/review/orchestrate.ts

Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts Outdated

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

All reported issues were addressed across 14 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts Outdated
Comment thread packages/opencode/src/altimate/review/orchestrate.ts
Comment thread packages/opencode/src/altimate/review/runner.ts Outdated
Comment thread packages/opencode/src/altimate/review/spec-test-sandbox.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 122b316d10

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1102 to +1103
kind: c.kind,
dbtTest: { column: c.column, test: c.kind, args },

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 Badge Emit a valid test for model-level unique constraints

When declaredConstraints passes a model-level unique/primary_key, c.column is undefined and args contains columns, so the default/no-warehouse path renders a schema patch like models: [{ tests: [{ unique: { columns: [...] } }] }]. The built-in dbt unique test expects a single column_name rather than a columns list, so users applying the suggested patch for composite keys get a test that fails to compile instead of enforcing the declared key; materialize these as an actual multi-column generic test or avoid proposing YAML for them.

Useful? React with 👍 / 👎.

Comment on lines +135 to +139
const columns = constraintColumns(args)
.map((c) => c.toLowerCase())
.sort()
.join(",")
return `${kind}:${(column ?? "").toLowerCase()}:${columns}`

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 Badge Include constraint arguments when matching tests

For argument-bearing constraints, this key ignores everything except the column set, so an existing accepted_values/relationships test on the same column marks a different declared constraint as hasEnforcingTest. In that case Track A never materializes or executes the declared values/to/field check, allowing a contract violation to be missed even with specTests.execute enabled; include the normalized argument payload in the key for these kinds.

Useful? React with 👍 / 👎.

return model ? relationForModel(model) : sqlTableRef(ref)
}
const source = /\bsource\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]\s*\)/.exec(raw)
if (source) return sqlTableRef(`${source[1]}.${source[2]}`)

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 Badge Resolve source() targets from the manifest

When a declared/proposed relationships test uses to: source('src','tbl'), this branch turns the macro arguments into the physical relation src.tbl. In dbt the first argument is the source name, not necessarily the database/schema/identifier, so projects with source('stripe','payments') backed by e.g. raw.stripe_payments will execute the generated relationship check against the wrong relation or error and then be dropped, leaving source relationships unverified under specTests.execute.

Useful? React with 👍 / 👎.

"unique",
"accepted_values",
"relationships",
"range",

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 Badge Do not emit unsupported range tests

By admitting range here, a Track-B response like dbtTest: { test: 'range' } passes the guard and is rendered verbatim into the proposed schema YAML, but dbt core has no built-in generic test named range and runGeneratedTests also reports it as unsupported. With the default propose-only mode this produces a non-compiling patch for users to apply; use a supported package test name or keep this kind out of emitted dbt tests.

Useful? React with 👍 / 👎.

Comment on lines +298 to +300
if (trackB) {
const targetCheck = sanitizeAssertionSql(`select 1 from ${target}`, expandedAllowedRelations(manifest, options))
if (!targetCheck.ok) return { detail: `relationships target rejected by sandbox: ${targetCheck.reason}` }

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 Badge Sandbox declared relationship targets too

With specTests.execute enabled, a PR-controlled schema can declare an unenforced relationships constraint whose to points at any relation, and Track A skips the sandbox because this check only runs for trackB. Fresh evidence: the current code validates LLM-inferred relationship targets but still executes declared-constraint joins directly, so a new model can make CI query an unrelated sensitive table under the review warehouse role.

Useful? React with 👍 / 👎.

- Sandbox: extract withoutCommentsAndStringLiterals (preserves quoted identifiers)
  and use it for BOTH cteNames and referencedRelations, so a FROM/JOIN inside a
  comment/string no longer false-rejects and quoted CTE/relation names resolve.
- P1: declared (track-A) relationship targets are now sandbox-validated against
  the allowlist unconditionally (not only track-B), and source('s','t') targets
  resolve via the manifest (skip, not guess, when unresolvable).
- constraintKey includes normalized args for accepted_values/relationships so an
  existing test on the same column no longer masks a different declared constraint.
- column_type declared constraints surface as a single non-blocking advisory
  finding instead of silently disappearing.
- Composite/model-level unique renders valid dbt_utils.unique_combination_of_columns
  YAML (core unique takes column_name); single-column keeps core unique.
- Drop range from ALLOWED_TEST_KINDS (no dbt core generic test named range).

Review blast radius 281 tests green.

Refs #989

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

2 similar comments
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

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

🧹 Nitpick comments (1)
packages/opencode/test/altimate/review-runner.test.ts (1)

310-343: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer await using for temp directory cleanup in newly added tests.

The runnerWithSourceManifest helper returns tmp for manual disposal, but if any assertion throws before await tmp[Symbol.asyncDispose]?.() executes, the temp directory leaks. Using await using ensures cleanup on scope exit regardless of assertion outcomes. Based on learnings, newly added code under packages/opencode/test/altimate/ should use await using tmp = await tmpdir() with per-test scoping.

♻️ Refactor helper to enable `await using`
 async function runnerWithSourceManifest() {
-  const tmp = await tmpdir()
+  await using tmp = await tmpdir()
   const manifestPath = path.join(tmp.path, "manifest.json")
   writeFileSync(
     manifestPath,
     JSON.stringify({
       metadata: { adapter_type: "duckdb" },
       nodes: { /* ... */ },
       sources: { /* ... */ },
     }),
   )
-  return { tmp, runner: createDispatcherRunner({ manifestPath }) }
+  return { runner: createDispatcherRunner({ manifestPath }) }
 }

Then in each test:

-  const { tmp, runner } = await runnerWithSourceManifest()
+  await using tmp = await tmpdir()
+  const { runner } = await runnerWithSourceManifest(tmp)
   // ... assertions ...
-  await tmp[Symbol.asyncDispose]?.()

Alternatively, inline the helper into each test so await using binds directly.

🤖 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/altimate/review-runner.test.ts` around lines 310 -
343, The new temp-directory helper in runnerWithSourceManifest should be updated
to avoid manual disposal and ensure cleanup even if a test assertion fails.
Refactor the helper so tmpdir() is bound with await using at the per-test scope,
or inline the setup into each test so the temporary directory is automatically
disposed when the scope exits. Keep the change localized to
runnerWithSourceManifest and the nearby review-runner.test.ts test setup.

Source: Learnings

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

Nitpick comments:
In `@packages/opencode/test/altimate/review-runner.test.ts`:
- Around line 310-343: The new temp-directory helper in runnerWithSourceManifest
should be updated to avoid manual disposal and ensure cleanup even if a test
assertion fails. Refactor the helper so tmpdir() is bound with await using at
the per-test scope, or inline the setup into each test so the temporary
directory is automatically disposed when the scope exits. Keep the change
localized to runnerWithSourceManifest and the nearby review-runner.test.ts test
setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 30d1be6d-8a73-4477-b5ab-77880f4aef75

📥 Commits

Reviewing files that changed from the base of the PR and between 122b316 and 73e93fb.

📒 Files selected for processing (8)
  • packages/opencode/src/altimate/review/orchestrate.ts
  • packages/opencode/src/altimate/review/runner.ts
  • packages/opencode/src/altimate/review/spec-test-gen.ts
  • packages/opencode/src/altimate/review/spec-test-sandbox.ts
  • packages/opencode/test/altimate/review-runner.test.ts
  • packages/opencode/test/altimate/review-spec-test-exec.test.ts
  • packages/opencode/test/altimate/review-spec-test-gen.test.ts
  • packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/opencode/test/altimate/review-spec-test-sandbox.test.ts
  • packages/opencode/src/altimate/review/spec-test-sandbox.ts
  • packages/opencode/src/altimate/review/spec-test-gen.ts
  • packages/opencode/src/altimate/review/orchestrate.ts
  • packages/opencode/test/altimate/review-spec-test-gen.test.ts

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

1 issue found across 8 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/orchestrate.ts">

<violation number="1" location="packages/opencode/src/altimate/review/orchestrate.ts:1109">
P2: Single-column model-level `unique` constraints are not executed: this branch stores the column only in `args.column_name`, but the executor does not read that key. Representing it as a column-level unique test keeps Track A executable and produces the expected YAML.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

}
}
if (columns.length === 1) {
return { test: "unique", args: { column_name: columns[0] } }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Single-column model-level unique constraints are not executed: this branch stores the column only in args.column_name, but the executor does not read that key. Representing it as a column-level unique test keeps Track A executable and produces the expected YAML.

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/orchestrate.ts, line 1109:

<comment>Single-column model-level `unique` constraints are not executed: this branch stores the column only in `args.column_name`, but the executor does not read that key. Representing it as a column-level unique test keeps Track A executable and produces the expected YAML.</comment>

<file context>
@@ -1086,10 +1086,32 @@ interface EnforcedConstraintMetrics {
+      }
+    }
+    if (columns.length === 1) {
+      return { test: "unique", args: { column_name: columns[0] } }
+    }
+  }
</file context>
Suggested change
return { test: "unique", args: { column_name: columns[0] } }
return { column: columns[0], test: "unique" }

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73e93fba51

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +301 to +304
const ref = /\bref\s*\(\s*['"]([^'"]+)['"]\s*\)/.exec(raw)?.[1]
if (ref) {
const model = manifest.byName.get(ref)
return { relation: model ? relationForModel(model) : sqlTableRef(ref) }

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 Badge Parse two-argument ref() relationship targets

For declared relationships tests that point to a package model with dbt's valid ref('package', 'model') form, this regex only recognizes single-argument refs, so the value falls through to sqlTableRef(raw) and is later rejected by the sandbox as an unknown relation. With specTests.execute enabled, those declared relationship constraints are recorded as execution errors and dropped, so cross-package foreign-key violations are never checked; parse the two-argument form and resolve the second argument against the manifest/package target instead of treating the macro text as a table name.

Useful? React with 👍 / 👎.

text: name,
})
}
for (const match of sql.matchAll(/\bsource\s*\(([^)]*)\)/g)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Strip comments before allowlisting SQL refs

When specTests.execute is enabled, specTestSynthesisLane feeds refs collected by this raw regex into allowedRelationsForSpecTests, and those allowed relations are passed to generated-test execution. Because the scan does not strip SQL comments or string literals, a PR can add a harmless-looking comment such as -- source('prod_sensitive','users'), causing the Track-B prompt and sandbox allowlist to treat that unrelated relation as an upstream and allowing a generated relationships/assertion test to query it under the review warehouse role. Only collect refs from parsed/model dependencies or strip comments/strings before deriving the execution allowlist.

Useful? React with 👍 / 👎.

log.warn("dropping candidate spec-test execution error", { test: test.id, detail: result?.detail })
continue
}
if (result.status === "pass") continue

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 Badge Preserve passing advisory tests as proposals

When specTests.execute is enabled and a Track-B generated test passes current data, this branch drops it without emitting the proposed YAML patch. In projects that enable execution to validate candidates before review, the useful passing proposals disappear entirely and only failing hypotheses surface as warnings; keep the proposal finding for passing advisory tests, optionally annotated as executed/passed.

Useful? React with 👍 / 👎.

Comment on lines +970 to +974
for (const parsed of parsedTests) {
out.push({
origin: "declared_constraint",
kind: parsed.kind,
ref: `schema.yml:${model}.${name}:${parsed.kind}`,

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 Badge Avoid proposing tests already declared in schema.yml

For a new model whose changed schema.yml already contains column tests, this loop adds those existing tests as spec sources for Track B, so the generator can return the same not_null/accepted_values test and the summary renders a duplicate YAML patch. Since entries from column.tests/data_tests are already dbt tests, they should not be fed back as candidate tests to add again; reserve proposal sources for unenforced constraints or soft intent.

Useful? React with 👍 / 👎.

Comment on lines +1108 to +1110
if (columns.length === 1) {
return { test: "unique", args: { column_name: columns[0] } }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Execute single-column model-level unique constraints

For a model-level unique/primary_key constraint with exactly one column, this branch materializes args.column_name but leaves dbtTest.column unset. The execution path's columnsForUnique() does not read column_name, so with specTests.execute enabled the generated check errors as missing columns and is dropped, letting duplicate values in a declared single-column key pass unreported.

Useful? React with 👍 / 👎.

Comment on lines +1239 to +1242
for (const source of specSources) {
if (source.kind !== "ref_edge") continue
const name = source.text ?? source.ref.replace(/^(ref|source):/, "")
if (name) out.add(name)

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 Badge Allow declared relationship targets during execution

For Track-A declared relationships constraints, the sandbox allowlist is built only from ref_edge sources found in the model SQL, not from the relationship constraint's own to target. A normal foreign-key declaration to ref('dim_customers') on a fact model that does not select from that dimension will therefore fail the target sandbox check and be dropped as an execution error, so the declared relationship is never verified; include the resolved declared target in the allowlist after manifest validation.

Useful? React with 👍 / 👎.

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.

feat: greenfield spec-test synthesis for dbt-pr-review (raise the no-reference verification ceiling)

1 participant