Skip to content

Commit d930e3c

Browse files
suryaiyer95claude
andcommitted
refactor: [AI-5975] drop by_severity from sql_quality telemetry, address PR review
- Remove `severity` from `Finding` interface — only `category` matters - Drop `by_severity` from `sql_quality` event type and `aggregateFindings` - Gate `sql_quality` emission on `!isSoftFailure` to avoid double-counting with `core_failure` events (Copilot review feedback) - Simplify semantics tool: use fixed `"semantic_issue"` category instead of dead `issue.rule ?? issue.type` fallback chain (CodeRabbit review feedback) - Update test header to accurately describe what tests cover - Fix sql_analyze test to use honest coarse categories (`"lint"`, `"safety"`) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7b74713 commit d930e3c

3 files changed

Lines changed: 68 additions & 84 deletions

File tree

packages/opencode/src/altimate/telemetry/index.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,6 @@ export namespace Telemetry {
413413
tool_name: string
414414
tool_category: string
415415
finding_count: number
416-
/** JSON-encoded Record<string, number> — count per severity level */
417-
by_severity: string
418416
/** JSON-encoded Record<string, number> — count per issue category */
419417
by_category: string
420418
has_schema: boolean
@@ -792,24 +790,18 @@ export namespace Telemetry {
792790
}
793791

794792
// altimate_change start — sql quality telemetry types
795-
/** Lightweight finding record for quality telemetry. Only category/severity — never SQL content. */
793+
/** Lightweight finding record for quality telemetry. Only category — never SQL content. */
796794
export interface Finding {
797795
category: string
798-
severity: string
799796
}
800797

801-
/** Aggregate an array of findings into counts suitable for the sql_quality event. */
802-
export function aggregateFindings(findings: Finding[]): {
803-
by_severity: Record<string, number>
804-
by_category: Record<string, number>
805-
} {
806-
const by_severity: Record<string, number> = {}
798+
/** Aggregate an array of findings into category counts suitable for the sql_quality event. */
799+
export function aggregateFindings(findings: Finding[]): Record<string, number> {
807800
const by_category: Record<string, number> = {}
808801
for (const f of findings) {
809-
by_severity[f.severity] = (by_severity[f.severity] ?? 0) + 1
810802
by_category[f.category] = (by_category[f.category] ?? 0) + 1
811803
}
812-
return { by_severity, by_category }
804+
return by_category
813805
}
814806
// altimate_change end
815807

packages/opencode/src/tool/tool.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,17 @@ export namespace Tool {
159159
})
160160
}
161161
// altimate_change start — emit sql_quality when tools report findings
162+
// Only emit for successful tool runs — soft failures already emit core_failure
162163
const findings = result.metadata?.findings as Telemetry.Finding[] | undefined
163-
if (Array.isArray(findings) && findings.length > 0) {
164-
const { by_severity, by_category } = Telemetry.aggregateFindings(findings)
164+
if (!isSoftFailure && Array.isArray(findings) && findings.length > 0) {
165+
const by_category = Telemetry.aggregateFindings(findings)
165166
Telemetry.track({
166167
type: "sql_quality",
167168
timestamp: Date.now(),
168169
session_id: ctx.sessionID,
169170
tool_name: id,
170171
tool_category: toolCategory,
171172
finding_count: findings.length,
172-
by_severity: JSON.stringify(by_severity),
173173
by_category: JSON.stringify(by_category),
174174
has_schema: result.metadata?.has_schema ?? false,
175175
dialect: (result.metadata?.dialect as string) ?? "unknown",

packages/opencode/test/altimate/sql-quality-telemetry.test.ts

Lines changed: 61 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/**
22
* SQL Quality Telemetry Tests
33
*
4-
* Verifies that the `sql_quality` event is emitted with correct aggregations
5-
* when tools report findings, and is NOT emitted when there are no findings.
4+
* Verifies the aggregation logic, event payload shape, and finding
5+
* extraction patterns used for the `sql_quality` telemetry event, and
6+
* that scenarios with no findings result in empty finding arrays (the
7+
* condition used by tool.ts to decide not to emit the event).
68
*/
79

810
import { describe, expect, test } from "bun:test"
@@ -12,99 +14,91 @@ import { Telemetry } from "../../src/altimate/telemetry"
1214
// 1. aggregateFindings
1315
// ---------------------------------------------------------------------------
1416
describe("Telemetry.aggregateFindings", () => {
15-
test("aggregates findings by severity and category", () => {
17+
test("aggregates findings by category", () => {
1618
const findings: Telemetry.Finding[] = [
17-
{ category: "missing_table", severity: "error" },
18-
{ category: "missing_column", severity: "error" },
19-
{ category: "cartesian_product", severity: "warning" },
20-
{ category: "missing_table", severity: "error" },
19+
{ category: "missing_table" },
20+
{ category: "missing_column" },
21+
{ category: "lint" },
22+
{ category: "missing_table" },
2123
]
2224
const result = Telemetry.aggregateFindings(findings)
23-
expect(result.by_severity).toEqual({ error: 3, warning: 1 })
24-
expect(result.by_category).toEqual({
25+
expect(result).toEqual({
2526
missing_table: 2,
2627
missing_column: 1,
27-
cartesian_product: 1,
28+
lint: 1,
2829
})
2930
})
3031

31-
test("returns empty objects for empty findings", () => {
32+
test("returns empty object for empty findings", () => {
3233
const result = Telemetry.aggregateFindings([])
33-
expect(result.by_severity).toEqual({})
34-
expect(result.by_category).toEqual({})
34+
expect(result).toEqual({})
3535
})
3636

3737
test("handles single finding", () => {
3838
const findings: Telemetry.Finding[] = [
39-
{ category: "syntax_error", severity: "error" },
39+
{ category: "syntax_error" },
4040
]
4141
const result = Telemetry.aggregateFindings(findings)
42-
expect(result.by_severity).toEqual({ error: 1 })
43-
expect(result.by_category).toEqual({ syntax_error: 1 })
42+
expect(result).toEqual({ syntax_error: 1 })
4443
})
4544

46-
test("handles all same category different severities", () => {
45+
test("handles all same category", () => {
4746
const findings: Telemetry.Finding[] = [
48-
{ category: "select_star", severity: "warning" },
49-
{ category: "select_star", severity: "info" },
50-
{ category: "select_star", severity: "error" },
47+
{ category: "lint" },
48+
{ category: "lint" },
49+
{ category: "lint" },
5150
]
5251
const result = Telemetry.aggregateFindings(findings)
53-
expect(result.by_severity).toEqual({ warning: 1, info: 1, error: 1 })
54-
expect(result.by_category).toEqual({ select_star: 3 })
52+
expect(result).toEqual({ lint: 3 })
5553
})
5654
})
5755

5856
// ---------------------------------------------------------------------------
5957
// 2. sql_quality event shape validation
6058
// ---------------------------------------------------------------------------
6159
describe("sql_quality event shape", () => {
62-
test("by_severity and by_category serialize to valid JSON strings", () => {
60+
test("by_category serializes to valid JSON string", () => {
6361
const findings: Telemetry.Finding[] = [
64-
{ category: "anti_pattern", severity: "warning" },
65-
{ category: "anti_pattern", severity: "warning" },
66-
{ category: "performance_issue", severity: "info" },
62+
{ category: "lint" },
63+
{ category: "lint" },
64+
{ category: "safety" },
6765
]
68-
const { by_severity, by_category } = Telemetry.aggregateFindings(findings)
69-
const severityJson = JSON.stringify(by_severity)
70-
const categoryJson = JSON.stringify(by_category)
66+
const by_category = Telemetry.aggregateFindings(findings)
67+
const json = JSON.stringify(by_category)
7168

7269
// Should round-trip through JSON
73-
expect(JSON.parse(severityJson)).toEqual({ warning: 2, info: 1 })
74-
expect(JSON.parse(categoryJson)).toEqual({ anti_pattern: 2, performance_issue: 1 })
70+
expect(JSON.parse(json)).toEqual({ lint: 2, safety: 1 })
7571
})
7672

7773
test("aggregated counts match finding_count", () => {
7874
const findings: Telemetry.Finding[] = [
79-
{ category: "a", severity: "error" },
80-
{ category: "b", severity: "warning" },
81-
{ category: "c", severity: "error" },
82-
{ category: "a", severity: "info" },
75+
{ category: "a" },
76+
{ category: "b" },
77+
{ category: "c" },
78+
{ category: "a" },
8379
]
84-
const { by_severity, by_category } = Telemetry.aggregateFindings(findings)
85-
const totalBySeverity = Object.values(by_severity).reduce((a, b) => a + b, 0)
86-
const totalByCategory = Object.values(by_category).reduce((a, b) => a + b, 0)
87-
expect(totalBySeverity).toBe(findings.length)
88-
expect(totalByCategory).toBe(findings.length)
80+
const by_category = Telemetry.aggregateFindings(findings)
81+
const total = Object.values(by_category).reduce((a, b) => a + b, 0)
82+
expect(total).toBe(findings.length)
8983
})
9084
})
9185

9286
// ---------------------------------------------------------------------------
9387
// 3. Finding extraction patterns (validates what tools produce)
9488
// ---------------------------------------------------------------------------
9589
describe("tool finding extraction patterns", () => {
96-
test("sql_analyze issues map to findings", () => {
90+
test("sql_analyze issues map to findings via issue.type", () => {
91+
// issue.type is coarse: "lint", "semantic", "safety"
9792
const issues = [
98-
{ type: "select_star", severity: "warning", message: "...", recommendation: "...", confidence: "high" },
99-
{ type: "cartesian_product", severity: "error", message: "...", recommendation: "...", confidence: "high" },
93+
{ type: "lint", severity: "warning", message: "...", recommendation: "...", confidence: "high" },
94+
{ type: "safety", severity: "high", message: "...", recommendation: "...", confidence: "high" },
10095
]
10196
const findings: Telemetry.Finding[] = issues.map((i) => ({
10297
category: i.type,
103-
severity: i.severity,
10498
}))
10599
expect(findings).toEqual([
106-
{ category: "select_star", severity: "warning" },
107-
{ category: "cartesian_product", severity: "error" },
100+
{ category: "lint" },
101+
{ category: "safety" },
108102
])
109103
})
110104

@@ -124,31 +118,32 @@ describe("tool finding extraction patterns", () => {
124118
}
125119
const findings: Telemetry.Finding[] = errors.map((e) => ({
126120
category: classify(e.message),
127-
severity: "error",
128121
}))
129-
const { by_category } = Telemetry.aggregateFindings(findings)
122+
const by_category = Telemetry.aggregateFindings(findings)
130123
expect(by_category).toEqual({
131124
missing_table: 1,
132125
missing_column: 1,
133126
syntax_error: 1,
134127
})
135128
})
136129

137-
test("semantics issues preserve rule/type as category", () => {
130+
test("semantics issues all map to semantic_issue category", () => {
131+
// Semantic findings don't have rule/type — always "semantic_issue"
138132
const issues = [
139-
{ rule: "cartesian_product", severity: "error", message: "..." },
140-
{ type: "null_misuse", severity: "warning", message: "..." },
141-
{ severity: "warning", message: "..." }, // no rule or type
133+
{ severity: "error", message: "..." },
134+
{ severity: "warning", message: "..." },
135+
{ severity: "warning", message: "..." },
142136
]
143-
const findings: Telemetry.Finding[] = issues.map((i: any) => ({
144-
category: i.rule ?? i.type ?? "semantic_issue",
145-
severity: i.severity ?? "warning",
137+
const findings: Telemetry.Finding[] = issues.map(() => ({
138+
category: "semantic_issue",
146139
}))
147140
expect(findings).toEqual([
148-
{ category: "cartesian_product", severity: "error" },
149-
{ category: "null_misuse", severity: "warning" },
150-
{ category: "semantic_issue", severity: "warning" },
141+
{ category: "semantic_issue" },
142+
{ category: "semantic_issue" },
143+
{ category: "semantic_issue" },
151144
])
145+
const by_category = Telemetry.aggregateFindings(findings)
146+
expect(by_category).toEqual({ semantic_issue: 3 })
152147
})
153148

154149
test("fix tool produces fix_applied and unfixable_error categories", () => {
@@ -158,12 +153,12 @@ describe("tool finding extraction patterns", () => {
158153
}
159154
const findings: Telemetry.Finding[] = []
160155
for (const _ of data.fixes_applied) {
161-
findings.push({ category: "fix_applied", severity: "warning" })
156+
findings.push({ category: "fix_applied" })
162157
}
163158
for (const _ of data.unfixable_errors) {
164-
findings.push({ category: "unfixable_error", severity: "error" })
159+
findings.push({ category: "unfixable_error" })
165160
}
166-
const { by_category } = Telemetry.aggregateFindings(findings)
161+
const by_category = Telemetry.aggregateFindings(findings)
167162
expect(by_category).toEqual({ fix_applied: 2, unfixable_error: 1 })
168163
})
169164

@@ -173,7 +168,7 @@ describe("tool finding extraction patterns", () => {
173168
const equivFindings: Telemetry.Finding[] = []
174169
if (!equivData.equivalent && equivData.differences?.length) {
175170
for (const _ of equivData.differences) {
176-
equivFindings.push({ category: "equivalence_difference", severity: "warning" })
171+
equivFindings.push({ category: "equivalence_difference" })
177172
}
178173
}
179174
expect(equivFindings).toEqual([])
@@ -183,22 +178,21 @@ describe("tool finding extraction patterns", () => {
183178
const diffFindings: Telemetry.Finding[] = []
184179
if (!diffData.equivalent && diffData.differences?.length) {
185180
for (const _ of diffData.differences) {
186-
diffFindings.push({ category: "equivalence_difference", severity: "warning" })
181+
diffFindings.push({ category: "equivalence_difference" })
187182
}
188183
}
189184
expect(diffFindings.length).toBe(2)
190-
const { by_category } = Telemetry.aggregateFindings(diffFindings)
185+
const by_category = Telemetry.aggregateFindings(diffFindings)
191186
expect(by_category).toEqual({ equivalence_difference: 2 })
192187
})
193188

194189
test("correct tool changes produce findings", () => {
195190
const data = { changes: [{ description: "a" }, { description: "b" }] }
196191
const findings: Telemetry.Finding[] = data.changes.map(() => ({
197192
category: "correction_applied",
198-
severity: "warning",
199193
}))
200194
expect(findings.length).toBe(2)
201-
const { by_category } = Telemetry.aggregateFindings(findings)
195+
const by_category = Telemetry.aggregateFindings(findings)
202196
expect(by_category).toEqual({ correction_applied: 2 })
203197
})
204198
})
@@ -211,18 +205,16 @@ describe("no findings = no sql_quality event", () => {
211205
const issues: any[] = []
212206
const findings: Telemetry.Finding[] = issues.map((i: any) => ({
213207
category: i.type,
214-
severity: i.severity,
215208
}))
216209
expect(findings.length).toBe(0)
217-
// tool.ts checks: Array.isArray(findings) && findings.length > 0
210+
// tool.ts guards: !isSoftFailure && Array.isArray(findings) && findings.length > 0
218211
// So no event would be emitted
219212
})
220213

221214
test("valid SQL with no errors produces no findings", () => {
222215
const data = { valid: true, errors: [] }
223216
const findings: Telemetry.Finding[] = (data.errors ?? []).map(() => ({
224217
category: "validation_error",
225-
severity: "error",
226218
}))
227219
expect(findings.length).toBe(0)
228220
})

0 commit comments

Comments
 (0)