pub fn build_phase_buckets(samples: &SampleSeries) -> Vec<PhaseBucket>Expand description
Build per-phase metric buckets from a sample series.
Walks crate::scenario::sample::SampleSeries::by_stamped_phase
to group every stamped sample under its bridge-stamped
step_index (NOT re-derived from elapsed-ms windows; the
bridge stamp is authoritative because the capture path knows
the phase it fired from while the time window cannot recover
the phase when stimulus events arrive late or out of order).
For each phase observed (BASELINE under step_index = 0,
scenario Steps under step_index = 1..=N per the 1-indexed
phase convention) emits one PhaseBucket with step_index
as the key, label derived per the BASELINE/Step[k]
convention, start_ms / end_ms from the first / last
sample’s elapsed_ms, sample_count from the bucketed
samples, and metrics from the per-kind reduction described
on PhaseBucket. Metrics whose per-sample reading returns
None for every sample in the bucket are omitted entirely
(absent → “no data”) rather than collapsed to Some(0.0)
(real zero), preserving the sentinel-free contract.
Returns an empty Vec when the input series is empty (no
samples captured), distinct from returning a single empty
BASELINE bucket — the former means the periodic-capture path
never fired, the latter means it fired but no metric reading
came back.
No live production caller: evaluate_vm_result in
src/test_support/eval/mod.rs builds its phase buckets through the
stimulus-aware sibling build_phase_buckets_with_stimulus (via
precompute_early_series), and AssertResult.stats.phases is
populated from those stimulus buckets. This plain variant is
reachable only by full path, for the rare no-stimulus-timeline
case. Exposed pub (not pub(crate))
so out-of-tree consumers — payload authors writing custom
eval paths against the publicly-drainable
result.snapshot_bridge — can produce the same per-phase
aggregate shape without re-implementing the bucketing logic.