Found while fixing the plain-"number" encoding gap, and deliberately not folded
into it: different member shape, different fix, and a worse symptom.
What
A bare morph::math::Rational member makes a generated form never reach
ready. Its schema is an inline object of num / den / dp with no
x-decimalPlaces, so DynamicForm finds no typed control that claims it,
classifies it unrepresentable, and holds the form short of ready — with the
reason shown, so the user sees a form they cannot submit and no way to fix it.
That is a harder failure than the plain-"number" one it was found beside. A
wrong JSON type reaches the action boundary and is rejected there; an unready
form cannot be submitted at all.
It is already declared, in two rungs
examples/ledger/include/ledger/dto/budget_dto.hpp:45 SetBudgetLimit::limit
examples/ledger/include/ledger/dto/budget_dto.hpp:59 limit
examples/ledger/include/ledger/dto/budget_dto.hpp:60 spent
examples/crm/include/crm/dto/quote_dto.hpp:47 quantity
No shipped form is affected, and only by accident: ledger's QML is
hand-built with no MorphForms dependency, and crm has no GUI layer at all —
measured at zero QML and zero gui_lib lines. So the blast radius is zero
today for the same reason the plain-number gap's was: nobody has pointed the
generated renderer at these types yet.
It is encodable — this is a recognition rule, not a new encoder
x-decimalPlaces on the property hands it the exact-decimal control, and that
control's literal is precisely the num/den/dp shape the schema
describes. Measured in the new plain-number suite:
{"limit":{"num":350,"den":100,"dp":2}}
So nothing needs writing to encode a Rational; what is missing is the rule
that recognises the inline num/den/dp object as that control's input.
Verification status
The declarations are measured — grep over all eight rungs' action headers,
on 7a994c15. The schema shape and the working literal are measured, in the
suite added alongside the plain-number fix.
Not verified: that adding the recognition rule is sufficient rather than
merely necessary — nobody has driven a Rational-bearing action through a
generated form end to end, because no such form exists. That is the experiment
that would close this.
Not verified: whether Quantity-wrapped and x-decimalPlaces-tagged
numbers share enough of the path that one rule covers all three. The
plain-number work established that x-decimalPlaces and Quantity are one
shape rather than two — "type": ["object","null"] with ExtUnits — so a
third arm is plausible but unmeasured.
The trap, which is the mirror of the one next door
The plain-"number" gap must not be routed through the unrepresentable
path, because a bare number is representable and needs an encoder. This one is
the opposite: Rational is currently routed through the unrepresentable
path, correctly given what the renderer knows, and the fix is to teach it to
recognise the shape — not to add an encoder, and not to relax readiness.
Getting these two backwards in either direction produces a form that submits a
wrong payload while claiming to be ready, which is what both fixes exist to
prevent.
What would change the verdict
Close when an action declaring a bare morph::math::Rational renders a form
that reaches ready and submits {"num":…,"den":…,"dp":…}, proven by a case
that fails against today's renderer.
Close as invalid if bare Rational members are deliberately unsupported and
authors are meant to tag them — in which case schemaJson<A>() should say so at
compile time rather than emitting a schema the renderer classifies as
unrenderable, and the four declarations above need changing.
Found while fixing the plain-
"number"encoding gap, and deliberately not foldedinto it: different member shape, different fix, and a worse symptom.
What
A bare
morph::math::Rationalmember makes a generated form never reachready. Its schema is an inline object ofnum/den/dpwith nox-decimalPlaces, soDynamicFormfinds no typed control that claims it,classifies it unrepresentable, and holds the form short of
ready— with thereason shown, so the user sees a form they cannot submit and no way to fix it.
That is a harder failure than the plain-
"number"one it was found beside. Awrong JSON type reaches the action boundary and is rejected there; an unready
form cannot be submitted at all.
It is already declared, in two rungs
No shipped form is affected, and only by accident:
ledger's QML ishand-built with no
MorphFormsdependency, andcrmhas no GUI layer at all —measured at zero QML and zero
gui_liblines. So the blast radius is zerotoday for the same reason the plain-number gap's was: nobody has pointed the
generated renderer at these types yet.
It is encodable — this is a recognition rule, not a new encoder
x-decimalPlaceson the property hands it the exact-decimal control, and thatcontrol's literal is precisely the
num/den/dpshape the schemadescribes. Measured in the new plain-number suite:
So nothing needs writing to encode a
Rational; what is missing is the rulethat recognises the inline
num/den/dpobject as that control's input.Verification status
The declarations are measured — grep over all eight rungs' action headers,
on
7a994c15. The schema shape and the working literal are measured, in thesuite added alongside the plain-number fix.
Not verified: that adding the recognition rule is sufficient rather than
merely necessary — nobody has driven a
Rational-bearing action through agenerated form end to end, because no such form exists. That is the experiment
that would close this.
Not verified: whether
Quantity-wrapped andx-decimalPlaces-taggednumbers share enough of the path that one rule covers all three. The
plain-number work established that
x-decimalPlacesandQuantityare oneshape rather than two —
"type": ["object","null"]withExtUnits— so athird arm is plausible but unmeasured.
The trap, which is the mirror of the one next door
The plain-
"number"gap must not be routed through the unrepresentablepath, because a bare number is representable and needs an encoder. This one is
the opposite:
Rationalis currently routed through the unrepresentablepath, correctly given what the renderer knows, and the fix is to teach it to
recognise the shape — not to add an encoder, and not to relax readiness.
Getting these two backwards in either direction produces a form that submits a
wrong payload while claiming to be ready, which is what both fixes exist to
prevent.
What would change the verdict
Close when an action declaring a bare
morph::math::Rationalrenders a formthat reaches
readyand submits{"num":…,"den":…,"dp":…}, proven by a casethat fails against today's renderer.
Close as
invalidif bareRationalmembers are deliberately unsupported andauthors are meant to tag them — in which case
schemaJson<A>()should say so atcompile time rather than emitting a schema the renderer classifies as
unrenderable, and the four declarations above need changing.