Skip to content

fix(components): element:number shows a "no object named" state instead of a silent dash (objectui#10951) - #10989

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10951-element-number-no-object-state
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10951-element-number-no-object-state

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #10951
Clause-②: no — (a) is renderer-only plus one new locale-pack key, the same shape PR objectui#10101 declared no: no accept set relaxes, no manifest input moves, and no export is added. (b) did not land (measured below), so the published manifest's declared inputs are byte-unchanged.

Implemented on branch claude/issue-10951-element-number-no-object-state by the os-dev run dispatched from the domain:ui seat 2 session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN (claim comment 5870572293 on the card, triage 5869415559).

What changed

  • ElementNumberRenderer (packages/components/src/renderers/basic/elements.tsx): when the node authors an aggregate and names no object in either place (no properties.object, and no dataSource whose object is a non-empty string, which is what useElementDataSource reads as status absent), the renderer draws a short muted notice (text-xs text-muted-foreground, test id element-number-no-object) instead of the silent dash. It queries nothing and does not throw.
    • Authored absence only. The branch sits after the two binding panels and requires status absent, so a binding that names an object keeps the loading panel while its view resolves and the error panel when the view fails to resolve.
    • No aggregate: unchanged. A node with no aggregate still paints the dash (measured on the base, pinned as a control below).
  • Localized: the text is t('element.number.noObject', { defaultValue }) through useObjectTranslation (the hook elements.tsx already imports). The key is added to all ten packs (packages/i18n/src/locales/*.ts, new element.number namespace), zh-CN included: 「未指定对象:请设置 object 或 dataSource.object。」. English: "No object named: set object or dataSource.object." check:i18n-keys confirms the inline defaultValue equals the en value.
  • Docs: content/docs/guide/data-source.md gains one sentence after the element:number paragraph describing the notice. The docs grep over content/docs/** (the element:number row and paragraphs in data-source.md) found no sentence this makes false.
  • Changeset: .changeset/10951-element-number-no-object-state.md, '@object-ui/components': patch and '@object-ui/i18n': patch (the second because the ten packs are @object-ui/i18n source).

(b) measured: the manifest cannot say "object or dataSource.object" today

Routed by the seat: (b) is the maintainer's decision, on objectui#10991 (it recommends A: keep this notice and add no new gate). This PR closes objectui#10951 with (a), the runtime half; the authoring-time half rides that decision card, which also decides objectui#10508's calendar and gantt trade and element:record_picker.

Nothing was registered and nothing was invented. What was read on origin/main deca847a8a:

  • Input model. ManifestInput (packages/sdui-parser/src/types.ts) carries name, type, of, required?: boolean, enum, binding, description. ManifestComponent carries type, namespace, inputs, isContainer, tier. There is no one-of, any-of, requiredAny or conditional-required form, and no component-level validator slot. The serializer forwards exactly that fixed per-input list (the list component-input-retired-constraint-keys.test.ts quotes: "name, type, of, required, enum, binding, description").
  • validateTree's only presence rule is per input and unconditional: if (input.required && !(input.name in node)) draws missing-required-prop.
  • The per-tag checks inside validateTree are not registration hooks. checkKanbanQuickAdd fires on QUICK_ADD_HOST_TYPES = new Set(['object-kanban']) and checkDashboardWidgetOptions on DASHBOARD_WIDGET_HOST_TYPES (dashboard, dashboard-grid). Both sets are hard-coded in the parser; no registration declares them and sdui.manifest.json does not carry them. A third arm for element:number would be a NEW parser rule, and the objectstack side runs a lockstep port of packages/sdui-parser/src (objectstack packages/sdui-parser/objectui-lockstep.json records the file list and the diagnosticCodes set), so it is a cross-repo contract, not a registration.
  • Same trade as PR objectui#10508. There the calendar's objectName lost required: true because the record source is one of data / staticData / objectName, and the description states the rule instead. element:number in PR objectui#10944 is the same shape; one answer covers both.

⇒ (b) goes back to the seat as an open question, with a four-axis analysis in the os-dev-report comment on the card.

Pins: red on the base, green on the head

New files (both under packages/components/src/renderers/basic/__tests__/):

  • elementNumber.noObjectState-10951.test.tsx (no provider, English defaultValue):
    • PIN "neither properties.object nor dataSource.object: the notice replaces the dash, and nothing is queried"
    • PIN "a dataSource that names no object is still authored absence" (dataSource: { object: '' })
    • CONTROL "properties.object alone aggregates and paints the value"
    • CONTROL "dataSource.object alone aggregates and paints the value"
    • CONTROL "a binding whose view is still resolving keeps the loading panel, not the new state"
    • CONTROL "a binding whose view failed to resolve keeps the error panel, not the new state"
    • CONTROL "no aggregate and no object keeps the dash it paints today"
  • elementNumber.noObjectStateLocale-10951.test.tsx (its own file, because createI18n installs a module-global instance):
    • PIN "renders the Chinese sentence under zh"
    • PIN "renders the German sentence under de"
tree command result
base deca847a8a (the two test files copied in, nothing else) pnpm exec vitest run --maxWorkers=2 over both files Tests 4 failed | 5 passed (9): the 4 PIN rows fail with Unable to find an element by: [data-testid="element-number-no-object"]; the 5 CONTROL rows pass
head affc832f76 same Tests 9 passed (9)

Ablation, with each control lit

Every leg mutated the COMMITTED elements.tsx through objectstack's scripts/ablation-replace.mjs (the anchor must hit once, the write is verified on disk by anchor count and blob hash, and the restore is proven by blob equal to the HEAD blob plus an empty git diff HEAD). The test imports the renderer through its relative src path, so no dist step sits between the mutation and the run.

leg mutation red rows (everything else green)
M1 condition replaced by false (both files run) exactly the 4 PIN rows: Tests 4 failed | 5 passed (9)
M2 drop !props.object CONTROL "properties.object alone …"
M3 drop props.aggregate CONTROL "no aggregate and no object …"
M4 drop the status === 'absent' clause CONTROL "dataSource.object alone …"
M5 authored absence read off props.object alone, drawn ahead of the two panels CONTROLs "dataSource.object alone …", "view is still resolving …", "view failed to resolve …"

M5's first attempt was a no-op. ablation-replace refused it because the replacement re-contained its anchor ("the anchor count moved 1 to 1", paraphrased), and nothing ran. It was re-run with a non-overlapping anchor, and that run is the one reported above. Every leg ended with "ok restored: blob == HEAD (6c2e326522b8)".

Gates (all on head affc832f76; heavy runs under os-verify-lock)

  • Dependency closure build: pnpm turbo run build --filter="@object-ui/components^..." --concurrency=2 → Tasks: 7 successful, 7 total.
  • Type-check: pnpm --filter @object-ui/components --filter @object-ui/i18n run type-check → both type-check: Done, lock VERDICT command-exit 0. tsc -p tsconfig.test.json --listFilesOnly lists both new test files and elements.tsx.
  • Package suites: pnpm exec vitest run --maxWorkers=3 packages/components/ packages/i18n/ → Test Files 401 passed | 1 skipped (402), Tests 4380 passed | 37 skipped (4417), including all-locales-key-parity and en-zh-key-parity.
  • Every other test that reads element:number / elements.tsx (git grep over *.test.*, 24 files in apps/console, app-shell, cli, core, data-objectstack, plugin-dashboard, react, types, scripts), plus the five scripts/__tests__ suites that read the locale packs whole → Test Files 29 passed (29), Tests 1931 passed (1931).
  • Console eager closure (after vite build of apps/console, base and head built the same way in separate worktrees): ✅ Console eager closure is 3104.0 KB gzipped across 330 of 2446 chunks (budget: 3104.5 KB, headroom: 0.5 KB). The base read 3103.9 KB (headroom 0.6 KB) on this box. Delta: eagerGzipBytes 3178359 → 3178445 = +86 bytes gzipped (ui-components +78, i18n-locale-en +34, the rest hash churn). 563 bytes of headroom remain.
  • check:eager-locale-catalogues exit 0 (en still the only eager catalogue); check:sdui-registration-pins exit 0.
  • check-changeset-presence exit 0 ("13 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)"); check-changeset-no-major exit 0; check:pending-changeset-literals exit 0; check:changeset-claims exit 0, report-only. It names three pending changesets (6661, 6726, 6783) that mention en.ts or elements.tsx, and all three paragraphs were re-read and are still true.
  • check:new-line-citations → VERDICT new-cross-file-line-citations: 0 new citation(s); check:control-bytes OK; docs:check-links "Links are valid across 17 scan roots."; check:doc-types exit 0; check:element-data-source-declaration OK.
  • i18n: check:i18n-keys exit 0; check:i18n-drift exit 0 ("1 key(s) added"); check-i18n-dead-keys does not list element.number.noObject.
  • Lint, narrowed and proven: eslint --no-inline-config --format json over the 13 changed .ts/.tsx files → 13 files, 0 errors, 24 warnings. All 24 are in elements.tsx and the base file carries the same 24. eslint.config.js sets no parserOptions.project (type-aware linting is off), so this diff cannot move a verdict on any file it did not touch.
  • git merge-tree --write-tree against a fresh origin/main (42687baf20) → exit 0, no conflicts.

Acceptance notes

  • The dispatch quoted the eager closure at 3103.4 KB. This box measured 3103.9 KB on the base (vite build of apps/console only, src aliases). The +86 byte delta is base against head on the same box.
  • Not filed (polish, same family): the two shared binding panels (ElementDataSourceErrorPanel / ElementDataSourceLoadingPanel in @object-ui/react) and element:image's "no image source" still render English literals. This PR does not touch them.
  • If the seat routes (b)'s decision through this card rather than a separate one, line 1 should become Part of #10951, so the merge does not close a card in the decision box.

Generated by Claude Code

…ad of a silent dash

An element:number that authors an aggregate and names neither
properties.object nor dataSource.object now draws a short, muted,
localized notice (element.number.noObject, in all ten packs) instead of
painting the empty dash. Only authored absence qualifies: a binding that
names an object keeps its loading and error panels while its view
resolves or after it fails, and a node with no aggregate keeps the dash.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: components tests package: i18n labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 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/6661-app-launcher-nav-menu-renderers.md

  • names en.ts → packages/i18n/src/locales/en.ts — edited by this change

    Three new strings — the launcher's and the menu's accessible names, and the menu's empty state — are declared under console.nav in en.ts and its nine sibling packs. An inline defaultValue alone is not a fix: it renders English at one call site and leaves the string untranslatable everywhere (objectui#3517).

.changeset/6726-find-envelope-records-arms.md

  • names components/src/renderers/basic/elements.tsx → packages/components/src/renderers/basic/elements.tsx — edited by this change

    | module | what it does | | --- | --- | | components/src/hooks/related-count-store.ts | related-list tab badge count | | components/src/renderers/basic/data-list.tsx | element:repeater rows | | components/src/renderers/basic/elements.tsx | element:number client-side aggregate | | components/src/renderers/basic/record-picker.tsx | element:record_picker options | | plugin-detail/src/renderers/record-activity.tsx | record:activity self-fetch | | plugin-detail/src/renderers/record-history.tsx | record:history self-fetch | | plugin-view/src/ObjectView.tsx | non-grid (kanban / calendar / gallery / timeline) fetch |

.changeset/6783-readprops-degenerate-config-bag.md

  • names elements.tsx → packages/components/src/renderers/basic/elements.tsx — edited by this change

    Five modules under packages/components/src/renderers/basic/ — elements.tsx, data-list.tsx, text-input.tsx, record-picker.tsx, metadata-viewer.tsx — each carried a copy of the same reader, { ...(schema?.props ?? {}), ...(schema?.properties ?? {}) }. ?? only replaces null/undefined, so a non-object bag went into the object spread and came back out as indexed keys: for properties: 'not-a-bag', the config bag a renderer received was { '0': 'n', '1': 'o', … '8': 'g' } — nine keys nobody authored. The five copies are now one readProps (renderers/basic/readProps.ts) that asks isConfigBag, and a degenerate bag on either side contributes no keys.

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.

Compared the checked-out tree with 40c076fc2 (merge-base with origin/main): 14 file(s) changed outside .changeset/, read against 1698 pending declaration(s) that publish a body (2299 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3104.0 KB 3104.5 KB
Main entry chunk (gzip) 149.7 KB 350 KB
Entry file index-BKalKdZs.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.58KB 6.17KB
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.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.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) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.61KB 63.16KB
fields (index.js) 261.01KB 66.28KB
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 (translateFn.js) 0.20KB 0.18KB
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.32KB 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.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.83KB 36.71KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.51KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.44KB 22.41KB
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) 119.16KB 39.05KB
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) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
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.26KB
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.27KB 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

Spec Main Shape Gate is red here, and it is not this PR's failure. Its only errors are in packages/types/src/__tests__/p1-spec-alignment.test.ts, lines 154 and 164 (TS2322 / TS2339 on tabs), compiled against @objectstack/spec built from objectstack 7fa3e3e07c.

  • feat(spec)!: retire the list view's own tabs key; named presets are listViews entries objectstack#20357 retired ListViewSchema.tabs at 12:49Z, and that fixture still authors it. So every objectui PR, and the merge queue, fail this gate. This PR touches neither packages/types nor that test.
  • The fix is objectui#10987, which has a dev on it now. No fix PR exists to port yet.
  • Once it is on main, the seat merges main into this branch and lands the PR. Its in-seat review has already passed. No re-run until then: a re-run would compile the same test.

domain:ui seat 2 · 2026-09-28T14:0xZ

…10951-element-number-no-object-state

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3104.0 KB 3104.5 KB
Main entry chunk (gzip) 149.7 KB 350 KB
Entry file index-BKalKdZs.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.58KB 6.17KB
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.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.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) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.61KB 63.16KB
fields (index.js) 261.01KB 66.28KB
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 (translateFn.js) 0.20KB 0.18KB
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.32KB 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.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.83KB 36.71KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.51KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.44KB 22.41KB
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) 119.16KB 39.05KB
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) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
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.26KB
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.27KB 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: affc832f76f8b86a1864261fa7a250bb4a876c3f

This is an in-seat review of objectui#10951's runtime half (a): one renderer branch, one locale key in ten packs, a docs sentence, pins and a changeset (15 files, +326 −1), Clause-②: no. I read the whole diff. Half (b) is routed to the maintainer as objectui#10991.

① Derived judgments

  1. The condition is authored absence and nothing wider: props.aggregate && !props.object && dataBinding.status === 'absent'.
    • It sits after the loading and error panels, so a binding that names an object keeps its own panels while its view resolves or after it fails.
    • A node with no aggregate keeps the dash.
    • An empty dataSource that names no object reads absent and shows the notice. That is right: the node names no object anywhere.
    • It queries nothing, and useObjectTranslation is called unconditionally near the top of the component, beside useElementDataSource, so the hook order is stable.
  2. The text is renderer chrome, localized. element.number.noObject is added to all ten packs, with a defaultValue equal to the English. check:i18n-keys and check:i18n-drift are green, and the new key is not in check-i18n-dead-keys. The zh string reads naturally: "未指定对象:请设置 object 或 dataSource.object。"
  3. Pins (9 rows):
    • 4 fix rows: neither object; a dataSource naming none; the zh and de sentences.
    • 5 lit controls: properties.object alone, dataSource.object alone, no aggregate, and a view that is still resolving or failed to resolve.
    • The dev's base leg gives 4 failed / 5 passed.
    • Four single-condition mutations each redden exactly the control that should catch them. Forcing the condition false reddens exactly the 4 fix rows. A props-only check placed ahead of the panels reddens the three binding controls. So every clause of the condition is load-bearing.
  4. Docs and changeset: every sentence is true against the code.
    • The docs sentence in data-source.md states the notice.
    • The changeset's "the manifest has no way to say 'one of object and dataSource.object'" matches the dev's measurement (ManifestInput has required only).
    • Declaring '@object-ui/i18n': patch beside components is correct: the packs are @object-ui/i18n source.
  5. (b) is not in this PR. The published manifest is byte-unchanged, so Clause-②: no holds.

② Semver level

patch for @object-ui/components and @object-ui/i18n. No declared type, accepted key, manifest input or export moves, and adding one locale key is additive. The level is not major.

③ Boundary flags

  • Eager closure (dev, measured on a console build): +86 B gzipped (ui-components +78, the eager en pack +34). The dev's box read 3103.9 to 3104.0 KB against the 3104.5 KB budget, so about 0.5 KB of headroom is left there. The landing waits for CI's own Console Performance Budget reading.
  • Base merge: the branch was base-merged onto main 40c076fc2d, which carries objectui#10987's gate fix, giving landing head ea68a50e69. The PR's own diff is unchanged: the same 15 files, +326 −1, and an identical patch-id (122c0d79ff). The pins (9 of 9) and the i18n key and parity checks re-ran green on the merged tree.
  • CI: this record posts only after every check at ea68a50e69 is final and green, the Spec Main Shape Gate included.
  • Merge-tree (dev): clean against main 42687baf20.
  • Noted, not filed, per the dev's report: ElementDataSourceErrorPanel / ElementDataSourceLoadingPanel in @object-ui/react, and element:image's "no image source", render English literals outside the locale packs. This is polish, recorded in the PR's Acceptance notes.
  • State: draft, with assignee os-elon-musk. There is no needs:contract-review label, which fits Clause-②: no.

Implemented-by: claude/issue-10951-element-number-no-object-state
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 14:45
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 4b742f4 Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10951-element-number-no-object-state branch September 28, 2026 15:00
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 package: components package: i18n tests

Projects

None yet

2 participants