Skip to content

run_summary.hpp

RunSummary — the end-of-run one-screen summary, as DATA.

This header declares 1 type (22 members).

Extracted from include/shulib/diag/run_summary.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.

Contents

struct RunSummary

struct RunSummary

The end-of-run summary as DATA, never as an assembled essay: structured fields that one producer fills and any number of renderers format — the boxed terminal block, an appended blackbox frame, a wire message. A VALUE TYPE that owns its provenance strings in bounded in-struct arrays and allocates nothing, so a sink may RETAIN a copy without holding a dangling view into some caller's stack. Assembled once per run and delivered through hal::ITelemetrySink::summarize().

struct, declared at include/shulib/diag/run_summary.hpp:45.

RunSummary::motionsStarted

int motionsStarted = 0

Motions the scheduler handed a start(). It EXCEEDS the four outcome counts below whenever a motion was still running when the summary was taken — they partition the FINISHED motions only, so started minus their sum is what was still in flight.

field, declared at include/shulib/diag/run_summary.hpp:50.

RunSummary::motionsSettled

int motionsSettled = 0

Exited inside its tolerances — the only outcome that means success

field, declared at include/shulib/diag/run_summary.hpp:51.

RunSummary::motionsTimedOut

int motionsTimedOut = 0

Exited on the watchdog; each one also raised MOTION_TIMEOUT

field, declared at include/shulib/diag/run_summary.hpp:52.

RunSummary::motionsCancelled

int motionsCancelled = 0

user/pre-empt cancels (no causal fault)

field, declared at include/shulib/diag/run_summary.hpp:53.

RunSummary::motionsAborted

int motionsAborted = 0

fault-policy / task-boundary aborts

field, declared at include/shulib/diag/run_summary.hpp:54.

RunSummary::hasHeadingData

bool hasHeadingData = false

