Skip to content

The related-list tab BADGE compiles its parent scope as bare equality too, so a multi-value related list shows rows with no count — and the store's own badge/row parity claim stops holding #8882

Description

@claude

Split out of objectui#7299 (dev seat, scope discipline — that card's ruling is RelatedList only). Filed as a separate card because the fix is NOT mechanical here: the badge probe has no field metadata in hand at all, so giving it arity is a design change in a different package, not a spelling change.

The measurement

RelatedList composes the parent-relationship condition for the ROWS. The tab badge composes it independently, for the COUNT:

packages/components/src/hooks/related-count-store.ts   parentScope[relField] = parentId;
packages/components/src/renderers/layout/containers.tsx  the probe that calls it

Both were bare equality. objectui#7299 compiles the ROW condition to match the relationship field's arity (multiple: true becomes { [relField]: { $contains: parentId } }, which is the spelling driver-sql prescribes in the 400 INVALID_FILTER it answers the equality form with). The badge still sends =.

Why it matters, stated no wider than it is

The store's own comment states the invariant this breaks, verbatim:

That shared sink is what makes badge/row parity a property of the code rather than of two implementations agreeing by luck: the badge cannot count a set the list does not show, because both sides send the same $filter.

And the spec text objectui#4664 pins says the same from the contract side: "The related-list tab badge count honors the same composed filter, so counts match the visible rows."

After objectui#7299, for a multiple: true relationship, the two sides send DIFFERENT $filters. The visible symptom today is milder than a wrong number, and that is worth stating precisely so nobody over-reads this card: fetchCount swallows the driver refusal (catch { return 0; }) WITHOUT calling setCount, so the store holds no entry, computeCount returns undefined, and the tab renders with NO badge at all. So the failure is an ABSENT count above a populated list, not a 0 above a populated list.

It is still the parity property failing, and it is still measured rather than predicted: the probe sends a predicate the driver refuses by name.

Why it was not folded into objectui#7299

The row fix reads arity off the child object's schema, which RelatedList already fetches for its own columns. The badge probe has nothing equivalent: containers.tsx walks SCHEMA NODES (rl.properties.objectName / relationshipField) and RelatedCountStore.fetch receives four scalars plus a filter. Getting arity there means a per-child-object metadata fetch inside the tab strip — new async state, new cache identity, new tests, in @object-ui/components rather than @object-ui/plugin-detail. That is a card, not a rider.

Shape of the fix (not a ruling — the seat that takes this decides)

Whatever route is chosen, the parity property is the acceptance criterion, not the spelling: the badge and the rows must send the same $filter for the same list. The cache key already discriminates on the declared scope filter (objectui#4664); arity would have to reach the same place.

⛔ Do not close objectui#7299 on this — that card's rows fix is complete and correct on its own terms.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpackage: componentspriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions