Summary
No pre-decode validation seam exists for morph::math::Rational — Rational::setWire (the glaze wire-codec entry point) silently clamps hostile wire input to a plausible value instead of rejecting the decode. A wire payload like {"num":5,"den":0,"dp":2} decodes into a plausible 5/1 rather than failing, and every dispatch path decodes before any model-level validate() runs, so an application has no seam to catch a clamped value as clamped — it only ever sees an already-plausible Rational.
Evidence
include/morph/util/rational.hpp's codec: Rational::setWire rebuilds through the canonicalising constructor, which silently clamps den == 0 to 1 (among other clamps: out-of-range dp, INT64_MIN components whose negation would overflow) instead of asserting or rejecting.
Confirmed via a real test (examples/ledger/tests/test_ledger_model.cpp, rung 5 / ledger): decoding {"num":5,"den":0,"dp":2} through glz::read_json into a Rational succeeds silently, producing 5/1. A ledger-shaped application's own zero-sum business invariant happens to catch most clamped legs incidentally (a clamped value is unlikely to still sum to zero) — but that's coincidental protection from a business rule, not a validation guarantee the framework provides. An application with a less-convenient invariant (or none at all) would have no way to know the value it received was ever clamped.
Suggested direction
A pre-decode validation hook — reject rather than clamp by default, or at minimum a decode-time flag surfacing "this value was clamped" that a caller can check — would close the gap for any application whose own invariants don't happen to catch it incidentally.
Reference
Filed from LASTRADA-Software/morph, branch ladder-ledger-rung5 (not yet merged), as docs/findings/002-rational-no-predecode-validation-seam.md (see that file for the same summary in the ladder's own findings-pipeline format, examples/FINDINGS.md) and the "clamped Rational leg" test in examples/ledger/tests/test_ledger_model.cpp for the reproducing test.
Summary
No pre-decode validation seam exists for
morph::math::Rational—Rational::setWire(the glaze wire-codec entry point) silently clamps hostile wire input to a plausible value instead of rejecting the decode. A wire payload like{"num":5,"den":0,"dp":2}decodes into a plausible5/1rather than failing, and every dispatch path decodes before any model-levelvalidate()runs, so an application has no seam to catch a clamped value as clamped — it only ever sees an already-plausibleRational.Evidence
include/morph/util/rational.hpp's codec:Rational::setWirerebuilds through the canonicalising constructor, which silently clampsden == 0to1(among other clamps: out-of-rangedp,INT64_MINcomponents whose negation would overflow) instead of asserting or rejecting.Confirmed via a real test (
examples/ledger/tests/test_ledger_model.cpp, rung 5 /ledger): decoding{"num":5,"den":0,"dp":2}throughglz::read_jsoninto aRationalsucceeds silently, producing5/1. A ledger-shaped application's own zero-sum business invariant happens to catch most clamped legs incidentally (a clamped value is unlikely to still sum to zero) — but that's coincidental protection from a business rule, not a validation guarantee the framework provides. An application with a less-convenient invariant (or none at all) would have no way to know the value it received was ever clamped.Suggested direction
A pre-decode validation hook — reject rather than clamp by default, or at minimum a decode-time flag surfacing "this value was clamped" that a caller can check — would close the gap for any application whose own invariants don't happen to catch it incidentally.
Reference
Filed from
LASTRADA-Software/morph, branchladder-ledger-rung5(not yet merged), asdocs/findings/002-rational-no-predecode-validation-seam.md(see that file for the same summary in the ladder's own findings-pipeline format,examples/FINDINGS.md) and the "clamped Rational leg" test inexamples/ledger/tests/test_ledger_model.cppfor the reproducing test.