From 667caa0b214b532a6064e6f06957b6119988fbb5 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Sun, 16 Aug 2026 03:04:45 -0600 Subject: [PATCH 1/2] Import the service-resources wire types from the server apps/web/src/hooks/use-service-resources.ts hand-mirrored the server's observability type tree: ResourceSample, SubsystemResourceSample, SubsystemSnapshot and ServiceResourcesResponse were all maintained as a second copy. The web copies now `import type` directly from observability/service-resources.ts and observability/subsystem-tracker.ts; esbuild erases type-only imports, so nothing reaches the bundle. ResourceHealthState stays web-side but is now derived as `SubsystemHealthState | "unavailable"` instead of restating six of the server's seven members. Every importer keeps its existing `@/hooks/use-service-resources` path. Co-Authored-By: Claude Opus 5 (1M context) --- apps/web/src/hooks/use-service-resources.ts | 129 ++------------------ 1 file changed, 13 insertions(+), 116 deletions(-) diff --git a/apps/web/src/hooks/use-service-resources.ts b/apps/web/src/hooks/use-service-resources.ts index 06c9a5f7..48fa1c7f 100644 --- a/apps/web/src/hooks/use-service-resources.ts +++ b/apps/web/src/hooks/use-service-resources.ts @@ -1,124 +1,21 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; +import type { ServiceResourcesResponse } from "../../../server/src/observability/service-resources"; +import type { SubsystemHealthState } from "../../../server/src/observability/subsystem-tracker"; -export type ResourceHealthState = - | "healthy" - | "degraded" - | "unavailable" - | "running" - | "idle" - | "disabled" - | "unknown"; +export type { + ResourceSample, + ServiceResourcesResponse, + SubsystemResourceSample, +} from "../../../server/src/observability/service-resources"; +export type { SubsystemSnapshot } from "../../../server/src/observability/subsystem-tracker"; -export type ResourceSample = { - at: number; - serverCpuPercent: number; - serverRssBytes: number; - serverHeapBytes: number; - agentCpuPercent: number | null; - agentRssBytes: number | null; - hostLoad1: number; - subsystems: Record; -}; - -export type SubsystemResourceSample = { - p95DurationMs: number | null; - failures: number; - metadata: Record; -}; - -export type SubsystemSnapshot = { - id: string; - label: string; - description: string; - state: ResourceHealthState; - statusReason: "failure" | "stale" | "stuck" | null; - expectedCadenceMs: number | null; - lastStartedAt: number | null; - lastCompletedAt: number | null; - lastSucceededAt: number | null; - lastFailedAt: number | null; - lastDurationMs: number | null; - p95DurationMs: number | null; - inFlight: number; - runs: number; - failures: number; - lastError: string | null; - metadata: Record; -}; - -export type ServiceResourcesResponse = { - collectionEnabled: boolean; - generatedAt: number; - processStartedAt: number; - availableHistoryMs: number; - sampleIntervalMs: number; - overall: { - state: "healthy" | "degraded" | "unavailable" | "unknown"; - reasons: Array<{ code: string; message: string }>; - }; - capabilities: { - processTreeMetrics: "available" | "unsupported" | "error"; - eventLoopMetrics: "available"; - }; - current: { - server: { - cpuPercent: number; - rssBytes: number; - heapUsedBytes: number; - heapTotalBytes: number; - externalBytes: number; - uptimeSeconds: number; - }; - host: { - load1: number; - load5: number; - load15: number; - cpuCount: number; - totalMemoryBytes: number; - freeMemoryBytes: number; - }; - agents: { - supported: boolean; - cpuPercent: number | null; - rssBytes: number | null; - processCount: number | null; - sampledAt: number | null; - error: string | null; - }; - database: { - state: "healthy" | "unavailable" | "unknown"; - latencyMs: number | null; - sampledAt: number | null; - pool: { total: number; idle: number; waiting: number; max: number }; - }; - eventLoop: { p95DelayMs: number }; - http: { - requestsPerMinute: number; - inFlight: number; - errorRatePercent: number; - p95DurationMs: number | null; - }; - workloads: { - runningAgents: number; - sseClients: number; - streams: number; - streamViewers: number; - terminalObservers: number; - terminalViewers: number; - scheduledJobs: number; - jobMonitors: number; - gitRefreshesInFlight: number; - uiEventsPublished: number; - uiWriteFailures: number; - terminalPolls: number; - terminalPollFailures: number; - }; - }; - subsystems: SubsystemSnapshot[]; - series: ResourceSample[]; -}; +/** + * Every state the resource dashboard renders a badge for: the subsystem states + * plus the `unavailable` that only `overall` and `database` report. + */ +export type ResourceHealthState = SubsystemHealthState | "unavailable"; export type ResourceWindow = "15m" | "1h"; From f510fe5377d7d316771ee084f86a76c805e4de68 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Sun, 16 Aug 2026 03:10:40 -0600 Subject: [PATCH 2/2] Derive ResourceHealthState from both server state unions Review feedback: `| "unavailable"` hardcoded today's difference between the subsystem and overall unions instead of tracking its source. If the server adds an overall-only state, the badge formatters would reject it and the wire-type coupling would not catch the drift. Same member set today, verified by a mutual-assignability assertion against the previous literal union. Co-Authored-By: Claude Opus 5 (1M context) --- apps/web/src/hooks/use-service-resources.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/web/src/hooks/use-service-resources.ts b/apps/web/src/hooks/use-service-resources.ts index 48fa1c7f..5cc06ba5 100644 --- a/apps/web/src/hooks/use-service-resources.ts +++ b/apps/web/src/hooks/use-service-resources.ts @@ -12,10 +12,14 @@ export type { export type { SubsystemSnapshot } from "../../../server/src/observability/subsystem-tracker"; /** - * Every state the resource dashboard renders a badge for: the subsystem states - * plus the `unavailable` that only `overall` and `database` report. + * Every state the resource dashboard renders a badge for. `stateLabel` and + * `stateBadgeVariant` are called with both `subsystem.state` and + * `overall.state`, so this tracks both server unions rather than restating + * where they differ. */ -export type ResourceHealthState = SubsystemHealthState | "unavailable"; +export type ResourceHealthState = + | SubsystemHealthState + | ServiceResourcesResponse["overall"]["state"]; export type ResourceWindow = "15m" | "1h";