Skip to content

ObjectTree's schema-settled latch is a bare boolean, so a query after an object switch carries the previous object's $expand #6481

Description

@claude

ObjectTree gates its record query on a schema-settled latch (objectui#6014), but the latch is a bare boolean that is never keyed by, or reset for, the object it belongs to. So on an object switch the query fires carrying the previous object's $expand set.

Found while implementing objectui#6453 (the calendar member of the same family). Filed separately rather than folded in: different package, and this is the keying half rather than the missing-gate half.

The shape, on origin/main

packages/plugin-tree/src/ObjectTree.tsx:341  const [objectSchema, setObjectSchema] = useState<any>(null);
                                       :356  const [schemaSettled, setSchemaSettled] = useState(false);
                                       :422  if (!schemaSettled) return;
                                       :423  const expand = buildExpandFields(objectSchema?.fields);
                                       :468  }, [dataConfig, dataSource, schema.filter, objectSchema, schemaSettled, (rest as any).data]);

setSchemaSettled(true) is only ever called with true (line 392, in a finally). Nothing sets it back to false. When the bound object changes, dataConfig changes and both effects re-run — but in the record effect schemaSettled is still true and objectSchema still holds the previous object's fields, so the query goes out as

find(newObject, { $filter: …, $expand: [ …previous object's relation fields… ] })

objectSchema is in the record effect's dependency list, so a correct second query follows once the new schema lands. The damage is therefore one query per switch carrying an expand set for fields the new object does not declare — rejected or silently ignored depending on the adapter — plus the transient it paints.

What the fix looks like

objectui#6271 (ObjectKanban), objectui#6419 (ObjectView) and objectui#6453 (ObjectCalendar) all carry the shape this needs: one piece of state { key, def } | null, with readiness derived during render as resolution !== null && resolution.key === currentKey. Comparing the key during render is what closes the gate in the same commit that changes the object, rather than one commit later. Two separate states (def + hasSettled) cannot express that, which is exactly what is being observed here.

Note the existing finally is correct and must be preserved: every exit settles, including the two early returns and a rejected read, so a tree whose adapter exposes no getObjectSchema still queries rather than waiting forever.

Evidence class

Read from source, not measured. Whoever picks this up should measure the switch on this component before adopting a shape — ObjectTree's record effect has its own dependency set (dataConfig, schema.filter, objectSchema, schemaSettled, (rest as any).data) and neither objectui#6453's numbers nor objectui#6419's transfer.

Blocked-by: #6482

Related: objectui#6271, objectui#6419, objectui#6453, objectui#6014.

Generated by Claude Code

Metadata

Metadata

Assignees

Labels

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

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions