Found while fixing objectui#7299 (dev seat, enumeration axis: who else compiles a parent-relationship condition). Filed separately rather than folded in, because it is a different block, a different authoring key and a different code path — objectui#7299's ruling scopes it to RelatedList.
The measurement, on origin/main
record:reference_rail takes an author-supplied relationshipField per entry and compiles it as bare equality — twice, in two different grammars:
packages/plugin-detail/src/renderers/record-reference-rail.tsx:187
$filter: { [entry.relationshipField]: parentId },
packages/plugin-detail/src/renderers/record-reference-rail.tsx:285
to={`/apps/${appName}/${entry.objectName}?filter%5B${entry.relationshipField}%5D=...`}
That is objectui#7299's defect, on another block. When the named field is multiple: true — Field.user({ multiple: true }) is the platform's own shape — the stored value is a JSON-valued array, = compares the whole serialized array against one id, and driver-sql refuses with 400 INVALID_FILTER naming the field and prescribing $contains. The author is naming a relationship; nothing on the authoring surface lets them say "membership".
Positive control for the negative result: git grep '\$contains' packages/plugin-detail/src/renderers/record-reference-rail.tsx → 0 hits, with relationshipField at 4 hits in the same read. The absence is measured, not a probe failure.
The second spelling is the harder half
:187 is fixable the way objectui#7299 fixed RelatedList: read the child object's declared arity and compile { [field]: { $contains: parentId } }.
:285 is NOT. It builds a link into the ADR-0055 bare data surface, whose one operator contract is drillUrlFilters' URL_FILTER_OPS (objectui#1752) — gte / lte / gt / lt and nothing else — and whose parser DROPS an unrecognised operator suffix rather than downgrading it. So filter[<field>][contains]= would arrive as no condition at all, i.e. the "View all" link would open the child object UNSCOPED: every row of the table, presented as this parent's related records. That is strictly worse than the 400 and it is why this needs a decision rather than a patch — the same reasoning objectui#7299 recorded for RelatedList's raw-URL fallback, where the answer was to refuse loudly instead.
So this card carries a real question: does the URL filter grammar grow a membership operator (a change to URL_FILTER_OPS, both sides, plus its pin), or does the rail suppress / re-target "View all" for a multi-value relationship?
Deliberately NOT claimed here
packages/plugin-form/src/LineItemsPanel.tsx:200 and packages/plugin-form/src/MasterDetailForm.tsx:762 compile the same bare-equality parent scope. They are NOT asserted as defects on this card: their relationship is a master_detail FK, and whether that field type can legally carry multiple: true was not verified. Whoever takes this card should answer that first — if it can, they belong here; if it cannot, they are correct as written and the sweep ends at the rail.
Generated by Claude Code
Found while fixing objectui#7299 (dev seat, enumeration axis: who else compiles a parent-relationship condition). Filed separately rather than folded in, because it is a different block, a different authoring key and a different code path — objectui#7299's ruling scopes it to
RelatedList.The measurement, on
origin/mainrecord:reference_railtakes an author-suppliedrelationshipFieldper entry and compiles it as bare equality — twice, in two different grammars:That is objectui#7299's defect, on another block. When the named field is
multiple: true—Field.user({ multiple: true })is the platform's own shape — the stored value is a JSON-valued array,=compares the whole serialized array against one id, anddriver-sqlrefuses with400 INVALID_FILTERnaming the field and prescribing$contains. The author is naming a relationship; nothing on the authoring surface lets them say "membership".Positive control for the negative result:
git grep '\$contains' packages/plugin-detail/src/renderers/record-reference-rail.tsx→ 0 hits, withrelationshipFieldat 4 hits in the same read. The absence is measured, not a probe failure.The second spelling is the harder half
:187is fixable the way objectui#7299 fixedRelatedList: read the child object's declared arity and compile{ [field]: { $contains: parentId } }.:285is NOT. It builds a link into the ADR-0055 bare data surface, whose one operator contract isdrillUrlFilters'URL_FILTER_OPS(objectui#1752) —gte/lte/gt/ltand nothing else — and whose parser DROPS an unrecognised operator suffix rather than downgrading it. Sofilter[<field>][contains]=would arrive as no condition at all, i.e. the "View all" link would open the child object UNSCOPED: every row of the table, presented as this parent's related records. That is strictly worse than the 400 and it is why this needs a decision rather than a patch — the same reasoning objectui#7299 recorded forRelatedList's raw-URL fallback, where the answer was to refuse loudly instead.So this card carries a real question: does the URL filter grammar grow a membership operator (a change to
URL_FILTER_OPS, both sides, plus its pin), or does the rail suppress / re-target "View all" for a multi-value relationship?Deliberately NOT claimed here
packages/plugin-form/src/LineItemsPanel.tsx:200andpackages/plugin-form/src/MasterDetailForm.tsx:762compile the same bare-equality parent scope. They are NOT asserted as defects on this card: their relationship is amaster_detailFK, and whether that field type can legally carrymultiple: truewas not verified. Whoever takes this card should answer that first — if it can, they belong here; if it cannot, they are correct as written and the sweep ends at the rail.Generated by Claude Code