Skip to content

data-table accepts a bind and silently renders its header over an empty body #6575

Description

@os-support-ai

Found while censusing bind producers and consumers for #6357 (PR #6574). Not that card's defect — filed separately per its dispatch.

The fact

bind is the data-scope binding vocabulary: a path string resolved by useDataScope(). Ten production sites read it. data-table is not one of them — it reads its rows from an inline data array on the node and never calls the hook.

So a bind on a data-table node is accepted by every gate and does nothing:

  • the TS side accepts it (BaseSchema's [key: string]: any);
  • the zod side accepts it (BaseSchema is .passthrough(), and DataTableSchema is BaseSchema.extend(…), which inherits that);
  • nothing reads it at render.

The result is a table that draws its header over an empty body — no error, no warning, no diagnostic.

This is already documented and already pinned — it just has no card

  • skills/objectui/rules/protocol.md:162 states it verbatim: "data-table does NOT: it reads its rows from an inline data array on the node, so a bind on it is ignored and the table renders its header over an empty body — no error, no warning."
  • packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx pins the behaviour, including the measured detail that "the bound array never reaches the renderer at all" — the body is one row, the empty state spanning the table.

So the behaviour is known and asserted. What is missing is a decision about whether it should stay.

Why declaring bind centrally did not change this

PR #6574 declares bind on BaseSchema (the #6357 census concluded it belongs there). That does not touch this: the key was accepted on every node before the declaration existed, via the index signature and .passthrough(). The declaration narrows the key's value to a string; it cannot refuse the key on a node that ignores it. Recording that explicitly so this is not read as a regression from that PR.

What needs deciding

Three shapes, not obviously one answer:

  1. Make data-table a reader — call useDataScope(schema.bind) like the other nine. Consistent with the vocabulary the protocol teaches, but it is a behaviour change on a published component: a node carrying both data and bind would need a stated precedence.
  2. Reject it loudly — refuse bind on data-table at parse time. Blocked on the same ceiling as everything else in this class: .passthrough() means a per-node strict shape is needed first (finding(types): BaseSchema's [key: string]: any leaves every component schema open, so a "declare the surface" fix can never reject a misspelled TOP-LEVEL key #5155 / finding(types): ObjectViewSchema's table and form slots declare ZERO properties — the same Omit-under-index-signature collapse as #6151, in property position #6269).
  3. Diagnose it at render — the repo already has a diagnostic channel for exactly this shape of silent failure (packages/plugin-grid's column-spelling diagnostic, visibilityDiagnostic.ts).

Leaning 3 then 1, but this is a published-surface behaviour question rather than a mechanical fix, which is why this is a finding and not a patch.

Reproduce

grep -rn "useDataScope" packages --include='*.tsx' --include='*.ts' | grep -v __tests__
grep -rn "useDataScope" packages/components/src/renderers/data-display/data-table.tsx   # no hits
sed -n '160,166p' skills/objectui/rules/protocol.md

Refs: #6357, PR #6574. Adjacent ceiling: #5155 / #6269.

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:queue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions