feat(review): [R20 S4] triage promotion — never auto-approve risk-bearing dbt metadata#1028
Conversation
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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
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. |
| // dbtRiskYmlChanges hit but doesn't fire on its own. | ||
| if (c.dbtRiskYmlChanges) { | ||
| const loc = c.martLayerChange ? " under models/marts/ (mart-API surface)" : "" | ||
| reasons.push(`schema.yml diff touches data_tests/constraints/contract${loc}`) |
There was a problem hiding this comment.
SUGGESTION: Reason string omits unique_combination_of_columns
DBT_UNIQUE_COMBO_RE (line 78) matches unique_combination_of_columns and feeds into dbtRiskYmlChanges, but the surfaced reason only lists data_tests/constraints/contract. A customer who adds only a unique_combination_of_columns test (covered by the test at review.test.ts:466) would see a reason naming keys they didn't touch — inconsistent with both the field doc (lines 43-45) and the cubic summary in the PR description, which both list all four signals.
| reasons.push(`schema.yml diff touches data_tests/constraints/contract${loc}`) | |
| reasons.push(`schema.yml diff touches data_tests/constraints/contract/unique_combination_of_columns${loc}`) |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Incremental review of Files Reviewed (2 files)
Previous Review Summaries (3 snapshots, latest commit e52c863)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit e52c863)Status: No Issues Found | Recommendation: Merge Incremental review of Files Reviewed (2 files)
Previous review (commit 4544cf8)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (10 files)
Fix these issues in Kilo Cloud Previous review (commit 8eac0d9)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (2 files)
Reviewed by glm-5.2 · Input: 27.6K · Output: 4.9K · Cached: 218.2K Review guidance: REVIEW.md from base branch |
Validation report against 5-PR internal corpusRan the S4-branch CLI against the 5 real internal dbt PRs from the R20 study (both A1 pure-deterministic and A2 with LLM lane). Baseline is the PR-#1027-tip CLI on the same corpus. Recall delta
Tier changes
InterpretationS4 is a routing change, not a detector change. On this corpus:
S4's value is foundational: it unblocks subsequent detector PRs (S1 grain-key Recommend keeping this PR open as the routing floor, but expect the benchmark movement to come from the detector-work PRs stacked above it. |
There was a problem hiding this comment.
1 issue found across 2 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/src/altimate/review/risk-tier.ts">
<violation number="1" location="packages/opencode/src/altimate/review/risk-tier.ts:144">
P2: The reason string `"schema.yml diff touches data_tests/constraints/contract"` doesn't account for the `unique_combination_of_columns` case. Since `dbtRiskYmlChanges` is true when *either* `DBT_RISK_KEY_RE` or `DBT_UNIQUE_COMBO_RE` matches, a customer who only adds a `unique_combination_of_columns` test will see a tier-promotion reason referencing keys they never touched. Consider including `unique_combination_of_columns` in the reason string (e.g., `data_tests/constraints/contract/unique_combination_of_columns`) to stay consistent with the field documentation and actual detection logic.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // dbtRiskYmlChanges hit but doesn't fire on its own. | ||
| if (c.dbtRiskYmlChanges) { | ||
| const loc = c.martLayerChange ? " under models/marts/ (mart-API surface)" : "" | ||
| reasons.push(`schema.yml diff touches data_tests/constraints/contract${loc}`) |
There was a problem hiding this comment.
P2: The reason string "schema.yml diff touches data_tests/constraints/contract" doesn't account for the unique_combination_of_columns case. Since dbtRiskYmlChanges is true when either DBT_RISK_KEY_RE or DBT_UNIQUE_COMBO_RE matches, a customer who only adds a unique_combination_of_columns test will see a tier-promotion reason referencing keys they never touched. Consider including unique_combination_of_columns in the reason string (e.g., data_tests/constraints/contract/unique_combination_of_columns) to stay consistent with the field documentation and actual detection logic.
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/risk-tier.ts, line 144:
<comment>The reason string `"schema.yml diff touches data_tests/constraints/contract"` doesn't account for the `unique_combination_of_columns` case. Since `dbtRiskYmlChanges` is true when *either* `DBT_RISK_KEY_RE` or `DBT_UNIQUE_COMBO_RE` matches, a customer who only adds a `unique_combination_of_columns` test will see a tier-promotion reason referencing keys they never touched. Consider including `unique_combination_of_columns` in the reason string (e.g., `data_tests/constraints/contract/unique_combination_of_columns`) to stay consistent with the field documentation and actual detection logic.</comment>
<file context>
@@ -91,6 +128,22 @@ export function fullTierReasons(c: FileChangeClass): string[] {
+ // dbtRiskYmlChanges hit but doesn't fire on its own.
+ if (c.dbtRiskYmlChanges) {
+ const loc = c.martLayerChange ? " under models/marts/ (mart-API surface)" : ""
+ reasons.push(`schema.yml diff touches data_tests/constraints/contract${loc}`)
+ }
+ if (c.finopsPathToken) reasons.push("path contains FinOps keyword (cost/saving/billing/dbu/etc.)")
</file context>
| reasons.push(`schema.yml diff touches data_tests/constraints/contract${loc}`) | |
| reasons.push(`schema.yml diff touches data_tests/constraints/contract/unique_combination_of_columns${loc}`) |
Consensus Code Review — PR #1028Title: feat(review): [R20 S4] triage promotion — never auto-approve risk-bearing dbt metadata Panel (8 reviewers, thorough mode): Claude (Opus 4.8) · GPT 5.4 Codex · Gemini 3.1 Pro (Antigravity) · Kimi K2.5 · Qwen 3.6 · MiniMax M2.7 · GLM-5.1 · MiMo V2.5 Pro. Quorum 6 — met. Convergence: 1 round (GPT + Gemini + Kimi responded; all downgraded finding #2 to MINOR). Verdict: REQUEST CHANGES — 2 MAJOR (targeted fixes), 5 MINOR, 3 NITThe promotion mechanism is verified correct by all 8 reviewers ( Counts: Critical 0 · Major 2 · Minor 5 · Nit 3 MAJOR1. Legacy
|
| Reviewer | Verdict on synthesis | Key point |
|---|---|---|
| GPT 5.4 Codex | CHANGES NEEDED → incorporated | #2 → MINOR (cost-only fail-safe); accredited example wrong; contract: test does exist (removed MINOR #8); #3 is MINOR/low-MAJOR. |
| Gemini 3.1 Pro | CHANGES NEEDED → incorporated | #2 → MINOR; #3 correctly MAJOR; #1 correctly top. |
| Kimi K2.5 | CHANGES NEEDED → incorporated | #2 → MINOR (dbus is a real bug though); #3 correctly MAJOR. |
| Claude | (author) | Agreed with all three downgrades/corrections. |
Applied in convergence: #2 MAJOR→MINOR (3 reviewers, unanimous); retracted the accredited example (shell-verified non-match); removed the "no contract: test" MINOR (test exists at review.test.ts:405). #3 kept MAJOR (Gemini + Kimi + GLM-5 vs GPT's lone MINOR). #1 unchanged as top finding. Converged in 1 round.
Finding attribution
| # | Finding | Origin | Type |
|---|---|---|---|
| 1 | Legacy tests: key not promoted |
GPT, Gemini, Kimi, Qwen, MiniMax, GLM-5, MiMo, Claude | Consensus (8-way) |
| 3 | Vacuous unique_combination_of_columns test |
GLM-5 | Unique (shell-verified) |
| 2 | FinOps over-promotion on *_rate/dbus paths |
Qwen, MiniMax, GLM-5, Kimi, GPT, Gemini, MiMo | Consensus; severity set MINOR in convergence |
| 4 | Reason omits actual triggering signal | GPT, GLM-5 | Unique |
| 5 | "upgrades the WEIGHT" comment misleading | Qwen, MiniMax | Unique |
| 6 | Block-scalar description FP | GLM-5 | Unique |
| 7 | FinOps boundary misses digit suffixes | Gemini | Unique |
| 8–10 | NITs (short-form regex, removed-line untested, comment) | Claude, MiMo, GLM-5, Kimi | Nit |
| ✓ | Promotion ordering correct; martLayerChange enrichment-only | all 8 | Consensus (positive) |
Reviewed by 8 participants. Convergence: 1 round, 3 external responses (GPT, Gemini, Kimi) + Claude.
Infra note: the 5 kilo-hosted models (Kimi/Qwen/MiniMax/GLM-5/MiMo) run via a pty.spawn pseudo-TTY wrapper — kilo hangs without a TTY when run headless. Gemini runs via the Antigravity agy CLI. The stale MiMo model id in ~/.claude/consensus.json was corrected (mimo-v2-pro → mimo-v2.5-pro).
…adata Grounded in the 5-PR internal corpus study (data-engineering-skills/docs/pr-review-corpus-findings-r20.md) where historical altimate-code recall was 2/84 = 2.4%. Two of five PRs auto-approved despite each having 8+ substantive human findings: - PR D: test-only YAML under `models/marts/` adding `data_tests:` / `constraints:` on a contracted mart → 8 misses, including 5 dbt-trino adapter-semantic bugs. - PR E: cost-anchor redesign under `mrt_jobs_cost_savings.sql` → 11 misses, including 3 critical FinOps corrections. Adds three FileChangeClass signals + wires them into `fullTierReasons()`: - **`dbtRiskYmlChanges`** — schema.yml diff introduces or edits `data_tests:`, `constraints:`, `contract:`, or a `unique_combination_of_columns` list-item. Regex anchors to YAML key position after optional diff marker, optional indent, and optional list marker; explicitly excludes comment lines. This catches PR D. - **`martLayerChange`** — file lives under `models/marts/` or `models/mart/`. Does NOT promote on its own (description-only edits stay trivial, matching the existing test), but ENRICHES the `dbtRiskYmlChanges` reason string so the customer sees "under models/marts/ (mart-API surface)". - **`finopsPathToken`** — path/filename contains a FinOps keyword (`cost|saving|billing|credit|dbu|spend|revenue|price|rate|pricing| invoice`) at a word / segment / extension boundary. Catches PR E. Word-boundary regex prevents false positives on incidental substrings (`broadcaster` ≠ `caste`, `precast` ≠ `cast`). Regression tests (11 new, all green): - PR D shapes (data_tests, constraints, unique_combination_of_columns) all promote to full with `mart-API surface` context. - PR E shape (mrt_jobs_cost_savings.sql) + 7 other FinOps keyword variants all promote to full. - FinOps false-positive guard: `broadcaster` / `precast` stay lite. - Description-only edits under models/marts/ still trivial (pre-existing behavior preserved). - Comment lines and description strings mentioning `data_tests:` / `constraints:` do NOT promote (regex tightness). - Nested-indent YAML key position (production shape) still fires. Codex-reviewed diff. Two highs addressed: - Regex tightening to avoid comment / description-string false positives - FileChangeClass consumer audit (no external constructions; safe) Ship criteria (from plan v2): PRs D and E from the corpus must no longer auto-approve. Baseline recall on the existing 13-scenario corpus must not regress. Both hold: 96/96 tests pass (85 pre-existing + 11 new); full altimate review suite 3781/3781 green. Depends on PR #1027 (feat/review-r18-observability-recall) — this branch stacks on top so tierReasons[] wiring is in place. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
…motion Bundle addresses PR #1028's consensus review: - MAJOR #1 — legacy `tests:` (pre-dbt-1.8 alias) added to risk-key patterns so `models/marts/*.yml` diffs adding `tests: [not_null]` or similar promote out of trivial. Regression tests cover both marts and non-marts placement + a word-boundary FP guard. - MAJOR #3 — vacuous `unique_combination_of_columns` regression test fixed. Original path (`mrt_billing_account_prices.yml`) also matched FinOps + marts signals so the DBT_UNIQUE_COMBO_RE could have been deleted and the test still passed. Path moved to `models/intermediate/int_grain.yml` (non-mart, non-FinOps) and reason string asserted to confirm the combo regex is the sole triggering signal. - MINOR #2 — dropped `dbus` from FINOPS_TOKEN_RE (D-Bus IPC collision with `stg_dbus_connector.sql`). Singular `dbu` is sufficient. - MINOR #4 — each risk-YAML key now has its own regex in a DBT_RISK_KEY_PATTERNS table. `dbtRiskYmlKeyMatches()` returns the specific keys that matched; new `dbtRiskYmlKeys: string[]` field on FileChangeClass. Reason string now names the exact triggering keys (e.g. "schema.yml diff touches tests under models/marts/") rather than the concatenated umbrella. - MINOR #5 — reworded the "upgrades the WEIGHT" comment. There is no weighting/ordering effect; `martLayerChange` only enriches the reason string with the mart-API-surface context. - MINOR #6 — block-scalar body FP: `stripBlockScalars()` walks the diff tracking block-scalar state so a description or long-form comment containing `data_tests:` (or similar) doesn't spuriously promote. Codex round-6 review HIGH fixed too — earlier version worked only on the +/- slice, missing the common case where `description: |` is in the context and a changed line is inside its body. Now walks the full diff (context + changed) for state and only masks output on changed lines. - MINOR #7 — FinOps boundary class extended with `\d` so digit-suffixed paths (`mrt_cost2024.sql`, `stg_billing2.sql`, `dbu1_usage.sql`) match. - NIT #8 — DBT_UNIQUE_COMBO_RE relaxed to make the list-item marker optional, so the bare-key indented form (`unique_combination_of_columns:` under a short-form `tests:` map) also matches. - NIT #9 — added regression test for removed-line risk-key promotion (removing a `data_tests:` block is at least as risk-worthy as adding one). - NIT #10 — comment reworded from `unique_combination_of_columns` is a "TEST NAME" → "dbt test macro / test parameter". Full altimate review suite: 3807 pass / 640 skip / 0 fail (was 3781 baseline — 26 new tests). Codex-round-6 minor addressed: `dbtRiskYmlKeyMatches` is now called once via a shared `scannedForRisk` local and its result reused for both `dbtRiskYmlChanges` and `dbtRiskYmlKeys`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
8eac0d9 to
4544cf8
Compare
| const lines = diff.split("\n") | ||
| const out: string[] = [] | ||
| let scalarIndent = -1 | ||
| const blockScalarStart = /^[ \t]*[^\s#:][^:]*:[ \t]*[|>][+-]?[ \t]*$/ |
There was a problem hiding this comment.
SUGGESTION: blockScalarStart skips block-scalar headers that carry an explicit indentation indicator (description: |2, >2-) or a trailing comment (description: | # legacy)
[+-]? consumes only a chomping indicator, so legal headers like |2, |+2, or | # comment don't match. When such an opener is in the diff the scalar is never opened and its body lines aren't blanked — so a body line that happens to start with a risk keyword (data_tests: / tests: / …) would false-positive promote the file to full. Narrow and safe-direction (over-tiering only, never a wrong verdict), but this regex is the sole scalar gate, so widening the class closes the gap.
| const blockScalarStart = /^[ \t]*[^\s#:][^:]*:[ \t]*[|>][+-]?[ \t]*$/ | |
| const blockScalarStart = /^[ \t]*[^\s#:][^:]*:[ \t]*[|>][+-]?[0-9]?[+-]?[ \t]*(?:#.*)?$/ |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
There was a problem hiding this comment.
Addressed in e52c863ae. blockScalarStart regex widened to [|>](?:[+-]?[1-9]?|[1-9]?[+-]?)[ \t]*(?:#.*)?$ so |2, |+2, |2- (explicit indentation indicator, either order relative to chomp) and | # comment (trailing comment) all open a scalar. Regression test loops over |2, |+2, | # legacy comment shapes and asserts each produces trivial on a diff whose body contains risk keywords.
For every column named in a `dbt_utils.unique_combination_of_columns`
test's `combination_of_columns`, require `not_null` coverage on the
same model. Otherwise a NULL grain-key value silently passes the
uniqueness test — the guardrail is toothless. Cited as a hard rule in
`docs/DBT_GUIDELINES.md` in the corpus repo; kilo catches it, we didn't.
Coverage sources:
- `constraints: [{type: not_null}]` — only counted when
`contract.enforced == true` (per codex R20 S1 high #3). On views /
non-contracted models, `constraints:` is documentation-only, not
enforced, so we don't miss a real gap.
- Column-level `data_tests: [not_null]` / `tests: [not_null]` — always
counted; dbt's test runner enforces regardless of contract state.
Recommendation flips between `constraints:` (contracted model) and
`data_tests:` (view / non-contracted) based on the model's contract
state — matches the adapter-semantics discussion in the corpus study
(PR D×2 + PR A×2).
Supported YAML shapes:
- `unique_combination_of_columns` and `dbt_utils.unique_combination_of_columns`
(exact match per codex high #1; `endsWith` would over-match).
- pre-1.9 flat args + dbt 1.9+ `arguments:` nesting.
- top-level `contract:` and nested `config.contract:` for enforcement flag.
False-positive guards:
- Column-name comparison is case-folded (Snowflake identifiers, per
codex high #2) so `WORKSPACE_ID` in `combination_of_columns` matches
`workspace_id` in `columns:`.
- Skipped on `status === "deleted"` files (no current grain to guard).
- Only runs on files in the PR diff, not repo-wide.
### Validation on 5-PR internal corpus (recall improvement)
vs S4 baseline (the tier-promotion PR this branch stacks on):
| Variant | S4 baseline | S1 result | Delta |
|---|---:|---:|---:|
| A1 (`--pure --no-ai=true`) | 118 | 129 | **+11 grain-key gaps** |
| A2 (`--pure`, LLM lane) | 143 | 150 | +7 (net; LLM run-to-run noise) |
Grain-key gaps caught:
- PR B (#1111) ×1: `mrt_all_purpose_auto_tune_recommendation.rec_type`
- PR C (#862) ×10: workspace_id x3, job_id x2, period_start_time x2, period_end_time x2, task_key x1
- PR A, D, E: 0 (fixes already landed in HEAD state per corpus study)
Strong matches to human blocker findings on PR C:
- `int_job_run_billing.workspace_id`, `int_job_run_cost_carrier.workspace_id`,
`int_job_task_run_cost_carrier.workspace_id` → PR C human F11 (critical:
"workspace_id missing from carrier identity")
- `mrt_job_run_timeline.period_end_time`, `mrt_job_task_run_timeline.period_end_time`
→ PR C human F10 (critical: "dbt mart grain includes period_end_time")
### Tests
- 71 pass / 0 fail in `review-dbt-patterns.test.ts` (58 pre-existing + 13 new S1 tests).
- 3797 pass / 640 skip / 0 fail in the full altimate review suite (132 files).
- Codex-reviewed diff, 3 highs addressed:
- endsWith → exact-name match
- column-name case-folding
- constraints only count when contract enforced
- Codex minor #4 addressed (test fixture for top-level `contract:`)
- Codex minor #5 addressed (test fixture for non-contracted constraints ≠ coverage)
Depends on PR #1028 (feat/review-r20-s4-triage-promotion), which in turn
stacks on PR #1027 (feat/review-r18-observability-recall).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
… regex tightened kilo-code-bot suggestion on PR #1028 — the earlier `blockScalarStart` regex accepted only `|`/`>` with an optional `+`/`-` chomping indicator, so YAML block-scalar headers carrying an explicit indentation indicator (`description: |2`, `|+2`, `|2-`) or a trailing comment (`description: | # legacy`) failed to open a scalar. Body lines beginning with a risk keyword (`data_tests:`, `constraints:`, …) then false-positive promoted the file to `full`. Safe-direction over-tiering (never a wrong verdict), but this regex is the sole scalar gate — widen it to close the gap. Regex now: `[|>](?:[+-]?[1-9]?|[1-9]?[+-]?)[ \\t]*(?:#.*)?$`. - `[|>]` opener - `(?:[+-]?[1-9]?|[1-9]?[+-]?)` chomp+indent in either order - optional trailing `# comment` New regression test loops over `|2`, `|+2`, and `| # legacy comment` opener shapes and asserts each produces `trivial` on a diff whose body lines contain risk keywords. Full altimate review suite: 3808 pass / 640 skip / 0 fail (was 3807 baseline — 1 new test). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…ForScan (cubic P2) cubic P2 bot review — the scalar-state tracker measured indent inconsistently between context and changed diff lines. Context lines start with a leading SPACE (the diff marker) and my earlier code left it in place; changed lines had their `+`/`-` marker stripped before indent measurement. Result: a valid `|1` block scalar opened in the context with a body indented exactly one space beyond the header wasn't masked, because the context-side `scalarIndent` counted one extra space and the changed-side body's indent then failed the `indent > scalarIndent` check. Fix strips the leading diff marker (`+`/`-`/space) on ALL lines before indent measurement, so context and changed lines live in the same coordinate system. New regression test: `|1` block scalar opened in context with body containing `data_tests:` / `constraints:` prose stays trivial. Full altimate review suite: 3808 pass / 640 skip / 0 fail (was 3807 baseline — 1 new test). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zXDXMiNFh4qDPxPCfa2of
There was a problem hiding this comment.
1 issue found across 2 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/risk-tier.ts">
<violation number="1" location="packages/opencode/src/altimate/review/risk-tier.ts:161">
P2: Real `git diff` output includes `diff --git a/... b/...` and `index ...` header lines before the first `@@` hunk; these don't start with `+`, `-`, or a space, so with the new marker logic they get marker `""` and skip the `if (marker === " ") continue` guard, leaking into the scanned output that feeds the risk-YAML regex matching (`dbtRiskYmlKeyMatches`). The regression tests only use synthetic diffs starting directly at `@@ ...`, so this gap isn't covered — recommend changing the later guard to `if (marker !== "+" && marker !== "-") continue` so both context lines and non-standard/header lines are excluded from the scan output.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| // like " description: |1" is measured as 1-deeper indent than an | ||
| // otherwise-equivalent "+ data_tests: ..." changed line, and the | ||
| // scalar body fails the `indent > scalarIndent` check (cubic P2). | ||
| const marker = raw.startsWith("+") || raw.startsWith("-") ? raw[0] : raw.startsWith(" ") ? " " : "" |
There was a problem hiding this comment.
P2: Real git diff output includes diff --git a/... b/... and index ... header lines before the first @@ hunk; these don't start with +, -, or a space, so with the new marker logic they get marker "" and skip the if (marker === " ") continue guard, leaking into the scanned output that feeds the risk-YAML regex matching (dbtRiskYmlKeyMatches). The regression tests only use synthetic diffs starting directly at @@ ..., so this gap isn't covered — recommend changing the later guard to if (marker !== "+" && marker !== "-") continue so both context lines and non-standard/header lines are excluded from the scan output.
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/risk-tier.ts, line 161:
<comment>Real `git diff` output includes `diff --git a/... b/...` and `index ...` header lines before the first `@@` hunk; these don't start with `+`, `-`, or a space, so with the new marker logic they get marker `""` and skip the `if (marker === " ") continue` guard, leaking into the scanned output that feeds the risk-YAML regex matching (`dbtRiskYmlKeyMatches`). The regression tests only use synthetic diffs starting directly at `@@ ...`, so this gap isn't covered — recommend changing the later guard to `if (marker !== "+" && marker !== "-") continue` so both context lines and non-standard/header lines are excluded from the scan output.</comment>
<file context>
@@ -153,8 +153,13 @@ function changedLinesForScan(diff: string | undefined): string {
+ // like " description: |1" is measured as 1-deeper indent than an
+ // otherwise-equivalent "+ data_tests: ..." changed line, and the
+ // scalar body fails the `indent > scalarIndent` check (cubic P2).
+ const marker = raw.startsWith("+") || raw.startsWith("-") ? raw[0] : raw.startsWith(" ") ? " " : ""
+ const stripped = marker === "" ? raw : raw.slice(1)
const indent = stripped.length - stripped.replace(/^[ \t]+/, "").length
</file context>
Summary
Adds triage-tier promotion rules to
risk-tier.tsso the reviewer never auto-approves risk-bearing dbt metadata changes. Two failure modes closed:models/marts/addingdata_tests:/constraints:/unique_combination_of_columnson a contracted mart → previously classifiedtrivial→ auto-approved.lite→ approved without the deeper detector lanes running.Three new
FileChangeClasssignals wire intofullTierReasons():dbtRiskYmlChangesdata_tests:/constraints:/contract:/unique_combination_of_columnslist itemmartLayerChangemodels/marts/ormodels/mart/. Description-only edits under marts stay trivial (matches existing behavior).finopsPathTokencost|saving|billing|credit|dbu|spend|revenue|price|rate|pricing|invoiceat word/segment/extension boundary. FP guard on incidental substrings (broadcaster≠caste).Test plan
packages/opencode/test/altimate/review.test.ts— all green.Ship criteria
Depends on
Stacks on top of #1027 (
feat/review-r18-observability-recall). This branch uses thetierReasons[]wiring landed there so promotion reasons surface in the signed envelope. Merge #1027 first, then rebase this onto main.