diff --git a/apps/web/src/components/cost-insights/overview/SpendEvidenceCard.tsx b/apps/web/src/components/cost-insights/overview/SpendEvidenceCard.tsx index 5a4f9f3d94..322e20443d 100644 --- a/apps/web/src/components/cost-insights/overview/SpendEvidenceCard.tsx +++ b/apps/web/src/components/cost-insights/overview/SpendEvidenceCard.tsx @@ -46,7 +46,7 @@ export function SpendEvidenceCard({ '90d': 'Last 90 days', }[range]; const highest = evidence - .filter(point => point.coverage === 'complete') + .filter(point => point.coverage !== 'unavailable') .reduce<(typeof evidence)[number] | undefined>((currentHighest, point) => { if (!currentHighest) return point; const currentTotal = currentHighest.variableUsd + currentHighest.scheduledUsd; @@ -105,7 +105,7 @@ export function SpendEvidenceCard({ ? `${rangeLabel}: spend total unavailable because one or more periods have incomplete coverage.` : `${rangeLabel}: ${money(completeTotal)} total.`}{' '} {highest - ? `Highest complete period was ${highest.label} at ${money(highest.variableUsd + highest.scheduledUsd)}.` + ? `Highest period with spend evidence was ${highest.label} at ${money(highest.variableUsd + highest.scheduledUsd)}.` : ''}
@@ -135,9 +135,9 @@ export function SpendEvidenceCard({
const isPeak = highest !== undefined && point.periodStart === highest.periodStart;
const showTick = index % tickStride === 0 || index === evidence.length - 1;
const accessibilityLabel =
- point.coverage === 'complete'
- ? `${point.label}: ${money(pointTotal)} total, ${money(point.variableUsd)} usage-based, ${money(point.scheduledUsd)} scheduled`
- : `${point.label}: spend data unavailable, ${point.coveredHours} of ${point.totalHours} hours covered`;
+ point.coverage === 'unavailable'
+ ? `${point.label}: spend data unavailable, ${point.coveredHours} of ${point.totalHours} hours covered`
+ : `${point.label}: ${point.coverage === 'partial' ? 'at least ' : ''}${money(pointTotal)} total, ${money(point.variableUsd)} usage-based, ${money(point.scheduledUsd)} scheduled${point.coverage === 'partial' ? `, ${point.coveredHours} of ${point.totalHours} hours covered` : ''}`;
return (
diff --git a/apps/web/src/components/cost-insights/types.ts b/apps/web/src/components/cost-insights/types.ts
index 86ddc192a0..05646a675d 100644
--- a/apps/web/src/components/cost-insights/types.ts
+++ b/apps/web/src/components/cost-insights/types.ts
@@ -35,7 +35,12 @@ export type SpendEvidencePoint =
scheduledUsd: number;
})
| (SpendEvidencePointBase & {
- coverage: 'partial' | 'unavailable';
+ coverage: 'partial';
+ variableUsd: number;
+ scheduledUsd: number;
+ })
+ | (SpendEvidencePointBase & {
+ coverage: 'unavailable';
variableUsd: null;
scheduledUsd: null;
});
diff --git a/apps/web/src/lib/cost-insights/presenter.test.ts b/apps/web/src/lib/cost-insights/presenter.test.ts
index 75bbbf0033..ae94b38142 100644
--- a/apps/web/src/lib/cost-insights/presenter.test.ts
+++ b/apps/web/src/lib/cost-insights/presenter.test.ts
@@ -555,7 +555,7 @@ describe('Cost Insights presenter', () => {
).toThrow('Covered Cost Insights evidence must include both spend categories.');
});
- it('marks aggregate evidence partial without exposing an understated covered subtotal', () => {
+ it('shows known spend in partial aggregate evidence without presenting it as a complete total', () => {
const points = [
{
hourStart: '2026-06-25T22:00:00.000Z',
@@ -594,8 +594,8 @@ describe('Cost Insights presenter', () => {
coverage: 'partial',
coveredHours: 1,
totalHours: 2,
- variableUsd: null,
- scheduledUsd: null,
+ variableUsd: 2,
+ scheduledUsd: 1,
},
{
label: 'Jun 26',
diff --git a/apps/web/src/lib/cost-insights/presenter.ts b/apps/web/src/lib/cost-insights/presenter.ts
index da46ab7cf5..442a2723b3 100644
--- a/apps/web/src/lib/cost-insights/presenter.ts
+++ b/apps/web/src/lib/cost-insights/presenter.ts
@@ -225,10 +225,6 @@ function presentEvidenceBucket(points: OwnerHourlySpend[]): SpendEvidencePoint {
if (covered.length === 0) {
return { ...common, coverage: 'unavailable', variableUsd: null, scheduledUsd: null };
}
- if (covered.length !== points.length) {
- return { ...common, coverage: 'partial', variableUsd: null, scheduledUsd: null };
- }
-
let variableMicrodollars = 0;
let scheduledMicrodollars = 0;
for (const point of covered) {
@@ -238,7 +234,7 @@ function presentEvidenceBucket(points: OwnerHourlySpend[]): SpendEvidencePoint {
}
return {
...common,
- coverage: 'complete',
+ coverage: covered.length === points.length ? 'complete' : 'partial',
variableUsd: microdollarsToUsd(variableMicrodollars),
scheduledUsd: microdollarsToUsd(scheduledMicrodollars),
};
@@ -472,7 +468,7 @@ function buildMetrics(params: {
params.currentHourVariableMicrodollars === null
? 'Current-hour spend evidence is unavailable'
: params.currentHourVariableMicrodollars >= params.anomalyThresholdMicrodollars
- ? 'Above current alert level'
+ ? 'Unusually high for this account'
: `Typical hour: ${money(params.anomalyBaselineMicrodollars)}`,
tone:
params.currentHourVariableMicrodollars !== null &&
-
) : (