Files
manage/openspec/changes/prometheus-direct-charting/specs/prometheus-charting/spec.md
T
Developer 3e77075171 spec(prometheus-direct-charting): sync into canonical prometheus-charting domain
New canonical domain openspec/specs/prometheus-charting/spec.md with all
27 requirements (SC-101..127) as the durable post-change contract. Change-side
delta specs/prometheus-charting/spec.md + sync-report.md. web-ui canonical
untouched (different concern).
2026-07-08 22:57:29 +00:00

9.5 KiB
Raw Blame History

Prometheus Charting — Delta (prometheus-direct-charting)

Change: prometheus-direct-charting · Domain: prometheus-charting · Phase: spec (reconciled during sdd-sync). Distilled verbatim from the verified flat spec.md (27 requirements, SC-101 … SC-127) of change prometheus-direct-charting, cross-referenced against design.md and verify-report.md. Captures the durable, post-change end-state contracts for direct Prometheus-backed metric visualization and the Grafana removal that established the new model.

ADDED Requirements

The canonical openspec/specs/prometheus-charting/spec.md did not exist before this change. All requirements below are therefore ADDED to a new prometheus-charting domain; sdd-sync copies them into the canonical spec (native helper rule: when the canonical spec does not exist, the change spec becomes the new canonical spec).

Requirement IDs (SC-101 … SC-127) and body text are preserved exactly from the verified flat spec.md, including the patched SC-116 / SC-118 wording. Requirements are grouped logically and listed in the following group order:

  • Direct Prometheus range query path — SC-101 … SC-104
  • Prometheus chart widget (rebrand + rebind) — SC-105 … SC-108
  • Prometheus gauge widget — SC-109 … SC-111
  • Prometheus mean widget — SC-112 … SC-114
  • Grafana removal — SC-115 … SC-120
  • Configuration documentation accuracy — SC-121 … SC-122
  • Test and build greenness — SC-123 … SC-125
  • Migration guidance — SC-126 … SC-127

Requirement: SC-101 — Prometheus range query returns the existing series shape

When a prometheus widget of kind chart is fetched, the backend MUST query {base_url}/api/v1/query_range with query, start, end, and step derived from the widget config, and return a payload of shape { "series": [{ "label": str, "points": [{ "t": int, "v": float|null }] }] } — the exact shape the frontend chart renderer already consumes.

Requirement: SC-102 — Series label normalization is shared and Prometheus-native

The metric-label → readable-label normalization MUST live in a single shared helper (not duplicated in a Grafana path) and MUST produce meaningful labels for Prometheus matrix results, including deduplicating repeated labels via a label (n) suffix.

Requirement: SC-103 — Range query errors degrade gracefully

A Prometheus timeout, connection error, or non-2xx response MUST cause the widget data fetch to return { "error": str } (not raise), so the frontend renders the standard per-widget error state and the rest of the dashboard remains functional.

Requirement: SC-104 — Step is derived from the window preset

Given a window preset (1h / 6h / 24h / 7d), the backend MUST derive a step that yields a reasonable number of points (target ~100300 points). Users do not configure step directly.

Requirement: SC-105 — Chart widget moves from grafana to prometheus

A widget kind named chart MUST be bound to the prometheus service type in both the backend registry and the frontend SERVICE_REGISTRY. The grafana service type MUST NOT offer a chart kind.

Requirement: SC-106 — Chart renderer is reused unchanged

The recharts rendering (line chart, multi-series, axes, tooltip, mergeSeries, color tokens) MUST be preserved in the rebranded PrometheusChartWidget. The rename is structural; the rendering code is not rewritten.

Requirement: SC-107 — Chart supports multiple series

The chart widget MUST render all series returned by the range query, each as its own line with a distinct color. There is no single-series restriction on chart.

Requirement: SC-108 — Chart window is a preset

The chart widget config MUST expose the time window as a preset selector (1h, 6h, 24h, 7d), not raw from/to/step fields. The preset is stored in widget config and resolved to start/end server-side.

Requirement: SC-109 — Gauge renders an instant scalar

A widget kind named gauge MUST be bound to the prometheus service. Its data fetch MUST run an instant PromQL query (/api/v1/query) and return the scalar result for rendering as a gauge.

Requirement: SC-110 — Gauge supports configurable threshold bands

The gauge widget config MUST accept optional threshold values (e.g. warn_at, crit_at) and the renderer MUST display green / amber / red bands accordingly. When thresholds are omitted, the gauge renders with a single neutral color and no bands.

Requirement: SC-111 — Gauge is scalar-only

