proposal
KaaG persona dashboards — backend gap analysis
Section titled “KaaG persona dashboards — backend gap analysis”Scope. The three Atlas · RADR KaaG persona flows (VP Product, Product Manager, Technical
Specialist) against what rechaj-backend-api (core-api), hermes-java-api (radr-api),
vcu-consumer, radar-gateway, and rechaj-vehicle-controller (firmware) provide today.
Codebase inventory date: 12 Jul 2026.
1 · What the backends already provide (build on these)
Section titled “1 · What the backends already provide (build on these)”| Capability | Where | Notes |
|---|---|---|
| Fleet roster + per-unit detail | GET /api/v1/radr/roster, /api/v1/kaag/vcu-pins, /api/v1/kaag/vcu/{serial}/detail, /api/v1/asset/{serial} (radr-api) |
Detail already carries SOC/SOH/V/I/temp/firmwareVersion/signal_4g/mileage/last-seen/rider/cluster/plate/hasTelpoVcu |
| Telemetry history | SOC history ≤7 d (1-min cagg w/ raw fallback), route replay 1-min buckets | 30 d windows not reachable (168 h clamp); 1-h cagg exists in Timescale but is never queried |
| Live fleet state | vcu-consumer → Redis veh:{id} → radar-gateway WS (JWT, binary viewport frames) |
Fleet/viewport only — no per-device channel |
| Command API skeleton | POST /api/v1/vcu/{serial}/command — enum, confirm token CONFIRM-<CMD>-<serial>, idempotency, canCommand |
Disabled (radr.commands.enabled=false), publisher is a logging stub, idempotency in-memory, nothing persisted |
| Battery unlock | POST /api/v1/battery/unlock → real MQTT (Magnetic/lockType:1) |
The only working command path; core-api has VcuUnlockAuditLog |
| Identity & segmentation | Rider, Tricycle (plate/chassis), VCU.serial_number, riders_with_kaag + riders_with_telpo_vcu mirrors, hasKaag/hasTelpo flags on VCU lookup |
KaaG opt-in + preferred time on RiderOnboarding |
| Inspection (coarse) | PATCH/GET /api/v1/admin/onboarding/{id}/inspection, MEI module |
Status enum only AWAITING / IN_PROGRESS / COMPLETED |
| Revenue & wallets | SRRR jobs, RevenueDistribution, wallet ledger, Paystack webhooks |
Swap-oriented; no energy coupling |
| Immutable audit pattern | BatteryAuditEvent (append-only, compensating records, 7-yr retention, payload_json, actor, linked events) |
The template for a VCU event timeline |
| Notification senders | Termii SMS, Loop email, Firebase push | No rules engine or digest on top |
| RBAC scaffolding | RADR gated ADMIN/SUPER_ADMIN in core-api; RadrAccessPolicy (FLEET_VIEW / BOARD / COMMAND) in radr-api; Authority.CAN_MANAGE_VCU_FIRMWARES exists (unbacked) |
No VP/PM/TS AppRoles, no RADR App in mocx-contracts |
| Threshold evaluation | deriveStatus() in vcu-consumer: SOC≤20, batt≥55 °C, motor≥95 °C, faults, unauthorized-move flag |
Ephemeral flag bits only — 48 h Redis TTL, never persisted |
| Offline bands | VcuActivityStatus GREEN<10m…BLACK≥24h |
Matches the dashboards’ age chips exactly |
2 · The gap map — persona feature → backend status
Section titled “2 · The gap map — persona feature → backend status”Status: ✅ exists · 🟡 partial · ❌ missing. Epic column keys into §3.
Shared surfaces (all three personas)
Section titled “Shared surfaces (all three personas)”| Dashboard feature | Backend status | Epic |
|---|---|---|
| Fleet table (filters, pagination, 9 columns) | 🟡 roster exists, returns full unpaged lists; no carrier/SIM field; no uptime or fault columns | E11 |
| Last-seen age chips / honesty buckets | 🟡 bands exist per unit; no bucket-counts endpoint | E2 |
| Uptime 7d sparkbars / 30d % | ❌ no uptime computation anywhere | E2 |
| Fault chips MCU·VCU·BMS·DU | ❌ fault register parsed on-device but publish commented out (RUNBOOK §4.4); DU/display-link health not on the wire | E13 |
| 24 h connectivity timeline + disconnect stats | ❌ no LWT ingestion, no gap detection, offline = ping staleness only | E2 |
| Display & indicators (0x18F86590, SOC-push freshness, odometer) | 🟡 odometer on wire; vehicle-lights + SOC-push-freshness publish commented out | E13 |
| Per-unit event timeline (commands/policy/connectivity/firmware/lifecycle) | ❌ nothing queryable; [radar-command-audit] is a log line; station events exist but internal-only |
E6 |
| Live decoded signals (2 s tick) + view-only raw stream | ❌ gateway is viewport-fleet only; findLast10ByDeviceId unexposed; no per-device WS/SSE channel |
E10 |
Product Manager
Section titled “Product Manager”| Feature | Status | Epic |
|---|---|---|
| KAAG_ON/OFF/AUTO with reason, instant vs queued | 🟡 API skeleton exists; publisher stub, disabled, no ack/queue/expiry, nothing persisted | E1 |
| Command history / shared audit table | ❌ | E1+E6 |
| Safety exceptions (inverter-on-while-moving, low-SOC w/ inverter, SOH drop) | 🟡 flags computed in consumer, ephemeral; inverter-on-while-moving not derived (needs inv+speed rule) | E8 |
| Sessions & energy (session list, kWh, SOC delta, end reason, data quality) | ❌ no session detection, no kWh integration, V/I stored but never integrated | E3 |
| Adoption 7-day | ❌ needs “actively using” definition from sessions | E3+E5 |
| Inspection pipeline (read-only 4-state) | 🟡 coarse 3-state onboarding inspection; no PASSED / PENDING_ASSIGNMENT | E9 |
| Rider SMS on inverter cut | 🟡 SMS sender exists; no trigger wiring | E8 |
Technical Specialist
Section titled “Technical Specialist”| Feature | Status | Epic |
|---|---|---|
| Live raw MQTT console (per-unit tail, gap/backfill markers) | ❌ | E10 |
| Commissioning: bench gates, enforcement, ready pool, install queue | ❌ no provisioning workflow; InverterAuthController is a hardcoded stub; SIM ICCID/IMEI never captured |
E9 |
| Diagnostics case queue (incl. unauthorized-move, thresholds) | 🟡 flags ephemeral; no case persistence | E8 |
| Firmware releases, waves, adoption, per-version coverage drill | ❌ no OTA server exists at all — the device’s hourly OTA check hits nothing; fvr per ping is the only building block; CAN_MANAGE_VCU_FIRMWARES authority is unbacked |
E7 |
| Full command set incl. IMMOBILIZE (confirm token), SENT/QUEUED/expired/suppressed | 🟡 enum + token + (in-memory) idempotency exist; everything downstream missing | E1 |
| Command audit page ([radar-command-audit] as queryable data) | ❌ log line only | E1+E6 |
| First-heartbeat validation (LWT cleared) | ❌ no LWT anywhere: firmware sets it, consumer never subscribes will/# |
E2+E13 |
VP Product
Section titled “VP Product”| Feature | Status | Epic |
|---|---|---|
| Adoption vs threshold, programme trajectory | ❌ | E3+E5 |
| kWh + ₦ @ tariff, nightly curve, ARPU, collections, idle-kit revenue at risk | ❌ no tariff, no energy billing, no ARPU anywhere in core-api | E3+E4 |
| Fleet health: 90 d uptime trend w/ incidents, carrier comparison, FW spread | ❌ uptime engine; carrier not captured; FW spread derivable from fvr (needs endpoint) |
E2, E11, E7 |
| Rollout vs 140 target, deployment readiness | 🟡 deployment counts derivable from mirrors + telemetry; no target/campaign config | E5+E9 |
| Management report (period narratives, WoW, DFI impact, thresholds, risks) | ❌ no report compiler, no PDF/CSV infra at all, no digest job | E5+E14 |
| Executive notifications (digest, adoption-below, offline>10%) | ❌ senders exist; no rules/digest engine | E5+E8 |
3 · Backend epics to implement
Section titled “3 · Backend epics to implement”Ordered by phase. Owner repos in brackets. Details assume the repos’ package-by-feature conventions and the two-datasource boundary (primary vs Timescale) in hermes-java-api.
Phase 0 — enablers (unblock everything else)
Section titled “Phase 0 — enablers (unblock everything else)”- E12 · RADR personas RBAC [mocx-contracts + core-api + radr-api]: add a RADR App and
three AppRoles (VP read-only → BOARD; PM → FLEET_VIEW + a limited COMMAND scope of
KAAG_ON/KAAG_OFF/AUTO; TS → full COMMAND + FIRMWARE backed by
CAN_MANAGE_VCU_FIRMWARES). ExtendRadrAccessPolicywith per-command-type scoping. Lift the admins-only RADR gate. - E13 · Firmware-side prerequisites [rechaj-vehicle-controller]: (a) uncomment the fault
register + MOSFET/motor temp publish (RUNBOOK §4.4 — columns and parser already exist);
(b) publish vehicle-lights frame states +
last_soc_send_timefor display-link health; (c) resolve thepub/vsvcu/pub/topic-namespace mismatch; (d) emit a command-ack frame. All four are small device changes that unblock fault chips, DU health, ack tracking, and clean ingestion. - E1 · Real command execution + durable audit [radr-api]: replace
LoggingVcuCommandPublisherwith a real MQTT publisher onsub/<device_id>; persist avcu_commandtable (status SENT/QUEUED/ACKED/EXPIRED/SUPPRESSED, actor, reason, confirm token, idempotency key — durable, not in-memory); ack consumption (from E13’s ack frame or next-frame state confirmation); queued-replay on reconnect + 72 h expiry job; command history endpoints (GET /api/v1/vcu/{serial}/commands, global list w/ filters); flipradr.commands.enabledafter the Stage-6 bench review. - E11 · Fleet-table API hardening [radr-api]: pagination/filter/sort on the roster; add uptime + fault-chip + carrier fields to the row DTO (carrier requires E9’s SIM capture or a new payload field); a bucket-counts endpoint for the honesty strip.
Phase 1 — visibility (make the telemetry surfaces real)
Section titled “Phase 1 — visibility (make the telemetry surfaces real)”- E2 · Connectivity & uptime engine [vcu-consumer + radr-api]: subscribe
will/#and persist connect/disconnect events; gap-detection job sessionizing pings into online/idle/offline segments; per-unit uptime 7 d/30 d + fleet daily uptime rollup (new cagg or table); endpoints for connectivity timeline, disconnect stats, uptime league, fleet trend (with incident annotation records). Also unlocks TS first-heartbeat validation. - E10 · Per-unit streams [radar-gateway + radr-api]: per-device WS/SSE channel — decoded
frames for PM/VP (view-only), raw JSON tail for TS (RBAC-scoped); expose a last-N-frames
REST tail (extend
findLast10ByDeviceId); backfill/gap markers derived from E2 events. - E8 · Alerts persistence & safety exceptions [vcu-consumer + radr-api + core-api]:
persist flag transitions into an
alerttable (low-SOC-with-inverter, inverter-on-while-moving =inv==ON && speed>threshold, batt-temp, unauthorized-move, SOH-drop); ack/resolve endpoints; wire rider SMS on inverter safety cut via Termii. - E6 · Unified VCU event timeline [radr-api, pattern from core-api’s
BatteryAuditEvent]: append-onlyvcu_eventstore fed by E1 (commands), E2 (connectivity), E7 (OTA), E8 (alerts), lifecycle (assignment/commissioning), device policy events (SoC-floor cut, night schedule — inferable from inv transitions until the device emits explicit events).GET /api/v1/vcu/{serial}/events?category=&page=.
Phase 2 — the KaaG business layer
Section titled “Phase 2 — the KaaG business layer”- E3 · Sessions & energy [vcu-consumer ingest + radr-api reads] — REVISED 2026-07-13:
Telpo firmware (VCU v1.6.x) now EMITS sessions; no derivation needed. The device publishes
{"cmd":"inverter_session"}frames on the telemetry topic: START (inv_sidper-device counter,inv_t0ISO-Z,inv_ah_start), periodic DURING (inv_durs,inv_kw,inv_ah,inv_kwh,inv_ah_bms,inv_v_avg,inv_kwh_bms), END (+inv_tN,inv_end_reason— seen:manual,vehicle_moving—inv_ah_end,inv_dq). Work: consumer upserts frames into akaag_sessionstable keyed (device_id, inv_sid, started_at) with missed-start tolerance (derivestarted_at = arrival − inv_dur); the BMS-sourced duplicates power the dual-method energy-divergence check; radr-api read endpoints (per-unit session list, daily unit/fleet kWh aggregates, nightly curve once Telpo adds bms_ah+v_avg to DURING frames — requested). Note env-broker split (DEV/UAT/PROD) and per-env firmware. - E4 · Energy commercial layer [core-api]: tariff config entity (₦/kWh, effective-dated); billable computation from E3 sessions; collections linkage to the wallet ledger; ARPU + idle-kit-revenue-at-risk aggregates; revenue endpoints for the VP commercial table.
- E5 · Adoption & programme metrics [radr-api + core-api]: “actively using” = sessions
within N days; adoption % vs configurable threshold; deployment-vs-target (140) campaign
config; weekly digest compiler + threshold notifications (adoption-below, offline-share
10%) using the existing email/push senders.
Phase 3 — operational depth
Section titled “Phase 3 — operational depth”- E7 · Firmware & OTA management [new service or radr-api + a device-facing endpoint]:
release registry (version, signed artifact, delta size, notes, status); the device-check
endpoint the firmware’s hourly OTA poll can finally hit; staged wave campaigns w/
server-evaluated guardrail preconditions (parked, SOC≥40, RSSI floor, not-in-session);
chunked resumable delivery; A/B outcome tracking (boot-check pass/rollback from
fvrconfirmation); coverage endpoints (on-version / not-on-version lists with per-unit reasons) — the dashboards’ drill-down maps 1:1 onto this. - E9 · Commissioning & provisioning [core-api]: device registry (VCU serial ↔ NVS/MAC id,
SIM ICCID/IMEI capture — new fields, kit/inverter serial registry replacing the
hardcoded
InverterAuthControllerstub); bench-gate state machine with ordered-gate enforcement + refusal reasons; ready-to-install pool with the pull-only constraint; 4-state inspection pipeline (awaiting / in-progress / passed / pending-assignment); install assignment writing the rider↔device custody history that’s currently missing. - E14 · Exports & reports [core-api or radr-api]: CSV/PDF generation infra (none exists); the VP management-report compiler (period narratives, WoW deltas, DFI impact constants 0.66 L/kWh + 1.53 kgCO₂e/kWh as auditable config); scheduled digest email job.
4 · Suggested sequencing rationale
Section titled “4 · Suggested sequencing rationale”Phase 0 is small and unblocks demos of the highest-value loops (a PM sending a real KAAG_OFF that lands, acks, and appears in an audit trail). Phase 1 makes every table and timeline in the dashboards truthful instead of mocked. Phase 2 is where KaaG becomes a measurable business (sessions → kWh → ₦ → adoption → the VP report). Phase 3 rounds out the operational lifecycle the TS persona models. The single highest-leverage item in the whole map is E13(a) — one uncommented firmware publish block that the RUNBOOK has been pointing at since Stage 4.4: it turns the fault-chip and diagnostics surfaces from derived guesses into wire truth.