Skip to content

feat(tools/infrastructure): starter KPI views and a provisioned Grafana dashboard - #358

Merged
Minipada merged 1 commit into
jazzyfrom
feature/304-starter-kpi-views-and-a-provisioned-graf
Aug 18, 2026
Merged

feat(tools/infrastructure): starter KPI views and a provisioned Grafana dashboard#358
Minipada merged 1 commit into
jazzyfrom
feature/304-starter-kpi-views-and-a-provisioned-graf

Conversation

@Minipada

Copy link
Copy Markdown
Owner

Closes #304

DC produced Records but nothing that turned them into the numbers an operations team reports on. This establishes the whole path — Records already in PostgreSQL, SQL definitions over them in the repo, and a Grafana dashboard provisioned with the demo — using only data the uptime Measurement already emits. The point is the conventions, not the metric: where definitions live, how they reach a database, and how one is tested. Every later KPI adds to what this creates.

Availability, defined once

tools/infrastructure/sql/kpi_views.sql. Each uptime Record vouches for the time back to the previous one, capped at a grace period (dc_kpi_max_gap(), 30 s — the demos poll every 5 s) and clipped to the window. An unbroken heartbeat reports 100 %; only a silence longer than the grace period costs anything, so a 5-minute outage inside a 15-minute window leaves 630 of 900 seconds covered — 70 %.

Object Kind Reports
dc_kpi_uptime_samples view One row per uptime Record, plus each Record's predecessor
dc_kpi_availability(from, to [, max_gap]) function Per robot over an arbitrary window: samples, first/last, uptime, covered, availability
dc_kpi_availability_5m view The same metric in 5-minute buckets, for charting
dc_kpi_max_gap() function The grace period, in exactly one place

The window is a function argument rather than a view predicate: a plain view cannot aggregate over a window the caller chooses, since a WHERE on a column the GROUP BY consumed has nowhere to go. That is also what makes the panels honest — they pass $__timeFrom()/$__timeTo(), so the number always describes the range on screen. Metrics are computed in SQL, never on the robot, so changing a definition is re-applying one file rather than redeploying a fleet.

Known limit, documented: a robot whose Records stopped before the window starts drops out of the result rather than reporting 0 %. Nothing in the database tells it apart from a robot that was never deployed; that needs a fleet registry, which DC has none of.

Applied two ways

docker-compose.postgresql.yaml mounts the tables and the KPI file into the init directory as 10_init.sql/20_kpi_views.sql (name order matters — the views need the tables), so a fresh demo comes up with both. PostgreSQL runs that directory only on an empty data directory, so tools/infrastructure/scripts/apply_kpi_views.bash applies the same file to any host/port/user/database — psql when installed, a postgres container otherwise. Every object is CREATE OR REPLACE, so re-running it is how a changed definition ships.

Dashboard

config/grafana/dashboards/kpi.json (uid dc-kpi) joins Home and Robot in the existing file provider: availability, uptime, unreported time and Record count as stats over the dashboard's range, availability in 5-minute buckets, reported uptime per Record, and a per-robot table. Default range now-1h, refresh 30 s, so a demo that just started fills the view.

Docs

New KPI views page: what ships, how availability is defined and what it does not claim, what the robot's config has to fill for the views to see anything, and how to point the views at a real deployment (apply script, read-only GRANTs, datasource, and the one object that names the dc table). Linked from the PostgreSQL, Grafana and TB3 demo pages.

Verification

  • Fixture test (tools/infrastructure/test/test_kpi_views.py, 10 cases): seeds a known heartbeat and asserts what comes back — full availability for an unbroken sequence, 630/900 for a 5-minute silence, 100 % for a silence inside the grace period, three windows over the same Records, per-robot separation, latest uptime reading, Records outside the window, an empty window, and the bucketed view. It applies init.sql + kpi_views.sql into a schema of its own, so it also proves both files apply cleanly. ./tools/infrastructure/scripts/test_kpi_views.sh brings up a throwaway Postgres with Podman and runs it; a standalone kpi-views CI job runs that script.
  • The demo path, run for real: a Postgres container with the compose file's exact two mounts came up with all four objects in public; Grafana 9.5.2 with this repo's provisioning listed the KPI dashboard alongside Home and Robot with no import; and all seven panels' queries, macros included, were executed through Grafana's own /api/ds/query against seeded Records — every one returned data (13 buckets over the seeded hour, 721 sample points).
  • prek run --all-files --skip build-doc green; mdbook build clean. Link-check turned up an existing orphan — the Grafana infrastructure page was never in SUMMARY.md; it is now, and its "How to use" section no longer describes Adminer with an Adminer screenshot.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ET1SENRfb6KD9q6wCvxLiT

…na dashboard

DC produced Records but nothing that turned them into the numbers an operations
team reports on. Adds the whole path on data the uptime Measurement already
emits: SQL definitions in the repo, applied by the demo stack, and a Grafana
dashboard provisioned alongside it.

tools/infrastructure/sql/kpi_views.sql defines availability and uptime over an
arbitrary window — a Record vouches for the time back to the previous one, capped
at a grace period and clipped to the window, so only a silence longer than that
costs availability. The window is a function argument, which is what lets the
Grafana panels report the range on screen; dc_kpi_availability_5m is the same
metric pre-bucketed for charting. Metrics are computed in SQL, so a definition
change is re-applying one file rather than redeploying a fleet.

The postgres compose file applies the definitions at first init; apply_kpi_views.bash
applies them to any database, including one that already exists. A fixture test
seeds a known heartbeat and asserts what the definitions report over it, run
against a throwaway Postgres by test_kpi_views.sh and by a standalone CI job.

Closes #304

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ET1SENRfb6KD9q6wCvxLiT
Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.20%. Comparing base (0e72e09) to head (dc59bbb).

Additional details and impacted files
@@           Coverage Diff           @@
##            jazzy     #358   +/-   ##
=======================================
  Coverage   67.20%   67.20%           
=======================================
  Files          96       96           
  Lines        5981     5981           
=======================================
  Hits         4019     4019           
  Misses       1962     1962           
Flag Coverage Δ
cpp-jazzy 67.20% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Minipada
Minipada merged commit f4cad58 into jazzy Aug 18, 2026
10 checks passed
@Minipada
Minipada deleted the feature/304-starter-kpi-views-and-a-provisioned-graf branch August 18, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant