Surfaced by the repo-wide type-check canary in #4528 (PR pending), which is the first thing ever to check ListView's call sites. Filed rather than fixed there: #4528 is a type-only card and wiring this is a behaviour change.
What was measured
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx renders the Data pillar's grid through the plugin ObjectView's renderListView slot. That slot supplies a refreshKey (declared at packages/plugin-view/src/ObjectView.tsx:207), and renderStudioGridList forwarded it straight on:
<ListView
schema={...}
dataSource={ds as never}
onEdit={onEdit}
onAddRecord={onAddRecord}
className={className}
refreshKey={refreshKey}
/>
refreshKey is not a prop of ListView, and it is not a prop of anything ListView renders. ListView keeps its own refreshKey local state (ListView.tsx:831) and drives refetching off schema.refreshTrigger plus that state; ObjectGrid likewise keeps its own local refreshKey state and declares no such prop. The incoming value rode ListView's {...props} forward into SchemaRenderer and was dropped.
Grepping the whole repo for refreshKey?: as a prop declaration returns plugin-view/ObjectView.tsx (the slot contract), StudioDesignSurface itself, and four unrelated components — no view component in the forward chain.
It type-checked only because ListViewProps carried a [key: string]: any that erased the whole interface (#4528), so no ListView call site was checked at all.
Why it was invisible
Bumping the prop still re-renders ListView (new prop object), and the Studio grid re-renders constantly anyway because its schema is a fresh object literal on every render. So "the list looks like it updates" — it just never refetches because of refreshKey.
The working precedent, 300 lines away
packages/app-shell/src/views/ObjectView.tsx:1592 consumes the same slot value correctly — it folds it into React's key so the subtree remounts:
const combinedRefreshKey = refreshKey + (pluginRefreshKey || 0);
const key = `${objectName}-${activeView.id}-${combinedRefreshKey}`;
Current state
#4528's PR removes the dead prop (behaviour-preserving) and leaves a comment at the declaration pointing here. So today the Studio Data pillar has no refreshKey path at all, which is what it effectively had before — the difference is that it is now visible.
Worth deciding
Whether the Studio Data pillar needs the slot refresh at all. If it does, the key fold above is the established shape. If it does not, the slot prop can be dropped from renderStudioGridList's signature entirely. Someone who knows what user action is meant to trigger it (record create / delete / publish?) should judge, since "remount the grid" also discards scroll position, selection and in-progress inline edits — AGENTS.md #8's "refresh data, don't rebuild UI" argues against the key fold as the long-term answer even though it is what the sibling does.
Refs #4528, #1126.
Generated by Claude Code
Surfaced by the repo-wide type-check canary in #4528 (PR pending), which is the first thing ever to check
ListView's call sites. Filed rather than fixed there: #4528 is a type-only card and wiring this is a behaviour change.What was measured
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsxrenders the Data pillar's grid through the plugin ObjectView'srenderListViewslot. That slot supplies arefreshKey(declared atpackages/plugin-view/src/ObjectView.tsx:207), andrenderStudioGridListforwarded it straight on:refreshKeyis not a prop ofListView, and it is not a prop of anythingListViewrenders.ListViewkeeps its ownrefreshKeylocal state (ListView.tsx:831) and drives refetching offschema.refreshTriggerplus that state;ObjectGridlikewise keeps its own localrefreshKeystate and declares no such prop. The incoming value rodeListView's{...props}forward intoSchemaRendererand was dropped.Grepping the whole repo for
refreshKey?:as a prop declaration returnsplugin-view/ObjectView.tsx(the slot contract),StudioDesignSurfaceitself, and four unrelated components — no view component in the forward chain.It type-checked only because
ListViewPropscarried a[key: string]: anythat erased the whole interface (#4528), so noListViewcall site was checked at all.Why it was invisible
Bumping the prop still re-renders
ListView(new prop object), and the Studio grid re-renders constantly anyway because itsschemais a fresh object literal on every render. So "the list looks like it updates" — it just never refetches because ofrefreshKey.The working precedent, 300 lines away
packages/app-shell/src/views/ObjectView.tsx:1592consumes the same slot value correctly — it folds it into React'skeyso the subtree remounts:Current state
#4528's PR removes the dead prop (behaviour-preserving) and leaves a comment at the declaration pointing here. So today the Studio Data pillar has no
refreshKeypath at all, which is what it effectively had before — the difference is that it is now visible.Worth deciding
Whether the Studio Data pillar needs the slot refresh at all. If it does, the
keyfold above is the established shape. If it does not, the slot prop can be dropped fromrenderStudioGridList's signature entirely. Someone who knows what user action is meant to trigger it (record create / delete / publish?) should judge, since "remount the grid" also discards scroll position, selection and in-progress inline edits — AGENTS.md #8's "refresh data, don't rebuild UI" argues against thekeyfold as the long-term answer even though it is what the sibling does.Refs #4528, #1126.
Generated by Claude Code