Measured while implementing objectui#7912 (the ruled narrowing of the renderer seam's
dataSource). Filed unassigned and deliberately not repaired there: objectui#7912's ruling is about the ADAPTER
declaration and is binding, while the question below is a second published-surface question
with its own precedence problem. Recording it because #7912's narrowing is what makes it
visible, and because it is the one thing that narrowing costs.
⚠️ Angle-bracket shaped fragments are spelled out in words below: a tag-shaped fragment does
not survive this field, and the passages this card is about are JSX attributes.
Measured on origin/main at 243fc8317, and on the #7912 branch at c3e363b5c.
The site — one key, two meanings
SchemaRendererContext.dataSource is read for two unrelated purposes:
- The adapter. Plugins call
find / getObjectSchema / aggregate on it. This is the
meaning objectui#7912 declared: it is now the published DataSource contract.
- The data scope. The same value is the root that
bind paths and data.* expressions
resolve against:
packages/react/src/context/SchemaRendererContext.tsx — useDataScope(path) splits the
path and walks it over context.dataSource.
packages/react/src/SchemaRenderer.tsx — the expression evaluator is constructed with
data: dataSource, so every visibleWhen / hidden / disabled predicate resolves
data.* against that same value.
packages/components/src/renderers/complex/dataTableBindDiagnostic.ts states the design
in as many words: "bind is the data-scope binding vocabulary: a path string resolved by
useDataScope(). list, tree-view and the object-* plugin widgets read it."
Production readers of meaning 2 today: components' list and tree-view,
plugin-charts' ObjectChart, plugin-dashboard' ObjectDataTable, plugin-list's
ObjectGallery — each useDataScope(schema.bind).
Why this is now a contract question and not just an oddity
A DataSource adapter has no users, no customers, no status member. So for a host that
injects a conformant adapter — which the seam now REQUIRES — every bind path and every
data.* predicate resolves to undefined. For a host that injects a data bag, they resolve,
and the adapter meaning is the one that breaks.
The two meanings were never compatible; any merely hid the choice. What changed with
objectui#7912 is that meaning 1 is declared, so meaning 2 is no longer expressible by a
conformant host: injecting a bag is now a type error, and the only way to keep it is a cast at
the injection site.
The repository already instruments the collision, which is the strongest evidence that it is
real and known: packages/react/src/utils/visibilityDiagnostic.ts carries
reportAdapterOnlyDataPredicate, a dev warning that fires when a data.* path does NOT
resolve against the injected value — an "adapter-only data predicate". That warning exists
because both meanings arrive through one key.
Measured population
On the objectui#7912 branch, every internal injection that relies on meaning 2 (or on a
partial double) is now marked with an explicit cast, so the population is greppable rather
than inferred:
- 65 marked injection sites across 33 test files in 8 packages
(grep -rn 'as unknown as DataSource').
- 1 production site was a truthy empty-object stand-in and was repaired there; the
remaining production readers of meaning 2 are the five useDataScope(schema.bind) callers
listed above, which read the value rather than inject it, so they compile either way.
Options, none taken here
- A. Give the data scope its own key on the provider (say a
data prop), keep
dataSource the adapter, and define the precedence between them. That is the option the
dataTableBindDiagnostic header already flags as needing its own ruling, including a
data-vs-bind precedence.
- B. Retire meaning 2 at this seam:
bind resolves only against a record scope
(RecordContext) and never against the injected adapter. Decides objectui#7206's sibling
question in the same direction.
- C. Declare the union on the key (
DataSource or a record bag). This is the half-typed
seam objectui#7912's ruling refused, so it is listed only to be refused explicitly.
⛔ No recommendation is implied by the order. What this card asks for is a decision, because
today the answer is "both, through one key, and the compiler can only describe one of them".
Related: objectui#7912 (the adapter declaration, landed), objectui#7206 (the
formValues / data tail on the same context, deliberately undecided), objectui#6575 and
objectui#6665 (the bind diagnostics that map meaning 2's blast radius), objectui#7483 (the
same defect class on ObjectView's props).
Filed by an ObjectUI development agent seat (Claude Code) while implementing objectui#7912;
the measurement commands and their readings are quoted above rather than attached.
Generated by Claude Code
Measured while implementing objectui#7912 (the ruled narrowing of the renderer seam's
dataSource). Filed unassigned and deliberately not repaired there: objectui#7912's ruling is about the ADAPTERdeclaration and is binding, while the question below is a second published-surface question
with its own precedence problem. Recording it because #7912's narrowing is what makes it
visible, and because it is the one thing that narrowing costs.
not survive this field, and the passages this card is about are JSX attributes.
Measured on
origin/mainat243fc8317, and on the #7912 branch atc3e363b5c.The site — one key, two meanings
SchemaRendererContext.dataSourceis read for two unrelated purposes:find/getObjectSchema/aggregateon it. This is themeaning objectui#7912 declared: it is now the published
DataSourcecontract.bindpaths anddata.*expressionsresolve against:
packages/react/src/context/SchemaRendererContext.tsx—useDataScope(path)splits thepath and walks it over
context.dataSource.packages/react/src/SchemaRenderer.tsx— the expression evaluator is constructed withdata: dataSource, so everyvisibleWhen/hidden/disabledpredicate resolvesdata.*against that same value.packages/components/src/renderers/complex/dataTableBindDiagnostic.tsstates the designin as many words: "
bindis the data-scope binding vocabulary: a path string resolved byuseDataScope().list,tree-viewand theobject-*plugin widgets read it."Production readers of meaning 2 today:
components'listandtree-view,plugin-charts'ObjectChart,plugin-dashboard'ObjectDataTable,plugin-list'sObjectGallery— eachuseDataScope(schema.bind).Why this is now a contract question and not just an oddity
A
DataSourceadapter has nousers, nocustomers, nostatusmember. So for a host thatinjects a conformant adapter — which the seam now REQUIRES — every
bindpath and everydata.*predicate resolves toundefined. For a host that injects a data bag, they resolve,and the adapter meaning is the one that breaks.
The two meanings were never compatible;
anymerely hid the choice. What changed withobjectui#7912 is that meaning 1 is declared, so meaning 2 is no longer expressible by a
conformant host: injecting a bag is now a type error, and the only way to keep it is a cast at
the injection site.
The repository already instruments the collision, which is the strongest evidence that it is
real and known:
packages/react/src/utils/visibilityDiagnostic.tscarriesreportAdapterOnlyDataPredicate, a dev warning that fires when adata.*path does NOTresolve against the injected value — an "adapter-only data predicate". That warning exists
because both meanings arrive through one key.
Measured population
On the objectui#7912 branch, every internal injection that relies on meaning 2 (or on a
partial double) is now marked with an explicit cast, so the population is greppable rather
than inferred:
(
grep -rn 'as unknown as DataSource').remaining production readers of meaning 2 are the five
useDataScope(schema.bind)callerslisted above, which read the value rather than inject it, so they compile either way.
Options, none taken here
dataprop), keepdataSourcethe adapter, and define the precedence between them. That is the option thedataTableBindDiagnosticheader already flags as needing its own ruling, including adata-vs-bindprecedence.bindresolves only against a record scope(
RecordContext) and never against the injected adapter. Decides objectui#7206's siblingquestion in the same direction.
DataSourceor a record bag). This is the half-typedseam objectui#7912's ruling refused, so it is listed only to be refused explicitly.
⛔ No recommendation is implied by the order. What this card asks for is a decision, because
today the answer is "both, through one key, and the compiler can only describe one of them".
Related: objectui#7912 (the adapter declaration, landed), objectui#7206 (the
formValues/datatail on the same context, deliberately undecided), objectui#6575 andobjectui#6665 (the
binddiagnostics that map meaning 2's blast radius), objectui#7483 (thesame defect class on
ObjectView's props).Filed by an ObjectUI development agent seat (Claude Code) while implementing objectui#7912;
the measurement commands and their readings are quoted above rather than attached.
Generated by Claude Code