The gauge widget MUST render exactly one scalar value. If the instant query returns multiple series, the adapter MUST return { "error": str } (not silently pick one), directing the user to refine the PromQL.

Requirement: SC-112 — Mean computes client-side over a window

A widget kind named mean MUST be bound to the prometheus service. Its data fetch MUST run a range query over the configured window preset and return the arithmetic mean of all non-null point values as a single scalar.

Requirement: SC-113 — Mean uses plain PromQL + window preset

The mean widget config MUST accept a plain PromQL expression (no requirement to wrap in avg_over_time) plus a window preset. Users do not write range-vector functions.

Requirement: SC-114 — Mean is scalar-only

The mean widget MUST render exactly one scalar value. If the range query returns multiple series, the adapter MUST return { "error": str } (not silently aggregate across series).

Requirement: SC-115 — No grafana references in backend source

After the change, grep -ri grafana backend/src --include='*.py' MUST return no matches (excluding comments/changelog that are explicitly about the removal, if any are retained — but ideally zero).

Requirement: SC-116 — No grafana references in frontend source

After the change, grep -ri grafana frontend/src MUST return no matches, excluding (a) test fixtures where "Grafana" appears as a user-authored dashboard shortcut label unrelated to the grafana service type (e.g. Dashboard.test.tsx), and (b) LinksTab.tsx / service-tabs/index.ts lines that are themselves being deleted as part of SC-118. (Source finding: ObservabilityPage.tsx was refactored into service-tabs/.)

Requirement: SC-117 — Grafana service type is gone from registries

Neither the backend SERVICE_DEFINITIONS / SERVICE_ADAPTERS nor the frontend SERVICE_REGISTRY / BUILTIN_WIDGETS MUST contain a grafana entry. The integrations/grafana.py file MUST be deleted.

Requirement: SC-118 — Grafana status checks and UI sections are removed

The get_grafana_status endpoint and its frontend hook (useGrafanaStatus) MUST be removed. The UI surface previously in ObservabilityPage.tsx has been refactored into a per-service-type service-tabs/ architecture; the Grafana removal targets are therefore service-tabs/LinksTab.tsx + its test, the grafana case in service-tabs/index.ts, the grafana entry in integrations/navEntries.ts, the grafana member of Dashboard.tsx's OBSERVABILITY_TYPES set, and any Grafana empty-state copy in ServicesPage.tsx. The literal ObservabilityPage.tsx no longer exists; SC-118's intent (no Grafana UI surface) is what is verified.

Requirement: SC-119 — Grafana widget instances degrade gracefully

An existing persisted widget row referencing a grafana service MUST NOT crash the dashboard. It resolves to the existing "unknown widget" error state and surfaces a clear message; the operator can then delete it.

Requirement: SC-120 — Grafana tests are removed

All Grafana-specific tests (backend and frontend) MUST be deleted; no test references grafana.

Requirement: SC-121 — config.yaml matches implementation

openspec/config.yaml MUST NOT contain the stale claims "Do NOT re-implement charting in-app" or "No recharts/d3 is in use." It MUST reflect that in-app charting via recharts is the sanctioned approach for Prometheus-backed series, and MUST NOT reference Grafana as a chart path.

Requirement: SC-122 — CHANGELOG documents the migration

CHANGELOG.md MUST include an entry instructing operators to delete existing Grafana service instances and recreate them as Prometheus services, noting that configured grafana/chart widgets must be recreated as prometheus/chart widgets.

Requirement: SC-123 — Backend tests pass

pytest run from backend/ MUST pass, including new tests covering: Prom range query → {series} normalization, gauge scalar-only enforcement, mean client-side aggregation, and the shared label helper.

Requirement: SC-124 — Frontend typechecks, builds, and lints

npm run build (which runs tsc -b + vite build) and npm run lint from frontend/ MUST pass.

Requirement: SC-125 — New widget kinds have tests

PrometheusChartWidget, PrometheusGaugeWidget, and PrometheusMeanWidget MUST each have a frontend test covering at least: loading state, error state, and a rendered data case.

Requirement: SC-126 — No silent data migration

The change MUST NOT attempt to auto-migrate existing grafana service rows into prometheus rows (URLs differ; true migration is impossible). Migration is operator-driven per the CHANGELOG note.

Requirement: SC-127 — Non-blocking on the service-storage-harness change

This change MUST NOT depend on the service-storage-harness change. It is independently buildable, testable, and deployable. (The reverse dependency holds: the qBit speed widget depends on this change's chart capability.)