Three review finds, all in the runtime-toggle path or edge math (the
steady-state path was confirmed correct):
1. GPU stale-slot across toggle-off/on: the disabled branch now
Disposes the GpuFrameTimer (and nulls it) instead of Stop()-ing and
keeping it — a kept instance would poll slots left pending from
BEFORE the pause on re-enable and report temporally stale GPU
samples. The re-enable branch's existing null guard rebuilds the
ring fresh. Dispose is safe there: the branch runs at the top of
OnRender with the GL context current.
2. Stale stage accumulation across mid-stage toggle-off: a StageScope
disposed after the flag flips still calls EndStage, leaving a
partial delta in _stageAccumTicks. The re-baseline branch now
Array.Clear()s it alongside the GC re-baselining.
3. FrameStatsBuffer.Max() seeded with 0, clamping all-negative windows
(the alloc channel can go negative if the boundary ever crosses
threads) and disagreeing with Percentile(). Now seeds from the
first live sample after the empty check (slots [0.._count) are
always the live window regardless of ring wraparound); empty still
returns 0 to match Percentile. TDD: Max_AllNegative_ReturnsTrueMax
failed (returned 0) before the fix, passes after.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>