Found while implementing #6726 (out of that card's scope — it names exactly seven direct call sites and does not mention this helper).
The shape
packages/core/src/utils/extract-records.ts is a shared normaliser:
exportfunctionextractRecords(results: unknown): any[]{if(Array.isArray(results))returnresults;if(results&&typeofresults==='object'){if(Array.isArray((resultsasany).records))return(resultsasany).records;// <- FIRSTif(Array.isArray((resultsasany).data))return(resultsasany).data;// <- secondif(Array.isArray((resultsasany).value))return(resultsasany).value;}return[];}records is tried beforedata — the same precedence inversion #5945 was filed about and #6726 repaired in seven modules by hand. QueryResult (packages/types/src/data.ts) declares data, total, page, pageSize, hasMore, cursor, metadata; records is not a member of it.
Why this is bigger than any one of #6726's seven
#6726's sites each carry their own copy of the read. This one is a helper, and nine renderers call it directly on a DataSource.find() answer:
| module | call site |
|---|
packages/plugin-charts/src/ObjectChart.tsx | :196, on dataSource.find(referenceTo, …) |
packages/plugin-kanban/src/ObjectKanban.tsx | :298 |
packages/plugin-tree/src/ObjectTree.tsx | :456, on dataSource.find(dataObjectName, …) |
packages/plugin-map/src/ObjectMap.tsx | :714 |
packages/plugin-timeline/src/ObjectTimeline.tsx | :213 |
packages/plugin-gantt/src/ObjectGantt.tsx | :680, :1132 |
packages/plugin-dashboard/src/ObjectDataTable.tsx | :680 |
packages/plugin-dashboard/src/ObjectPivotTable.tsx | :157 |
packages/plugin-calendar/src/ObjectCalendar.tsx | :357 |
Measured on 26896c689 (#6726's base) with a repo-wide sweep of every tracked file for an object-literal records: key: no producer emits records at the DataSource.find() seam. ObjectStackAdapter.normalizeQueryResult CONSUMES the server/SDK records envelope and returns { data, total, page, pageSize, hasMore }; ApiDataSource, ValueDataSource, the runner and example mocks, and the @object-ui/types REST example all return data or a bare array. Control for that zero: the same sweep DOES find the legitimate records producers on other seams (ViewDataProvider's ResolvedData, the raw Cloud HTTP payloads, the client-SDK doubles below normalizeQueryResult), so a producer at this seam would have been caught.
What makes this NOT a copy-paste of #6726's fix
⚠️ The helper is genuinely dual-seam, and that is why it was left alone rather than repaired on #6726's PR:
packages/core/src/data-scope/ViewDataProvider.ts also calls it, and its own ResolvedData / DataFetcher.fetchRecords interfaces declare recordslegitimately. On that seam the records arm is load-bearing.- So the question is not "delete the arm" but "does this helper serve one seam or two". Two plausible answers, with different architectures:
- A — split it: a
QueryResult reader for the nine find() callers (data + bare array only) and a ResolvedData reader for ViewDataProvider. Each seam then has one contract and no tolerance. - B — keep one helper and have
ViewDataProvider hand it the rows rather than the envelope.
Per-module measurement is still owed before any deletion: each of the nine call sites needs its own producer check, and any pin has to be per module (a single "nothing reads records" assertion passes even if one module was repaired wrongly). The value arm is on the same footing and is recorded in the sibling finding.
Why it is worth recording
AGENTS.md #0.1: every extra arm is a place a non-conforming producer keeps working unrejected. A helper multiplies that — nine renderers inherit a tolerance none of them chose, and the one place where the tolerance is correct is exactly what stops anyone deleting it.
Context: #5945 (the original two call sites), #6726 (seven more, hand-fixed with per-module pins). No PR here; recording only.
Found while implementing #6726 (out of that card's scope — it names exactly seven direct call sites and does not mention this helper).
The shape
packages/core/src/utils/extract-records.tsis a shared normaliser:recordsis tried beforedata— the same precedence inversion #5945 was filed about and #6726 repaired in seven modules by hand.QueryResult(packages/types/src/data.ts) declaresdata,total,page,pageSize,hasMore,cursor,metadata;recordsis not a member of it.Why this is bigger than any one of #6726's seven
#6726's sites each carry their own copy of the read. This one is a helper, and nine renderers call it directly on a
DataSource.find()answer:packages/plugin-charts/src/ObjectChart.tsx:196, ondataSource.find(referenceTo, …)packages/plugin-kanban/src/ObjectKanban.tsx:298packages/plugin-tree/src/ObjectTree.tsx:456, ondataSource.find(dataObjectName, …)packages/plugin-map/src/ObjectMap.tsx:714packages/plugin-timeline/src/ObjectTimeline.tsx:213packages/plugin-gantt/src/ObjectGantt.tsx:680,:1132packages/plugin-dashboard/src/ObjectDataTable.tsx:680packages/plugin-dashboard/src/ObjectPivotTable.tsx:157packages/plugin-calendar/src/ObjectCalendar.tsx:357Measured on
26896c689(#6726's base) with a repo-wide sweep of every tracked file for an object-literalrecords:key: no producer emitsrecordsat theDataSource.find()seam.ObjectStackAdapter.normalizeQueryResultCONSUMES the server/SDKrecordsenvelope and returns{ data, total, page, pageSize, hasMore };ApiDataSource,ValueDataSource, the runner and example mocks, and the@object-ui/typesREST example all returndataor a bare array. Control for that zero: the same sweep DOES find the legitimaterecordsproducers on other seams (ViewDataProvider'sResolvedData, the raw Cloud HTTP payloads, the client-SDK doubles belownormalizeQueryResult), so a producer at this seam would have been caught.What makes this NOT a copy-paste of #6726's fix
packages/core/src/data-scope/ViewDataProvider.tsalso calls it, and its ownResolvedData/DataFetcher.fetchRecordsinterfaces declarerecordslegitimately. On that seam therecordsarm is load-bearing.QueryResultreader for the ninefind()callers (data+ bare array only) and aResolvedDatareader forViewDataProvider. Each seam then has one contract and no tolerance.ViewDataProviderhand it the rows rather than the envelope.Per-module measurement is still owed before any deletion: each of the nine call sites needs its own producer check, and any pin has to be per module (a single "nothing reads
records" assertion passes even if one module was repaired wrongly). Thevaluearm is on the same footing and is recorded in the sibling finding.Why it is worth recording
AGENTS.md #0.1: every extra arm is a place a non-conforming producer keeps working unrejected. A helper multiplies that — nine renderers inherit a tolerance none of them chose, and the one place where the tolerance is correct is exactly what stops anyone deleting it.
Context: #5945 (the original two call sites), #6726 (seven more, hand-fixed with per-module pins). No PR here; recording only.