Found while implementing #5458 (not fixed there — out of that card's scope).
The shape
Two app-shell modules normalise a find() result with the same chain:
packages/app-shell/src/hooks/useApproverDirectory.ts:82packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx:88
Array.isArray(res) ? res : res?.records??res?.items??res?.data??[];
QueryResult (packages/types/src/data.ts) declares exactly data, total, page, pageSize, hasMore, cursor, metadata. records and items are not members of it. Only the last arm of that chain reads the contract; the two before it read shapes the contract does not define, and they are tried first.
Why it is worth recording
This is AGENTS.md #0.1 in miniature — a tolerant reader that lets a non-conforming producer keep working, so the wrong shape never gets rejected anywhere and becomes a second de-facto contract. The cost is not hypothetical: the same records/items confusion was live in three separate places that #5458 had to fix, each reading a key the adapter never returns —
apps/console/src/sdui-workbench-preview.tsx read all.records,packages/plugin-dashboard/src/DashboardFilterBar.tsx read records.items with nodata arm at all, so against a real adapter its filter produced zero options,- and
skills/objectui/guides/data-integration.md publishes return result.records to skill consumers, under a QueryResult sketch that declares records?: T[] and omits data entirely.
A helper that quietly accepts all three spellings is why nobody found out that two of them are wrong.
Worth checking before acting
Whether any live DataSource implementation actually returns records or items at this seam. ObjectStackAdapter.normalizeQueryResult maps the server's records envelope to data before returning, so from a consumer's side the answer looks like "no" — but that should be measured across the adapters, not assumed, since the bare-array arm is genuinely needed (test fakes and the console preview harness return plain arrays).
If nothing returns them, the fix is to drop the two non-contract arms; if something does, that producer is the thing to fix, not the reader.
Found while implementing #5458 (not fixed there — out of that card's scope).
The shape
Two app-shell modules normalise a
find()result with the same chain:packages/app-shell/src/hooks/useApproverDirectory.ts:82packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx:88QueryResult(packages/types/src/data.ts) declares exactlydata,total,page,pageSize,hasMore,cursor,metadata.recordsanditemsare not members of it. Only the last arm of that chain reads the contract; the two before it read shapes the contract does not define, and they are tried first.Why it is worth recording
This is AGENTS.md #0.1 in miniature — a tolerant reader that lets a non-conforming producer keep working, so the wrong shape never gets rejected anywhere and becomes a second de-facto contract. The cost is not hypothetical: the same
records/itemsconfusion was live in three separate places that #5458 had to fix, each reading a key the adapter never returns —apps/console/src/sdui-workbench-preview.tsxreadall.records,packages/plugin-dashboard/src/DashboardFilterBar.tsxreadrecords.itemswith nodataarm at all, so against a real adapter its filter produced zero options,skills/objectui/guides/data-integration.mdpublishesreturn result.recordsto skill consumers, under aQueryResultsketch that declaresrecords?: T[]and omitsdataentirely.A helper that quietly accepts all three spellings is why nobody found out that two of them are wrong.
Worth checking before acting
Whether any live
DataSourceimplementation actually returnsrecordsoritemsat this seam.ObjectStackAdapter.normalizeQueryResultmaps the server'srecordsenvelope todatabefore returning, so from a consumer's side the answer looks like "no" — but that should be measured across the adapters, not assumed, since the bare-array arm is genuinely needed (test fakes and the console preview harness return plain arrays).If nothing returns them, the fix is to drop the two non-contract arms; if something does, that producer is the thing to fix, not the reader.