Skip to content

fix(plugin-dashboard): a dataset-less provider: 'object' metric widget draws the retired-format placeholder on both surfaces (objectui#11525) - #11538

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11525-inline-metric-retired
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11525-inline-metric-retired

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11525

Clause-②: no (narrowing)

The maintainer's ruling C on objectui#11525 (record 5964914121) retires one stored widget form. The form is a dataset-less single-value widget whose options.data (or widget-level data) is { provider: 'object', … }. The single-value family is metric, gauge, solid-gauge, kpi and bullet, plus a typeless widget, which resolves to metric since objectui#11514.

  • Both surfaces. The metric arms of DashboardRenderer and DashboardGridLayout now return the shared LEGACY_RETIRED_WIDGET_SCHEMA. It is the same imported object the pivot arms already return for that input (objectui#10528).
  • The producer is gone. The flat object-metric node both arms used to build is removed. That node carried an ObjectQL-dialect filter that no node type declares.
  • What does not move. The inline chart and table arms are untouched. No zod or TS authoring face moves.

Implemented by the domain:spec @ objectui seat's dev, session session_01XvhGmGAP79ZB8swnkapxPC, under the PM claim 5965174453.

Before and after, per surface

The before/after run went through the real SchemaRenderer and real registrations. The adapter's aggregate answers 4242 and queryDataset answers 510000. "Before" is base 9d7419b9; "after" is this branch.

surface widget before after
DashboardRenderer metric, provider options.data "Pipeline 4,242", 1 aggregate call the placeholder, 0 calls
DashboardRenderer kpi, same data heading "Pipeline", then "Pipeline 4,242" heading "Pipeline", then the placeholder, 0 calls
DashboardRenderer no type, same data "Pipeline 4,242", 1 call the placeholder, 0 calls
DashboardRenderer pivot, same data (control) heading "Deals", then the placeholder unchanged
DashboardRenderer metric bound to a dataset (control) "Revenue 510000", 1 queryDataset call unchanged
DashboardGridLayout the same five rows the same readings as above the same readings as above

The placeholder text is "This widget uses a retired data format. Edit it to bind a dataset."

Chrome, measured (the dispatch's H2). The tile's chrome follows the widget's family, not the placeholder.

  • A dataset-less metric (and a typeless widget) is self-contained on both surfaces, so its tile draws the placeholder without the widget's heading. That is the same tile the retired top-level object metric shape already drew, and a pin holds the two DOMs equal.
  • gauge, solid-gauge, kpi and bullet take the Card chrome, so their heading is drawn above the placeholder, as the pivot's is.
  • I did not change the chrome. Doing so would also move the legacy-shape metric's tile, which no ruling covers. This is listed under Acceptance notes and in the report's open question.

What stays, and why: the filter broadcast's object-metric member

DashboardRenderer's FilterableComponentType / FILTERABLE_COMPONENT_TYPES keeps object-metric. The member is not dead after the producer goes, measured:

  • An author's legacy component envelope holding an object-metric node still reaches the merge.
  • The filter bar scopes it today. A probe got filter: { region: 'EMEA' } on that node, for both an untyped and a custom envelope.
  • Dropping the member would silently stop that, which the ruling did not ask for.

Its docblock now says so, and a pin holds it. DashboardGridLayout has no filter bar, so it has no such plumbing.

The pins

The pins are in packages/plugin-dashboard/src/__tests__/inlineObjectMetricRetired-11525.test.tsx: 29 tests, both surfaces.

  • Subjects. Each single-value type with provider options.data is read off METRIC_LIKE_TYPES, not restated. Widget-level data and a typeless widget are cases too. Each draws LEGACY_RETIRED_WIDGET_SCHEMA, checked by identity on the recorded node. The test also reads the placeholder text on screen, finds no object-metric node, and sees no aggregate, find or queryDataset call. One more case holds that the retired metric tile's DOM equals the retired legacy-shape metric tile's.
  • Controls. The provider pivot draws the same placeholder object, and a dataset-bound metric draws "510000". A static metric draws its options.value through plugin-dashboard:metric. A provider bar still builds an object-chart node naming deal, and a provider table still builds object-data-table and fetches. An envelope object-metric receives { region: 'EMEA' } from the filter bar and queries.

Reverse verification. The fix was committed first (856f56f0). The two producer files were then checked out at 9d7419b9, with a trap restoring them from HEAD.

  • Before running. On disk, the base object-metric producer lines were 2 in DashboardRenderer.tsx and 1 in DashboardGridLayout.tsx. The renderer's 2 are the producer and the kept union member. The retired-arm marker read 0 and 0.
  • Result. The run gave 16 failed, 13 passed (29). The failures are every subject (8 per surface); the passes are every control. This is the direction stated in the file header.
  • Restore. The restored blob hashes equal HEAD's, git diff HEAD on both files is 0 bytes, and the rerun is 29 passed.

Corpus re-judged (H3)

The census. I ran git grep over the whole tracked tree for a file that pairs a provider: 'object' literal with a metric-family type. It found no test, fixture, story or example that authors a dataset-less inline provider metric through a dashboard surface or asserts its number. The hits are prose, CHANGELOGs, this card's own files, and two plugin-dashboard tests whose provider widget is a bar.

The full plugin-dashboard suite was green before this branch's pins were added. Verdicts:

  • DashboardGridLayout.legacyRetired.test.tsx: its header said the nested provider config is live for every family. It gets a dated note. Its provider control is a bar, so the control stands as written.
  • DashboardGridLayout.datasetPath.test.tsx, widgetDataProviderRetired-7353.test.tsx, dashboard-producer-node-types-11514.test.tsx and the three DashboardChart.* files: these are provider bar, table or pivot widgets, all live or already retired. Left alone.
  • DashboardRenderer.legacyRetired.test.tsx: it covers the top-level object shape only. Left alone.
  • Docs. content/docs/guide/dashboard-filters.md and content/docs/plugins/plugin-dashboard.mdx each get one paragraph naming the form that now draws the prompt. In packages/plugin-dashboard/README.md, the filters example's comment does the same, and the accent table's "inline object-metric" now reads "an object-metric block".
  • Comments falsified by this change: legacyRetiredWidget.ts (the dated, append-only correction the ruling names), ObjectMetricWidget.tsx's aggregate docblock (it said both relays compose this node), and the render-site and broadcast comments in the two surfaces.

Pending changesets

  • New entry. .changeset/11525-inline-metric-retired.md (@object-ui/plugin-dashboard: minor) states the render change: what the tile showed before, what it shows now, and the fix, which is to bind a dataset.
  • Dated note. .changeset/11514-dashboard-slot-entry-types.md gets one: its "Every widget that names a known family draws as before" no longer holds for this form. Its frontmatter is unchanged.
  • Left alone, and why. I read every pending entry that names the inline provider, object-metric, ObjectMetricWidget, legacyRetiredWidget, objectui#4612, objectui#10528, objectui#11479 or the filter broadcast.
    • 11348-dashboard-widget-reads still holds: the type guard still covers the same three object-* types.
    • 10528-grid-object-table-fetch and 7353-retire-widget-dataprovider cover the table and pivot only.
    • The object-metric block entries (8613, 8970, 9002, 10221, 10356, 10572, 10993, 11254, 11444, 11475, 7352, 7752, 11506) describe the widget, which stays live as an authored block. None says a dataset-less provider widget reaches it.
    • check:changeset-claims named three entries for files this branch edits: 11483-metric-card-needs-value, 8114-detail-tab-activity-timeline and 8760-unfulfilled-chart-stubs. Their paragraphs concern metric-card, a detail tab and line-chart, which this branch does not move.

V1 census (H4)

This was a throwaway worktree detached at PR #11512's head ff7f748e. I applied git merge-tree of that head and this branch, which had one conflict, zod-mirror-parity.test.ts; I took V1's side because no plugin-dashboard program reads that file. After a fresh install and a closure build, I ran plugin-dashboard's two programs. Nothing was committed or pushed from the probe, and the probe was removed.

  • As is, with this change: 4 lines per program (8 in all), only the four SchemaRenderer cast sites, two in each surface.
  • With the probe-only annotation that types both getComponentSchema returns as the renderer prop:
    • Base producers: 12 lines per program. That is 5 object-metric lines (3 in DashboardRenderer, 2 in DashboardGridLayout), 2 plugin-dashboard:metric, 1 static pivot and the 4 casts.
    • This branch: 7 lines per program, with object-metric at 0. Left are 2 plugin-dashboard:metric, 1 static pivot and the 4 casts, which belong to objectui#11466.

Gates

Read at 29b4ab28 unless stated. objectui has no dispatch-gates.mjs; the list is hand-derived from package.json.

  • Closure build. pnpm --filter '@object-ui/plugin-dashboard^...' build exited 0 under the verify lock.
  • pnpm --filter @object-ui/plugin-dashboard run type-check exited 0. The script echoed tsc --noEmit && tsc -p tsconfig.test.json. The test program covers the new pin file: its first run failed on that file's case-table typing, which was then fixed.
  • pnpm exec vitest run packages/plugin-dashboard/ (from the worktree root): 165 files and 1620 tests passed, 6 skipped. This ran at a1c7e645; the only later change is the pin file's stub registration, whose own rerun gave 29 passed.
  • Readers of the touched files, 29 test files: 720 passed. They are the doc readers, sdui-parser's options census, the app-shell DashboardView and preview suites, the console drill-down suites, the schema-catalog gallery render and the widget DOM-leak sweep.
  • Light gates, all exit 0:
    • check:new-line-citations: 0 new.
    • check:control-bytes, check:doc-types and check-changeset-no-major.
    • check-changeset-presence: 1 changeset for 6 published source files.
    • check:changeset-claims and check-changeset-overwrite: reports only, judged above.
  • Targeted eslint over the six touched TS files: 0 errors. Each edited source file has the same warning count as at base. The new test carries 2 any warnings, the same two the objectui#7353 listener carries. This is a targeted run, not the repo lint, which CI owns.
  • NOT MEASURED: check:doc-snippets and check:doc-examples. They exited 2, PREREQUISITE NOT MET, because they need a 34-package build. They compile ts/tsx fences only; this branch edits no such fence and moves no published type.
  • Not reached: check:component-surface-parity and check:sdui-registration-pins. No registration input moves.

Acceptance notes

  • Chrome. A retired dataset-less metric tile (and a typeless one) draws no heading, as the retired legacy-shape metric tile already does. Giving retired tiles Card chrome would move the legacy shape too. Not done; the report carries it as an open question.
  • DashboardGridLayout.tsx line addresses. The one cross-file line address in the paragraph this branch edits was replaced by a symbol citation. Eleven others stay, in the props docblock and the render-site comment. Two of them also carry content that is stale at base: the sibling's as any read of dataset, and a second fork in the sibling's self-contained branch. Their repair is a content correction outside this card. Carrier: none.
  • packages/sdui-parser/src/dashboard-widget-options.ts. Its scope note lists object-metric among the legacy inline spread targets, which no longer holds for the metric family. It is a comment in a package outside this card's surface. Carrier: none.
  • packages/plugin-dashboard/src/index.tsx. ObjectMetricBlock's docblock names "the dashboard grid path" as a props-only host; no dashboard path builds the node now. Comment only. Carrier: none.
  • packages/data-objectstack/src/index.ts. A comment there cites isObjectProvider as how a metric's groupBy arrives. It is a dated measurement (2026-09-08) and is left as written.

Generated by Claude Code

claude added 3 commits October 3, 2026 03:53
…raws the retired-format placeholder on both surfaces (objectui#11525)

Maintainer ruling C on objectui#11525: both dashboard surfaces map a
single-value widget (metric, gauge, solid-gauge, kpi, bullet, and a
typeless widget, which resolves to metric) with no dataset and an
options.data / widget-level data of { provider: 'object', ... } to the
shared LEGACY_RETIRED_WIDGET_SCHEMA, as the pivot arms already do
(objectui#10528). The flat object-metric producer is removed from both
metric arms. The inline chart and table arms are untouched, and the
filter broadcast keeps object-metric in its filterable set, because an
author's component-envelope node still reaches the merge.

legacyRetiredWidget.ts gets a dated correction for the metric family;
the docs page, the filters guide and the README say what the stored
form now draws. New pins in inlineObjectMetricRetired-11525.test.tsx.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…ject metric, a dated note on 11514's entry, and a typed case table in the pin (objectui#11525)

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
… (objectui#11525)

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests labels Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 3 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/11483-metric-card-needs-value.md

  • names content/docs/plugins/plugin-dashboard.mdx → content/docs/plugins/plugin-dashboard.mdx — edited by this change

    • What changed. DashboardWidgetTypeSchema is now the spec's families plus list and custom, so the slot reads a metric-card through its component arm alone. The TypeScript twin, DashboardWidgetTypeName, drops DashboardComponentWidgetType the same way. DASHBOARD_COMPONENT_WIDGET_TYPES still lists metric-card: it is the component arm's type, and only that. - What now refuses that did not. Directly in widgets[], both faces refuse a metric-card with no value. That holds whatever else the card carries: title, description, id and layout, a dataset with values, or its figure under options (options: { value }), which the tolerant face accepted. On its own, DashboardWidgetSchema (zod) refuses type: 'metric-card' at type. On the TypeScript face, 'metric-card' is no longer a DashboardWidgetTypeName, so assigning it to that type is a compile error. - Why no widget key stands in for value. This was measured through the real DashboardRenderer and DashboardGridLayout. A dataset makes the dashboard draw DatasetWidget in the card's place, by a rule that does not read the type; the card is never mounted. options is spread onto the node as literal props: a second spelling of value, not a binding. No other widget key reached the figure. - What does not move. On the TypeScript face, DashboardWidgetSchema['type'] still includes 'metric-card'. That interface is also the read type of every widgets[] entry, and the component arm stays assignable to it. So a metric-card literal with no value still compiles directly in widgets[], while the validator refuses it on both faces. A metric-card in a widget's legacy component envelope is judged as before: there, the tolerant face's BaseSchema fallback still accepts a card that fails the arm. Every metric-card in the schema catalog, the READMEs and the docs fences carries its value. The one docs example that did not, under "TypeScript Support" in content/docs/plugins/plugin-dashboard.mdx, is rewritten.

.changeset/8114-detail-tab-activity-timeline.md

  • names plugin-dashboard/README.md → packages/plugin-dashboard/README.md — edited by this change

    README.md ships in this package's files, so the example went out in every tarball. DetailTabs renders a tab's content through ANGLE-BRACKETS(SchemaRenderer schema={toRenderableSchema(tab.content)} /), which makes content.type an SDUI node position judged by the component registry — so a reader copying the tab got the registry's Unknown component type panel (OBJUI-001) where the timeline should be. Same shape as the line-chart widget in plugin-dashboard/README.md (objectui#7896's census; fixed by objectui#7951) and the fourth known instance.

.changeset/8760-unfulfilled-chart-stubs.md

  • names content/docs/plugins/plugin-dashboard.mdx → content/docs/plugins/plugin-dashboard.mdx — edited by this change

    • At render. SchemaRenderer's lazy branch re-checks hasLazy(type) on every pass and returns the Loading ANGLE-BRACKETS(type)… placeholder. Registry.register() deletes a lazy entry only for keys the loaded module actually registers, so for an unfulfilled key the entry SURVIVES the load and every later pass takes the same branch. Measured on b775500af through the real chain: { "type": "line-chart" } painted role="status" / data-lazy-loading="line-chart" / Loading line-chart…, permanently. Not the OBJUI-001 panel the card expected — no alert, no error, no console warning. A skeleton that never resolves reads to a user as a slow network. - At authoring. A stub is enough to put a key into getKnownTypes(), so check:doc-types and the CLI's generated KNOWN_SCHEMA_TYPES snapshot both blessed all three. content/docs/plugins/plugin-dashboard.mdx taught "type": "line-chart" inside a card body, and every gate was green on it.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 8bfc0012e (merge-base with origin/main): 9 file(s) changed outside .changeset/, read against 2068 pending declaration(s) that publish a body (2700 pending in total). · run

@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3551.1 KB 3574.6 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-DnEr0WvZ.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
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.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 571.09KB 136.86KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 261.51KB 66.22KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.60KB 22.87KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.26KB 38.44KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.43KB 64.53KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 233.13KB 63.89KB
plugin-kanban (index.js) 49.59KB 15.58KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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.81KB 3.58KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.16KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 5.07KB 2.39KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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: 29b4ab282b7e7ff20cccdeb8c176fe25bcd24e66
Local-runs: none

Inputs: card objectui#11525 (body and all four comments: ruling 5964914121, claim 5965174453, dev report 5965452133, seat answer 5965464762), PR #11538 (body, 11-file list, net diff against main), the head tree (one tarball GET, read as text), and the head's check-runs read twice (04:20:58Z and 04:27:03Z). Nothing built, run or re-run.

① Derived judgments

  1. The retirement, both surfaces — RIGHT. DashboardRenderer's metric arm (head line 869) and DashboardGridLayout's (head line 382) return the imported LEGACY_RETIRED_WIDGET_SCHEMA when isObjectProvider(widgetData), with widgetData = widget.data || options.data. The arm is dispatch.family === 'metric', so it serves every member of METRIC_LIKE_TYPES (metric, gauge, solid-gauge, kpi, bullet); a typeless entry resolves to metric through resolveWidgetType on both surfaces, and the renderer's mobile metric row renders through the same renderWidget, so there is no third path. This is the ruled path (5964914121: both surfaces, the existing placeholder, as the pivot arm already does). Reaching the whole single-value family is right, not over-reach: the family arm WAS the producer, and retiring type: 'metric' alone would leave gauge/kpi/… building the undeclared node the ruling exists to remove.
  2. The producer is gone — RIGHT, and no accept set moves. The flat object-metric node with its ObjectQL-dialect filter is removed from both arms. At the head no non-test source under packages/ or apps/ spells a type: 'object-metric' node literal except the kept union member type in DashboardRenderer.tsx:188. The diff touches no zod or TS face; @object-ui/types is untouched; the package's exports are unchanged.
  3. Confinement — RIGHT. The series arms (renderer 774, grid 306) and table arms (renderer 917, grid 411) are not in the diff and keep their isObjectProvider branches; the pivot arms are unchanged; isLegacyRetiredWidget is unchanged. A dataset-bound metric is unaffected because the render-site datasetBound fork picks DatasetWidget on both surfaces regardless of what getComponentSchema returns; the static-metric branch follows the early return and still builds DASHBOARD_NODE_TYPES.metric.
  4. The kept broadcast member — RIGHT, justified by measurement. FilterableComponentType / FILTERABLE_COMPONENT_TYPES keep object-metric. At the head getComponentSchema returns the envelope's node first (entryComponent → toRenderableSchema(toDashboardNodeType(…)), line 711) and the merge at 1028-1031 applies isFilterableComponentSchema to that return, so an authored legacy component envelope holding an object-metric node still reaches the merge. The pin asserts the node's filter equals { region: 'EMEA' } and that aggregate is called. The claim's own condition ("removed where nothing else reads them") was measured not met; the seat accepted (5965464762). The type is module-private, so no public surface moves.
  5. Chrome — RIGHT, nothing moved. isSelfContained = entryType === 'metric' && !datasetBound (renderer 1047, grid 624) is untouched: a retired metric or typeless tile draws the placeholder without the Card heading, exactly as the retired legacy top-level object metric tile already does (the DOM-equality pin holds the two equal), while gauge/solid-gauge/kpi/bullet take the Card heading, as the pivot does.
  6. Text that follows the code — RIGHT. legacyRetiredWidget.ts gets the dated, append-only correction the ruling names (lines 44-58; the detector itself unchanged, as the note says). The ObjectMetricWidget.tsx docblock, both render-site comments, the broadcast docblock, the two docs pages, the README comment and its accent-table row all describe the change as it is. One cross-file line address in the edited grid paragraph became a symbol citation; Line Citation Gate is green.
  7. Pins — RIGHT. inlineObjectMetricRetired-11525.test.tsx: 29 tests through the real SchemaRenderer behind a recording listener; subjects read off METRIC_LIKE_TYPES with a non-vacuity check, plus widget-level data and a typeless widget; the placeholder checked by identity, object-metric nodes counted zero, no aggregate/find/queryDataset call; controls for the pivot arm, the dataset-bound metric (510000), the static metric, the provider bar, the provider table and the envelope broadcast. The dev's reverse verification reads 16 red / 13 green at base, the stated direction. The objectui#4612 grid header gets a dated note; its provider control is a bar, so it stands.

Check-runs on the head (42). Second read, 04:27:03Z: 32 success, 3 skipped (the two coverage jobs and dependabot), 0 failure, 7 in_progress: Test (shard 1/8, 3/8, 4/8, 5/8, 6/8, 7/8) and Spec Main Shape Gate. Green and completed include Type Check, Lint, Test (shard 2/8), Test (shard 8/8), Test (dist pins), Build & E2E, Build Docs, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Declaration, Changeset Claim Re-read, Changeset Overwrite Report, Line Citation Gate, Doc Snippet Type Check, Doc Example Id Check, Doc Component Type Check, Governed Surface Queue Guard. Nothing red bears on the contract; the pending shards and the shape gate are landing gates the merge queue holds on, and they do not change what the diff publishes.

② Semver level

  • .changeset/11525-inline-metric-retired.md — @object-ui/plugin-dashboard: minor — RIGHT. The diff publishes a behaviour change in the package's render of stored metadata: on both surfaces a stored dataset-less provider: 'object' single-value tile stops drawing its number and draws the rebind prompt. That is breaking for stored dashboards, so not patch; major is refused by check-changeset-no-major and the one fixed group; the house spelling for a retired render path is minor with the break stated (7353, 9165, retire-options-shorthand-lift-4356; 10528's patch was the grid catching up to the renderer, not a retirement of a drawing path). The entry states the before (both surfaces aggregated and drew the number), the after (the exact placeholder text, no query), the fix (set dataset, select values), names all six forms (metric, gauge, solid-gauge, kpi, bullet, typeless) and both data spellings, and lists what does not move (chart and table arms, static metric, both faces, the envelope member). Changeset Fixed Group Check and Changeset Bump Policy are green.
  • The claim's line Clause-②: no (narrowing) — RIGHT. The value answers whether the card widens an accept set or a public surface: no — no validator face moves, @object-ui/types is untouched, both faces already refuse a dataset-less widget, and the diff exports nothing new. The arm names the breaking direction: the fleet's reader defines no (narrowing) as "not a widening, but breaking", which is exactly a render retirement with no validator move. The dev's tests and the changeset carry the break in words.
  • Pending-changeset sweep, read not recalled. .changeset tree 3e286b3f878c64e1284300a2bff786477c1dd724 at the head, listed from the git tree: 2,702 entries (2,701 .md plus config.json), not truncated. Every entry was swept as text for the four descriptions (an inline provider metric drawing its number or an object-metric node built for it; the nested provider config live for every family; "every widget that names a known family draws as before"; the filter broadcast reaching an inline metric), and the forty-odd hits were read in full.
    • False, and noted — 11514-dashboard-slot-entry-types.md. Its "Every widget that names a known family draws as before" bullet no longer holds for this form. The PR appends a dated note in the house form (date, title, card, what held then, what holds now, the pointer to the 11525 entry, "the rest is kept as the reading"); frontmatter unchanged; Changeset Overwrite Report green (case 2). RIGHT.
    • Left alone, rightly. 10528-grid-object-table-fetch (table, list and pivot only), 7353-retire-widget-dataprovider (table and pivot nodes; "no longer writes it on any node it builds" stays true), dashboard-inline-chartconfig-4044 (the chart node the relays compose), 10778-dashboard-bus-readers and dashboard-stable-empty-rows-4629 (table and pivot), 11348-dashboard-widget-reads (the guard still covers the same three object-* types, and "a child node" is still what the envelope hands over), 7627-shared-record-source-reader (isObjectProvider still exists), 7151-legacy-retired-provenance (test provenance only). The object-metric entries (8613, 8970, 9002, 10221, 10356, 10572, 10993, 11254, 11444, 11475, 7352, 7752, 8071, olive-flowers-yawn, 9165, 10859-pivot-object-block-zod-arms, 10859-object-timeline-zod-arm, the 10872 arms, 11468, 11513, 9256, 7712, 6442, 10935) each describe ObjectMetricWidget, the authored object-metric block or its validator arms, all of which stay live; none says a dataset-less provider widget reaches it. The provider: 'object' hits outside plugin-dashboard (7477, 7632, 7638, 7780, 8348 ×2, 9239, 9473, 10036, 10392, 10767, 10778-tree, 6592, 6700) describe a view block's data record source, not a dashboard widget. 11483, 8114 and 8760, which check:changeset-claims named, concern metric-card, a detail tab and line-chart. No pending entry describes the nested provider config as live for every family (that sentence lives in legacyRetiredWidget.ts, corrected in source) or the broadcast reaching an inline metric widget.
    • Result: no pending reading is false without a note.

③ Boundary flags

  • Open question, the chrome of a retired dataset-less metric tile — answered, not escalated: RIGHT. The seat's 5965464762 takes A. It is self-contained: the ruling names the node (the existing placeholder "as the pivot arm already does"), not a chrome change; the chrome rule is by family on both surfaces and predates this card; the retired legacy-shape metric tile already draws exactly this tile and the pin holds the two DOMs equal; B would move that legacy tile under no ruling. The diff makes no chrome change, consistent with the answer.
  • ObjectMetricWidget.tsx docblock (outside the claim's bullets, inside its declared directory) — forced, in-surface. The sentence that both relays compose the object-metric node out of one provider block was made false by this diff, and the claim's surface covers plugin-dashboard text where it describes the inline metric path. Accepted.
  • The retained broadcast member — judged in ① item 4; the claim's own "where nothing else reads them" governs and was measured not met. Accepted, as the seat already read it.
  • Partial AGENTS.md [WIP] Update documentation for project #11 line-address repair in DashboardGridLayout.tsx (eleven cross-file addresses left, two with content stale at base): outside this card's change; the one address inside the edited paragraph was repaired. Comment drift, no behaviour; carrier none is acceptable. Not blocking.
  • V1 probe leg A outside the verify lock; probe worktree removed with --force: process flags on a read-only measurement in a throwaway worktree; nothing committed or pushed; nothing of it is in the diff. Noted.
  • Commit trailers: three commits (856f56f0, a1c7e645, 29b4ab28), each stamped Claude-Session: …/session_01XvhGmGAP79ZB8swnkapxPC with objectui's model-free Co-authored-by: Claude pair; the target repo's AGENTS.md governs. Fine.
  • Out-of-scope findings (four, carrier none). The sdui-parser scope note and the dated data-objectstack comment are outside the surface and rightly left. One is not: packages/plugin-dashboard/src/index.tsx's ObjectMetricBlock docblock still says a host rendering it "with explicit props and no schema at all (the dashboard grid path) is untouched", a parenthetical naming a host that no longer exists — the same class as the ObjectMetricWidget.tsx sentence the dev did correct, and inside the declared directory. Comment only, no behaviour or contract effect; not blocking. The seat may fold it, with the grid's stale addresses, into the next plugin-dashboard touch.
  • check:doc-snippets / check:doc-examples NOT MEASURED locally: answered by the head's green Doc Snippet Type Check and Doc Example Id Check.
  • Nothing to escalate.

Implemented-by: claude/issue-11525-inline-metric-retired
Reviewed-by: session_01XvhGmGAP79ZB8swnkapxPC

VERDICT: PASS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation plugin tests

Projects

None yet

2 participants