Skip to content

fix(core,plugins): honour only the data spelling each block's spec row declares - #9234

Merged
os-tesla merged 3 commits into
mainfrom
claude/issue-8348-data-spelling-per-spec-row
Sep 12, 2026
Merged

fix(core,plugins): honour only the data spelling each block's spec row declares#9234
os-tesla merged 3 commits into
mainfrom
claude/issue-8348-data-spelling-per-spec-row

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Part of #8348 — the ladder half of decision batch #83. ⛔ Deliberately not a closing keyword: the card also carries findings this PR reports rather than repairs (see the last section), so the PM lands it and decides the card's state.

The ruling this implements

Decision batch #83 (2026-09-08), maintainer verbatim, kept in the original and untranslated:

8348 以协议为准

⇒ option A. A renderer honours the data spelling its block's published row declares and no other: what os validate and the save gate refuse, the renderer refuses too. Where a pin contradicts its block's row, the pin is corrected to the spec — never the spec to the code.

What changed

resolveRecordSourceConfig (@object-ui/core) now takes a second, required argument: the arm the calling block's published data row declares (view-data, array, or undeclared). Rung 1 of the ruled three-rung ladder applies only on that arm; rungs 2 and 3 are untouched, as is their order.

The arm is passed at the call site rather than looked up from schema.type. Every one of these renderers is registered twiceobject-grid plus the view:grid alias grid, object-calendar plus calendar, and so on — so a table keyed by type would answer for one tag and silently miss the other. A required parameter makes the arm a compile-time obligation at each site instead; it is what caught all five sites in one type-check run.

The delivery surface: all five renderers, enumerated from the ladder itself

git grep -n resolveRecordSourceConfig -- packages apps examples returns exactly five calling renderers — ObjectCalendar.tsx, ObjectGantt.tsx, ObjectGrid.tsx, ObjectMap.tsx, ObjectTree.tsx — which is also the list the shared function's own docblock names. object-kanban is deliberately not among them: it walks its own resolver, pinned by packages/types/src/__tests__/object-kanban-record-source-7780.test.ts, which asserts zero resolveRecordSourceConfig in that block.

Per block, judged against its own row

Rows measured on @objectstack/spec 17.4.0, which is what pnpm-lock.yaml resolves for this repo, read from node_modules/@objectstack/spec/dist/ui in this worktree after pnpm install — not from an objectstack checkout, so the reading is the published package.

block published data row arm what moves
object-calendar ComponentPropsMap row: z.array(z.unknown()).optional(), "Pre-fetched records — skips the internal fetch" array the { provider, … } config object stops being a record source
object-grid ComponentPropsMap row: the ViewData union; its own description says "the bare-array shortcut is refused" view-data getDataConfig's Array.isArray head removed
object-map no ComponentPropsMap row; ObjectMapSchema.data = ViewDataSchema.optional() view-data its array-shorthand head removed
object-gantt no ComponentPropsMap row; ObjectGanttSchema.data = ViewDataSchema.optional() view-data a bare array stops being a record source (it was already inert)
object-tree no row on any face undeclared nothing — see "reported, not fixed"

⚠️ The accepted cost — and ⛔ a measured limit on where it lands

object-calendar: a stored calendar authored data: { provider: 'value', items: [...] } (or any other provider object) stops drawing those rows. The ladder falls past data to staticData, then to objectName, so it queries its object instead, or draws nothing when it names neither. Accepted by the ruling under the standing 2026-08-27 posture: no transition windows, no staged deprecation. Pinned end-to-end, through the card's own harness.

object-grid and object-map: ⛔ MEASURED, and it corrected this PR's own first draft. Removing the ladder head is real at the ladder and is not, on its own, an end-to-end change for a node rendered through SchemaRenderer. An authored data array arrives twice: as schema.data, where the head used to lift it, and as the data React prop, because SchemaRenderer spreads every non-metadata node key and both wrappers forward it — and each component lifts an array on that prop at higher priority than the ladder (ObjectGrid's passedData; ObjectMap's objectui#5003 precedence). So such a node still draws its rows. The first version of the grid pin asserted the opposite and went red, which is how this was found; both pins now state the verdict per carrier.

