Skip to content

fix(plugin-dashboard): the editable grid's object-bound table fetches its rows, and its object-bound pivot shows the retired-widget placeholder (objectui#10528) - #10596

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10528-grid-object-table-pivot-fetch
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10528-grid-object-table-pivot-fetch

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10528

Clause-②: no

What changed

The editable dashboard-grid (DashboardGridLayout) turned a table-family widget whose data is { provider: 'object', object } into a static data-table node. That node had data: [] and an objectName that data-table never reads. So the tile drew an empty table and sent no query. DashboardRenderer turns the same stored widget into object-data-table, which fetches its rows.

The grid's table arm now emits the same node DashboardRenderer's table arm emits, prop for prop:

Prop Grid before Grid now (same as the renderer)
type data-table object-data-table
data [] not set (the node fetches)
filter not set the provider's filter, else the widget's filter
searchable / pagination always false widget.searchable ?? false / widget.pagination ?? false; always false on a list
drillDown not set options.drillDown ?? { enabled: true, mode: 'record' }
objectName, className, and the ...options spread without data unchanged unchanged

Files changed, round 1 (the table half):

  • packages/plugin-dashboard/src/DashboardGridLayout.tsx: only the provider: 'object' branch of the table arm;
  • one new pin file;
  • one changeset.

Rounds 2 and 3 (below) add the pivot half and two prose corrections: at the head the PR changes six files. DashboardGridLayout.tsx's provider-object pivot branch, DashboardRenderer.tsx's pivot-arm comment (comment only), the 7353 pin's grid-pivot row and the pending 7353 changeset's prose all move. These stay byte-unchanged: the static-data branches of the table and pivot arms.

drillDown on the editable grid: kept on by default, as on the renderer

I measured this with a one-off probe on the fixed tree, in "Edit Layout" mode. The probe was not committed.

happy-dom gives the grid a zero width and a null offsetParent, and react-grid-layout's drag start returns early without an offsetParent. So the probe stubbed both. In the first two runs, without those stubs, the positive control did not fire either, so I discarded those readings.

Probe leg Reading
drag handle rendered in edit mode true
row cell is inside .drag-handle false
press and move on the row cell starts a drag (react-draggable-dragging) false
press and move on the drag handle starts a drag (positive control) true
row click opens the record drawer (record-detail-body) true

On the grid, a drag can start only from dragConfig.handle: '.drag-handle'. The grid also has no widget selection: there is no onWidgetClick or selectedWidgetId, unlike DashboardRenderer's design mode. So a row click cannot start a drag or a selection, and the renderer's default stays as it is. There is no difference to pin, and the parity pin keeps the two nodes equal.

Round 1: the provider-object pivot arm stopped at the decision

I measured the dispatch's pivot hypothesis, and the pivot half stops here:

  • object-pivot (ObjectPivotTable) exists and is registered. It fetches through objectName and filter, then cross-tabs the rows client-side over rowField / columnField / valueField / aggregation. It reads no provider aggregate.
  • Mapping the grid's pivot arm to it would contradict ADR-0021 in the tree's own words:
    • widgetDispatch.ts documents the family as "Cross-tab — dataset-bound only; a non-dataset pivot is stale metadata."
    • DashboardRenderer's pivot arm answers every non-dataset pivot with LEGACY_RETIRED_WIDGET_SCHEMA.
    • The spec's widget guidance set LEGACY_WIDGET_ANALYTICS_KEYS names rowField / columnField / valueField / aggregation as part of the removed pre-ADR-0021 inline analytics shape.

Round 1 left that arm untouched and took the question to the card, analysed on the four axes. The seat decided it (option B) in the amended claim 5831757648; round 2 below carries it out.

Round 2 (claim 5831757648, head 7a4f697)

