Files
ruvnet--RuView/docs
ruv 74eb09f604 feat(adr-110): Prometheus exposition endpoint /api/v1/mesh/metrics
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>
2026-05-23 15:03:51 -04:00
..