Iter 36 — Grafana / Home Assistant Prometheus integration / Cognitum
Seed observability stack can now scrape mesh state directly with no
JSON-to-metric translation layer.
Endpoint: GET /api/v1/mesh/metrics → text/plain (Prometheus exposition
format v0.0.4). Eight gauges, one per NodeSyncSnapshot field, labeled
by node:
wifi_densepose_mesh_offset_us{node="N"} <signed-int>
wifi_densepose_mesh_is_leader{node="N"} 0|1
wifi_densepose_mesh_is_valid{node="N"} 0|1
wifi_densepose_mesh_smoothed{node="N"} 0|1
wifi_densepose_mesh_sequence{node="N"} <u32>
wifi_densepose_mesh_csi_fps{node="N"} <float>
wifi_densepose_mesh_csi_fps_samples{node="N"} <u32>
wifi_densepose_mesh_staleness_ms{node="N"} <u64>
Each metric carries the standard `# HELP` + `# TYPE` headers before
its series block, exactly the format Prometheus + most scrape-format
implementations expect.
Implementation reuses iter-30's `NodeState::sync_snapshot()` as the
single source of truth — same data the JSON endpoints emit, just
text-formatted with `{node=...}` labels. Nodes without a fresh sync
are absent (Prometheus handles missing series natively).
Test added (8/8 sync_snapshot_helper_tests now green):
bool_metric_returns_zero_or_one_as_text
Pins the Prometheus convention that boolean gauges emit "0" or "1"
literally, never "false"/"true" — if anyone refactors the helper
to format!("{b}"), Prometheus would 400-reject the scrape; this
test catches that drift before production.
User-guide REST table updated with the new endpoint.
Grafana / HA scrape config:
- job_name: wifi-densepose-mesh
scrape_interval: 5s
metrics_path: /api/v1/mesh/metrics
static_configs:
- targets: ['localhost:3000']
Co-Authored-By: claude-flow <ruv@ruv.net>