The seat took option B for the pivot half (amended claim 5831757648): the grid's provider: 'object' pivot arm is retired, the same answer the read dashboard gives (ADR-0021).

  • DashboardGridLayout.tsx: the pivot arm's provider-object branch returns LEGACY_RETIRED_WIDGET_SCHEMA, imported from ./legacyRetiredWidget (the renderer's own source; no second copy). The static-data pivot branch is byte-unchanged.
  • DashboardRenderer.tsx: comment only (0 non-comment lines). The pivot-arm comment now says the grid still draws static-data pivots and answers the provider-object pivot with the same placeholder.
  • widgetDataProviderRetired-7353.test.tsx: the grid-pivot row that pinned the removed branch is replaced by a row pinning the placeholder (identity), with no find call, citing the claim.
  • New pins in DashboardGridLayout.objectProviderFetch-10528.test.tsx: an object-provider pivot on the grid renders the placeholder and calls no find; the grid and DashboardRenderer hand SchemaRenderer the same placeholder object for the same widget. The static-data table and pivot lit controls and all table-half pins stay green.
  • Reverse verification: with DashboardGridLayout.tsx at the round-2 base, Tests 3 failed | 12 passed (15) (exit 1; exactly the three pivot cases); at 7a4f697, Tests 15 passed (15) (exit 0).
  • Producer census re-measured with a quote-agnostic pattern and positive controls: still zero provider-object pivot producers in either tree.
  • Gates on 7a4f697: the plugin-dashboard suite plus 13 cross-package readers (143 files, 1712 tests, exit 0); type-check for @object-ui/plugin-dashboard (exit 0); eslint (0 errors, no new warnings); the control-byte, test-path, vi-mock, line-citation and changeset gates (exit 0 each); check-governed-queue-guard NOT GOVERNED. CI: 43 check-runs, 40 success, 3 skipped, 0 failed.
  • The changeset now covers both halves and stays patch.

Pins

packages/plugin-dashboard/src/__tests__/DashboardGridLayout.objectProviderFetch-10528.test.tsx held 8 tests in round 1; round 2 adds the two pivot cases (10 at the head):

  • Grid, provider-object table. The node is object-data-table, with objectName: 'account' and no dataProvider. The adapter's find is called, and only for account. The rows it returns (Acme, Globex) are on screen.
  • Grid, filter. The provider's filter reaches find as $filter.
  • Parity, over 4 widgets. For each widget, the node the grid composes toEquals the node DashboardRenderer composes. The widgets: a plain table; a table with a provider filter, searchable, pagination and columns; a list with searchable and pagination authored; a table with an authored drillDown.
  • Lit controls. On the grid, a static-data table and a static-data pivot draw their authored rows, compose data-table / pivot, and never call find.

Red, then green, run from the committed state. The script is trap-guarded. The restore is proven by the file's blob hash equalling HEAD's and by an empty git diff HEAD.

Tree Pin result Exit
DashboardGridLayout.tsx at base e5f4343 6 failed, 2 passed (the two lit controls). First failure: "Unable to find an element with the text: Acme" 1
HEAD 6e9abea 8 passed 0

Gates, on 6fce3f1 (after merging origin/main at caf0ed0)

  • Tests. pnpm exec vitest run --maxWorkers=2 packages/plugin-dashboard/, plus the 9 test files outside the package that render or read the grid: 139 files (130 package files + 9), 1562 tests passed, exit 0.
  • Type-check. pnpm turbo run type-check --filter @object-ui/plugin-dashboard --concurrency=2: 14/14 tasks, exit 0. The package task was a cache miss and ran both tsc legs, and tsconfig.test.json --listFilesOnly lists the new pin.
  • eslint on the 2 changed files: 0 errors, exit 0. See Acceptance notes.
  • Check gates, each at exit 0 with its own verdict line clean:
    • check:control-bytes, check:test-path-roots
    • check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape
    • check:new-line-citations (0 new; report-only)
    • check:changeset-claims, check:pending-changeset-literals
    • check-changeset-presence, check-changeset-no-major
  • Governed surface. check-governed-queue-guard --test on the 3 paths: NOT GOVERNED.

