mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-21 06:24:32 +00:00
fix(optimize): scope the apply-able subtotal to the local MCP subset
A mixed local + claude.ai connector finding is class `fix`, but `--apply` only mutates the local servers. classTotals now credits the `fix` group with `applyTokensSaved` when present, so the "Fix now (apply-able)" subtotal, the "apply-able: ~$X" headline and `summary.byClass.fix` (CLI, TUI and desktop all read these) describe what apply can actually recover. The finding keeps the whole opportunity in its own `tokensSaved`. Also fixes the desktop connector fixture, which predated the class/basis fields, and adds class-level coverage: connector-only findings resolve to `nudge` (no apply payload), a local server named like a connector stays manual-only, and local-only findings keep their full subtotal.
This commit is contained in:
parent
c5df60e69a
commit
117aa833cc
3 changed files with 71 additions and 3 deletions
|
|
@ -214,11 +214,14 @@ describe('Optimize', () => {
|
|||
id: 'mcp-low-coverage', title: 'Underused claude.ai connector',
|
||||
explanation: 'The connector loads unused tools.', severity: 'medium',
|
||||
trend: null, tokensSaved: 2_000, estimatedSavingsUSD: 1,
|
||||
// Connector-only: no appliable plan, so the finding is a nudge.
|
||||
class: 'nudge', basis: 'estimated',
|
||||
fix: {
|
||||
type: 'paste', destination: 'manual', label: 'Manage the connector where it loads:',
|
||||
text: 'Open /mcp and disable claude.ai Google Calendar.',
|
||||
},
|
||||
})
|
||||
report.summary.byClass.nudge = { tokensSaved: 19_400, savingsUSD: 9.7, count: 2 }
|
||||
getOptimizeReport.mockResolvedValue(report)
|
||||
render(<Optimize period="30days" provider="all" />)
|
||||
const row = await screen.findByRole('button', { name: /Underused claude.ai connector/ })
|
||||
|
|
|
|||
|
|
@ -403,9 +403,17 @@ export function classTotals(findings: WasteFinding[], costRate: number): Record<
|
|||
keep: { tokensSaved: 0, savingsUSD: 0, count: 0 },
|
||||
}
|
||||
for (const f of findings) {
|
||||
const t = totals[findingClass(f)]
|
||||
t.tokensSaved += f.tokensSaved
|
||||
t.savingsUSD += f.tokensSaved * costRate
|
||||
const cls = findingClass(f)
|
||||
// A `fix` whose plan owns only part of its estimate (a mixed local +
|
||||
// claude.ai connector MCP finding) contributes only the apply-able
|
||||
// subset, so this subtotal and the "apply-able" headline never promise
|
||||
// what `--apply` cannot recover. The finding keeps the whole
|
||||
// opportunity in its own `tokensSaved`, so the fix subtotal can be
|
||||
// smaller than the findings listed under it.
|
||||
const tokens = cls === 'fix' ? f.applyTokensSaved ?? f.tokensSaved : f.tokensSaved
|
||||
const t = totals[cls]
|
||||
t.tokensSaved += tokens
|
||||
t.savingsUSD += tokens * costRate
|
||||
t.count++
|
||||
}
|
||||
return totals
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { describe, it, expect, vi } from 'vitest'
|
|||
import {
|
||||
aggregateMcpCoverage,
|
||||
buildOptimizeJsonReport,
|
||||
classTotals,
|
||||
findingClass,
|
||||
detectMcpProfileAdvisor,
|
||||
detectMcpToolCoverage,
|
||||
estimateMcpSchemaCost,
|
||||
|
|
@ -925,3 +927,58 @@ describe('detectMcpProfileAdvisor', () => {
|
|||
expect(detectMcpProfileAdvisor(projects, coverage)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Connector findings under the fix/nudge/keep classification (#1019)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('connector findings and finding class', () => {
|
||||
const inventoryFor = (servers: string[]) => servers.flatMap(server =>
|
||||
Array.from({ length: 20 }, (_, i) => `mcp__${server}__t${i}`),
|
||||
)
|
||||
const twoSessions = (servers: string[]) => ['a', 'b'].map(sessionId => makeSession({
|
||||
sessionId,
|
||||
inventory: inventoryFor(servers),
|
||||
turns: [makeTurn([makeCall({ cacheCreation: 50_000 })])],
|
||||
}))
|
||||
|
||||
it('classifies a connector-only finding as a nudge, since nothing is appliable', () => {
|
||||
const finding = detectMcpToolCoverage([project(twoSessions(['claude_ai_Gmail']))])
|
||||
|
||||
expect(finding).not.toBeNull()
|
||||
expect(finding!.apply).toBeUndefined()
|
||||
expect(findingClass(finding!)).toBe('nudge')
|
||||
expect(finding!.tokensSaved).toBeGreaterThan(0)
|
||||
// Never lands in the "apply-able" subtotal.
|
||||
expect(classTotals([finding!], 0.00002).fix).toEqual({ tokensSaved: 0, savingsUSD: 0, count: 0 })
|
||||
})
|
||||
|
||||
it('counts only the local subset of a mixed finding towards the apply-able subtotal', () => {
|
||||
const finding = detectMcpToolCoverage([project(twoSessions(['filesystem', 'claude_ai_Slack']))])
|
||||
|
||||
expect(finding).not.toBeNull()
|
||||
expect(findingClass(finding!)).toBe('fix')
|
||||
expect(finding).toMatchObject({ tokensSaved: 40_000, applyTokensSaved: 20_000 })
|
||||
expect(classTotals([finding!], 0.00002).fix).toEqual({ tokensSaved: 20_000, savingsUSD: 0.4, count: 1 })
|
||||
})
|
||||
|
||||
it("leaves a local-only finding's subtotal at its full estimate", () => {
|
||||
const finding = detectMcpToolCoverage([project(twoSessions(['filesystem']))])
|
||||
|
||||
expect(finding).not.toBeNull()
|
||||
expect(finding!.applyTokensSaved).toBeUndefined()
|
||||
expect(classTotals([finding!], 0.00002).fix.tokensSaved).toBe(finding!.tokensSaved)
|
||||
})
|
||||
|
||||
it('treats a local server named like a connector namespace as manual only', () => {
|
||||
// Known limitation: the namespace prefix is the only connector signal in
|
||||
// the transcript, so a local server literally named claude_ai_* gets the
|
||||
// manual /mcp guidance rather than a remove command. Conservative by
|
||||
// design: CodeBurn never emits a command that could hit a connector.
|
||||
const finding = detectMcpToolCoverage([project(twoSessions(['claude_ai_homegrown']))])
|
||||
|
||||
expect(finding!.fix.type).toBe('paste')
|
||||
expect(finding!.apply).toBeUndefined()
|
||||
expect(findingClass(finding!)).toBe('nudge')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue