Summary
Form generation is documented as flat-only. From docs/spec/forms/forms.md:
A member that is itself an aggregate is emitted by glaze into $defs and referenced by
$ref; the generator does not recurse into that definition, so its sub-members receive
none of the x-* annotations and are not part of any synthesised required
array … Actions meant to drive a generated form must therefore be flat … Nesting is
not a documented form-generation path.
That is a clear and honest boundary. This issue is about what it costs, and whether
recursing is worth doing.
Where it bites
Domains where a form maps onto a measurement or a document tend to be naturally nested,
and the nesting is not incidental — it is the shape of the data:
struct Measurement {
struct Specimen { // repeated sub-record
double massDry = 0.0;
double massWet = 0.0;
};
struct Record {
static constexpr std::string_view Name = "Record";
struct Payload {
std::string operatorName; // flat -> renders
double temperature = 0.0; // flat -> renders
Specimen reference; // nested -> $ref, no annotations
std::vector<Specimen> replicates; // nested -> $ref, no annotations
};
struct Result { bool ok = false; };
using ReturnType = Result;
Payload payload;
};
};
operatorName and temperature render. reference and replicates land in $defs
with no x-* and no required, so the generated form is silently incomplete — the
fields are simply absent rather than visibly broken.
The workarounds both have real costs:
- Flatten the action —
referenceMassDry, referenceMassWet, … The action type stops
reflecting the domain, and repeated sub-records (std::vector<Specimen>) cannot be
flattened at all without a fixed maximum count.
- Hand-write the form — then the schema-driven generator is unused for exactly the
screens that would benefit most from it.
What recursing would need
Sketching the questions rather than a design:
- Annotation reach.
mergeSchemaExtras patches dom["properties"][name] for each
top-level member. Recursing means walking into the $def a $ref points at and
patching there — and deciding how x-order composes across levels.
required inside $defs. A nested definition currently gets no required array.
Synthesising one needs the same isStdOptional rule applied one level down.
- Repeated aggregates.
std::vector<Sub> is the interesting case: the renderer needs
add/remove affordances and per-row indices, which flat forms never required.
- Depth limit. Probably worth capping at one or two levels rather than arbitrary
recursion, to keep generated forms comprehensible.
- Opt-in. Existing flat actions must render identically; recursion could be gated by
an annotation on the nesting member.
Filing this to see whether recursion is considered in scope for the generator, or whether
flat-only is a deliberate long-term boundary. Either answer is useful — if it is
deliberate, saying so explicitly in the spec would help people decide sooner whether to
adopt the generator at all.
Summary
Form generation is documented as flat-only. From
docs/spec/forms/forms.md:That is a clear and honest boundary. This issue is about what it costs, and whether
recursing is worth doing.
Where it bites
Domains where a form maps onto a measurement or a document tend to be naturally nested,
and the nesting is not incidental — it is the shape of the data:
operatorNameandtemperaturerender.referenceandreplicatesland in$defswith no
x-*and norequired, so the generated form is silently incomplete — thefields are simply absent rather than visibly broken.
The workarounds both have real costs:
referenceMassDry,referenceMassWet, … The action type stopsreflecting the domain, and repeated sub-records (
std::vector<Specimen>) cannot beflattened at all without a fixed maximum count.
screens that would benefit most from it.
What recursing would need
Sketching the questions rather than a design:
mergeSchemaExtraspatchesdom["properties"][name]for eachtop-level member. Recursing means walking into the
$defa$refpoints at andpatching there — and deciding how
x-ordercomposes across levels.requiredinside$defs. A nested definition currently gets norequiredarray.Synthesising one needs the same
isStdOptionalrule applied one level down.std::vector<Sub>is the interesting case: the renderer needsadd/remove affordances and per-row indices, which flat forms never required.
recursion, to keep generated forms comprehensible.
an annotation on the nesting member.
Filing this to see whether recursion is considered in scope for the generator, or whether
flat-only is a deliberate long-term boundary. Either answer is useful — if it is
deliberate, saying so explicitly in the spec would help people decide sooner whether to
adopt the generator at all.