Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,15 @@ export default function App() {
</StalePanel>

<div className="grid grid-cols-1 gap-3.5 lg:grid-cols-[1.9fr_1fr]">
<StalePanel reason={staleTraceDerived}>
<LatencyChart series={data.latencyP95} sample={conn.status === 'sample'} />
{/* Fed by `summary`, not by traces: dimmed with the KPI tiles it agrees with. */}
<StalePanel reason={staleReason(channels, ['summary'])}>
<LatencyChart
points={data.latency}
range={settings.chartRange}
sample={conn.status === 'sample'}
windowMs={data.summary?.windowMs ?? null}
capacity={platform?.capacity ?? null}
/>
</StalePanel>
<StalePanel reason={staleTraceDerived}>
<StatusMix traces={data.traces} />
Expand Down
26 changes: 17 additions & 9 deletions src/components/kpi-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,22 @@ function Tile({
}

/**
* Header KPIs. The big values are the platform's all-time cumulative aggregates
* (from `metrics`); the sub-line under each is the recent-window trend from the
* `summary` SSE event (windowed count / error rate / p95 vs the previous window).
* When `summary` is absent or a trend field is null, the sub-line renders a muted
* placeholder rather than a made-up delta.
* Header KPIs. Requests and error rate are the platform's all-time cumulative
* aggregates (from `metrics`); the sub-line under each is the recent-window trend
* from the `summary` SSE event (windowed count / error rate / p95 vs the previous
* window). When `summary` is absent or a trend field is null, the sub-line renders
* a muted placeholder rather than a made-up delta.
*
* Latency p95 is the exception: it is `summary`'s windowed p95, the same number the
* latency chart plots, so the tile reads as the chart's newest point. It used to be
* the worst per-module p95 from `metrics`, which sat beside a chart drawn from a
* third source and agreed with neither. Until a `summary` arrives it shows a dash —
* the per-module figure is a different measurement, not a stand-in for this one.
*/
export function KpiRow({ metrics, summary }: { metrics: ModuleMetricsSnapshot[]; summary: TraceSummary | null }) {
const total = metrics.reduce((a, m) => a + m.count, 0)
const errors = metrics.reduce((a, m) => a + m.errorCount, 0)
const errorRate = total ? errors / total : 0
const p95 = Math.max(0, ...metrics.map((m) => m.p95LatencyMs))
const p99 = Math.max(0, ...metrics.map((m) => m.p99LatencyMs))
const activeModules = metrics.filter((m) => m.moduleId !== '(platform)').length

const win = summary ? `last ${Math.round(summary.windowMs / 1000)}s` : 'window'
Expand All @@ -119,13 +123,17 @@ export function KpiRow({ metrics, summary }: { metrics: ModuleMetricsSnapshot[];
)}
</Tile>

<Tile label="Latency p95" value={String(p95)} unit="ms">
<Tile
label="Latency p95"
value={summary ? String(summary.p95LatencyMs) : '—'}
unit={summary ? 'ms' : undefined}
>
{summary?.p95DeltaMs != null ? (
<Delta dir={dirOf(summary.p95DeltaMs)} tone={badWhenUp(summary.p95DeltaMs)}>
{Math.abs(summary.p95DeltaMs)}ms · p99 {summary.p99LatencyMs}ms
</Delta>
) : (
<Muted>{win} · p99 {summary?.p99LatencyMs ?? p99}ms</Muted>
<Muted>{summary ? `${win} · p99 ${summary.p99LatencyMs}ms` : 'no summary yet'}</Muted>
)}
</Tile>

Expand Down
Loading
Loading