Round 4 of Task 4 review. Four defects in FrameDecoder's rule 3:
- The undeclared-rate (hrt) path positioned each burst at an ABSOLUTE
hrt/ticksPerSecond(). hrt counts from the producer's boot, so it is ~1e11
ticks by the time a scope attaches, and the rate is refitted every packet
with a few parts in 1e4 of wobble. The product is tens of milliseconds of
jitter in BOTH directions -- not merely imprecise, non-monotonic. Integrate
short tick deltas into accProdSec instead and let ClockOffset latch the
epoch that leaves behind.
- The lead bleed used a fixed 0.9 factor, which converges only while the
declared rate is within ~10%. Squeeze proportionally to the excess instead
(floored at kMinBleedFactor), settling it in a single burst.
- A single-sample flush fell through to the plain-scalar rule, dating it from
arrival and leaving lastCounter stale so the next real burst reinstated a
hole that never existed. Accumulate mode flushes on a timer, so a short
cycle legitimately yields one sample; keep it on the chain.
- kMaxCounterGap was inert: an absurd gap yields an absurd prediction that the
arrival backstop already rejects, and no input can distinguish the two
rules. Removed rather than left implying a behaviour it did not have.
FrameDecoder.h now states the deliberate divergence from StreamHub -- which
converts hrt with the LOCAL MARTe timer frequency, valid only because it runs
on the producer's host -- and why a remote scope's drift is irreducible.
Three new tests, each sabotage-proven non-vacuous: producer restart, short
flushes staying on the chain, and a 20000-packet undeclared run after a
day of producer uptime that asserts SPACING as well as ordering (the
monotonic guard alone restores order while leaving positions wrong).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>