False when no motion produced heading data (record stream off, or nothing ran) — renderers show "n/a", never a fabricated 0.0 (a 0.0° claim with no data behind it is exactly the lying-number failure C5's brief bans).

field, declared at include/shulib/diag/run_summary.hpp:60.

RunSummary::headingMax

units::AngleDim headingMax{}

worst per-motion final |heading error| (radians)

field, declared at include/shulib/diag/run_summary.hpp:61.

RunSummary::headingFinal

units::AngleDim headingFinal{}

the LAST motion's final |heading error| (radians)

field, declared at include/shulib/diag/run_summary.hpp:62.

RunSummary::gatingRejects

int gatingRejects = 0

GPS_GATE_REJECT episodes (FaultLatch tally)

field, declared at include/shulib/diag/run_summary.hpp:65.

RunSummary::brownout

bool brownout = false

HealthMonitor::brownedOut() — latched, E1 semantics

field, declared at include/shulib/diag/run_summary.hpp:66.

RunSummary::worstLoopDt

units::Time worstLoopDt{}

LoopMonitor::worstDt()

field, declared at include/shulib/diag/run_summary.hpp:67.

RunSummary::firstFault

FaultCode firstFault = FaultCode::None

the ROOT CAUSE (FaultLatch first-fault)

field, declared at include/shulib/diag/run_summary.hpp:68.

RunSummary::firstFaultTime

units::Time firstFaultTime{}

when it latched (0 if none)

field, declared at include/shulib/diag/run_summary.hpp:69.

RunSummary::droppedRecords

std::uint32_t droppedRecords = 0

RateLimitedSink::droppedRecords()

field, declared at include/shulib/diag/run_summary.hpp:72.

RunSummary::droppedLines

std::uint32_t droppedLines = 0

RateLimitedSink::droppedLines()

field, declared at include/shulib/diag/run_summary.hpp:73.

RunSummary::blackboxDropped

std::uint32_t blackboxDropped = 0

Frames the E1 blackbox (diag::SdSink) dropped because its RAM byte budget was exhausted, or because a device write failed. A SEPARATE counter from the two above on purpose: those are rate-limiter drops on the terminal channel, and merging two different failures into one number is how a diagnostic starts lying. 0 also means "no blackbox was attached", which is why renderers show this one only when it is non-zero (TermSink's summarize note). — E1

field, declared at include/shulib/diag/run_summary.hpp:80.

RunSummary::batteryStart

units::Voltage batteryStart{}

Pack volts READ at session start, never caller-typed: a typed 12.6 that was really 11.9 is exactly the lying number this record exists to avoid.

field, declared at include/shulib/diag/run_summary.hpp:85.

RunSummary::batteryEnd

units::Voltage batteryEnd{}

Pack volts read when the summary was assembled; with batteryStart, the run's sag. Both are 0 V on a summary nobody filled in — there is no "unset" sentinel here.

field, declared at include/shulib/diag/run_summary.hpp:88.

RunSummary::setBuildHash

void setBuildHash(std::string_view hash) noexcept

Empty ⇒ MISSING (rendered loudly; header note). 47 bytes admits a full 40-char git SHA plus a "-dirty" suffix.

function, declared at include/shulib/diag/run_summary.hpp:92.

RunSummary::setRoutineId

void setRoutineId(std::string_view id) noexcept

Copy the auton routine's name (e.g. "redLeftTall") in, TRUNCATED at 31 characters. Empty is ordinary here — only buildHash treats empty as the loud MISSING case.

function, declared at include/shulib/diag/run_summary.hpp:95.

RunSummary::buildHash

[[nodiscard]] std::string_view buildHash() const noexcept

The stored hash; EMPTY means the build system provided none, which renderers must print as MISSING rather than anything plausible-looking. LIFETIME: the view points into THIS object — it dies with the summary, the next setBuildHash() invalidates it, and a copied summary hands back views into the COPY. That is the whole reason this is a value type rather than a struct of string_views.

function, declared at include/shulib/diag/run_summary.hpp:102.

RunSummary::routineId

[[nodiscard]] std::string_view routineId() const noexcept

The stored routine name; empty if never set. Same lifetime rule as buildHash(): the view is into this object, never into what the caller passed setRoutineId().

function, declared at include/shulib/diag/run_summary.hpp:106.

Design commentary, from the header

The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.

The header’s own reasoning — 27 lines
 RunSummary — the end-of-run one-screen summary, as DATA (master plan §18.3; WS13,
 chunk C5). The §18.3 block is one of the two ideas worth salvaging from the legacy
 "logging extreme" code, re-expressed the §18 way: STRUCTURED FIELDS the sinks
 format, never an essay assembled in a motion loop.

 Why a struct on the sink seam (hal::ITelemetrySink::summarize) rather than text
 pushed through log(): the summary is a RECORD with one producer and many possible
 renderings — TermSink draws the boxed block, E1's SdSink will append it to the
 blackbox, H1's wire can carry it to VexBuilder. Formatting it before the seam
 would freeze the terminal rendering as the only consumer (§18.1 "one record, many
 sinks" applies to every record type, not just the per-tick one).

 VALUE TYPE, deliberately: the provenance strings live in bounded in-struct arrays
 (set via setBuildHash/setRoutineId), not string_views — a sink that RETAINS a
 summary (FakeTelemetrySink, the E1 blackbox) must not be handed dangling views
 into some caller's stack. Nothing here allocates.

 Content mapping, stated honestly (§18.3's sketch shows "scored 6 pin · 1 cup"):
 game-object scoring belongs to the strategy layer (the G-phase command registry —
 the library cannot know what a motion scored). The M2-honest equivalent is the
 MOTION LEDGER (started/settled/timeout/cancelled/aborted); when G2's registry
 exists, scoring lands as an ADDITIVE field, not a reshape.

 The "MISSING" contract (§18.5): an EMPTY buildHash means the build system did not
 provide one. Renderers must say MISSING, loudly — never invent a plausible value.
 A wrong hash is worse than an absent one.

Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.