feat(tools/infrastructure): utilisation, intervention rate and MTBF/MTTR KPI views - #369
Merged
Minipada merged 1 commit intoAug 18, 2026
Conversation
…TTR KPI views #304 shipped availability and uptime through the whole path; this adds three more metrics on the same conventions and says plainly why the fourth is not here. - dc_kpi_utilisation(): moving time under a known driving mode over reported time, with the per-mode seconds broken out. No speed Record in the window makes the ratio NULL, not 0 % — unreported movement is not zero movement. - dc_kpi_intervention_rate(): takeovers per autonomous hour and per kilometre. Both denominators, because they fail differently; a denominator nothing reported gives NULL, never a rate over zero. Only an end Record carries a duration, so a takeover still running is counted and never timed. - dc_kpi_reliability(): MTBF and MTTR per component, over the fault Measurement's own durations. ERROR to STALE is neither a raise nor a clear, WARN is not a failure by default, and the failure levels are a query parameter. Mission success rate is deferred: #305 has not agreed what a Mission Measurement consumes, and a view written first would pin that contract from the reporting end. intervention (#362) and fault (#365) do not exist yet, so their views return no rows until they land — the fixture test seeds the Records directly, which is where a definition change has to break. Their columns are added to init.sql now because Vector's postgres sink drops keys with no column to land in. The demo enables driving_type off Nav2's /cmd_vel so the utilisation panels populate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dWQxJJEaDcsD5szRu2LEG Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## jazzy #369 +/- ##
=======================================
Coverage 67.55% 67.55%
=======================================
Files 98 98
Lines 6045 6045
=======================================
Hits 4083 4083
Misses 1962 1962
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
9 tasks
Minipada
deleted the
feature/363-extend-the-kpi-views-utilisation-interve
branch
September 2, 2026 12:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #363
#304 established where KPI definitions live, how they reach a database, how they are tested and
how they are charted, and shipped one metric through it. This finishes the starter set except
for the one whose contract nobody has agreed yet.
Utilisation
dc_kpi_driving_samplesnarrowsdctodriving_typeRecords, each carrying thespeedtherobot last reported at or before it — a lookup bounded by
dc_kpi_max_gap(), so a stale speedRecord vouches for one grace period and no longer.
dc_kpi_utilisation(from, to [, max_gap [, min_speed]])credits time exactly the waydc_kpi_availability()does and splits it four ways:autonomous_seconds,manual_seconds,teleop_seconds,unknown_seconds, which add up toreported_secondsso the ratio can alwaysbe checked against its parts. Productive is moving under a known mode —
manualandteleopcount, since a human driving the robot is still the robot being used.
What it deliberately does not claim:
utilisationNULL, not 0 %. A deployment collectingdriving_typebut notspeedhas unreported movement, not zero movement;speed_samplesisin the output so a NULL can be told from an empty range.
distinction needs the Mission Measurement.
Intervention rate
dc_kpi_intervention_eventsderivesis_start(leavingautonomousformanual/teleop) andis_end(returning to it) from the modes a Record names, so the shape of the metric lives inone place.
dc_kpi_intervention_rate()reports both denominators —per_autonomous_houroff
dc_kpi_utilisation()'sautonomous_seconds,per_kmoff summeddistance_traveled—because they fail differently: a robot parked all shift has no autonomous hours, a robot doing
tight manoeuvring has hours and few kilometres.
interventionsand absent frommean_intervention_secondsrather than a zero in it.MTBF and MTTR
dc_kpi_reliability(from, to [, failure_levels])averages the fault Records' ownprevious_duration: healthy time before a raise is MTBF, time in a failure level before a clearis MTTR.
failure_levelsdefaults toARRAY['ERROR','STALE']and is a query parameter, so adeployment treating a silent component as reportable-but-not-broken passes
ARRAY['ERROR'].ERROR → STALEis neither a raise nor a clear: a component that goes quiet while alreadybroken is still one fault.
WARNis not a failure by default, soOK → WARN → ERRORis one failure timed from the lasthealthy state.
failuresandopen_faultsand is left out ofmttr_seconds.components that nothing here knows, and averaging independent components reports a number for
a failure mode no component has.
Mission success rate is deliberately not in this PR
#305 — how far a nav2 adapter infers, what the escape-hatch message in
dc_interfaceslookslike, how a mission still running at shutdown is represented — is an open design decision that
says on its face it needs a human. A view written before it would pin the contract from the
reporting end, which is the wrong end.
doc/src/dc/kpi_views.mdhas a section saying so ratherthan leaving a gap to rediscover; that acceptance criterion is the one thing left open here.
The Records the views read
intervention(#362) andfault(#365) do not exist yet, so their views return no rows untilthose Measurements land. That is what the fixture test is for: it seeds the Records directly, so
a definition change breaks a test here rather than a dashboard later.
Both Measurements are
StateTransitionDetectorprojections and share a column vocabulary, addedto
init.sqlnow because Vector'spostgressink maps a Record's keys onto existing columnsand silently drops the rest:
previous_duration,sequence,open, plusmode,from_mode/to_modeandcomponent/from_level/to_level/reason.openis reported butnever load-bearing — correctness comes from only end/clear Records carrying a duration.
Demo and dashboard
driving_typeis enabled intb3_simulation_pgsql_minio.yamloff Nav2's/cmd_vel(the onlycommand source the simulation has, so the mode is
autonomouswhile Nav2 publishes andunknownotherwise) and routed to thepgsqlDestination, so the utilisation panels populateon the existing demo. Nine panels are added to the provisioned
dc-kpidashboard; theintervention and fault ones stay empty until #362/#365, for the same reason a battery panel
would.
Verification
tools/infrastructure/test/test_kpi_views.pygoes from 10 to 29 cases. New coverage: themissing-speed NULL, a speed Record older than the grace period, another robot's speed, an open
takeover, a missing denominator,
ERROR → STALE,WARN, a never-cleared fault, thefailure_levelsparameter, and each bucketed view../tools/infrastructure/scripts/test_kpi_views.sh→ 29 passed.expanded, not eyeballed.
prek run --all-files --skip build-docis green.🤖 Generated with Claude Code
https://claude.ai/code/session_018dWQxJJEaDcsD5szRu2LEG