From 586cb00174894c22d82937db6c322d00ce3f53a2 Mon Sep 17 00:00:00 2001 From: Paul Holstein <44263169+holstein13@users.noreply.github.com> Date: Sun, 22 Feb 2026 08:43:33 -0500 Subject: [PATCH] =?UTF-8?q?fix(report):=20address=20CodeRabbit=20review=20?= =?UTF-8?q?=E2=80=94=20pricing=20match,=20sidechain=20filter,=20UX=20guard?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getPricing: use tokenized order-insensitive matching so Claude 3 era models (e.g. "claude-3-opus-20240229") correctly match pricing key "opus-3" - sessionAnalyzer: skip isSidechain messages in parentCost loop to prevent double-counting with processSubagentCost - CostSection: disable row expansion when token stats are missing - tabSlice: guard against undefined sessionId/projectId in openSessionReport Co-Authored-By: Claude Opus 4.6 --- src/renderer/components/report/sections/CostSection.tsx | 8 ++++---- src/renderer/store/slices/tabSlice.ts | 1 + src/renderer/utils/sessionAnalyzer.ts | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/report/sections/CostSection.tsx b/src/renderer/components/report/sections/CostSection.tsx index f0316403..3e65ee47 100644 --- a/src/renderer/components/report/sections/CostSection.tsx +++ b/src/renderer/components/report/sections/CostSection.tsx @@ -182,17 +182,17 @@ export const CostSection = ({ {modelEntries.map(([model, cost]) => { const stats = tokensByModel[model]; - const isExpanded = expandedModel === model; + const isExpanded = expandedModel === model && !!stats; const pricing = getPricing(model); return ( setExpandedModel(isExpanded ? null : model)} + className={`border-border/50 border-b ${stats ? 'hover:bg-surface-raised/50 cursor-pointer' : ''}`} + onClick={() => stats && setExpandedModel(isExpanded ? null : model)} > - {isExpanded ? '\u25BC' : '\u25B6'} + {stats ? (isExpanded ? '\u25BC' : '\u25B6') : ''} {model} diff --git a/src/renderer/store/slices/tabSlice.ts b/src/renderer/store/slices/tabSlice.ts index 1735c03e..677733a0 100644 --- a/src/renderer/store/slices/tabSlice.ts +++ b/src/renderer/store/slices/tabSlice.ts @@ -387,6 +387,7 @@ export const createTabSlice: StateCreator = (set, ge const allTabs = getAllTabs(state.paneLayout); const sourceTab = allTabs.find((t) => t.id === sourceTabId); if (sourceTab?.type !== 'session') return; + if (!sourceTab.sessionId || !sourceTab.projectId) return; const tabData = state.tabSessionData[sourceTabId]; const firstMsg = tabData?.sessionDetail?.session.firstMessage; diff --git a/src/renderer/utils/sessionAnalyzer.ts b/src/renderer/utils/sessionAnalyzer.ts index 93b65ae3..46773633 100644 --- a/src/renderer/utils/sessionAnalyzer.ts +++ b/src/renderer/utils/sessionAnalyzer.ts @@ -104,9 +104,10 @@ const DEFAULT_PRICING: ModelPricing = { }; export function getPricing(modelName: string): ModelPricing { - const name = modelName.toLowerCase(); + const nameTokens: string[] = modelName.toLowerCase().match(/[a-z0-9]+/g) ?? []; for (const [key, pricing] of Object.entries(MODEL_PRICING)) { - if (name.includes(key)) return pricing; + const keyTokens: string[] = key.match(/[a-z0-9]+/g) ?? []; + if (keyTokens.every((t) => nameTokens.includes(t))) return pricing; } return DEFAULT_PRICING; } @@ -454,7 +455,9 @@ export function analyzeSession(detail: SessionDetail): SessionReport { } // --- Token usage, cache economics, and cost --- - if (m.usage && m.model) { + // Skip sidechain messages to avoid double-counting (subagent costs are + // accounted for separately via processSubagentCost). + if (m.usage && m.model && !m.isSidechain) { const model = m.model; const u = m.usage; const inpTok = u.input_tokens ?? 0;