RationalSerializer
Decodes a Rational through Rational.of, so a value that arrived over the wire is indistinguishable from one the factory built (#1647, #1752).
Rational is deliberately not @Serializable (see its KDoc): the plugin-generated serializer would write straight into the private primary constructor, skipping the only path that reduces to lowest terms and forces a positive denominator. An unreduced 2/4 would then be structurally unequal to 1/2, and a negative denominator would invert Rational.compareTo — whose cross-multiplication assumes a positive denominator — so a corrupt or hostile peer could flip sibling ordering cluster-wide by encoding 1/2 as -1/-2. Where a Rational genuinely must be replicated the property names this serializer explicitly; Gauge.floor is the one such site today.
Repair where a canonical form exists; refuse only where none does — the same split WeightSerializer makes, and for the same reason, which applies verbatim here because a Gauge rides the wire inside the very same EntitlementLedger frames a Weight does. Both decode boundaries that carry this state — Quilter's delta dispatch and the heddle control plane's applyEntry — swallow a decode failure and drop the entire frame. Rejecting a merely denormalized encoding would therefore discard every legitimate record travelling with it, and anti-entropy would re-send the same frame forever: a silent, permanent convergence wedge between two peers over a difference with no semantic content. Sign-normalization and reduction have a unique correct answer, so they are applied — Rational.of does both.
A zero denominator names no rational at all. There is nothing to repair to, and substituting a default would fabricate a virtual-time claim out of hostile input — a fairness-relevant lie, since a gauge floor is what seats an edge. That throws SerializationException at the decode boundary, before any lattice state is touched; the frame is dropped and the merge loop survives.