Skip to content

chore(app-shell): the dead slot refreshKey parameter is removed (#4549) - #4571

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4549-studio-grid-refresh
Aug 13, 2026
Merged

chore(app-shell): the dead slot refreshKey parameter is removed (#4549)#4571
yinlianghui merged 1 commit into
mainfrom
claude/issue-4549-studio-grid-refresh

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #4549

The premise did not survive measurement

The card says the Data pillar "has no refreshKey path at all". Measured on current main: it has one, and it works. The slot's refresh signal reaches the grid today.

plugin-view's ObjectView holds one refreshKey useState and hands it to the renderListView slot twice:

  • schema.refreshTriggerpackages/plugin-view/src/ObjectView.tsx:1057
  • a bare refreshKey argument — same call, :1063

Only the first is a real input. ListView lists schema.refreshTrigger in its fetch effect's dependency array (packages/plugin-list/src/ListView.tsx:1609); refreshKey is a prop of neither ListView nor anything it renders — the dead forward #4528 measured and removed. And renderStudioGridList spreads the slot schema into ListView's schema, so refreshTrigger rides that spread. The pillar has been refreshing through it since f591c5d (2026-03-25).

So the ruling's preference order lands on option 1 already satisfied: ListView's existing declarative refresh input is not merely available, it is already connected. Nothing needed wiring — no key-fold, no handle, no remount, and no plugin-list change. Scroll position, selection and in-progress inline edits were never at risk.

Step 0 — does the signal bump on the Studio path?

Yes, via one of the six increment sites. The other five are unreachable from this mount:

site reachable in Studio
:327 onMutation auto-subscribe no — guarded by if (renderListView) return;
:586 handleDelete no — wired only to the built-in ObjectGrid branch
:591 handleBulkDelete no — same branch
:598 handleFormSuccess yes — the slot passes onEdit/onAddRecord, Studio wires both into ListView's toolbar, and a successful submit bumps
:608 handleRefresh no — never referenced anywhere (filed as #4568)

Since it bumps, deletion-for-never-bumping was not the outcome; deletion still is, for the corrected reason that the same number already arrives on a live channel.

What changed

  • The unread refreshKey parameter is dropped from renderStudioGridList's signature.
  • The comment claiming wiring it would be a behaviour change is corrected — it asserted the opposite of what is true, and would have sent the next reader to build a second refresh path on top of the working one.
  • A comment on the spread pins why refreshTrigger must not be dropped or shadowed there.
  • New tests pin the channel.

Tests

packages/app-shell/src/views/studio-design/StudioDesignSurface.gridRefresh.test.tsx, driving the real slot function:

  1. bumping the slot's signal refetches;
  2. it keys off the signal VALUE, not renders — three re-renders each handing a fresh schema literal with an unchanged counter produce zero extra fetches (the ruling's no-storm pin);
  3. the real plugin-view ObjectView emits schema.refreshTrigger and refreshKey in lockstep, which is what makes the harness in 1 and 2 faithful rather than a fiction.

Reverse verification

A behaviour-neutral deletion has no red-first direction, so the honest check is severing the live channel instead. Adding refreshTrigger: undefined after the spread turns test 1 red:

AssertionError: expected 1 to be greater than 1

Tests 2 and 3 stay green there, correctly — no refetch at all still satisfies "no storm", and the producer contract is untouched. Restore verified by sha256 (b09de723c17ed610...).

Verification

  • pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/studio-design/ — 25 files, 144 tests passed (the fix(app-shell): the default-inspector family and its panel hosts gate Save on CEL errors (#4527) #4558 panel/inspector suites among them, untouched).
  • pnpm --filter @object-ui/app-shell type-check — both tsc passes clean.
  • ESLint net zero on the touched files: 0 errors / 15 warnings, identical to the origin/main baseline. The test-only export trips react-refresh/only-export-components (a render-prop callback is not a component), so it carries one narrowly-scoped, justified disable.
  • check:control-bytes, check:phantom-deps, check-changeset-presence green; control-byte self-scan over every touched file clean.

Changeset

Empty frontmatter — the gate's documented first-class answer for a no-behaviour-change src/ edit. Verified both ways: @object-ui/app-shell's public dist/index.d.ts is byte-identical before and after (index.ts re-exports a named list, and package.json exports only .), so the test-only export widens nothing. Note skip-changeset is not a mechanism in this repo — scripts/__tests__/ci-cd-pipeline-doc.test.ts:184 records that neither that workflow nor that label was ever real.

Findings filed, not fixed


Generated by Claude Code

The Studio Data pillar's grid slot declared a `refreshKey` parameter it never
read. #4528 removed the dead forward to `ListView` (which declares no such
prop) and left the parameter behind for triage.

Measured before changing anything: the pillar was never missing its refresh.
plugin-view's ObjectView holds ONE `refreshKey` state and emits it to the slot
twice — as `schema.refreshTrigger` (:1057) and as a bare `refreshKey` (:1063).
`schema.refreshTrigger` is the only one `ListView` reads (its fetch effect's
dependency array), and `renderStudioGridList` spreads the slot schema into
`ListView`'s schema, so that signal already arrives. Bumping it refetches;
severing it in reverse verification turns the new test red.

So the parameter is dropped as dead, the comment asserting the opposite is
corrected, and the live channel gains pin tests — including one that the
refetch keys off the signal VALUE, not renders, and one that the producer
really does emit both keys in lockstep.

Empty-frontmatter changeset: no behaviour change, and the package's public
`index.d.ts` is byte-identical before and after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 13, 2026 11:19am

Request Review

@github-actions github-actions Bot added the tests label Aug 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-Blymy7nn.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 38.46KB 10.17KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.32KB 108.45KB
core (index.js) 3.37KB 1.34KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 163.56KB 44.83KB
fields (index.js) 230.37KB 57.17KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.86KB 12.91KB
plugin-charts (index.js) 62.10KB 17.67KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 120.99KB 31.55KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.88KB 59.99KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.30KB 40.02KB
plugin-grid (index.js) 189.37KB 50.33KB
plugin-kanban (index.js) 52.74KB 14.53KB
plugin-list (index.js) 111.13KB 27.12KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.16KB 10.96KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.08KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.73KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

PM step-7 复核 — ACCEPT (session_017Qqyix2QcnpUC9XeYVDzx3)

Auto-merge armed (squash) — landing verified per the merge-queue discipline. #4567 will be dispatched next, gated on this landing.


Generated by Claude Code


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 13, 2026 11:29
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit ffca56f Aug 13, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4549-studio-grid-refresh branch August 13, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio Data pillar ignores its renderListView slot's refreshKey — the prop was forwarded to a component that never declared it

2 participants