Skip to content

fix(plugins,react,i18n): bound registrations stop requiring objectName, and a node with neither shows a no-object hint (objectui#11605) - #11612

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-11605-objectname-binding
Oct 4, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-11605-objectname-binding

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11605
Clause-②: yes

What changes

Every object-bound registration whose spec row, or the documented binding contract, waives objectName stops declaring it required: true, and each one gains a description that names the binding. So the page compile (compile() in @object-ui/sdui-parser, whose ok is the save gate) accepts a node whose dataSource.object names the object. This is the triage ruling (a), the same move as objectui#11569 (PR objectui#11604).

Each member that answered a node with neither key with a blank now shows a visible hint instead: "No object named: set objectName or dataSource.object." The hint comes from one place. ElementDataSourceGate gains an opt-in requiresObject prop, and each call site states whether its block has another record source. The hint is drawn after the binding lands, so a bound node never sees it. The string is the new view.noObject key in all ten packs, worded as element.number.noObject with the property as a hole.

No new manifest vocabulary and no new compile rule (ruling (a), "no (b)"). packages/types and sdui-parser are not edited.

The census, and one evidence line per site

The census was re-run at the base b92329c three ways:

  • git grep "name: 'objectName'.*required: true" finds 16 sites.
  • A looser pass finds the multi-line form (name: 'objectName', alone on a line) on 3 inputs (action:button, action:icon, object-tree), none required. A double-quoted spelling finds 0.
  • The manifest-derived census is the one the gate reads. Read off the shipped sdui.manifest.json, objectName was required on 10 public tags: the 9 members below plus record:related_list. The other 6 sites are non-public aliases or blocks.

Per site, the reading is: does the registered renderer wrap ElementDataSourceGate (so dataSource.object lands on objectName, the default mapping key), and what do the spec row and the binding doc say.

Site (cited by symbol) Tags Gate-wrapped Spec row / doc Decision Neither node, before, then after
plugin-grid GRID_QUERY_INPUTS object-grid, view:grid yes (ObjectGridRenderer) row: optional "because the component-level dataSource binding can supply the object instead" drop its own red "Object name required for data fetching" error, which is a hint already; not opted in
plugin-kanban OBJECT_KANBAN_INPUTS object-kanban yes (ObjectKanbanRenderer) row: optional, same reason drop "No cards" empty board, now the hint
plugin-dashboard object-metric registration object-metric yes (ObjectMetricBlock) row: optional, same reason drop a bare dash, now the hint
plugin-dashboard object-pivot registration object-pivot yes (ObjectPivotBlock) no row; doc table row object-pivot drop "loaded successfully and its query returned no records yet", now the hint
plugin-dashboard object-data-table registration object-data-table (not public) no: ObjectDataTable is registered bare no binding supplies it keep n/a
plugin-list list-view registration list-view yes (ListViewBlock) no row; doc table row, plus the list-view record-source rule that counts the binding drop "Nothing here yet" empty state, now the hint
plugin-list list registration view:list (not public) yes (same ListViewBlock) as list-view drop as list-view
plugin-charts ObjectChart.tsx object-chart registration (outside triage's 15) object-chart yes (ObjectChartBlock) no row; doc table row object-chart drop an empty chart frame with no message, now the hint
plugin-charts index.tsx chart registration view:chart (not public) yes (same ObjectChartBlock) as object-chart drop as object-chart
plugin-form object-form registration object-form yes (ObjectFormRenderer) row: optional, same reason drop (shared OBJECT_FORM_OBJECT_NAME_INPUT) a field-less card with Cancel / Update, now the hint
plugin-form form registration view:form (not public) yes (same renderer) as object-form drop (same const) as object-form
plugin-form embeddable-form registration embeddable-form yes (EmbeddableFormRenderer) no row; doc table row drop (formId stays required) a form with no fields and a Submit button, now the hint
plugin-form object-master-detail-form registration object-master-detail-form yes (MasterDetailFormRenderer) row: optional, same reason drop (details stays required) an empty parent form and a detail note about a link to a blank parent, now the hint
plugin-detail detail registration view:detail (not public) no: registers the raw DetailView no binding supplies it keep n/a
plugin-detail related_list registration record:related_list yes row: requires objectName keep, by spec row n/a
plugin-view object-view registration object-view (not public) no: ObjectViewRenderer reads no binding no binding supplies it keep n/a

"Before" was measured through SchemaRenderer under a provider with a recording adapter, at the base. Every member made zero data calls, and none threw. After the change, each opted-in member draws the hint and still makes zero data calls (pin rows below).

Other record sources opt out of the hint, each read from the block's own fetch guard. Lists take inline data. Forms (object-form, view:form) take inline fields, answered by the shared hasInlineFieldSource: non-empty customFields, or sections whose every field is inline, the collector the tabbed, wizard, split, drawer and modal variants render. Kanban takes data, a bind path, a parent view's rows, or lanes that carry their own cards. Metric takes fallbackValue. Chart takes data, dataset or bind. Pivot takes data or bind. Embeddable and master-detail have none.

Accept-set changes (for the contract review)

Measured against the manifest the console ships (buildArtifacts over dev/manifest-registry.ts). "Before" is the base b92329c; "after" is this head.

Node Before After
each of the 9 public members with dataSource={{ object: "account" }} and its other required props, no objectName ok: false, one missing-required-prop error naming objectName ok: true, no diagnostics, the binding site recorded
each of those 9 with neither objectName nor a binding ok: false, the same error no compile diagnostic about objectName; the runtime answers with the hint (or the grid's own error)
embeddable-form naming objectName but no formId (control) refused, one error naming formId the same
record:related_list without objectName (control) refused naming objectName the same; its spec row requires it

Generated artifacts move with it, from the same generator: the sdui.manifest.json entries, sdui-intrinsics.d.ts (objectName?: string on the 9 tags) and the sdui-blocks.md required-props column. None of them is tracked.

The zod faces (objectui validate) were read and not touched. A bound node of each member passes safeValidateSchema at this head, and the record-source refinements still refuse some neither nodes there.

Pins

New:

  • apps/console/src/__tests__/objectname-binding-required-11605.test.ts, judged against the shipped sdui.manifest.json:
    • The enumeration pin. The bound population is read from the manifest itself: entries whose inputs carry the injected dataSource input with binding: 'object'. For each, every required: true input must be required by its ComponentPropsMap row, not supplied by the binding, or in the LEDGER with a reason. It runs one test per tag, so a new contradiction is red by name. What the binding supplies is read off the binding doc's "which keys each one honours" table: the object key (objectName, childObject on record:line_items, object on element:*), plus columns where a named view fills them.
    • The ledger holds one row, record:related_list.columns (see Acceptance notes). A stale row is red.
    • The doc table must name every bound tag, and each object key must be an input the entry declares.
    • Per member: objectName not required with a description naming dataSource.object; the bound node compiles ok; the neither node draws no objectName diagnostic. Plus the two controls in the table above.
  • apps/console/src/__tests__/objectname-neither-hint-11605.test.tsx, for 11 tags (the 9 public members plus view:list, view:form and view:chart):
    • The neither node draws TESTID-no-object with the exact sentence and makes zero data calls.
    • Controls: no hint with objectName, none when bound by dataSource.object, none with the block's other record source, and none on a kanban whose lanes carry cards.
    • For object-form and view:form, each sectioned formType (tabbed, wizard, split, drawer, modal) with fully-inline sections renders its collector, not the hint (10 rows, red before 34eda84).
  • packages/react/src/element-data-source/__tests__/ElementDataSourceGate.requiresObject-11605.test.tsx: the gate's own rows.
    • The hint names the mapping's key (childObject under object: 'childObject').
    • A blank name names nothing.
    • The binding lands before the check.
    • No opt-in, or object: false, changes nothing.

Reproduced first. The two console pins were run against the base b92329c in a separate worktree. 47 rows were red: 9 enumeration rows, 9 published-input rows, 9 bound-compile rows, 9 neither-compile rows and 11 hint rows. Each enumeration row named its tag, for example "object-grid declares "objectName" required, but its dataSource binding supplies it and its spec row does not require it". All controls were green. At 34eda84 all 108 rows pass across the three new files (hint 53, enumeration 48, gate 7).

Ablations, on the committed fix, through ablation-replace.mjs. The console project aliases each @object-ui/plugin-* and @object-ui/react to src, so a mutation reaches the test without a build.

  1. required: true put back on object-grid's objectName (packages/plugin-grid/src/index.tsx, blob 103b678daf64 became b146f9f85b4d): 4 rows went red (enumeration, published input, bound compile, neither compile), each naming object-grid, with 44 passed.
  2. The same on object-form, through OBJECT_FORM_OBJECT_NAME_INPUT (69df173d5b9f became 4706c474205e): the same 4 rows went red, naming object-form.
  3. Kanban's gained hint removed by falsifying its requiresObject predicate (54919dea475e became 21d282a24bf0): exactly the object-kanban neither-hint row went red, with 42 passed.
  4. The object-form opt-out put back to the customFields-only predicate (packages/plugin-form/src/index.tsx, blob b730e588d559 became db8cd30dafb7): exactly the 10 sectioned-variant rows went red, 43 passed; restored by blob hash.

Each restore is proven by blob hash: blob equals HEAD and git diff HEAD is empty. Ablations 1 and 3 each had a first attempt the tool refused before any test ran, because the replacement contained its own anchor. Those attempts restored to HEAD and measured nothing.

Reverse type check. requiresObject={ was renamed requiresObjekt={ in plugin-kanban. tsc --noEmit then failed with TS2322 against the rebuilt @object-ui/react declarations, and the file was restored by blob hash (54919dea475e).

Other edits

  • Fixtures that pinned the old behaviour, each re-read rather than re-spelled:
    • plugin-kanban index.test.ts: "objectName is required" became "declared, not required, names dataSource.object".
    • plugin-form embeddable-form-arm-inputs-11440.test.ts: the required list is now ['formId']. The arm's record-source rule is unchanged.
    • plugin-kanban registration.test.tsx: the stub probe now names its object.
    • plugin-list htmlTierColumnSpellings-6598.test.tsx form 8a: object-name still fails loudly, now through the block's hint rather than the compile error. The invariant's "(b) fails loudly" names both.
    • Console sdui-workbench-preview-listview-binding-8510.test.tsx: the stripped-binding control waits on the list's hint. Its data-call assertions are unchanged.
  • Comment-only: two guideCrudAppRenders test headers said the block "declares objectName as required".
  • scripts/markdown-test-inputs.mjs: an ADJUDICATED entry for the new pin's read of content/docs/guide/data-source.md. Its --audit is green, and scripts/__tests__/markdown-test-inputs.test.ts passes.
  • content/docs/guide/data-source.md: one prose paragraph, no code fence. It says the page compile accepts a bound node on the objectName rows, with record:related_list as the exception. It also states the hint, the grid's own error, and which record sources opt out.
  • packages/react/README.md: one prose paragraph on requiresDataSource and requiresObject, no code fence.
  • Changesets, one per package that moves, each minor, each widening paragraph opening **Clause-②: yes (widening)** —: @object-ui/plugin-grid, plugin-kanban, plugin-dashboard, plugin-list, plugin-charts, plugin-form, react (the new optional prop) and i18n (the new key). @object-ui/console changes only test files, the same standing objectui#11604's review accepted.

Verification (head 34eda84)

  • The patch round, 582982e → 34eda84:

    • 067f3f0: chartNeedsObject reads data and bind as BaseSchema members and dataset with Reflect.get, so there is no second bind declaration (base-bind-declared.test.ts green; plugin-charts suite 89 files, 1213 passed / 9 skipped; type-check exit 0).
    • 34eda84: the form opt-out uses hasInlineFieldSource (plugin-form suite 163 files, 1889 passed / 1 skipped; type-check exit 0).
    • Every test that scans the tree wholesale was re-run at 34eda84: 127 package/app files (2576 passed) and 69 scripts files (2816 passed). The gates listed below were re-run at 34eda84, each exit 0.
    • The rest of this section was measured at 582982e.
  • Build: pnpm exec turbo run build --concurrency=2 filtered to the ^... closure of the six changed plugins. Exit 0, 14 tasks, 7 cache hits; @object-ui/react and @object-ui/i18n rebuilt.

  • type-check: each of react, i18n, plugin-grid, plugin-kanban, plugin-dashboard, plugin-list, plugin-charts and plugin-form exits 0 (tsc --noEmit && tsc -p tsconfig.test.json). react was re-run at this head.

  • Full package suites (pnpm exec vitest run packages/PKG/):

    • plugin-dashboard + plugin-charts: 257 files, 2882 passed, 15 skipped.
    • plugin-form: 163 files, 1889 passed, 1 skipped.
    • plugin-grid: 185 files, 1711 passed.
    • plugin-list + plugin-kanban: 178 files, 1663 passed.
    • react + i18n: 187 files, 2689 passed, 13 skipped.
    • plugin-view + examples/schema-catalog: 104 files, 2861 passed.
    • All of apps/console: 145 files, all pass after the 8510 control above.
    • Each run was taken at a head whose files it covers are byte-identical at 582982e, apart from three test files and scripts/markdown-test-inputs.mjs. Those were re-run at 582982e: 12 files, 538 passed.
  • Narrowed consumer sweep: the 53 test files outside the moved packages that name a moved tag (app-shell, components, cli, sdui-parser, plugin-detail, plugin-calendar, plugin-map, plugin-gantt), plus element-data-source-objectname-waiver-11117 (it reads data-source.md) and markdown-test-inputs: 55 files, 882 passed. Selected by tag literal and by source-text read of a changed file. A consumer that renders a moved block without naming its tag is outside this selection, and CI covers it.

  • Gates at this head, each exit 0:

    • changeset:check, check-changeset-presence (8 changesets over 9 released packages), check-changeset-overwrite, check:changeset-claims, check:pending-changeset-literals;
    • check:control-bytes, check:new-line-citations (0 new citation(s));
    • check:i18n-keys, check:i18n-drift (1 key added, 0 en values changed), check:i18n-dead-keys (view.noObject not listed);
    • check:element-data-source-declaration, check:test-path-roots, docs:check-links, check:doc-types, markdown-test-inputs --audit.
    • Earlier on the branch, also each exit 0: check:handler-key-reads, check:doc-example-readers, check:spec-symbols, check:doc-fences, check:doc-example-ids, check:component-surface-parity, check:registry-bare-names, check:prompt-keys, check:vi-mock-override-shape, check:unreferenced-sources, check:side-effects-array.
  • check-governed-queue-guard --test over the 40 paths: NOT GOVERNED.

  • eslint (--no-inline-config --format json) over the 30 touched ts/tsx/mjs files: 0 errors. For each modified file, the warning count and the multiset of rule ids are equal at base (fed by --stdin --stdin-filename) and at head. The three new test files carry 0 warnings. The config has no type-aware linting (no project / projectService), so this diff cannot move the verdict on an untouched file.

  • NOT MEASURED, left to CI:

    • check:eager-closure, check:eager-locale-catalogues and check:sdui-registration-pins need a built console (PREREQUISITE NOT MET). This diff adds one short string to each eager pack and a small component to the gate.
    • check:doc-snippets and check:doc-examples need built packages (exit 2). Neither docs edit adds a code fence.
    • check:readme-exports is judged on built type entries and could not judge any README here. The README edit adds no fence.

Acceptance notes

  • record:related_list.columns is ledgered, not moved. The registration requires columns, but the spec row leaves it optional, and the gate maps a named view's field list onto it (columns: true). So a node bound to a view with its own columns is refused. Measured at this head: record:related_list with objectName, relationshipField and dataSource={{ object: "task", view: "open_tasks" }} gets ok: false, missing-required-prop naming columns. It is outside this card's objectName scope, and a node bound by object alone still needs columns. The dev report carried it to the seat, which filed it as objectui#11613.
  • object-grid is not opted into the hint. Its own error already answers the neither node.
  • A metric's fallbackValue opts out of the hint, because the author chose a static tile. With an adapter in context the tile still shows its dash rather than that value, as before this change. Recorded only.
  • The two faces differ on the neither node, as the ruling expects. The page compile accepts it, and the zod record-source rule still refuses it on list-view, object-grid, object-kanban, embeddable-form and object-pivot.
  • A simple (or unset) formType whose sections are fully inline also opts out of the hint now, because hasInlineFieldSource answers for every variant. ObjectForm's simple path does not read inline sections, so with no object that node still draws an empty form, as it did before this PR. It is a missed hint, not a regression.
  • A designer preview of a placed block with no object yet now shows the hint rather than an empty shell. The app-shell tests that name these tags are green.

Implemented by the os-dev run under session https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2, for the claim on the card.


Generated by Claude Code

claude added 6 commits October 4, 2026 11:01
…e, and a node with neither shows a no-object hint (objectui#11605)

The object-bound registrations whose spec row or binding doc waives
`objectName` (object-grid, view:grid, list-view, view:list, object-form,
view:form, embeddable-form, object-master-detail-form, object-kanban,
object-metric, object-pivot, object-chart, view:chart) drop `required: true`
and describe the binding, so the page compile accepts a node bound by
`dataSource.object`. `ElementDataSourceGate` gains `requiresObject`, and each
member whose neither-node answered with a blank opts in, so that node shows
"No object named: set objectName or dataSource.object." (`view.noObject`,
ten packs). Pins: the manifest enumeration pin and per-member compile rows,
the neither-node hint rows, and the gate's own rows.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
…rce, so a static board shows no no-object hint (objectui#11605)

The board keeps a lane's own `cards` when it merges rows into its lanes, so a
board authored with card-carrying `columns` and no object draws cards. The
kanban call site now opts out of `requiresObject` for it, and the neither-hint
pin gains that control. Two fixtures follow the hint: the kanban registration
probe names its object, and the html-tier `object-name` spelling (6598 form
8a) now fails loudly through the block's hint rather than the compile.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
…of the binding doc

The pin parses `content/docs/guide/data-source.md`'s "which keys each one
honours" table, so the markdown-test-inputs ledger records that read and a
markdown-only change to that page runs the pin.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
…tName, so it reads the same before and after (objectui#11605)

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
…no-object hint (objectui#11605)

With the `data` binding stripped, the workbench's list names no object, so the
block now draws the "no object named" hint instead of mounting `ListView`. The
control's data-call assertions are unchanged; only its "the block has
answered" signal moves to the hint.

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

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

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3313.4 KB 3330.4 KB
Main entry chunk (gzip) 151.4 KB 350 KB
Entry file index-D6uHRFot.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) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
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) 572.97KB 137.34KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 261.62KB 66.20KB
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) 35.66KB 9.49KB
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) 84.27KB 23.05KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 143.54KB 38.79KB
plugin-designer (index.js) 231.41KB 48.84KB
plugin-detail (index.js) 245.74KB 64.59KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.49KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.92KB 64.87KB
plugin-kanban (index.js) 50.05KB 15.73KB
plugin-list (index.js) 116.72KB 29.10KB
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.31KB 2.07KB
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.44KB 2.07KB
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

claude added 2 commits October 4, 2026 12:37
…`bind` as BaseSchema members, with no second `bind` declaration (objectui#11605)

`chartNeedsObject` read the record sources through a type literal that
re-declared `bind` beside `BaseSchema`'s, which the `bind`-declared-once pin
(`base-bind-declared.test.ts`, objectui#6357) refuses. It now reads `data` and
`bind` as the `BaseSchema` members they are and `dataset`, this block's own
key, by name with `Reflect.get`. The helper also moves above the
`ObjectChartBlock` doc comment it had been inserted beneath, so that comment
documents the block again.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
… collector, not a node that names no object (objectui#11605)

The `object-form` / `view:form` opt-out read non-empty `customFields` only.
`ObjectForm` dispatches the `tabbed`, `wizard`, `split`, `drawer` and `modal`
variants to renderers that also treat sections whose every field is an inline
runtime `FormField` as an inline field source (objectui#10254), so such a
node with no object and no binding had the "no object named" hint painted over
a working collector. The predicate is now `!hasInlineFieldSource(schema)`, the
shared helper those renderers use. The neither-hint pin gains one row per
sectioned variant on both tags, red before this change; the published
`objectName` description, the plugin-form changeset and the binding doc name
inline sections beside `customFields`.

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

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3313.4 KB 3330.4 KB
Main entry chunk (gzip) 151.4 KB 350 KB
Entry file index-BBd6Ui1j.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) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
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) 572.97KB 137.34KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 261.62KB 66.20KB
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) 35.66KB 9.49KB
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) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 143.54KB 38.79KB
plugin-designer (index.js) 231.41KB 48.84KB
plugin-detail (index.js) 245.74KB 64.59KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.66KB 45.52KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.92KB 64.87KB
plugin-kanban (index.js) 50.05KB 15.73KB
plugin-list (index.js) 116.72KB 29.10KB
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.31KB 2.07KB
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.44KB 2.07KB
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: 34eda8423ff4a362c71f7ec26f61f7d3e4cc1535
Local-runs: none

Resume after the HELD draft on 582982ed7 (no verdict; Test (shard 4/8) was red there on base-bind-declared.test.ts). Read-only, against: the card (objectui#11605 body, the seat's question 5978510533, triage 5978738288 ruling (a), claim 5979045539, dev reports 5979882855 and 5980241939, seat review 5979908360); the precedent PR objectui#11604 (merged as 902ebab) and its records 5978497549 (FAIL) and 5978691275 (PASS); the PR body as it reads now (re-read; the dev's five pr_body_update items are applied and each sentence was checked against the diff), its 40-file list, its two comments (both the performance-budget and bundle-size bot, which re-reads no pending changeset, so no paragraph is judged false there) and the net diff b92329c..34eda84 (merge-base b92329c, 40 files); objectui origin/main b508ac5 (unchanged since the first read) and the head by git show; the published @objectstack/spec 17.6.0 tarball, the version main's lockfile resolves (npm pack, dist/ui/index.mjs read as text). Check-runs on the head at the final read (2026-10-04T13:19Z): 43 runs, all completed, 40 success, 3 skipped (Test (coverage), Test (coverage shard), dependabot), 0 failed, 0 in progress; Test (shard 4/8), Test and Type Check are success on 34eda84; mergeable_state is clean; the PR head is still 34eda84. The seat's review was read as a claim to test; every reading below is my own.

What 34eda84 changes, read from the head: two commits since 582982ed7 (067f3f0, 34eda84), five files move and the other 35 are blob-identical (git rev-parse per path across the two heads). The five: packages/plugin-charts/src/ObjectChart.tsx (the chartNeedsObject predicate, ①.9), packages/plugin-form/src/index.tsx (the object-form / view:form opt-out and the shared input's description, ①.4), apps/console/src/__tests__/objectname-neither-hint-11605.test.tsx (10 new control rows and a body-text read for the portal variants), .changeset/11605-plugin-form-objectname-optional.md and content/docs/guide/data-source.md (the inline-sections wording). Every reading taken on 582982ed7 for the 35 unchanged files holds here unchanged; the five are re-read below.

① Derived judgments

  1. The 12 moved sites: each move is justified, and no moved site is one where no binding supplies the object. Unchanged from 582982ed7 (the six registration files other than plugin-form/src/index.tsx are blob-identical, and that file's registrations did not move). The base census finds 16 sites; 12 move, covering 13 tags (view:grid shares GRID_QUERY_INPUTS with object-grid). Each moved registration's renderer wraps ElementDataSourceGate, none of their mappings sets object, so useElementDataSourceSchema writes next['objectName'] = composed.object whenever a binding is present. The waiver per tag: object-grid, object-kanban, object-metric, object-form, object-master-detail-form have 17.6.0 ComponentPropsMap rows whose objectName is z.string().optional() described "Optional because the component-level dataSource binding can supply the object instead"; list-view, object-chart, object-pivot, embeddable-form have no row and each has an object ✅ row in the binding doc's "which keys each one honours" table. On each of the 13 tags a bound node with no objectName goes from refused to ok: right, the same move as objectui#11569. The neither-node compiles too, on each, consistent with ruling (a); its runtime answer is the gate's hint (item 4) or the grid's own error (item 6).

  2. The 4 kept sites are kept for true reasons. Unchanged. record:related_list: RecordRelatedListProps.objectName is z.string() with no .optional(), so the spec row requires it, and the pin's control refuses a node without it. object-data-table, view:detail (the raw DetailView under namespace view) and object-view (ObjectViewRenderer reads the context adapter and no binding): none is wrapped by elementDataSourceBlock, and Registry.withElementDataSourceInput injects the dataSource input only for isElementDataSourceBlock(component), so no binding lands on them. None of the three has a spec row.

  3. ElementDataSourceGateProps.requiresObject?: boolean, published. Unchanged; ElementDataSourceGate.tsx is blob-identical. packages/react/src/index.ts does export * from './element-data-source/ElementDataSourceGate.js', so the new optional member is on the public type surface: an additive widening. The check runs after requiresDataSource, after the malformed-filter, missing and loading states, on bound.schema with the composed object already landed; objectKey = mapping?.object ?? 'objectName'; object: false skips it; namesObject trims, so a blank string names nothing. Without the prop nothing changes. The provider-less defaultValue is byte-identical to the en value.

  4. The opt-out predicates, read against each block's own fetch ladder; the object-form hole is closed. list-view / view:list (data only, matching ListView's three data shapes and no bind), object-kanban (data, bind, a data prop array, lanes with cards, matching externalData || boundData || schema.data || fetchedData), object-pivot (data, bind), object-chart / view:chart (data, dataset, bind), object-metric (fallbackValue, the tile's one static source), embeddable-form and object-master-detail-form (unconditional: the public form create()s into config.objectName; the parent form derives its details from schema.objectName, a batch submit through the parent; neither can do its job without the object) hold as read on 582982ed7, their files blob-identical. object-form / view:form now reads requiresObject={!hasInlineFieldSource(schema)}, the shared helper from submitTarget.ts with no logic copied, and the hole the HELD draft named is closed: the helper's two limbs are non-empty customFields and sectionsAreFullyInline, and judged against each sectioned variant, TabbedForm, WizardForm, SplitForm, DrawerForm and ModalForm each import that same helper and each treats a fully-inline-sections form as the legitimate target-less collector in its submit chain, so the predicate opts out exactly the forms those renderers render. The 10 new pin rows (object-form and view:form × the five formTypes, fully-inline sections, no objectName, no binding, no customFields) assert no hint and the collector's field label on screen (read from document.body because the drawer and modal render into a portal); the dev reports them red at 067f3f0 and green at 34eda84, and ablation 4 (the customFields-only predicate put back, blob b730e588d559 to db8cd30dafb7, restored by hash) turns exactly those 10 red. The variants' object-schema effect still calls dataSource.getObjectSchema(schema.objectName) with an undefined name when an adapter is present (the if (!dataSource) guard is the only one), and the collector renders regardless, which is why the new rows assert the render and not zero calls; that call predates this PR and is outside the hint's scope. The dev's new Acceptance note is accurate: a simple (or unset) formType with fully-inline sections also opts out, because the helper answers for every variant, while SimpleObjectForm's own path reads customFields only (its comment says sections[].fields only select fields already resolved), so with no object that node draws the empty form it drew before this PR. A hint missed on one authoring form, where every behaviour is as at base, is the right thing to record and not a defect; a formType-aware predicate would copy ObjectForm's dispatch into the gate call, the drift the shared helper exists to stop. The pre-existing requiresDataSource predicate keeps its customFields-only reading; it fires only with objectName present and is unchanged by this PR.

  5. Published descriptions: accurate. The grid's, list's, chart's, metric's and pivot's are unchanged. OBJECT_FORM_OBJECT_NAME_INPUT's description now reads "With neither, and no inline fields (non-empty customFields, or sections whose every field is inline), the form shows a hint", which matches the predicate in item 4 (the simple case is a hint not shown, which the sentence does not overclaim).

  6. object-grid keeping its own error: consistent. Unchanged. ObjectGrid's fetch effect throws 'Object name required for data fetching' when objectName is absent and renders it as its error state; the neither node was already loud there. Right not to opt in.

  7. Emitted typings and block list move with the manifest, nothing regenerated is owed. Unchanged. sdui.manifest.json, sdui-intrinsics.d.ts and sdui-blocks.md are build-time outputs of buildArtifacts, none tracked on main. packages/types and packages/sdui-parser are untouched; validateTree's only requiredness read is input.required && !(input.name in node). No new vocabulary.

  8. The enumeration pin's inputs. Unchanged (its file is blob-identical). It reads the shipped manifest (getPublicConfigs(), so the view:* aliases are not in it), the 17.6.0 rows through isOptional(), and the binding doc's table; the doc's rows and the *_DATA_SOURCE maps agree row for row on which tags the binding supplies columns. The ledger holds one row, record:related_list.columns, with a reason, and its stale-row test guards it. The doc edit at 34eda84 is to the prose paragraph below the table, not to the table the pin parses.

  9. The red check is cleared, and the fix keeps the bind opt-out. chartNeedsObject now reads node.data == null && node.bind == null && Reflect.get(node, 'dataset') == null on a BaseSchema parameter: data?: any and bind?: string are BaseSchema members (packages/types/src/base.ts), so no local type re-declares bind, and dataset, the chart's own key, is read by name in a spelling that also survives objectui#11607's removal of BaseSchema's index signature. A scan of packages for a line-initial bind?: at this head finds only the two declarations the objectui#6357 pin allows (BaseSchema and schemaHostProps.ts). The helper moved above the ObjectChartBlock doc comment, which the round-1 insertion had split from its block; the gate call requiresObject={chartNeedsObject(props.schema)} and the object-chart registration are as on 582982ed7, and no other hunk in ObjectChart.tsx moved. base-bind-declared is green: Test (shard 4/8) and the Test rollup are success on this head.

② Semver level

  • Clause-②: yes is right, and minor matches it on every package. The page compile's accept set widens on 9 public tags (bound node and neither node, ok: false to ok: true), 13 registrations' declared inputs change shape, @object-ui/react gains an optional member on a published interface, and @object-ui/i18n gains a key on a published payload. The fleet rule (objectstack AGENTS.md Post-Task Checklist item 3; the check-changeset-no-major LEVEL axis) asks at least one moved package at minor once yes is declared, and the WHICH LEVEL ruling (batch [WIP] Evaluate which controls should be placed in plugins #35) grades "a new accepted key or value" on a published surface minor per package. Each of the 8 changesets grades minor, each opens its widening paragraph **Clause-②: yes (widening)** —, the em-dash spelling objectui#11604's PASS record accepted, and each states both accept-set moves and what stays required. The plugin-form changeset at 34eda84 names both inline field sources (customFields, and sections whose every field is inline, for the five sectioned variants) beside formId and details staying required, so the one undeclared runtime change the HELD draft flagged is now neither present nor undeclared. @object-ui/console changes only test files, the standing objectui#11604's review accepted; check-changeset-presence counts 9 released packages against 8 changesets. All five changeset check-runs are success on this head.
  • Nothing is narrowed in any gate's accept set. The hint replaces an empty render on a neither node for each opted-in block, stated per block in its changeset's banner, and the one form where it replaced a working render is fixed and pinned (①.4).
  • The i18n question, one line: a new locale key is a new key on a published payload and on the published TranslationKeys = typeof en, so this PR's yes (widening) / minor on @object-ui/i18n is the rule's reading; the objectui#11606 regrade is recorded on objectui#11591 and raised to the maintainer by the seat, and nothing is owed on this PR.

③ Boundary flags

  1. react and i18n beyond the claim's surface: accepted. One gate prop and one key serve 8 members where 8 renderer edits would, and the kanban hint lands without ObjectKanban.tsx; each package carries its own minor changeset, and the dev report declares the deviation. The prop is opt-in.
  2. The non-public view:grid / view:list / view:form / view:chart aliases moving with their twins: right. Same renderer, same declaration; the shipped manifest excludes them, so the enumeration pin cannot see them; the hint pin renders view:list, view:form and view:chart, and at 34eda84 the 10 sectioned rows cover view:form beside object-form.
  3. The fixture edits, each re-read rather than re-spelled: accepted. The kanban index.test.ts and registration.test.tsx, embeddable-form-arm-inputs-11440, htmlTierColumnSpellings-6598 form 8a, the console 8510 control and the two guideCrudAppRenders headers are blob-identical to 582982ed7 and hold as read there. The hint pin's edit at 34eda84 adds the 10 sectioned control rows and a bodyText read for the portal-rendering drawer and modal; its existing rows are unchanged.
  4. scripts/markdown-test-inputs.mjs ledger entry: required and right. Unchanged; the enumeration pin's cross-package read of data-source.md is spelled join(HERE, ...) from dirname(fileURLToPath(import.meta.url)), and the --audit adjudication names it.
  5. The doc paragraph in data-source.md and the packages/react/README.md paragraph: each sentence checked. The doc's opt-out clause now reads "a form's inline fields — customFields, or sections whose every field is inline —", matching the predicate in ①.4; its other clauses (accepts on every objectName row; record:related_list the exception by spec row; the hint's wording; the grid's own error; data, dataset, bind, fallbackValue) are unchanged and true. The README paragraph is unchanged and matches the gate. Prose faces are outside the contract tier; judged because the brief asked.
  6. The class-(b) finding, objectui#11613: right to ledger, right to file. RecordRelatedListProps.columns is optional, RECORD_RELATED_LIST_DATA_SOURCE maps columns: true, the registration requires columns; the ledger row carries the reason, and the card is open for triage. The PR's Acceptance note now names the card. Nothing owed here.
  7. The three acceptance notes: right carriers. The metric's fallbackValue dash under an adapter is pre-existing and recorded only. The zod record-source rule still refusing five neither nodes the page compile accepts is the split ruling (a) accepts. The new simple-formType note is accurate (①.4).
  8. objectui#11607's named files: confirmed absent; the two-file overlap (ObjectChart.tsx, plugin-kanban/src/index.tsx, comment-only hunks on that side, in regions this PR does not touch, and the round-2 ObjectChart.tsx hunk stays in the predicate's region) is a rebase matter the seat holds, with no action here.
  9. Round-2 deviations: accepted. Two commits in one push rather than one, in order (067f3f0 the pin, 34eda84 the review item); the helper move in ObjectChart.tsx, which restores a doc comment to the block it documents; the worktree re-added at the remote head 582982ed7 (verified equal) and removed after the push. The PR body's five updates were applied by the seat and read back; the "Verification" section says which measurements were taken at 34eda84 and which stand from 582982ed7, which matches the blob comparison above.

Implemented-by: claude/issue-11605-objectname-binding
Reviewed-by: session_01FjqrwXPfSMkSfkKYDSRkN2

VERDICT: PASS

Written 2026-10-04T13:24Z. The HELD draft's two open items are cleared on this head: the bind re-declaration is gone and base-bind-declared is green, and the object-form opt-out reads the shared hasInlineFieldSource, pinned by 10 rows that were red before it. ① holds on the 35 unchanged files as on 582982ed7 and on the five re-read here; ② and ③ hold. Every check-run on 34eda84 is complete and none failed.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

needs:contract-review removed: an at-tier PASS is on record at this head · domain:ui seat 1 · session_01FjqrwXPfSMkSfkKYDSRkN2 · 2026-10-04T13:24Z.

  • Record: 5980434378 on this PR: ## Contract review, Served-tier: CONTRACT_REVIEW_TIER, Head-sha: 34eda8423ff4a362c71f7ec26f61f7d3e4cc1535, Local-runs: none, Implemented-by: claude/issue-11605-objectname-binding / Reviewed-by: session_01FjqrwXPfSMkSfkKYDSRkN2, PASS.
    • The seat made one edit to the record before posting: a date-only stamp quoting the WHICH LEVEL ruling (batch #35) was dropped, because the stamp contract accepts only YYYY-MM-DDThh:mmZ. The citation is kept by batch number, and no reading changed.
  • Its history on this PR:
    • The same reviewer's first pass on 582982e was HELD (no verdict, not posted), because CI was red there. Test (shard 4/8) failed on base-bind-declared.test.ts: chartNeedsObject re-declared bind?:.
    • That pass also found a hole in this PR's object-form / view:form opt-out. It counted customFields only, so the hint would paint over a sectioned form whose sections are fully inline, a working collector.
    • The dev's patch round fixed both. 067f3f0 reads data / bind as BaseSchema members. 34eda84 uses !hasInlineFieldSource(schema) and adds 10 pin rows, red before the fix, and ablation 4 turns exactly those 10 red. The dev's pr_body_update was applied by the seat and read back identical.
    • The PASS re-renders on 34eda84: 5 files moved, and 35 are blob-identical to 582982e.
  • The reviewer's escalations, answered:
    • objectui#11606 under-declared @object-ui/i18n (patch for a new key, where a new locale key is Clause-②: yes). This is recorded on objectui#11591 and raised to the maintainer, since its changeset is still pending.
    • objectui#11607 now shares ObjectChart.tsx and plugin-kanban/src/index.tsx with this PR, in comment-only hunks in other regions. That is a rebase for whichever lands second.
  • The reviewer's writes: none. It ran read-only, and the seat posted the record from its file.
  • Pre-landing checks on 34eda84:
    • 43 check runs: 40 success and 3 expected skips (dependabot, Test (coverage) and its shard matrix);
    • mergeable_state: clean;
    • check-governed-merges --pr objectstack-ai/objectui#11612: NOT governed, 1,251 changed lines, under the human-merge threshold.
  • Carried: objectui#11613 (record:related_list.columns, the pin's one ledger row) is filed for triage. Three Acceptance notes are in the PR body.
  • Landing in this act: ready, then auto-merge, so the PR enters the merge queue. Fixes #11605 closes the family's closing card on merge.

Generated by Claude Code

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

Projects

None yet

2 participants