Acceptance notes

  • eslint narrowing.
    • --format json counts 2 files linted.
    • The grid file has the same 7 warnings, by rule, on base and head.
    • The new pin adds 2 no-explicit-any warnings, in the listener mock copied from widgetDataProviderRetired-7353.test.tsx. This repo deliberately leaves --max-warnings unset.
    • Type-aware linting is not enabled, and no eslint-rules/ rule reads other files. So this diff cannot change the lint verdict of any file it does not touch.
    • The repo-wide pnpm lint run is CI's.
  • The 7353 pin's grid table row. In widgetDataProviderRetired-7353.test.tsx, the grid table row still passes. It now reads the data-table node that ObjectDataTable renders onward, not a node the grid composes. The new pin checks that the grid's own object-data-table node carries no dataProvider. The row's name ("becomes a data-table node") now describes that onward node. That table row is left as it is; round 2 edits only the file's grid-pivot row, which the amended claim 5831757648 brought into the surface.
  • The pending 7353 changeset. Round 2 made its sentence about the grid's data-table / pivot nodes false (the grid now builds object-data-table for a provider-object table and the retired-widget placeholder for a provider-object pivot). The prose round (3e3c9ae, seat-authorized) rewrote those two sentences so each reads true at the head; its front matter is byte-identical.
  • Changeset name. It is 10528-grid-object-table-fetch.md, not the dispatched 10528-grid-object-table-pivot-fetch.md; the name dates from round 1. It now covers both halves, and it is kept as is because the presence gate passes with it.
  • The grid's dataSource prop. The grid hands its dataSource prop to DatasetWidget only. The object-* nodes it composes, the new table node included, read the adapter from SchemaRendererContext. DashboardRenderer also passes its prop to each child SchemaRenderer. Not measured as reachable: no host in this repo mounts the grid outside a provider. Carrier: none. Noted, not filed.
  • DashboardRenderer's pivot-arm comment. It says the grid "does still draw pivots from static data and from the provider: 'object' config". In effect, the second half has been false: the grid draws an empty pivot and sends no query. This belongs to the pivot decision.
  • Docs. No README or content/docs statement covers the grid's object-bound tables (checked by grepping for dashboard-grid and DashboardGridLayout), so no doc edit is owed.

Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy


Generated by Claude Code

…fetches its rows (objectui#10528)

DashboardGridLayout mapped a table-family widget whose data is
`{ provider: 'object', object }` to a static `data-table` node with
`data: []` and an `objectName` that `data-table` never reads, so the tile
drew an empty table and issued no query. DashboardRenderer maps the same
stored widget to the self-fetching `object-data-table`.

The grid's table arm now emits that node with the renderer's props: the
provider filter, the declared searchable/pagination (never on a list), and
default-on drill-to-record. Drag stays gated to the `.drag-handle`
element, and the grid has no widget selection, so the row drill does not
fight the editor. A parity pin holds the two copies equal. The static
branches and the provider-object pivot arm are unchanged.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.3 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-BTAPGKiQ.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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.68KB 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) 546.32KB 130.64KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 258.79KB 65.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.67KB 35.41KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.59KB 61.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.31KB 21.78KB
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.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

…vot like the read dashboard (objectui#10528)

Round 2 of the card, amended claim 5831757648 (option B). The grid's
pivot arm turned a `provider: 'object'` pivot into a static `pivot` node
with `data: []` and an `objectName` PivotTable never reads, so it drew an
empty cross-tab and sent no query. DashboardRenderer answers every
non-dataset pivot with LEGACY_RETIRED_WIDGET_SCHEMA, since ADR-0021 puts
a cross-tab on the dataset layer only. The grid's provider-object pivot
branch now returns that same shared object. The static-data pivot branch
is unchanged.

DashboardRenderer's pivot-arm comment (comment only) now says the grid
still draws static-data pivots and retires the provider-object shape.
The 7353 pin's grid-pivot row, which pinned the removed branch, is
replaced by a row pinning the placeholder. The 10528 pin gains a
placeholder case and a same-object parity case against the renderer.
The changeset covers both halves and stays patch.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.7 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-Lo3YhaE7.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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.68KB 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) 546.37KB 130.65KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.41KB 65.82KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.54KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.31KB 21.78KB
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.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

…objectui#10528