That surviving carrier is the one a host such as ListView legitimately uses for rows it pre-fetched, and at the component boundary it is indistinguishable from an authored key. Collapsing the two would take the host path with it, so it is reported (below) rather than changed in passing.

object-gantt: nothing observable moves — the array carried no provider, matched no fetch branch, and this block's wrapper forwards no host props.

⇒ the ruling's user-visible cost lands on object-calendar, which is where the card was filed. On the object-armed blocks this PR removes the second read, not the last one.

Authored usage of the tolerated spellings — swept, and REPORTED not migrated

Per the ruling the seat reports real authored instances rather than silently migrating them. The sweep found none outside test fixtures. Counts read before paging, no truncated enumeration:

  • { provider, items } under data on object-calendar: intersecting the 84 files naming object-calendar with the files naming provider gives 43 files, all of them source, tests, changelogs or prose — zero authored documents. No examples/** calendar schema carries a "provider" key at all.
  • bare array under data on object-grid / object-map / object-gantt / object-tree: 98 hits for a data:-followed-by-[ shape across content, examples, apps, e2e, public; every one is a find response envelope, a data-table / chart / kanban block (none of which is on this ladder), or a calendar — where the array is the declared spelling. Zero on the four object-armed blocks.
  • The two calendar catalog entries (examples/schema-catalog/src/schemas/plugin-calendar/month-view-calendar.json, week-view-calendar.json) author "data": [ … ], which is the arm the spec row declares. They render exactly as before.

What the sweep did move is test fixtures, and each carries the reason in place: calendar fixtures that spelled inline rows as data: { provider: 'value', items } now use staticData (the block's declared inline-rows door, resolving to the identical config one rung down); the calendar memo fixture that spelled its binding as data: { provider: 'object', object } now uses objectName; the map memo fixture now uses data: { provider: 'value', items }.

Pins corrected to the spec (⛔ never the spec to the code)

  • packages/plugin-grid/src/__tests__/gridDataInputContract.test.ts — named by the ruling. Its assertions already agreed with the row; two prose claims did not, and both are corrected: (1) it said the parity gate "judges against ComponentPropsMap['object-grid'].data, which is z.array() and DISAGREES" — measured on 17.4.0 the two authorities have converged, the row is the ViewData union; (2) it said "the renderer's array tolerance is NOT asserted away here. It stays as back-compat" — the ruling removes the tolerance, so declaration and read now say the same thing.
  • packages/core/src/utils/record-source.ts docblock — named by the ruling. It stated as a fact about the whole ladder that "an array under data cannot be published". True of the ViewData blocks, false of object-calendar, whose row is the array. Replaced with the measured per-block table.
  • packages/plugin-map/src/ObjectMap.schemaDataShorthand.test.tsx — its subject was the array shorthand, justified by "a live convention in six sibling blocks", an argument from what the code does. Rewritten around the row: the shorthand's retirement is pinned, and the objectui#5305 rows survive as the ⛔ CONTROLS (the declared form still paints, still takes the no-fetch path, still repaints on changed rows; the data prop still outranks the schema).
  • packages/plugin-calendar/src/__tests__/ObjectCalendar.navigationRecordSource-7638.test.tsx — two rows pinned that data: { provider: 'object', object } re-points the calendar's record source. On this block that spelling is off-arm, so both are rewritten to the ruled outcome, with a new control showing the declared doors still route.
  • packages/types/src/__tests__/object-calendar-record-source-7313.test.ts — it source-greps the ladder for rung order. Kept, re-aimed at the new spelling, and one false green repaired while in there: its "the renderer still calls the ladder" row looked for the bare resolveRecordSourceConfig(schema), which ObjectCalendar.tsx satisfies from a docblock line that merely names the function — so it would have stayed green through a call site that had stopped existing. It now asserts the call with its arm.

New pins

  • packages/core/src/utils/__tests__/record-source-config.behaviourNeutrality-7632.test.ts (extended) — the arm verdict at the resolver, with the pre-collapse bodies kept as the ⛔ CONTROL that makes "changed" a measurement, plus a non-vacuity row proving the ruled-away family is non-empty.
  • packages/plugin-calendar/src/__tests__/ObjectCalendar.dataObjectArmRefused-8348.test.tsx — the card's own harness: a real node through the real SchemaRenderer with a recording data source. Row 1 is the card's measurement inverted; row 2 shows the accepted cost on screen (the block now queries); rows 3-5 are controls holding the declared carriers still; row 6 reads the spec row rather than restating it.
  • packages/plugin-grid/src/__tests__/gridBareArrayDataRefused-8348.test.tsx — the same shape for the grid's half of the ruling.

Verification

All numbers below are from the final head, 81fe3e18f, which already contains the merge of origin/main (objectui#9224 / objectui#8674 landed; the one plausible meeting point, packages/plugin-grid/src/index.tsx, produced no conflict). Every test command runs from the repo root — a package-directory vitest is refused by this repo's own guard — and through the shared verify lock. Exit codes captured by redirect-then-capture, never through a pipe.

what command verdict
tests, six packages pnpm exec vitest run packages/core/ packages/plugin-calendar/ packages/plugin-map/ packages/plugin-gantt/ packages/plugin-tree/ packages/types/ Test Files 466 passed (466) · Tests 7650 passed (7650)
tests, grid + the merge's other touched package pnpm exec vitest run packages/plugin-grid/ packages/plugin-designer/ Test Files 146 passed (146) · Tests 1262 passed (1262)
the two console parity gates pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts apps/console/src/__tests__/public-block-binding-reach.test.tsx Test Files 2 passed (2) · Tests 214 passed (214)
types, seven packages pnpm --filter … run type-check exit 0, no error TS
lint, seven packages (the real gate — pnpm lint is turbo run lint) pnpm --filter … run lint 0 errors in each; warnings are pre-existing no-explicit-any / react-refresh noise
changeset declared node scripts/check-changeset-presence.mjs ✅ 23 source file(s) of 7 released package(s) changed, and this change declares 1 changeset(s)
no major bump node scripts/check-changeset-no-major.mjs ✅ No changeset declares a 'major' bump.
governed surface node scripts/check-governed-queue-guard.mjs --test … ✅ NOT GOVERNED — 19 path(s) checked against 5 governed surface(s); none matched.
control bytes grep -naP over the diff for [\x00-\x08\x0b\x0c\x0e-\x1f\x7f] no hits

The fixture triage is visible in the run history rather than hidden: the six-package run reported 16 failures on its first pass, 2 on its second and 0 after the last two assertions were re-aimed; packages/plugin-grid/ went 23 → 1 → 0 the same way. Every one was a fixture or a pin whose subject the ruling moves, and each carries its reason in place.

The repo-wide eslint . reading, with its three parts so the narrowing above is a measurement and not an omission. (1) Population read from eslint's own config, not guessed: npx eslint . --no-inline-config --format json reports 4855 files. (2) All 23 of this PR's changed lintable files are inside that population, and linting exactly those reports 0 errors (498 warnings, all pre-existing rule noise on files that already carried it). (3) The flat run reports 95 errors across 79 files repo-wide; the intersection with this PR's changed set is empty, and those files are untouched here. That flat run is not the CI gate in any case — pnpm lint is turbo run lint, the per-package scripts already reported above.

⛔ Not measured here, by design: CI's own convergence. The lint and test farms are CI's run, and this report is delivered when the local checks are done rather than after waiting on them.

Bump level: minor, chosen not copied. The lane's test — does existing stored data render differently — is demonstrably YES here, which is breaking. major is unavailable by repo rule: the 39-package fixed group tracks @objectstack's major, and scripts/check-changeset-no-major.mjs fails any changeset declaring one. So the repo's own idiom applies: minor, with the breaking semantics spelled out in the changeset body, per block.

Reported, not fixed

  1. object-tree has no published data row on any face — not ComponentPropsMap (no object-tree key exists in it at all), not ObjectTreeSchema in @object-ui/types (which declares objectName required and no data / staticData), not the plugin's registration inputs. Both arms of the ruling decide by reference to the block's row, so neither reaches this block, and inventing one would be a contract decision rather than an implementation of this one. Its rung 1 is therefore left exactly as it was, marked undeclared at the call site with the reasoning in place. The block still honours both a ViewData object and a bare array under data — and it has a third carrier besides, a rest.data ?? schema.data array read inside its own fetch effect.
  2. object-map and object-gantt have no ComponentPropsMap row either. They are decided here because a published row does exist for them — this repo's own ObjectMapSchema.data / ObjectGanttSchema.data. Worth a maintainer's eye: three of the five ladder blocks have no spec row, so "judge against the spec row" resolves through @object-ui/types for two of them and not at all for the third.
  3. The props channel is a second, unruled carrier of the same authored key. SchemaRenderer spreads every non-metadata node key as a React prop, so data authored on a node reaches a block both as schema.data — which this PR now judges against the block's row — and as the data prop, which several blocks lift with an unconditional Array.isArray. The two are indistinguishable at the component boundary, because the prop is also how a host hands down rows it already fetched. packages/plugin-calendar/src/__tests__/ObjectCalendar.recordSourceMembers-8314.test.tsx already documents this arrangement for the calendar and warns that a pin written from the read that looks right cannot fail. Deciding it is a contract question about the props channel, not about the ladder, so it is left whole here and reported.
  4. ObjectCalendarSchema.data in @object-ui/types contradicts the spec row — filed as objectui#9239. Both faces of this repo's own mirror — packages/types/src/zod/objectql.zod.ts and packages/types/src/objectql.ts — declare data?: ViewData for object-calendar, while ComponentPropsMap['object-calendar'].data is z.array(z.unknown()). Under the ruling's direction the mirror follows the spec, but this is a declaration on a published contract package, not a pin: moving it changes what safeValidateSchema accepts from authors, and it is held in place by a type-level equality with object-gantt, by the objectui#7313 document fixtures, and by the plugin docs page. Filed as objectui#9239 rather than folded in, so the contract change gets its own review. It is not a defect this PR introduces — the divergence predates it, and the renderer's read is now on the spec's side of it.

This pull request was generated by Claude Code, in session session_01UzHd6hDYatoDn17BuwKxnZ — recorded here as prose because a PATCH rewrites the footer block (AGENTS.md, "GitHub 会改写你写进 issue/PR 正文的字节").


Generated by Claude Code

…row declares

The shared record-source ladder returned an authored `data` verbatim for every
block, so two spellings of one key were live at once and in opposite
directions: `object-calendar`, whose `ComponentPropsMap` row is
`z.array(z.unknown())`, honoured the `{ provider, items }` config object the
row refuses by kind; `object-grid` and `object-map`, whose rows are the
`ViewData` object union, lifted a bare array their rows refuse by kind. Both
shapes are rejected by `os validate` and the save gate, and the renderer
honoured them anyway — the second de-facto contract AGENTS.md #0.1 exists to
stop.

`resolveRecordSourceConfig` now takes the arm the calling block's published
`data` row declares, and rung 1 applies only on that arm. The arm is a required
parameter rather than a lookup on `schema.type`, because every one of these
renderers is registered twice (`object-grid` and the `view:grid` alias, and so
on), so a table keyed by type would answer for one tag and miss the other.

Per block, measured on the `@objectstack/spec` this repo resolves (17.4.0):
object-calendar honours the array arm; object-grid, object-map and object-gantt
honour the ViewData arm, and grid's and map's normalizing heads are removed;
object-tree publishes no `data` row on any face, so neither arm of the ruling
reaches it and nothing about it changes.

Stored documents authored against the tolerated spellings stop rendering those
rows — accepted by the ruling, with no transition window. Two pins that
contradicted their block's row are corrected to the spec, never the spec to the
code: the `object-grid.data` declaration pin's docblock and the shared ladder's
own.

Part of #8348

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.5 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-j4PHiID2.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.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) 500.84KB 114.81KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.27KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.02KB 65.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.51KB 57.49KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

…lock

Measured while pinning the grid half: an authored `data` array reaches
`ObjectGrid` and `ObjectMap` twice. `SchemaRenderer` spreads every non-metadata
node key as a React prop and both wrappers forward it, and each component lifts
an array arriving on that prop to `{ provider: 'value', items }` at higher
priority than the ladder — the channel a host such as `ListView` uses for rows
it already fetched, indistinguishable at the component boundary from an
authored key.

So removing the ladder head is real at the ladder and is NOT, on its own, an
end-to-end change for a node rendered through `SchemaRenderer`. The first draft
of the grid pin asserted otherwise and went red, which is how this was found.
Both pins now state the verdict per carrier and the surviving one is reported
rather than changed: collapsing it would take the host path with it.

`object-calendar` is unaffected by this caveat and keeps the full end-to-end
change — its boundary keeps `data` only when `Array.isArray`, so the config
object the ruling removes had exactly one carrier.

The changeset and both renderer docblocks are corrected to match, and the grid
fixtures that spelled inline rows as a bare array move to the declared
`{ provider: 'value', items }` form — except the two that deliberately keep the
array, one because it uses the props carrier and one because it asserts the
retirement.

Part of #8348

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.6 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-CEHH68PX.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.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) 500.84KB 114.81KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.27KB 34.62KB
plugin-designer (index.js) 215.81KB 44.32KB
plugin-detail (index.js) 253.38KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.62KB 57.50KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.6 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-CEHH68PX.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.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) 500.84KB 114.81KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.27KB 34.62KB
plugin-designer (index.js) 215.81KB 44.32KB
plugin-detail (index.js) 253.38KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.62KB 57.50KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

Copy link
Copy Markdown
Collaborator Author

PM ruling on both open questions, and Contract review — head 81fe3e18ffPASS

Reviewed-by: domain:ui PM seat (os-tesla), R16, 2026-09-12T03:1xZ.

Clause-② yes — ⛔ --pair 9234 first read C1: the PR carried needs:contract-review and the card did not. That was mine, not this delivery's — see the carrier note at the end. Repaired and re-read: ✓, both carriers agree. CI: 36 checks, 0 pending, 0 failing.


Q1 — three of five blocks have no ComponentPropsMap row → A now, C filed as the durable fix

⭐ This falsified a mechanism assumption in my own dispatch. I wrote "judge each block against its own spec row" as if every block had one. Three do not. The seat measured that instead of guessing, and reported it as a falsification rather than quietly picking a rule — which is exactly what the dispatch asked for and rarely gets.

Ruled A, which is what shipped: the spec row decides where one exists (object-calendar, object-grid); this repo's own published row decides where it does not (object-map, object-gantt, both ViewDataSchema); and a block with no row on any face (object-tree) is left alone and reported.

⛔ Not B. "No published row ⇒ not a record source" is a behaviour change on off-contract documents that no ruling licenses. The seat's own note that it "contradicts nothing, because there is nothing to contradict" is the argument against it, not for it: an unruled silent narrowing is the thing this lane files cards about.

⛔ Not C in this PR — it changes @objectstack/spec, which routes to the spec seat.

⚠️ Recorded as a deviation from the ruling's letter, not hidden. 「8348 以协议为准」 and two of the five blocks were decided by this repo's published row rather than the protocol's. The ruling's intent is satisfied — judge by a published declaration, never by the code — but the asymmetry is real, and I am carrying it to the maintainer rather than letting A stand as if it were uniform. C is the only option under which "the spec decides" is true for all five.

Q2 — the props channel is a second, unruled carrier → A, with the limitation stated plainly

SchemaRenderer spreads every non-metadata node key as a React prop, so an authored data reaches a block twice: as schema.data, now judged against the row, and as the data prop, which ObjectGrid (passedData) and ObjectMap lift with an unconditional Array.isArray at higher priority than the ladder. Measured: on those two blocks an authored bare array still draws after this PR.

Ruled A. ⛔ Not B — it deletes a working host path (a host such as ListView handing down pre-fetched rows) to close an authoring gap, and the two are indistinguishable at the component boundary. C is the shape worth costing and it is filed.

⚠️ So the ruling does NOT fully bite on object-grid and object-map, and I will not let that be discovered later. What saves the card is an asymmetry the seat found and named: object-calendar's boundary keeps data only when Array.isArray, so the {provider, items} config this card is actually about had exactly one carrier, and its removal is end-to-end. ⇒ the filed defect is fully closed; the generalisation to grid and map is partial, by a carrier the ruling never addressed.


The delivery

The five renderers were enumerated from the ladder itself, not from the card: git grep -n resolveRecordSourceConfig -- packages apps examples returns exactly ObjectCalendar, ObjectGantt, ObjectGrid, ObjectMap, ObjectTree — the same list the shared function's docblock names — with object-kanban excluded by its own pin asserting zero calls.

The arm is a REQUIRED parameter passed at the call site, not dispatched on schema.type — because every one of these renderers is registered twice (object-grid plus the view:grid alias, object-calendar plus calendar), so a type-keyed table would answer for one tag and miss the other. And making it required is what proved every call site had been found: the first type-check named exactly the one stale caller. That is a compiler used as a census, and it is stronger than any grep would have been.

Spec read from node_modules at 17.4.0 after pnpm install in the worktree, ⛔ not from the objectstack checkout — the instrument caveat honoured.

Two false greens repaired in passing, both worth more than the lines they touch

  • object-calendar-record-source-7313.test.ts asserted the renderer "still calls the shared ladder" by looking for the bare string resolveRecordSourceConfig(schema) — which ObjectCalendar.tsx satisfied from a docblock line that merely names the function. ⇒ it would have stayed green through a call site that had stopped existing. ⭐ Measure the CALL, not the NAME — the same rule that bit me twice this round, found here by a seat in someone else's pin. Repaired rather than filed, correctly: this PR's own edit is what made it live.
  • gridDataInputContract.test.ts's docblock claimed ComponentPropsMap['object-grid'].data "is z.array() and DISAGREES" with ViewDataSchema. Measured on 17.4.0 the two authorities have converged. Corrected under the ruling's clause 3 — ⛔ the pin moves to the spec, never the spec to the pin.

Triage history kept visible

Run (1) went 16 failures → 2 → 0; plugin-grid went 23 → 1 → 0. ⭐ And the grid pin's first draft asserted an end-to-end behaviour change, went red, and that red is what produced open question 2. A pin that fails honestly and is then narrowed to what is true is worth more than one written to pass.

Bump

23 source file(s) of 7 released package(s) changed, 1 changeset, no major. Existing stored documents authored in the tolerated spelling stop rendering — the cost the ruling accepted under the 2026-08-27 no-transition-window posture — and the changeset states the change per block, as instructed.


⛔ The carrier failure was mine, and it was a second instance

--pair 9234 opened C1 because card objectui#8348 had lost needs:contract-review, pm:dispatched, its finding label and its assignee — the whole write reverted to the pre-claim state. I wrote them at claim time and ⛔ did not read back, which is this lane's rule 13 verbatim (a label PUT gets clobbered by the size-labeler's whole-set write) and the one card of the four where I skipped the read-back. Restored and read back twice, twelve seconds apart, both stable; assignee restored and confirmed.

⇒ Two carrier defects on my side this round, from the same root: a write is not a write until it is read back, and a record is not a record until the predicate that reads it can find it.

Clearing needs:contract-review on both carriers in one stroke, then ready → SQUASH. ⛔ Card objectui#8348 is not closed by this PR.


Generated by Claude Code

@os-tesla
os-tesla marked this pull request as ready for review September 12, 2026 03:15
@os-tesla
os-tesla added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit 91facae Sep 12, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-8348-data-spelling-per-spec-row branch September 12, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants