Skip to content

feat(react)!: unbind the data-source adapter from the expression scope, and point bind at the scope channel - #9369

Merged
os-tesla merged 4 commits into
mainfrom
claude/issue-9308-unbind-data-root
Sep 13, 2026
Merged

feat(react)!: unbind the data-source adapter from the expression scope, and point bind at the scope channel#9369
os-tesla merged 4 commits into
mainfrom
claude/issue-9308-unbind-data-root

Conversation

@os-tesla

Copy link
Copy Markdown
Collaborator

Fixes #9308
Fixes #9346

Maintainer ruling 2026-09-13, verbatim 「同意」 on option B, both halves. Option A (a new published provider prop) and option C were not taken.

What landed

b1 — data: dataSource leaves the evaluator scope. packages/react/src/SchemaRenderer.tsx no longer publishes the injected adapter as an expression root. @object-ui/app-shell's ExpressionProvider states the governing principle — every root a tier binds must be one the engine accepts AND one that tier can actually answer — and objectui#8155 (app) / objectui#8166 (data) already applied it one tier up. Against a conformant DataSource adapter every data.* path resolved undefined, so this tier bound a root it could not answer.

b2 — useDataScope reads usePredicateScope(). A node's bind now resolves a path in the ambient scope a host publishes through PredicateScopeProvider — the channel ExpressionProvider already feeds — instead of walking the adapter. No published key is added; that is what makes B strictly better than A.

Side effect the ruling's own appendix named: ...predicateScope was spread before data: dataSource, so a host that legitimately published data through the documented scope channel was silently overwritten by the adapter. Removing the line un-shadows it. Pinned as leg 3 of the new test.

Measurements the dispatch asked for

1. The verdict DOES move — the dispatch's premise here was falsified

The dispatch predicted the verdict would not move "because data.* already resolved undefined against an adapter". Measured on the built evaluator, that is wrong, and the distinction is present-but-undefined key vs absent key:

scope data.status == 'draft' with throwOnError
{ data: undefined } false false
no data key at all true throws data is not defined

So a page/component visible gate reading data.* was a clean constant false (node hidden on every row) and is now unevaluable, which this fail-soft surface answers true (node shown on every row). It stops being silent in the same stroke: objectui#5454's reportUnresolvableVisibilityPredicate names it, in production as well as development, with a message that is true for it. That is the breaking half of this change, it is stated plainly in the changeset, and it is pinned as leg 7 of SchemaRenderer.dataRootUnbound-9308.test.tsx rather than left to be discovered in a console.

2. reportAdapterOnlyDataPredicate — measured, then re-aimed

It does not flood: its trigger reads the object passed as boundData, not the evaluator scope, so b1 changed its firing set for a conformant host by exactly nothing (every data.* path was already undefined on an adapter). What b1 broke is its truth, in both directions at once:

  • left pointing at the adapter it would have gone silent for precisely the hosts this change breaks — a bag injected as dataSource still answers the read, while the evaluator no longer sees it;
  • and loud for hosts whose scope-channel data answers perfectly.

⇒ Decision: keep the diagnostic, keep its severity and its dev-only gate, and hand it the data the evaluator actually bound (predicateScope?.data). Its copy moves off the "at the node tier data is the DATA-SOURCE ADAPTER" claim, which this PR makes false. Reachability is now stated in the source rather than assumed: with no host data, a data.* predicate throws and objectui#5454 takes it, so this leg speaks only for a host that published a data root which does not answer — the population it can still speak for.

3. celAuthoring's rowCanonAdvisory — more correct, and untouched

Its message says a wrong-layer data.* "faults at runtime with Unknown variable: data and the rule never fires". Before this PR the node tier contradicted that by resolving data to the adapter; now it agrees. ⛔ Severity unchanged (warning) — every save gate on that tier counts severity === 'error', and promoting it would refuse predicates already in customer metadata.

4. dataTableBindDiagnostic's data-vs-bind precedence question — DISSOLVED

