Recorded while implementing objectui#6453, which is where the third hand copy of the schema-settled gate was written. This card exists so the convergence question is asked deliberately rather than answered by accretion, and so the second half — five sibling views that resolve the schema but do not gate on it — is not lost.
Filed as an observation. Nothing here is a correctness defect; objectui#6481 (ObjectTree's unkeyed latch) is filed separately as one.
Where the family stands today
| component | shape | keyed by object? |
|---|
plugin-kanban/src/ObjectKanban.tsx | { key, def } | null, gates the whole fetch effect (objectui#6271) | yes |
plugin-view/src/ObjectView.tsx | { key, def } | null, gates the non-grid fetch effect (objectui#6419) | yes |
plugin-calendar/src/ObjectCalendar.tsx | { key, def } | null, gates the object-provider branch (objectui#6453) | yes |
plugin-tree/src/ObjectTree.tsx | objectSchema + a bare schemaSettled boolean (objectui#6014) | no — objectui#6481 |
Four copies. objectui#6453's triage note asked the implementer to prefer adopting a shared helper if objectui#6419's landed shape extracted one; it did not (e929c562a touched only ObjectView.tsx, one test and a changeset), so objectui#6453 wrote the third copy deliberately and declared it. The reasons it did not extract, which are the inputs to this card rather than a pre-ruling of it:
- The resolution half is genuinely common:
useState<{key, def} \| null>(null), a key, ready = res !== null && res.key === key, def = ready ? res.def : null, and an effect whose every exit settles. - The gate half is not.
ObjectCalendar keys on dataConfig.object ?? schema.objectName rather than schema.objectName, and gates one provider branch rather than the whole effect, because an inline value data set issues no metadata read at all — a whole-effect gate would hold its query open on a resolution nothing was going to produce. A helper would hold roughly the resolution half and leave each component its own gate placement. - A helper would have to live in
@object-ui/react (AGENTS.md §3 bars React from @object-ui/core), which makes it a new export on a published package — a permanent public-surface commitment. All four plugins already depend on @object-ui/react, so it is feasible; the question is whether it is wanted. - Doing it would refactor two already-landed fixes (objectui#6271, objectui#6419) plus a fourth divergent copy.
The second half: five views resolve the schema but do not gate on it
Every other buildExpandFields consumer already lists the schema in its fetch effect's dependency list, so none of them carries the never-expands defect. They carry the other regime — two queries, the first unexpanded:
plugin-gantt/src/ObjectGantt.tsx:536 deps [... , objectSchema]
plugin-map/src/ObjectMap.tsx:708 deps [... , objectSchema]
plugin-timeline/src/ObjectTimeline.tsx:208 deps [... , objectDef]
plugin-list/src/ObjectGallery.tsx:354 deps [... , objectDef]
plugin-tree/src/ObjectTree.tsx:468 gated, but see objectui#6481
What that costs is not uniform, and objectui#6453 measured why. On ObjectCalendar, instrumented with three latency profiles: when the schema read is the slower of the two, the dependency version painted raw ids, reverted to the loading placeholder, then swapped — a three-step paint; when it was the faster one, the first response was discarded on arrival — a round trip bought and thrown away. ObjectKanban measured the discard case, ObjectView measured the visible-swap case. So whether gating is worth it in each of the five above is a per-component measurement, not an inference from this table.
What this card is asking for
A decision, then whatever work follows from it:
- Extract a shared settled-schema resolution hook, or keep hand copies? If extracted, the new export must be declared as a public-surface widening, not slipped in.
- Independently: measure each of the four ungated views and gate the ones where an extra re-run costs a visible wrong paint.
Related: objectui#6271, objectui#6419, objectui#6453, objectui#6481, objectui#6014.
Generated by Claude Code
Generated by Claude Code
Recorded while implementing objectui#6453, which is where the third hand copy of the schema-settled gate was written. This card exists so the convergence question is asked deliberately rather than answered by accretion, and so the second half — five sibling views that resolve the schema but do not gate on it — is not lost.
Filed as an observation. Nothing here is a correctness defect; objectui#6481 (
ObjectTree's unkeyed latch) is filed separately as one.Where the family stands today
plugin-kanban/src/ObjectKanban.tsx{ key, def } | null, gates the whole fetch effect (objectui#6271)plugin-view/src/ObjectView.tsx{ key, def } | null, gates the non-grid fetch effect (objectui#6419)plugin-calendar/src/ObjectCalendar.tsx{ key, def } | null, gates theobject-provider branch (objectui#6453)plugin-tree/src/ObjectTree.tsxobjectSchema+ a bareschemaSettledboolean (objectui#6014)Four copies. objectui#6453's triage note asked the implementer to prefer adopting a shared helper if objectui#6419's landed shape extracted one; it did not (
e929c562atouched onlyObjectView.tsx, one test and a changeset), so objectui#6453 wrote the third copy deliberately and declared it. The reasons it did not extract, which are the inputs to this card rather than a pre-ruling of it:useState<{key, def} \| null>(null), a key,ready = res !== null && res.key === key,def = ready ? res.def : null, and an effect whose every exit settles.ObjectCalendarkeys ondataConfig.object ?? schema.objectNamerather thanschema.objectName, and gates one provider branch rather than the whole effect, because an inlinevaluedata set issues no metadata read at all — a whole-effect gate would hold its query open on a resolution nothing was going to produce. A helper would hold roughly the resolution half and leave each component its own gate placement.@object-ui/react(AGENTS.md §3 bars React from@object-ui/core), which makes it a new export on a published package — a permanent public-surface commitment. All four plugins already depend on@object-ui/react, so it is feasible; the question is whether it is wanted.The second half: five views resolve the schema but do not gate on it
Every other
buildExpandFieldsconsumer already lists the schema in its fetch effect's dependency list, so none of them carries the never-expands defect. They carry the other regime — two queries, the first unexpanded:What that costs is not uniform, and objectui#6453 measured why. On
ObjectCalendar, instrumented with three latency profiles: when the schema read is the slower of the two, the dependency version painted raw ids, reverted to the loading placeholder, then swapped — a three-step paint; when it was the faster one, the first response was discarded on arrival — a round trip bought and thrown away.ObjectKanbanmeasured the discard case,ObjectViewmeasured the visible-swap case. So whether gating is worth it in each of the five above is a per-component measurement, not an inference from this table.What this card is asking for
A decision, then whatever work follows from it:
Related: objectui#6271, objectui#6419, objectui#6453, objectui#6481, objectui#6014.
Generated by Claude Code
Generated by Claude Code