Skip to content

Commit e5ea708

Browse files
committed
fix(cloud-agent-next): harden outcome reporting
Preserve initial setup failures and isolate diagnostic scrubbing from purge failures while reducing the admin dashboard to used, efficient health endpoints.
1 parent e5373e1 commit e5ea708

8 files changed

Lines changed: 134 additions & 713 deletions

File tree

apps/web/src/app/admin/api/cloud-agent-next/hooks.ts

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useTRPC } from '@/lib/trpc/utils';
44
import { useQuery } from '@tanstack/react-query';
55

6-
export type CloudAgentNextFilters = {
6+
type CloudAgentNextFilters = {
77
/** Inclusive ISO datetime lower bound for observed-outcome reporting. */
88
startDate: string;
99
/** Exclusive ISO datetime upper bound for observed-outcome reporting. */
@@ -15,24 +15,12 @@ export type CloudAgentNextHealthFilters = CloudAgentNextFilters & {
1515
createdOnPlatform?: string | null;
1616
};
1717

18-
export type CloudAgentNextHealthError = {
18+
type CloudAgentNextHealthError = {
1919
source: 'setup' | 'run';
2020
stage: string;
2121
code: string;
2222
};
2323

24-
export type CloudAgentNextSessionListFilters = CloudAgentNextFilters & {
25-
runStatus?: 'queued' | 'accepted' | 'completed' | 'failed' | 'interrupted';
26-
cloudAgentSessionId?: string;
27-
kiloSessionId?: string;
28-
sandboxId?: string;
29-
messageId?: string;
30-
failureStage?: string;
31-
failureCode?: string;
32-
page: number;
33-
pageSize: number;
34-
};
35-
3624
function enabledForInterval(params: CloudAgentNextFilters) {
3725
return Boolean(params.startDate && params.endDate);
3826
}
@@ -70,51 +58,3 @@ export function useCloudAgentNextHealthErrorSessions(
7058
enabled: enabledForInterval(params) && Boolean(error),
7159
});
7260
}
73-
74-
export function useCloudAgentNextRunCohortStats(params: CloudAgentNextFilters) {
75-
const trpc = useTRPC();
76-
return useQuery({
77-
...trpc.admin.cloudAgentNext.getRunCohortStats.queryOptions(params),
78-
enabled: enabledForInterval(params),
79-
});
80-
}
81-
82-
export function useCloudAgentNextTerminalOutcomeTrend(params: CloudAgentNextFilters) {
83-
const trpc = useTRPC();
84-
return useQuery({
85-
...trpc.admin.cloudAgentNext.getTerminalOutcomeTrend.queryOptions(params),
86-
enabled: enabledForInterval(params),
87-
});
88-
}
89-
90-
export function useCloudAgentNextFailureBreakdown(params: CloudAgentNextFilters) {
91-
const trpc = useTRPC();
92-
return useQuery({
93-
...trpc.admin.cloudAgentNext.getFailureBreakdown.queryOptions(params),
94-
enabled: enabledForInterval(params),
95-
});
96-
}
97-
98-
export function useCloudAgentNextLatencyStats(params: CloudAgentNextFilters) {
99-
const trpc = useTRPC();
100-
return useQuery({
101-
...trpc.admin.cloudAgentNext.getLatencyStats.queryOptions(params),
102-
enabled: enabledForInterval(params),
103-
});
104-
}
105-
106-
export function useCloudAgentNextSessions(params: CloudAgentNextSessionListFilters) {
107-
const trpc = useTRPC();
108-
return useQuery({
109-
...trpc.admin.cloudAgentNext.listSessions.queryOptions(params),
110-
enabled: enabledForInterval(params),
111-
});
112-
}
113-
114-
export function useCloudAgentNextSessionDetail(cloudAgentSessionId: string | null) {
115-
const trpc = useTRPC();
116-
const queryOptions = trpc.admin.cloudAgentNext.getSessionDetail.queryOptions({
117-
cloudAgentSessionId: cloudAgentSessionId ?? 'not-selected',
118-
});
119-
return useQuery({ ...queryOptions, enabled: Boolean(cloudAgentSessionId) });
120-
}

apps/web/src/app/admin/components/CloudAgentNextTelemetry/CloudAgentNextOutcomesPage.tsx

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ function formatBucketStart(bucketStart: string, bucket: HealthBucket): string {
158158
return bucket === 'day' ? `${utcShortDay.format(date)} UTC` : `${utcLongLabel.format(date)} UTC`;
159159
}
160160

161-
type MetricTone = 'success' | 'danger' | 'warning' | 'neutral';
161+
type MetricTone = 'success' | 'danger' | 'warning';
162162

163163
const metricToneStyles: Record<MetricTone, { panel: string; value: string }> = {
164164
success: {
@@ -173,10 +173,6 @@ const metricToneStyles: Record<MetricTone, { panel: string; value: string }> = {
173173
panel: 'border-yellow-500/20 bg-yellow-500/5',
174174
value: 'text-yellow-400',
175175
},
176-
neutral: {
177-
panel: 'bg-muted/30',
178-
value: 'text-foreground',
179-
},
180176
};
181177

182178
function Metric({
@@ -293,52 +289,6 @@ function HealthTooltip({
293289
);
294290
}
295291

296-
function OutcomeDataTable({ data, bucket }: { data: SeriesPoint[]; bucket: HealthBucket }) {
297-
const label = bucketLabel(bucket);
298-
return (
299-
<details className="mt-4 rounded-lg border px-4 py-3">
300-
<summary className="cursor-pointer text-sm font-medium">
301-
View {label.toLowerCase()} data table
302-
</summary>
303-
<div className="mt-4 max-h-80 overflow-auto">
304-
<Table>
305-
<TableCaption className="sr-only">{label} Cloud Agent outcome counts.</TableCaption>
306-
<TableHeader>
307-
<TableRow>
308-
<TableHead>{bucket === 'day' ? 'Day' : 'Hour'} (UTC)</TableHead>
309-
<TableHead className="text-right">Completed</TableHead>
310-
<TableHead className="text-right">Failed</TableHead>
311-
<TableHead className="text-right">Setup failed</TableHead>
312-
<TableHead className="text-right">Interrupted</TableHead>
313-
</TableRow>
314-
</TableHeader>
315-
<TableBody>
316-
{data.map(point => (
317-
<TableRow key={point.bucketStart}>
318-
<TableCell className="font-mono text-xs whitespace-nowrap">
319-
{formatBucketStart(point.bucketStart, bucket)}
320-
</TableCell>
321-
<TableCell className="text-right font-mono tabular-nums">
322-
{point.completedRuns.toLocaleString()}
323-
</TableCell>
324-
<TableCell className="text-right font-mono tabular-nums">
325-
{point.failedRuns.toLocaleString()}
326-
</TableCell>
327-
<TableCell className="text-right font-mono tabular-nums">
328-
{point.setupFailures.toLocaleString()}
329-
</TableCell>
330-
<TableCell className="text-right font-mono tabular-nums">
331-
{point.interruptedRuns.toLocaleString()}
332-
</TableCell>
333-
</TableRow>
334-
))}
335-
</TableBody>
336-
</Table>
337-
</div>
338-
</details>
339-
);
340-
}
341-
342292
function OutcomeTrendChart({
343293
data,
344294
range,
@@ -398,7 +348,6 @@ function OutcomeTrendChart({
398348
</BarChart>
399349
</ResponsiveContainer>
400350
</div>
401-
<OutcomeDataTable data={data} bucket={bucket} />
402351
</CardContent>
403352
</Card>
404353
);

apps/web/src/app/admin/components/CloudAgentNextTelemetry/health-period-preference.test.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
import { describe, expect, it } from '@jest/globals';
2-
import {
3-
DEFAULT_HEALTH_PERIOD,
4-
getHealthPeriodStorageKey,
5-
parseHealthPeriod,
6-
} from './health-period-preference';
2+
import { DEFAULT_HEALTH_PERIOD, parseHealthPeriod } from './health-period-preference';
73

84
describe('Cloud Agent health period preference', () => {
9-
it('uses a stable storage key for the selected admin period', () => {
10-
expect(getHealthPeriodStorageKey()).toBe('cloud-agent-next:admin-health-period');
11-
});
12-
135
it('restores every supported period preset', () => {
146
for (const period of ['1h', '3h', '24h', '7d', '14d', '30d']) {
157
expect(parseHealthPeriod(period)).toBe(period);

apps/web/src/app/admin/components/CloudAgentNextTelemetry/health-period-preference.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ export function parseHealthPeriod(value: string | null): HealthPeriod {
1515
return value && isHealthPeriod(value) ? value : DEFAULT_HEALTH_PERIOD;
1616
}
1717

18-
export function getHealthPeriodStorageKey() {
19-
return HEALTH_PERIOD_STORAGE_KEY;
20-
}
21-
2218
export function getStoredHealthPeriod(): HealthPeriod {
2319
return parseHealthPeriod(safeLocalStorage.getItem(HEALTH_PERIOD_STORAGE_KEY));
2420
}

0 commit comments

Comments
 (0)