The pending `.changeset/7353-retire-widget-dataprovider.md` said the
grid's provider-object `data-table` and `pivot` nodes still write
`objectName`. After objectui#10528 the grid builds the self-fetching
`object-data-table` for such a table widget and shows the retired-widget
placeholder for such a pivot widget, which writes no `objectName`. Only
those two sentences of that bullet change; the front matter and every
other sentence are byte-identical. Prose only, authorized by the seat for
this card's ACCEPT.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot changed the title fix(plugin-dashboard): the editable grid's object-bound table widget fetches its rows (objectui#10528) fix(plugin-dashboard): the editable grid's object-bound table fetches its rows, and its object-bound pivot shows the retired-widget placeholder (objectui#10528) Sep 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.8 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-4vtbYIEA.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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.68KB 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) 546.99KB 130.83KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.68KB 65.73KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.54KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.60KB 28.31KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
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.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3e3c9aeedaa2231afe3e6b700669a1a8b491e5c7

① Derived judgments

Read: card objectui#10528 (body); triage 5830081995; claim 5830327059 and the amended claim 5831757648 (the binding one); PR #10596 body, file list and check-runs; the diff of refs/review/pr-10596 (3e3c9ae) against the merge-base caf0ed0 with refs/review/main (ac2d6f1); AGENTS.md, .changeset/config.json; ADR-0021 at /home/user/objectstack/docs/adr/0021-analytics-dataset-semantic-layer.md. The os-dev-report comment 5831159459 was not used as evidence. Six files change: .changeset/10528-grid-object-table-fetch.md (new), .changeset/7353-retire-widget-dataprovider.md (prose only), packages/plugin-dashboard/src/DashboardGridLayout.tsx, packages/plugin-dashboard/src/DashboardRenderer.tsx (comment only), the new pin packages/plugin-dashboard/src/__tests__/DashboardGridLayout.objectProviderFetch-10528.test.tsx, and packages/plugin-dashboard/src/__tests__/widgetDataProviderRetired-7353.test.tsx.

Behaviour changes, item by item (line numbers at the head):

  1. Table arm, provider: 'object' branch, DashboardGridLayout.tsx:391-404. RIGHT. It now returns type: 'object-data-table', ...restOptions (the widget options minus data), objectName: widgetData.object, filter: widgetData.filter || widget.filter, searchable: isList ? false : (widget.searchable ?? false), pagination: isList ? false : (widget.pagination ?? false), drillDown: options.drillDown ?? { enabled: true, mode: 'record' as const }, className: "border-0". Compared key by key with DashboardRenderer.tsx:784-796: identical (the renderer computes isList at 781 outside the branch, the grid at 392 inside it; same value). This is the triage's execution note verbatim: "map the object-provider arms to the self-fetching nodes DashboardRenderer uses." ObjectDataTable.tsx:714-716 forwards schema.filter as $filter into dataSource.find(schema.objectName, params), so the tile now issues the query. DashboardWidgetSchema (packages/types/src/complex.ts, members filter?, searchable?, pagination?) declares the three top-level keys the arm reads, so the reads are typed. list sits in TABLE_LIKE_TYPES (widgetDispatch.ts:67), so a list widget takes this arm as the card and the changeset say.

  2. Pivot arm, provider: 'object' branch, DashboardGridLayout.tsx:443: if (isObjectProvider(widgetData)) return LEGACY_RETIRED_WIDGET_SCHEMA;. RIGHT against the amended claim 5831757648: "the grid's provider: 'object' pivot arm returns LEGACY_RETIRED_WIDGET_SCHEMA, the same answer as the read dashboard." The placeholder is the renderer's own object: the grid imported LEGACY_RETIRED_WIDGET_SCHEMA from ./legacyRetiredWidget at line 12 already at the merge-base (the same module DashboardRenderer.tsx:45 imports and returns at 837), the diff adds no import and no second declaration (no new content: string anywhere in it), and the pin at new-file 227-243 holds identity across both surfaces. ADR-0021 (Status "Accepted & shipped"; D2 "report becomes a pure pivot presentation over a dataset"; DashboardWidgetSchema loses object, valueField, aggregate and the inline query keys) and widgetDispatch.ts:88 ("Cross-tab — dataset-bound only; a non-dataset pivot is stale metadata") support the retirement rather than a mapping to object-pivot.

  3. Static-data table branch DashboardGridLayout.tsx:406-425 and static-data pivot branch 445-449: byte-identical to merge-base lines 393-412 and 428-432 (a diff of the extracted ranges is empty). The amended claim's "⛔ The grid's static-data pivot arm is not touched" holds. The dataset-bound fork at the render site (datasetBound, lines 593 and 658) is untouched, so a dataset-bound pivot still renders through DatasetWidget.

  4. DashboardRenderer.tsx:826-835: comment only; the arm at 836-838 is unchanged (0 non-comment lines in the hunk). The old sentence said the grid draws pivots "from static data and from the provider: 'object' config"; the new text says it still draws static-data pivots and answers the provider-object pivot with this same placeholder, imported from the same module. Both halves are true at the head. The amended claim names this comment as in-surface. RIGHT.

  5. Nothing done that was not asked: every changed path is named in the amended claim (the grid's two provider-object branches, the renderer's pivot-arm comment, the 7353 pin's grid-pivot row, pins beside the grid tests, the one 10528 changeset), plus the prose-only edit of the pending 7353 changeset, which the brief records as seat-authorized. The barrel index.tsx, packages/types and every zod schema are untouched (git diff --stat on them is empty).

  6. Nothing asked that is missing: the table arm fix (done), the pivot half per the seat's option B (done), pins for each arm plus lit controls (done, below), and the triage's "Serial after finding(plugin-dashboard): dataProvider is declared on two widget schema types and written by two producers, and no renderer reads it #7353 (PR objectui#10522 edits the same arms): land or rebase on it first" — the merge-base caf0ed0 already carries .changeset/7353-retire-widget-dataprovider.md (blob cf6cc63 on main) and the grid at the merge-base writes no dataProvider, so 7353 landed first.

Existing pins edited or deleted:

  • widgetDataProviderRetired-7353.test.tsx:156-158: the it.each row ['pivot', 'pivot', { rowField: 'region', valueField: 'amount' }] is deleted. The flip was load-bearing only for the removed branch: the row awaited a pivot node with objectName: 'account' for a provider-object pivot on the grid, and at the head that branch emits no pivot node (grid 443), so the row could only time out. Its replacement at 176-192 pins the same widget's new answer: the placeholder by identity (toContain on received), that the placeholder carries no dataProvider, that no pivot node is composed, and that adapter.find is not called. The guard the row carried (no dataProvider on whatever the grid emits for a provider-object pivot) survives in a stronger form. Not weakened.
  • The file's grid table row ['table', 'data-table', {}] (157) is unedited. At the head it passes by reading the onward data-table that ObjectDataTable renders (ObjectDataTable.tsx:1037-1040, ...schemaWithoutBind, type: 'data-table', which carries objectName), while the grid-composed object-data-table node's own no-dataProvider guard now lives in the new pin (137-142). The row's name ("becomes a data-table node") now describes that onward node; the row is outside the claimed surface. Style, not a defect.

New pins can fail:

  • DashboardGridLayout.objectProviderFetch-10528.test.tsx holds 10 tests (7 it/it.each statements, the it.each at 162-178 has 4 rows). Each positive leg fails on the base: 119-143 needs Acme/Globex on screen, find called only for account, and an object-data-table node with objectName and no dataProvider (the base emitted data-table with data: [], so no rows, no such node, no find); 145-158 needs the provider filter to reach find as $filter (resolveFilterPlaceholders in packages/core/src/utils/filter-tokens.ts:235-242 returns a plain filter unchanged); 162-200 needs the grid node to toEqual the renderer node over 4 widgets, with non-vacuity (objectName === 'account', find called on the grid); 217-225 needs the placeholder text on screen and its identity in received (the base composed a pivot node instead); 227-243 needs the identical object on both surfaces. Negative legs are present: no dataProvider, no pivot, no object-pivot, find not called. toContain on an array is identity, so the "same object" legs cannot be satisfied by a copy.
  • Lit controls 247-260 and 262-284 exercise the byte-unchanged static branches: rows drawn, a data-table / pivot node composed, no object-data-table, find not called; the pivot control scopes to data-testid="grid-layout" (grid 469).
  • The vi.mock('@object-ui/react') is a listener that re-exports the real SchemaRenderer, so the nodes are rendered for real (the same shape as the 7353 file); the side-effect imports are at module scope per AGENTS.md's flaky-test rule.

② Semver level

.changeset/10528-grid-object-table-fetch.md declares '@object-ui/plugin-dashboard': patch. The diff changes src/ of a fixed-group published package, so a bump is owed (AGENTS.md's changeset-presence rule, mechanically enforced), and the 版本号策略 section forbids major and asks minor only for objectui's own breaking changes. No export, type, registry name or accept set moves; the table half restores the read dashboard's behaviour on the editable grid, and the pivot half retires a branch that drew nothing and issued no query. patch is correct. (The pending 7353 changeset is already minor, so the group's next bump is the same either way.)

10528 changeset prose, sentence by sentence:

  1. "Fix: the editable dashboard grid (dashboard-grid, DashboardGridLayout) now treats widgets whose data is { provider: 'object', object } the way the read-only DashboardRenderer already does (objectui#10528)." TRUE: identical node for table/list, identical placeholder for pivot (the chart and metric arms already matched).
  2. "Before this change the grid turned a table, list or pivot widget with that config into a static node with data: []." TRUE: merge-base 380-390 (type: 'data-table', data: []) and 418-426 (type: 'pivot', data: []); list is table-family.
  3. "The tile showed an empty table or cross-tab and sent no query." TRUE: the static renderers read no objectName (the card's probe measured 0 find calls; the new lit controls show the static nodes issue no find).
  4. "Table and list widgets now fetch their rows. The grid builds the same node DashboardRenderer builds for the widget: an object-data-table, which loads the object's rows through the data source." TRUE (grid 395; ObjectDataTable.tsx:716).
  5. "The props match too:" TRUE (key-by-key comparison in ① item 1).
  6. "the provider's filter (or the widget's filter) reaches the query;" TRUE (grid 398; ObjectDataTable.tsx:714).
  7. "the widget's searchable and pagination are honoured, and a list never gets them;" TRUE (grid 399-400).
  8. "a row click opens the record in a drawer by default, as on the read-only dashboard." TRUE (grid 401; ObjectDataTable.tsx:643-644 enables record drill, 1062 defaults the target to drawer).
  9. "options.drillDown still overrides this." TRUE (options.drillDown ??).
  10. "In edit mode, dragging still starts only from the drag handle." TRUE (dragConfig={{ enabled: editMode, handle: ".drag-handle" }}, grid 548, unchanged).
  11. "Pivot widgets with that config now show the retired-widget placeholder." TRUE (grid 443).
  12. "It is the same placeholder DashboardRenderer already shows for them ("This widget uses a retired data format. Edit it to bind a dataset.")." TRUE (legacyRetiredWidget.ts:56-62; renderer 837).
  13. "Under ADR-0021 a pivot is bound to a dataset, and a dataset-bound pivot still renders as before." TRUE (ADR-0021 D2; the grid's datasetBound fork at 593/658 is untouched).
  14. "Widgets bound to inline rows are unchanged, including static-data pivots." TRUE (byte-identical ranges in ① item 3).

Pending .changeset/7353-retire-widget-dataprovider.md, edited prose-only in 3e3c9ae: front matter byte-identical to main ('@object-ui/plugin-dashboard': minor, '@object-ui/types': minor; a diff of the two front-matter blocks is empty). The edited sentences (lines 17-23):

  1. "DashboardRenderer no longer writes dataProvider on the object-data-table node, and DashboardGridLayout no longer writes it on any node it builds for a provider: 'object' widget." TRUE: the grid's four provider-object emissions (object-chart 310-325, object-metric 353-364, object-data-table 394-403, the placeholder at 443) carry none.
  2. "Since objectui#10528 the grid builds that same self-fetching object-data-table node for such a table widget, and shows the retired-widget placeholder for such a pivot widget." TRUE.
  3. "The object-data-table node still writes objectName on both surfaces, and the object-backed table widget fetches through it." TRUE (grid 397, renderer 789; ObjectDataTable.tsx:716).
    The sentence it replaced ("DashboardGridLayout no longer writes it on the data-table and pivot nodes. All three still write objectName") would have been false at the head, since the grid no longer builds those two nodes for a provider-object widget; the widening was needed, not cosmetic.

Docs/README prose added by the diff: none. No content/docs/** or README file changes, so no empty-frontmatter changeset question arises. Checked that no existing doc sentence describes the grid's object-bound table or pivot arms: content/docs/api/schema-reference.md:1043, content/docs/plugins/plugin-dashboard.mdx:105 and packages/plugin-dashboard/README.md:21,81,644-664 name the grid only for layout and registration, so no doc edit is owed.

③ Boundary flags

PR body flags and open questions:

  • drillDown kept on by default on the editable grid: the grid has no onWidgetClick or selectedWidgetId (0 hits; the renderer has 17) and its drag starts only from .drag-handle (grid 548), so a row click can neither drag nor select; parity with the renderer is the right choice. The probe was not committed and is not re-run here.
  • "Changeset name … because it ships no pivot change": stale at the head, which ships the pivot change (grid 443) and whose changeset prose covers it; the filename 10528-grid-object-table-fetch.md is cosmetic.
  • "The 7353 pin's grid table row … The file is outside this claim's surface, so it is not edited here": stale; the amended claim widened the surface to that file's grid-pivot row, which round 2 edits. The table row is indeed left alone.
  • "The pending 7353 changeset … stays true": stale; commit 3e3c9ae edits it (judged in ②; true at the head).
  • "What changed / Files changed" (three files; "DashboardRenderer.tsx is not edited"; "byte-unchanged: … the provider-object pivot arm") and "Pins … holds 8 tests": round-1 text left standing. At the head six files change, the renderer's comment is edited, the provider-object pivot arm is changed, and the pin file holds 10 tests. The body's own "Round 2" section states the current facts, and the PR title is current. The body should be reconciled before the PR leaves draft; a stale body is not a defect in the change.
  • Gate numbers on 6fce3f1 and 7a4f697, and the red/green reverse verification, are the author's measurements and were not re-run here. The 15-test total (10 in the new file plus 5 in the 7353 file) matches the files at the head.
  • "The grid's dataSource prop … Noted, not filed": out of scope; both surfaces' pins wrap the render in SchemaRendererProvider, which is how the composed object-data-table reads its adapter.
  • The spec symbol LEGACY_WIDGET_ANALYTICS_KEYS is cited as rationale only: @objectstack/spec is not installed under /home/user/objectui/node_modules in this container, and the diff imports nothing from the spec, so nothing in the change turns on it. (objectui's own scrub list, WidgetConfigPanel.tsx:599-606 LEGACY_ANALYTICS_KEYS, does name rowField, columnField, valueField and aggregation.) Not verified against the installed package; not load-bearing.

CI at the head 3e3c9ae: 43 check-runs, 40 success, 3 skipped, 0 failed, 0 in progress (final poll 12:57Z). Earlier polls: 12:47Z 42 runs, 27 success / 3 skipped / 12 in progress; 12:51Z 42 runs, 33 / 3 / 6. No conclusion other than success or skipped at any poll. The three skipped are dependabot, Test (coverage) and Test (coverage shard …/4), path-filter skips that AGENTS.md counts as green.

Draft: yes (draft: true), as a dispatched PR must be (AGENTS.md: a PR from a dispatch stops at draft; the dispatch seat flips it ready and queues it). mergeable: true, mergeable_state: behind (GitHub's recorded base sha 50e41f7 predates the branch's own merge of origin/main at caf0ed0 in 6fce3f1).

Clause-②: no holds. No accept set moves (DashboardWidgetSchema and every zod schema untouched), no published export or registry name moves (packages/plugin-dashboard/src/index.tsx unchanged, object-data-table, object-pivot, pivot and dashboard-grid registered as before). What changes is which registered node two renderers emit for the same stored widget.

Main movement: refs/review/main is ac2d6f1, 22 commits past the merge-base caf0ed0; git diff --stat caf0ed0 ac2d6f1 -- restricted to the six PR files is empty. Main did not move under any of the PR's files.

Implemented-by: claude/issue-10528-grid-object-table-pivot-fetch
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 13:03
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 668dd59 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10528-grid-object-table-pivot-fetch branch September 25, 2026 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants