You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(components): the only working route from a provider dataSource into a data-table is the properties envelope protocol.md tells authors not to use #5372
Found while implementing #5126 / #5337 (PR #5371), which corrects three skill guides that taught data-table + bind. Direction there was settled by inheritance from #5125 — data-table does not gain bind, the teaching changes — so this is filed separately rather than folded in. It is the measurement anyone weighing that decision again would want, and it is not an argument for re-opening it.
The question
After PR #5371 the guides teach data-table's inline data array, which is correct and renders. That leaves an unanswered one: how does an author put provider data (SchemaRendererProvider dataSource) into a data-table at all, without the host pre-resolving the array into the schema?
Measured
Real SchemaRenderer inside a SchemaRendererProvider holding dataSource = { customers: [ 2 records ] }, columns identical in all three legs, reading tbody td text (objectui@99d8721a3, vitest from the repo root):
["No results foundTry adjusting your filters or search query."]
So exactly one of the three spellings works, and it is B.
Why
SchemaRenderer's evaluatedSchema memo evaluates expressions in exactly four places: properties.*, content, props.*, and the visibility / disabled predicates. A top-level key is never evaluated, which kills A — Array.isArray is false for the raw ${...} string, so DataTableRenderer falls back to EMPTY_ROWS.
Then the COMPAT hoist copies every properties.* value onto the node's top level. props.* gets evaluated but is not hoisted, and DataTableRenderer destructures data off the node, not off schema.props — which kills C.
B survives because it is the only spelling that gets both halves: evaluated, then hoisted.
Why it is worth recording
skills/objectui/rules/protocol.md opens by telling authors the opposite:
The one exception is the element:* namespace. Those components read their config out of properties / props by design ... Match the envelope to the namespace; do not apply either shape everywhere.
So the one envelope the published rules tell an author not to reach for is the only one that reaches the provider's data, and the two spellings the rules endorse both fail in the shape this repo keeps paying for — no throw, no console line, a header over an empty state. An AI author following the rules file cannot get there; one ignoring it can.
This is also the inverse of the shape #4799 corrected. There the canonical properties spelling was starved while the legacy props alias was fed; here properties is the only fed spelling and the canonical node-level one is starved. Same seam, opposite polarity.
Not proposed here
No fix is proposed and none should be inferred. The plausible directions all cost something and at least one of them was already declined in the neighbourhood:
Evaluate data (or a declared set of array-valued keys) at the node level. Widens what expressions reach, on the generic path, for every component.
Rule that host-side resolution is the only supported route, and say so in the guides — the cheapest, and it makes the current teaching complete rather than merely true.
Change nothing and record the measurement, which is what this card does today.
Nothing here is blocked on it and no behaviour is broken; #5126 is not addressed by this card and #5120 remains open as its own separate question about the columns key spelling.
Reproducing
Three nodes above, rendered through SchemaRenderer with a provider; the evaluation order is in packages/react/src/SchemaRenderer.tsx (the evaluatedSchema memo), and the row read is data: rawData = EMPTY_ROWS in packages/components/src/renderers/complex/data-table.tsx.
Found while implementing #5126 / #5337 (PR #5371), which corrects three skill guides that taught
data-table+bind. Direction there was settled by inheritance from #5125 —data-tabledoes not gainbind, the teaching changes — so this is filed separately rather than folded in. It is the measurement anyone weighing that decision again would want, and it is not an argument for re-opening it.The question
After PR #5371 the guides teach
data-table's inlinedataarray, which is correct and renders. That leaves an unanswered one: how does an author put provider data (SchemaRendererProvider dataSource) into adata-tableat all, without the host pre-resolving the array into the schema?Measured
Real
SchemaRendererinside aSchemaRendererProviderholdingdataSource = { customers: [ 2 records ] }, columns identical in all three legs, readingtbody tdtext (objectui@99d8721a3, vitest from the repo root):{ "type": "data-table", "data": "${data.customers}", "columns": [...] }["No results foundTry adjusting your filters or search query."]{ "type": "data-table", "properties": { "data": "${data.customers}" }, "columns": [...] }["Ada Lovelace","ada@example.com","Grace Hopper","grace@example.com"]{ "type": "data-table", "props": { "data": "${data.customers}" }, "columns": [...] }["No results foundTry adjusting your filters or search query."]So exactly one of the three spellings works, and it is B.
Why
SchemaRenderer'sevaluatedSchemamemo evaluates expressions in exactly four places:properties.*,content,props.*, and the visibility / disabled predicates. A top-level key is never evaluated, which kills A —Array.isArrayis false for the raw${...}string, soDataTableRendererfalls back toEMPTY_ROWS.Then the COMPAT hoist copies every
properties.*value onto the node's top level.props.*gets evaluated but is not hoisted, andDataTableRendererdestructuresdataoff the node, not offschema.props— which kills C.B survives because it is the only spelling that gets both halves: evaluated, then hoisted.
Why it is worth recording
skills/objectui/rules/protocol.mdopens by telling authors the opposite:So the one envelope the published rules tell an author not to reach for is the only one that reaches the provider's data, and the two spellings the rules endorse both fail in the shape this repo keeps paying for — no throw, no console line, a header over an empty state. An AI author following the rules file cannot get there; one ignoring it can.
This is also the inverse of the shape #4799 corrected. There the canonical
propertiesspelling was starved while the legacypropsalias was fed; herepropertiesis the only fed spelling and the canonical node-level one is starved. Same seam, opposite polarity.Not proposed here
No fix is proposed and none should be inferred. The plausible directions all cost something and at least one of them was already declined in the neighbourhood:
data(or a declared set of array-valued keys) at the node level. Widens what expressions reach, on the generic path, for every component.data-tableabindread. This is the direction finding(components):table被注册两次,data-display 的 SimpleTableRenderer 被 complex/table.tsx 完全遮蔽 —— 而它是唯一读bind的表格渲染器 #5125's ruling declined for its sibling renderer; it would need a new Feature card through the decision inbox.Nothing here is blocked on it and no behaviour is broken; #5126 is not addressed by this card and #5120 remains open as its own separate question about the
columnskey spelling.Reproducing
Three nodes above, rendered through
SchemaRendererwith a provider; the evaluation order is inpackages/react/src/SchemaRenderer.tsx(theevaluatedSchemamemo), and the row read isdata: rawData = EMPTY_ROWSinpackages/components/src/renderers/complex/data-table.tsx.Generated by Claude Code