diff --git a/src/components/time-series-chart/chart.tsx b/src/components/time-series-chart/chart.tsx
index bdaca025..bdd76dfe 100644
--- a/src/components/time-series-chart/chart.tsx
+++ b/src/components/time-series-chart/chart.tsx
@@ -6,7 +6,7 @@ import {
type LineWithColor,
} from "./scales";
import { YAxis, XAxis } from "./axis";
-import { SeriesGradients, SeriesPaths, DowntimeBands, HoverMarkers, type DrawnLine } from "./series";
+import { SeriesGradients, SeriesPaths, HoverMarkers, type DrawnLine } from "./series";
import { Legend } from "./legend";
import { Tooltip } from "./tooltip";
@@ -22,11 +22,6 @@ type ChartProps = {
onZoom?: (next: { startFrac: number; endFrac: number } | null) => void;
onToggleExclude?: (slug: string) => void;
onResetExcluded?: () => void;
- /** Only render the downtime bands + pill labels when the parent bench
- * opted in (spec declared live_activity). Otherwise natural nulls in
- * any bench's series24h would fire a "DATA MISSING" pill on charts
- * that never asked for the feature (observed on rpc-reliability). */
- showDowntime?: boolean;
};
export function Chart({
@@ -38,7 +33,6 @@ export function Chart({
onZoom,
onToggleExclude,
onResetExcluded,
- showDowntime = false,
}: ChartProps) {
const W = 1000;
const H = 360;
@@ -392,12 +386,9 @@ export function Chart({
{/* X tick labels */}
- {/* Downtime bands. Rendered before the lines so the stroke stays
- on top of the highlight. Contiguous null-buckets (from spec
- `unless changes == 0`) become visible red columns. */}
- {showDowntime && }
-
- {/* Areas + lines */}
+ {/* Areas + lines. Gaps in the series render as natural breaks in
+ the stroke — no tinted band, no pill: the missing pixels are
+ the signal. */}
{/* Drag-to-zoom selection rect. Brushed window highlighted in
diff --git a/src/components/time-series-chart/index.tsx b/src/components/time-series-chart/index.tsx
index 4a2e2bc3..6ed8ddb3 100644
--- a/src/components/time-series-chart/index.tsx
+++ b/src/components/time-series-chart/index.tsx
@@ -483,7 +483,6 @@ export function TimeSeriesChart({
onZoom={setZoom}
onToggleExclude={toggle}
onResetExcluded={excluded.size > 0 ? reset : undefined}
- showDowntime={benchmark.results.some((r) => r.liveStatus != null)}
/>
)}
diff --git a/src/components/time-series-chart/series.tsx b/src/components/time-series-chart/series.tsx
index 3b6865b1..afe17328 100644
--- a/src/components/time-series-chart/series.tsx
+++ b/src/components/time-series-chart/series.tsx
@@ -16,119 +16,10 @@ type SeriesPathsProps = {
unit: string;
};
-type DowntimeBandsProps = {
- drawn: DrawnLine[];
- padT: number;
- innerH: number;
-};
-
-/** Colored rectangles that highlight the exact window each line was
- * silent (contiguous gap indices), plus a high-contrast pill label
- * above each band naming the provider that dropped off. Pills stack
- * vertically when multiple providers went silent at the same time so
- * simultaneous outages read as separate lines instead of colliding
- * as unreadable overlapping text. Rendered under SeriesPaths so the
- * line stroke stays on top; skipped for excluded (legend-toggled)
- * providers so their bands disappear with the line. */
-export function DowntimeBands({ drawn, padT, innerH }: DowntimeBandsProps) {
- // Collect every gap band across every visible provider. Labels used to
- // be rendered on top of each band; they were removed because the pill
- // often mislabelled the cause (harness reconnect vs real crash vs
- // sample alignment artefact). The tinted band + dashed edges alone
- // read as "no data here" without asserting a cause.
- type Band = {
- slug: string;
- color: string;
- x: number;
- w: number;
- };
- const all: Band[] = [];
- for (const d of drawn) {
- if (d.excluded) continue;
- // Only count gap runs that start AFTER the first observed sample.
- // A leading run of nulls (Prom retention shorter than the visible
- // window, harness started mid-range, or provider added recently)
- // is "we didn't measure yet", not "provider was down for a week".
- // Without this guard the 30D view on aggregator-head-lag rendered
- // a chart-wide band during Prom's initial fill period.
- let seenData = false;
- let runStart: number | null = null;
- const push = (endX: number) => {
- if (runStart == null) return;
- if (!seenData) {
- runStart = null;
- return;
- }
- const startX = d.pts[runStart].x;
- all.push({
- slug: d.slug,
- color: d.color,
- x: startX,
- w: Math.max(6, endX - startX),
- });
- runStart = null;
- };
- for (let i = 0; i < d.pts.length; i++) {
- const p = d.pts[i];
- if (p.gap) {
- if (runStart == null) runStart = i;
- } else {
- push(d.pts[i].x);
- seenData = true;
- }
- }
- // Trailing gap that runs to the current time — meaningful only
- // when the series had prior data (same seenData guard).
- if (runStart != null) push(d.pts[d.pts.length - 1].x);
- }
- if (all.length === 0) return null;
-
- // Label pills were removed intentionally: the guard upstream can't
- // always tell "aggregator crash" apart from "our harness reconnect"
- // or "worker step misalignment on a 30 min silence in a 7 d view",
- // and a mislabelled pill blames the wrong side. The tinted band +
- // dashed edges alone read as "no data recorded here" without
- // asserting a cause. When we have a first-party signal that a
- // specific provider went down, a proper status badge will be added
- // separately — the sparkline is not the place to assign blame.
-
- return (
-
- {all.map((b, i) => (
-
-
-
-
-
- ))}
-
- );
-}
+// DowntimeBands was removed: tinted bands + pill labels were misleading
+// (mislabelled provider, alignment artefacts). Gaps in the series line
+// now speak for themselves — SeriesPaths renders a natural break wherever
+// the underlying values are null.
export function SeriesPaths({ drawn, unit }: SeriesPathsProps) {
return (