Confirmed, and said so in the module's own docblock. While useDataScope walked the adapter, bind and the node's data were two spellings of one idea and a reader of both would have needed a precedence rule. Now bind resolves the ambient scope and data is an inline array on the node: different channels, no precedence to decide. ⛔ data-table still does not read bind (objectui#6575) and does not start.

Blast radius — all nine readers re-verified per reader, not inherited

Each already falls back, so the behaviour against a conformant host is unchanged (the fallback is what was running):

reader resolution
components/.../list.tsx Array.isArray(boundData) ? boundData : Array.isArray(schema.items) ? schema.items : []
components/.../tree-view.tsx boundData || schema.nodes || []
plugin-charts/ObjectChart.tsx boundData || schema.data || fetchedData
plugin-dashboard/ObjectDataTable.tsx boundData || schema.data || fetchedData
plugin-dashboard/ObjectPivotTable.tsx boundData || schema.data || fetchedData
plugin-grid/ObjectGrid.tsx if (boundData && Array.isArray(boundData)) → else falls through to getDataConfig(schema)
plugin-kanban/ObjectKanban.tsx external || boundData || schema.data || fetchedData
plugin-list/ObjectGallery.tsx props.data || boundData || schema.data || fetchedData || []
plugin-timeline/ObjectTimeline.tsx props.data || boundData || fetchedData

None treats boundData as its sole source, so the shape of b2 is unchanged. The card body's "five components" is wrong; nine is right.

The documentation half

Doc Snippet Type Check was red on main with three fences, all teaching meaning 2 — publish page values through dataSource, read them back under data.*:

content/docs/guide/schema-rendering.md:99:29    TS2740
content/docs/guide/schema-rendering.md:434:29   TS2740
packages/react/README.md:73:29                  TS2740

All three move to PredicateScopeProvider, and so does the prose around them — including packages/react/README.md's sentence that dataSource "is what the evaluator sees under the name data", which the gate cannot see and which b1 deletes outright. packages/types/src/base.ts's bind example comment (// → dataSource.customerNames) moves with it.

The pin, written first and run red on the unmodified tree

packages/react/src/__tests__/SchemaRenderer.dataRootUnbound-9308.test.tsx, red-first reading verbatim:

 ❯ |dom| packages/react/src/__tests__/SchemaRenderer.dataRootUnbound-9308.test.tsx (7 tests | 5 failed)
     × leg 1: `${data.stats.total}` against an injected bag does NOT resolve 32ms
     × leg 3: a host-published `data` is no longer shadowed by the adapter 5ms
     × leg 4: a path resolves against the ambient scope 3ms
     × leg 5: the same path against the ADAPTER seam resolves to nothing 2ms
     × leg 7: a `data.*` visibility gate becomes unresolvable — shown, and LOUD 4ms

AssertionError: expected '99' to be '${data.stats.total}' // Object.is equality
      Tests  5 failed | 2 passed (7)

Legs 2 and 6 are the lit controls and pass on the unmodified tree, which is what makes the other five a reading. After b1 + b2: Tests 7 passed (7).

objectui#8021's pin was re-derived, never weakened or skipped. Leg A still READS the Data Context fence off the page on every run (it now branches over three carriers instead of two, so moving the fence back to either of the others changes what it renders); leg C's wiring and leg D's expression swapped to the new teaching; and a new leg E pins the adapter seam directly — provider dataSource plus ${data.user.name} now renders the characters the author typed. The FORWARDED_LOOKALIKES sweep gained scope= so the new carrier's own look-alike is covered.

Two stale claims were corrected while re-deriving neighbouring pins: three test files carried /** The ambient scope app-shell's `ExpressionProvider` really mounts. */ over a bag containing data: {}. buildExpressionScope has published no data since objectui#8166, so the comment was false before this PR and would have hidden the verdict move after it. Those bags are kept — they are now the only way the adapter-only leg is reachable at all — and re-documented as deliberate HOST publications.

Verification

See the report comment on #9308 for the full command list and VERDICT lines.

Acceptance notes

  • skills/objectui/** now teaches a false wiringguides/schema-expressions.md (| `data` | Alias for dataSource root |, "dataSource = { customerNames: [...] }, list calls useDataScope("customerNames")", ""bind": "app.settings.users" resolves dataSource.app.settings.users"), guides/data-integration.md and rules/protocol.md. ⛔ Not touched here: skills/** is a governed surface, and one path would park this whole PR in the draft-and-wait lane while it is the PR unblocking a red main. Filed separately; the six affected pins in packages/components publish their host values through the scope channel, which keeps them measuring their own subject.
  • objectui#7206 (the formValues / data tail) is pointed the same way by this ruling but is ⛔ not ruled and ⛔ not addressed here; it remains open.
  • objectui#6575data-table not reading bind — is untouched.

维护者速读(草稿)

改了什么。 渲染器不再把宿主注入的 dataSource(适配器)当作表达式根 data 发布;节点的 bind 改为在宿主通过 PredicateScopeProvider 发布的环境作用域里解析。文档与 README 随之改教作用域通道。

为什么改。 这是仓库自己已裁过两次的原则(objectui#8155 / objectui#8166)贯彻到最后一层:一个层只能绑定它答得出的根。面对合规适配器,data.* 恒为 undefined,这一层答不出。

风险与代价(含回滚)。 已在客户元数据里的 data.* 可见性判据,行为从「恒隐藏」变为「恒显示」,并在控制台响亮报出。这是唯一的行为断点,已在 changeset 正文点名。回滚只需还原 SchemaRenderer.tsx 的一行与 useDataScope 的一处读取,其余为文档与钉子。

席位意见。

你要做的。 确认「data.* 判据由恒隐藏翻为恒显示」这一取舍可接受;若希望改为保守方向(保留 data 为一个恒空对象以维持旧裁决),请说,那是另一张卡。

🤖 Generated with Claude Code

https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ


Generated by Claude Code

objectui#9308, maintainer ruling 2026-09-13, option B.

b1 — `SchemaRenderer` no longer writes `data: dataSource` into the evaluator
scope. Against a conformant `DataSource` adapter every `data.*` path already
resolved `undefined`, so this tier published a root it could not answer. Same
principle `ExpressionProvider` states and objectui#8155 / objectui#8166 already
applied one tier up.

b2 — `useDataScope` resolves `bind` against `usePredicateScope()`, the ambient
scope a host publishes through `PredicateScopeProvider`, instead of walking the
injected adapter. No published key is added; the channel already existed.

Also re-aims `reportAdapterOnlyDataPredicate` at the `data` the evaluator
really bound, and moves its copy off the "data is the adapter" claim. Left on
the adapter it would have gone silent for exactly the hosts this change breaks
and loud for the ones whose scope-channel `data` answers.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…d it

objectui#9308 + objectui#9346.

`Doc Snippet Type Check` was red on `main` with three fences that taught
publishing page values through `dataSource` and reading them back under
`data.*`. Under the 2026-09-13 ruling that teaching is wrong, so the Data
Context passages of `content/docs/guide/schema-rendering.md` and
`packages/react/README.md` move to `PredicateScopeProvider`, and the README
sentence asserting that `dataSource` "is what the evaluator sees under the name
`data`" — which the gate cannot see — goes with them.

objectui#8021's pin is RE-DERIVED, not relaxed: leg A still reads the page on
every run, leg C's wiring and leg D's expression swap to the new teaching, and
a new leg E pins the adapter seam as a non-root.

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

Copy link
Copy Markdown
Contributor

⚠️ Console Performance Budget — gauge not trustworthy

The eager closure was measured, but one of the ceilings it is measured against no longer means what it names, so this run carries no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing grew: the half marked below is a verdict about the gauge, and a ceiling that has stopped measuring anything can neither clear a bundle nor condemn one.

Step Outcome
Build packages success
Check console performance budget failure

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ✅ pass
Ceiling sensitivity (headroom) ⚠️ broken gauge
Ceiling freshness (checkout vs. base branch) ✅ pass

⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. The Check console performance budget step log carries the ceiling and the number it was compared against.

Reason: The entry chunk measured 144.3 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.

See the workflow run for details.


📦 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) 502.02KB 115.16KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.89KB 62.50KB
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.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
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.83KB 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.25KB 13.99KB
plugin-charts (index.js) 71.51KB 19.97KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.22KB 34.59KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.46KB 65.85KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.77KB 34.17KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.01KB 14.30KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.79KB
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) 96.00KB 31.71KB
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 — gauge not trustworthy

The eager closure was measured, but one of the ceilings it is measured against no longer means what it names, so this run carries no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing grew: the half marked below is a verdict about the gauge, and a ceiling that has stopped measuring anything can neither clear a bundle nor condemn one.

Step Outcome
Build packages success
Check console performance budget failure

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ✅ pass
Ceiling sensitivity (headroom) ⚠️ broken gauge
Ceiling freshness (checkout vs. base branch) ✅ pass

⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. The Check console performance budget step log carries the ceiling and the number it was compared against.

Reason: The entry chunk measured 144.3 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.

See the workflow run for details.


📦 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) 502.02KB 115.16KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.89KB 62.50KB
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.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
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.83KB 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.25KB 13.99KB
plugin-charts (index.js) 71.51KB 19.97KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.22KB 34.59KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.46KB 65.85KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.77KB 34.17KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.01KB 14.30KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.79KB
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) 96.00KB 31.71KB
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

…h through the scope channel

objectui#9308 b2 — `useDataScope` resolves the ambient predicate scope, so a
pin that fed the bound rows through `SchemaRendererProvider.dataSource` was
measuring a channel the hook no longer reads. Both keep the adapter seam
mounted, where it is now inert, and neither assertion moved.

Found by narrowing the dependent packages to the tests that can reach b1 or b2
at all: 1987 candidate test files, 7 reachable, 4 failures — all in this pair.

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

Copy link
Copy Markdown
Contributor

⚠️ Console Performance Budget — gauge not trustworthy

The eager closure was measured, but one of the ceilings it is measured against no longer means what it names, so this run carries no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing grew: the half marked below is a verdict about the gauge, and a ceiling that has stopped measuring anything can neither clear a bundle nor condemn one.

Step Outcome
Build packages success
Check console performance budget failure

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ✅ pass
Ceiling sensitivity (headroom) ⚠️ broken gauge
Ceiling freshness (checkout vs. base branch) ✅ pass

⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. The Check console performance budget step log carries the ceiling and the number it was compared against.

Reason: The entry chunk measured 144.3 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.

See the workflow run for details.


📦 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) 502.02KB 115.16KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.89KB 62.50KB
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.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
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.83KB 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.25KB 13.99KB
plugin-charts (index.js) 71.51KB 19.97KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.22KB 34.59KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.46KB 65.85KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.77KB 34.17KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.01KB 14.30KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.79KB
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) 96.00KB 31.71KB
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

Contract review

Head judged: 4de2534f25e67873d88f16ac5bfe8864395e4919 (base 69aa9c017).

⛔ First: this seat falsified TWO premises I wrote into its dispatch

1. reportAdapterOnlyDataPredicate would flood. I told it to measure whether removing the data root makes every data.* predicate unresolved and floods the console. False, and for a reason I had not looked at: that diagnostic resolves against the argument passed as boundData, not against the evaluator scope. b1 changed its firing set for a conformant host by zero. What b1 broke is not its volume but its truth, in both directions — pointed at the adapter it would go SILENT for exactly the hosts this change breaks and LOUD for hosts whose scope-channel data answers. The seat re-aimed it at predicateScope?.data and rewrote the copy off the now-false "data is the adapter" claim.

2. "Confirm the verdict does not move for a conformant host — it should not."Falsified, and this is the load-bearing measurement of the whole round:

{ data: undefined }      data.status == 'draft'  ->  false
no `data` key at all     data.status == 'draft'  ->  true   (and THROWS under throwOnError)

A present-but-undefined root and an absent root are not the same to the engine. So a data.* visibility gate flips from constant-HIDE to constant-SHOW. I predicted no movement because I reasoned "it resolved undefined before, it resolves nothing now" — which conflates exactly those two states. Pinned as leg 7, stated in the changeset and the PR body.

① Derived judgments

# move judged
1 data: dataSource removed from the evaluator scope correct — verified by content, not by count
2 useDataScope reads usePredicateScope() correct
3 the shadowing defect retired as a side effect correct, and it was a real latent bug
4 nine bind readers, per-reader fallback re-verified correct
5 objectui#8021's pin re-derived, not weakened correct
6 the three fences + the README sentence the gate cannot see correct

1 — enumerated rather than counted. grep -c "data: dataSource" returns 2 on this branch, which looks like a failed removal. Both hits are comments explaining the removal (:835, :848). The live scope reads:

new ExpressionEvaluator({
  ...predicateScope,
  current_user: (predicateScope as any)?.user,
  ...(boundRecord &&  ? { record: boundRecord } : null),
  page: pageVariables,
});

No data. ⭐ Recording the method because the count alone would have been a false alarm, and I have made that mistake three times this session.

3 — I raised the shadowing hazard independently during the analysis: ...predicateScope at :842 followed by data: dataSource at :847 meant a host publishing data through the documented scope channel had it silently overwritten by the adapter. b1 retires it, and :848's comment names it rather than fixing it in silence.

4 — the reason b2 is safe, re-read per reader on this branch: list.tsx Array.isArray(boundData) ? boundData : …schema.items; tree-view boundData || schema.nodes || []; ObjectChart / ObjectDataTable / ObjectPivotTable boundData || schema.data || fetchedData; ObjectGrid falls through to getDataConfig(schema); ObjectKanban external || boundData || schema.data || fetchedData; ObjectGallery props.data || boundData || …; ObjectTimeline props.data || boundData || fetchedData. None treats boundData as its sole source. (The card said five readers; there are nine — my dispatch carried that correction and the seat re-verified all nine rather than inheriting it.)

6 — the deliverable, and CI is the witness, not the seat. check:doc-snippets exit 0 with Semantic phase: 649 of 649 block(s) judged, 0 failed, and the ablation reproduces the card's exact three diagnostics by taking only the two documents back to base. Confirmed independently: PR CI shows Doc Snippet Type Check GREEN on this head, and the only red is Bundle Analysis, which is inherited. All three fences moved to PredicateScopeProvider scope={…}, and packages/react/README.md:44's \`dataSource\` is what the evaluator sees under the name \`data\`. — which no gate could see — is deleted.

② Semver

minor across @object-ui/react, @object-ui/components, @object-ui/types, and the note leads with the break and who it breaks:

Breaking, deliberately — and minor only because this repo's fixed group of 40 packages may not carry a major. Read the migration note below before upgrading if any of your metadata reads data.* at the page/component tier.

Correct, and it carries the verdict-flip. ⛔ Note the changeset was not edited by me for a spelling convention — there is none, as I had to retract to four seats this round.

③ Boundary flags — and the two questions, ruled

Q2 — the verdict flip. RULING: A, as ruled. Proceed.

The conservative variant (data: EMPTY instead of removing the key) keeps verdicts byte-identical and nothing goes loud. ⛔ Refused, and the seat's reason is the right one: it keeps publishing a root this tier cannot answer — the exact defect objectui#8155, objectui#8166 and this ruling each removed — and trades a loud failure for a silent one.

⚠️ But the maintainer ruled B without this consequence in hand, so it is stated rather than buried. For any host that authored a data.* gate, an element that was constantly HIDDEN becomes constantly SHOWN. The gate was never actually gating on data — it was a constant hide either way — but what a user sees changes. It is diagnosable (objectui#5454 makes it loud), pinned, and in the changeset's migration note. ⇒ this is a statement to the maintainer, not a request to re-rule; A is what B means.

Q1 — the published skills/** guides this ruling makes false. RULING: A, with C as the follow-up.

The seat hit two instructions pointing opposite ways — "fix what this round makes false" vs "⛔ do not touch skills/**" — and took A: filed objectui#9370 with a per-line census rather than folding them in. Correct. Folding three governed files in would have parked the PR that clears a red on main behind a human merge. ⇒ the coverage hole is a filed card, not a silence. C (hand objectui#9370 to the seat already in the governed skills lane) is the follow-up and costs one human review instead of two.

other flag disposition
objectui#9297's docs census accepted — this ruling makes those pages more wrong rather than newly wrong; that card already carries them.
objectui#7206 (formValues / data tail) accepted — pointed the same way, ⛔ not ruled, not in this PR.
three test files claimed data: {} was "the ambient scope ExpressionProvider really mounts" accepted — false since objectui#8166 and load-bearing after this change; fixed in place because those files were being re-derived anyway.
dataTableBindDiagnostic's data-vs-bind precedence accepted — DISSOLVED as predicted, and said so in the module docblock. objectui#6575 untouched; data-table still does not read bind.
celAuthoring's rowCanonAdvisory accepted — becomes more correct, severity left at warning, untouched.

Independence

Implemented-by: claude/issue-9308-unbind-data-root   (mode:subagent — a subagent has no session of its own)
Reviewed-by:    session_01UzHd6hDYatoDn17BuwKxnZ     (domain:ui PM seat)

⛔ For this lane a clause-② review is default-tier self-review plus the gates. The readings carry the verdict.

Verdict

PASS. Both carriers cleared in the same stroke as this comment.

⚠️ On landing pre-check ③, and stated because I broke this rule once today: this PR's only red is Bundle Analysis, inherited from main via objectui#9316. ⛔ I am not inventing an exception for convenience — the argument is specific and it is about the rule's own satisfiability: a PR whose purpose is to clear a red on main cannot be gated on main being green. Holding this one would leave Doc Snippet Type Check red until a different, unrelated red is fixed by the maintainer, which is a deadlock on the rule's own repair path. Recorded as a concrete sub-case on objectstack#17934, which is the card asking for ③ to state what it does about base-inherited reds.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Bundle Analysis on this PR is the standing ui-components debt — taken to the chunk's own decision, as the checker instructs

Reproduced locally at origin/main 69aa9c017 (a full package build plus a console build, then node scripts/check-eager-closure-budget.mjs), because the Actions job-log blob host is refused by this container's egress policy and the check-run carries no file-anchored annotation:

❌ chunk `ui-components`   387.8 KB measured / 389.6 KB ceiling
   (headroom 1.9 KB = 0.02x the 89.0 KB regression, under the 0.10x floor and held open
    by its declared 4289-byte allowance, which may only be paid DOWN; reds below 3378 bytes)
chunk `ui-components` was ALREADY declared exhausted before this run.
BUDGET_EXIT=2

Every other row is green: aggregate 3105.8 / 3134.8 KB (0.32x), vendor-objectstack 0.15x, i18n-locale-en 0.10x, framework 0.61x.

The checker's own verdict text says what to do with it, and this comment is that instruction being followed:

This row's headroom is a standing debt that predates this change, and it moves under traffic that has nothing to do with the chunk — measured at three gzipped bytes across five unrelated merges. So this verdict is NOT an accusation that your diff spent the bytes … ⛔ There is therefore nothing here for this pull request to "fix", and the two edits that would turn this green are both forbidden: ⛔ never raise the ceiling, and ⛔ never raise the allowance. Paying the row down is the open decision on the chunk, ⛔ not a task for whichever change the queue happened to weigh — take it there, and say on this pull request that you did.

⛔ No ceiling was moved, no allowance was moved, no exemption was added. Taken to objectui#9204, the chunk's own decision card, where the domain:ui PM seat is carrying it.

This diff cannot have spent those bytes, and that is checked rather than asserted

This PR removes an expression root and re-points a hook; it adds no dependency and no module to the console's eager closure. The row read 387.8 KB on main itself, before this branch existed — the same figure this PR's check reports.

Why this PR is armed anyway

Its own deliverable is green: check:doc-snippets reaches exit 0 here (Semantic phase: 649 of 649 block(s) judged, 0 failed), and CI confirms Doc Snippet Type Check green on this head. That is the check this PR exists to clear on main.

⚠️ Stated plainly because I broke a landing rule once today: Bundle Analysis is the only red on this head and it is inherited from main via objectui#9316. Holding this PR would leave Doc Snippet Type Check red on main until an unrelated standing debt is paid — a deadlock on the rule's own repair path. The general form of that gap is filed as objectstack#17934; this instance is resolved by the checker's own instruction above, which tells the weighed pull request to route the debt and proceed.


Generated by Claude Code

Merged via the queue into main with commit 8524372 Sep 13, 2026
37 of 38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9308-unbind-data-root branch September 13, 2026 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment