7440603cdb
Add loading-state tests to the three Prometheus widget test files (closes SC-125 PARTIAL). Write apply-progress.md, tick all 39 tasks, add verify-report.md (26/27 PASS, 1 PARTIAL->PASS). All gates green: 293 pytest, ruff clean, npm build+lint 0 errors. No blocking findings.
4.9 KiB
4.9 KiB
Apply Progress: Prometheus Direct Charting
Change: prometheus-direct-charting
Phase: apply-progress
Date: 2026-07-08
Status: complete — all 39 tasks done, all gates green, verified (see verify-report.md)
Slices delivered
Three slices, each its own commit, each leaving pytest / npm run build / npm run lint / ruff green.
Slice 1 — Prometheus range query + chart rebrand (commit 5dad982, amended)
- Created
backend/src/media_library_viewer_api/widgets/prometheus_range.pywithstep_for_window(s)(max(15, round(s/200))→ ~200 pts/window) andnormalize_prometheus_matrix(result)(shared label rule extracted from the to-be-removed Grafana path; drops__name__/__*, joins sortedk=v, falls back to"value", dedups with(n)). - Extended
PrometheusWidgetSource.fetchto dispatchwidget_kind == "chart"→_fetch_charthitting/api/v1/query_rangedirectly, returning{series:[...]}(SC-101..104). Grafana path left intact at this slice. - Declared
chartwidget kind inintegrations/prometheus.py(config:promql,window∈{1h,6h,24h,7d}). git mv GrafanaChartWidget.tsx → PrometheusChartWidget.tsx(recharts body preserved verbatim; empty-state copy updated);git mvof its test. Reboundchartgrafana→prometheus in both registries (SC-105..108).- Backend tests: new
test_prometheus_range.py(step + normalization); chart-adapter test intest_widgets.py. Frontend registry test updated.
Slice 2 — Gauge + mean widgets (commit 58be6e0, amended)
- Extracted shared
_instant_queryhelper; added_fetch_gauge(instant → scalar, multi-series →{error}) and_fetch_mean(range query over preset → client-side arithmetic mean of non-null values, scalar-only). - Declared
gauge(promql,warn_at/crit_at/min/max/unit) andmean(promql,window,unit) kinds inintegrations/prometheus.py. - Created
PrometheusGaugeWidget.tsx(rechartsRadialBarChart; green/amber/red threshold bands whenwarn_at+crit_atset; neutral single track otherwise) andPrometheusMeanWidget.tsx(MetricCard-style single value). Wired both into the frontendprometheusbinding + barrel. - Tests: backend adapter tests (scalar-only enforcement, mean aggregation incl. NaN-skip, error cases); frontend component tests (error + rendered, gauge with/without bands, mean with/without unit).
Slice 3 — Grafana removal + config + changelog (commit ba94317, amended)
- Deleted
integrations/grafana.py,GrafanaLinkWidget.tsx,service-tabs/LinksTab.tsx(+ test). RemovedGrafanaWidgetSource+ adapter registration;grafanafromSERVICE_DEFINITIONS/SERVICE_ADAPTERS(BE) andSERVICE_REGISTRY/BUILTIN_WIDGETS(FE);get_grafana_statusendpoint;useGrafanaStatus/fetchGrafanaStatus/GrafanaStatus; nav entry;service-tabs/index.tsgrafana case;Dashboard.tsxOBSERVABILITY_TYPESgrafana member;ServicesPage.tsxempty-state copy; grafana tests. - Rewrote
openspec/config.yaml: removed stale "Do NOT re-implement charting in-app" + "No recharts/d3" claims; states Manage renders Prometheus-backed metrics directly via recharts and that Grafana is no longer integrated. - Added
CHANGELOG.md[Unreleased]entry: BREAKING — Grafana service type removed; migrate by deleting grafana instances and recreating as Prometheus;grafana/chartwidgets →prometheus/chart. - Net: −920 lines across 27 files.
Coverage close — SC-125 loading-state tests
- Added one
it("renders skeleton while loading")case to each of the three Prometheus widget test files, asserting theSkeleton(data-slot="skeleton") renders under{ data: undefined, isLoading: true }. Closes the PARTIAL finding on SC-125.
Deviations from tasks.md
- None functional. The only textual drift is SC-118:
ObservabilityPage.tsxhad already been refactored intoservice-tabs/(the project map was stale). Removal targets adjusted to the real files (LinksTab.tsx,service-tabs/index.ts,navEntries.ts,Dashboard.tsx,ServicesPage.tsx); the spec was patched (SC-116/SC-118) to reflect this before apply. SC-118's intent (no Grafana UI surface) is fully satisfied.
Final gate results (re-run after coverage close)
| Gate | Result |
|---|---|
backend && PYTHONPATH=src python3 -m pytest -q |
293 passed, 2 warnings (pre-existing pythonjsonlogger DeprecationWarning) |
backend && PYTHONPATH=src python3 -m ruff check src tests |
All checks passed |
frontend && npm run build (tsc -b + vite build) |
exit 0 (pre-existing chunk-size warning) |
frontend && npm run lint |
0 errors, 1 pre-existing warning (WidgetConfigDialog.tsx:370, untouched) |
frontend && npx vitest run (3 Prom widget tests) |
14 passed (11 original + 3 new loading) |
Verification
See verify-report.md — adversarial fresh-context review: 26/27 fully PASS, 1 PARTIAL→PASS (SC-125 closed here). No blocking findings remain.