Skip to content

docs(core): re-state the dataSource.filter note for the two populations the convergence split (objectui#8945) - #11286

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-8945-element-datasource-filter-note
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-8945-element-datasource-filter-note

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #8945

Clause-②: no

This PR re-states the ElementDataSourceConfig docblock in @object-ui/core for the two populations that the upstream filter convergence split apart. It changes comments and adds a patch changeset. No type, export, runtime behaviour or accepted set moves. Dispatched by the domain:spec @ objectui seat, session https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT.

What changed

  • packages/core/src/data-scope/element-data-source.ts, the ElementDataSourceConfig docblock. It said three shapes "legitimately reach a renderer here". It also typed filter as unknown "rather than the spec's FilterCondition". The docblock now separates two populations:
    • What an author may write is the ViewFilterRule array alone. ElementDataSourceSchema refuses the record form by kind. It refuses an AST tuple array at its first member, because each member must be a rule object.
    • What a renderer may still receive is all three shapes. Upstream's D2 conversion page-component-filter-record-to-rule-array rewrites a stored filter only where the rule array spells it losslessly (a flat record, an operator object whose operators the rule vocabulary spells, several such keys, or a single-level AST tuple array), on every ObjectStack stored-row read and under os migrate meta --stored. It leaves exactly as stored a filter carrying $and / $or / $not, any filter with a part that has no lossless rule spelling, and every filter of a component whose rows are inline. This renderer is backend-agnostic and replays no conversion itself, so all three shapes may still arrive, and mergeFilterNodes still lowers each shape. (Corrected in round 3 at a9dd51ca0, answering contract review 5921608196 ①-8.)
    • The unknown type stays, and so does its reason: narrowing the type would only move the cast. The spec's type is now named ViewFilterRule[], not FilterCondition.
  • The same file's module docblock has an example binding that wrote "filter": { … }, which is the record form. It now writes "filter": [ … ]. This is a bounded in-place fix outside the docblock the claim named. See "File surface" below.
  • .changeset/8945-element-datasource-filter-note.md adds a patch changeset for @object-ui/core.

Premises, measured on this worktree's install

The install is @objectstack/spec 17.5.0, and the branch is based on e420df310.

  • H1 holds. The card's Restart-when: probe, run as written from the worktree root, exits 0. Its controls are inside the predicate, so a dark instrument would have exited 9. A wider ElementDataSourceSchema.safeParse reading:
    • {object} is ACCEPTED. This is a control.
    • An unknown key is REFUSED with unrecognized_keys. This is a control.
    • The record form { status: 'open' } is REFUSED with invalid_type at filter, expected an array.
    • The operator-object record { amount: { $gt: 5 } } is REFUSED the same way.
    • The rule array [{ field, operator, value }] is ACCEPTED.
    • The AST tuple array [['status','=','open']] is REFUSED with invalid_type at filter.0, expected an object.
  • H2 holds. The docblock still read 「three shapes legitimately reach a renderer here」 at e420df310. The installed .d.ts declares filter as an optional array of ViewFilterRule objects, and the spec's own describe() text for the key names the rule array and says the record form is refused.
  • H3 holds, so record-picker.tsx is untouched. I read the three sites the card named, at the card's pin and on main:
    • The binding-composition comment carries no shape claim.
    • The comment saying the filter input is DECLARED carries no shape claim.
    • The registration's filter input was the site that described FilterCondition at the pin. It now declares type: 'array' and a rule-array description.
    • I checked that description against ElementRecordPickerPropsSchema on 17.5.0. The rule array is ACCEPTED. The record form and $and record are REFUSED at filter, expected an array. A tuple member is REFUSED at filter.0. The controls are {object}, ACCEPTED, and an unknown key, REFUSED. No sentence there misstates the door.

File surface

The claim's surface is the ElementDataSourceConfig docblock, a record-picker.tsx sentence only if one still misstates the door, and one patch changeset. The diff also touches one token in the same file's module docblock: the example binding's "filter": { … } becomes [ … ]. All four bounded-fix conditions hold:

  1. It is the same defect class, prose teaching the retired authoring shape.
  2. It is a mechanical change, and the spec pins the target shape.
  3. The claim reports that no other claim holds this file.
  4. It is the same file and the same gates.

Leaving the example as it was would have made the file contradict its own re-stated note. The seat owes a same-round addition of this site to the claim's file surface.

Gates, all run at HEAD 4837c74e6

  • pnpm --filter '@object-ui/core^...' build, under the verify lock: VERDICT command-exit 0. The scope was 2 of 47 projects.
  • pnpm --filter @object-ui/core type-check, under the lock: exit 0. The script name is echoed and both tsc legs ran, with 0 TS errors.
  • pnpm exec vitest run --maxWorkers=2 packages/core/, under the lock: exit 0. Test Files 193 passed (193), Tests 3797 passed | 27 skipped (3824).
  • pnpm --filter @object-ui/core lint: exit 0. It reported 0 errors and 568 pre-existing warnings, none of them in the touched file.
  • These check:* gates all exited 0:
    • check:control-bytes
    • check:new-line-citations, which printed 0 new citation(s), compared against merge base e420df310 over 2 files.
    • check:element-data-source-declaration
    • check:spec-symbols
    • check:installed-pin-claims
    • check:changeset-claims, which printed No pending changeset names a file this change touches.
    • check:pending-changeset-literals
    • check:comment-mask-corpus. Its one disagreement is in apps/console/src/pages/DocsIndex.tsx, not in this diff.
  • node scripts/check-changeset-presence.mjs: exit 0. It found 1 released source file and 1 changeset added.
  • node scripts/check-changeset-no-major.mjs: exit 0.
  • node scripts/check-governed-queue-guard.mjs --test on both paths printed NOT GOVERNED.

Author population: the card's open measurement

This is a historical reading at HEAD 4837c74e6 on spec 17.5.0. Nothing re-derives it.

Method. It parses candidates, and no regex is involved.

  1. Enumerate tracked files from git ls-tree -r HEAD with a clean-tree assertion. That gives 6742 .ts/.tsx/.js/.jsx/.mjs/.cjs/.mts/.cts/.json files; all 6742 parsed and 0 failed.
  2. Walk the TypeScript AST for every object literal that is the value of a dataSource property, a dataSource={{…}} JSX attribute, or an X.dataSource = assignment, and that carries a filter key.
  3. Evaluate the literal statically.
  4. Judge each candidate with the installed ElementDataSourceSchema.safeParse three times: on the value with filter removed, which decides whether it is a binding at all; on the full value; and on filter alone.

The instrument controls, rule array ACCEPTED and record REFUSED, are lit. A positive control is also lit: the eight fixtures listed on objectui#11117 were all found, 8 of 8.

Reading. There are 36 candidates, all in test sources and none in non-test source.

  • 6 bindings already write the rule array and parse clean.
  • 24 bindings write a retired orthography across 18 test files: 19 AST tuple arrays and 5 records.
  • 1 rule array is refused for its value shape: an equals with an array comparand. It is a deliberate objectui#8557 fixture, not an orthography author.
  • 4 have a non-static filter and were not measured.
  • 1 is not a binding: its other keys fail, with invalid_type at object, deliberately.

.md and .mdx fences are outside this instrument; they belong to objectui#11117.

The upstream entry's "seventeen (fifteen tuple arrays, two records)" was taken at a different pin. It is not comparable, and this PR does not assert a rival reading of it.

These are the 18 files and how many retired-orthography fixtures each holds:

  • packages/components/src/__tests__/record-picker-element-data-source.test.tsx: 2 tuple
  • packages/components/src/__tests__/record-picker-malformed-filter-10789.test.tsx: 2 record
  • packages/plugin-charts/src/ObjectChart.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-dashboard/src/ObjectPivot.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-detail/src/__tests__/RecordRelatedListRenderer.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-detail/src/renderers/__tests__/record-related-list.propsRefusal-9963.test.tsx: 1 tuple
  • packages/plugin-form/src/EmbeddableForm.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-form/src/LineItemsPanel.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-form/src/MasterDetailForm.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-gantt/src/ObjectGantt.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-grid/src/__tests__/ObjectGrid.elementDataSource.test.tsx: 2 tuple
  • packages/plugin-kanban/src/ObjectKanban.elementDataSource.test.tsx: 2 tuple
  • packages/plugin-list/src/__tests__/ListView.elementDataSource.test.tsx: 1 record
  • packages/plugin-list/src/__tests__/ListView.sharedGate.test.tsx: 1 record
  • packages/plugin-map/src/ObjectMap.elementDataSource.test.tsx: 1 tuple
  • packages/plugin-timeline/src/ObjectTimeline.elementDataSource.test.tsx: 2 tuple
  • packages/react/src/element-data-source/__tests__/ElementDataSourceGate.malformedFilter-10789.test.tsx: 1 record
  • packages/react/src/element-data-source/__tests__/ElementDataSourceGate.test.tsx: 2 tuple

Patch round 2, at HEAD 0b4445d5a (written by the seat)

  • .changeset/7297-record-id-filter-token.md gains a dated correction, append-only (+2/−0), with frontmatter md5 2935e1e656cbeb29b6093519d07dde85 before and after.
    • Its record-form element:number filter example { "assignee": "{record_id}" } is refused on @objectstack/spec 17.5.0 (invalid_type at filter, expected array).
    • The note gives the accepted ViewFilterRule array, [{ "field": "assignee", "operator": "equals", "value": "{record_id}" }]. It was measured with ElementNumberPropsSchema.safeParse and ElementDataSourceSchema.safeParse before writing, with both controls lit.
  • This is a bounded in-place fix the seat requested, because the text is release-bound (release PR chore: release packages #5400 is open). The claim's file surface was amended in the same round: objectui#8945 comment 5921413381.
  • Gates at 0b4445d5a: check:changeset-claims, check:pending-changeset-literals, check-changeset-presence, check-changeset-no-major, check:control-bytes and check:new-line-citations each exit 0. No package source changed since 4837c74e6, so round 1's @object-ui/core build, type-check, test and lint still stand.

Acceptance notes

  • objectui#11117 is not addressed here. Its listed fixture population, eight fixtures in six files, is a subset of the 24 above. Some of those fixtures pin how a renderer handles stored legacy shapes, for example the objectui#10789 malformed-filter pins. Those fixtures belong to the renderer population this note now names, so converting them all to the rule array could delete legitimate coverage. That judgement is objectui#11117's to make.
  • objectui#8220 is not addressed here.
  • The toFilterNode doc in packages/core/src/utils/filter-converter.ts says "three shapes in circulation, all legitimate" about what a view hands a renderer. That statement is about the renderer population and stays accurate, so it is not edited.
  • record-picker.tsx is unchanged because H3 held.

Generated by Claude Code

…two populations

The note said three filter shapes "legitimately reach a renderer here" and
typed filter as unknown "rather than the spec's FilterCondition". Since the
spec's filter doors converged on the ViewFilterRule array (objectui#6206),
that mixes what an author may write (the rule array alone; the record form
and AST tuple arrays are refused at ElementDataSourceSchema) with what a
renderer may still receive (all three, because metadata at rest is not
rewritten). The note now states both, names the spec type as
ViewFilterRule[], and keeps the unknown type and its reason. The module
example binding writes a rule array instead of the record form.

Comments and a patch changeset only.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3584.5 KB 3607.4 KB
Main entry chunk (gzip) 150.0 KB 350 KB
Entry file index-sY3hKuzY.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.18KB 136.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 41.23KB 11.53KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.27KB 37.26KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 48.62KB 15.21KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…used on 17.5.0

The pending objectui#7297 changeset teaches { "assignee": "{record_id}" }
as an element:number filter. @objectstack/spec 17.5.0 refuses that record
form at the filter key (invalid_type, expected array) and accepts the
ViewFilterRule array spelling. Append a dated correction giving the
accepted spelling; frontmatter and existing sentences are unchanged.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3584.5 KB 3607.4 KB
Main entry chunk (gzip) 150.0 KB 350 KB
Entry file index-sY3hKuzY.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.18KB 136.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 41.23KB 11.53KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.27KB 37.26KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 48.62KB 15.21KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0b4445d5a174f6eff45d5a6a935ad46c8189dff4
Local-runs: none

Inputs, and nothing else: card #8945 (body and all 11 comments, through the seat's ACCEPT 5921470598), PR #11286 (body, 3-file list, net diff against main at merge base e420df310 — the API diff and a local three-dot diff are byte-identical apart from index lines), the check-runs on the head (read last), objectui AGENTS.md at origin/main, and the installed spec's source at the 17.5.0 tag commit 0f6dcac5e9 (ui/page.zod.ts, ui/component.zod.ts, ui/view.zod.ts, ui/filter-rule-array.ts, migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts, conversions/registry.ts, conversions/apply.ts, conversions/stored.ts). Read-only throughout: no worktree, build, test or gate run.

① Derived judgments

The diff publishes no type, export, runtime or accept-set change: ElementDataSourceConfig.filter?: unknown is untouched, and the three files are JSDoc plus two .changeset bodies. Each claim the new prose makes, judged against the installed spec:

  1. Author population = the ViewFilterRule array alone — right. ElementDataSourceSchema (page.zod.ts) is a strictObject whose filter is z.array(ViewFilterRuleSchema, { error: ruleArrayFilterError({ migration: 'element-data-source-and-object-block-filter-rule-array' }) }); its describe() says the record form is refused. The record form is refused by kind (invalid_type, expected array); a tuple member is refused at filter.0 because ViewFilterRuleSchema (view.zod.ts) is itself a strictObject. The docblock's two examples ({ status: 'open' }, [['status', '=', 'open']]) are exactly those two refusals.
  2. Spec type renamed from FilterCondition to ViewFilterRule[] — right. The old sentence named a type the door no longer declares.
  3. Migration id cited — right. element-data-source-and-object-block-filter-rule-array is the id: of migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts (the 18. is the filename prefix, not part of the id), and it is the id the door's own ruleArrayFilterError names.
  4. filter kept unknown — right. The card's fence (narrowing "would only move the cast") and the triage/ruling fence (reader tolerance is a separate, evidence-backed step) both hold; mergeFilterNodes remains the single sink and is not edited.
  5. Module-docblock example "filter": [ … ] — right. A record-form example in the same file would contradict the re-stated note; bounded in-place fix, surface amended in-round (5921413381).
  6. record-picker.tsx untouched — right. At origin/main its filter registration input already says "CONVERGED this key onto z.array(ViewFilterRuleSchema) … It was FilterConditionSchema" and describes the rule array; the other two sites carry no shape claim; ElementRecordPickerPropsSchema.filter (component.zod.ts) is the array door. ELEMENT_DATA_SOURCE_INPUT.description and toFilterNode's "three shapes in circulation" doc (renderer population) are consistent and rightly left alone.
  7. 7297 changeset correction — right. ElementNumberPropsSchema.filter (component.zod.ts) is z.array(ViewFilterRuleSchema, …), so { "assignee": "{record_id}" } is refused at filter, expected array; equals is the first canonical member of VIEW_FILTER_OPERATORS; a string value is legal; dataSource.filter takes the same array. Append-only (+2/−0, frontmatter untouched), and eight dated **Correction, …** paragraphs already sit in main's pending changesets, so the form has precedent.
  8. "The convergence deliberately does not rewrite metadata at rest, so a stored page carrying the record form or an AST tuple array keeps arriving here" — WRONG. The docblock says it, the 8945 changeset repeats it ("The convergence does not rewrite metadata at rest…"), and the PR body carries it. The installed spec records the opposite disposition, in the very migration entry the docblock cites as its authority. Its replacement text: "Metadata AT REST: the mappable part of the table above is a D2 conversion, page-component-filter-record-to-rule-array (ruled twelve September, option B: convert what maps losslessly and name what does not, rather than leave every stored row to its next save or flatten combinators), so os migrate meta --stored … rewrites a stored page whose filter is a flat record, an operator object …, several such keys, or a single-level AST tuple array, and every stored-row read replays the same rewrite until it does." The conversion exists in conversions/registry.ts (toMajor: 18, retiredFromLoadPath: true, retiredAfter: '17.4.0'; reach: "dataSource.filter on any component (ElementDataSourceSchema)"); conversions/apply.ts walks ALL_CONVERSIONS with no toMajor gate; conversions/stored.ts — "the one primitive every stored-row rehydration seam calls" — pins includeRetired: true. So on 17.5.0 the flat record and the single-level tuple array, the docblock's own two examples, are the shapes that ARE rewritten at rest, on every stored-row read. What is left as stored is the residue the entry lists: combinator records ($and / $or / $not), parts with no lossless rule spelling (a null value, $null / $exists, an AST like, an array or object comparand in equality position, an AST and / or group), and every filter on an inline-row block. The conclusion survives — a renderer may still receive all three, from that residue, from rows not yet re-read, and because this renderer is backend-agnostic and nothing outside an ObjectStack stored-row seam replays the chain — but the published reason is the inverse of the upstream ruling and is attached to a citation of the text that contradicts it. This is the card's own defect class (a note that misdescribes the upstream contract and so teaches the wrong tolerance), on the note the card exists to fix, and in scripts/check-changeset-claims.mjs's own taxonomy it is a BORN FALSE changeset claim, which that report-only gate cannot see. The card body's premise sentence (filed before the at-rest ruling) went false two days later; the dev's premise_still_valid: true re-measured the door but not the migration entry that the quoted describe() text points to by name.

⇒ ①-8 fails the record. What flips it, inside the claim's surface and comments-only: re-word the reason in the ElementDataSourceConfig docblock, the 8945 changeset and the PR body to the disposition upstream records — e.g. "upstream's D2 conversion page-component-filter-record-to-rule-array rewrites the losslessly mappable record forms and single-level AST tuple arrays at rest, on every stored-row read and under os migrate meta --stored; combinator records, parts with no lossless rule spelling and inline-row blocks' filters are left as stored, and this renderer is backend-agnostic, so all three shapes may still arrive" — keeping the unknown type, its cast argument and items 1–7 exactly as they are. Same file, same gates; the 7297 correction needs no change.

② Semver level

  • .changeset/8945-element-datasource-filter-note.md declares '@object-ui/core': patch — right level. The diff edits packages/core/src/** of a fixed-group package, so AGENTS.md §9 (Housekeeping) and scripts/check-changeset-presence.mjs require a declaration; the published .d.ts changes only in JSDoc text and nothing in the API or accept set moves, so patch, not minor. An empty-frontmatter declaration would also have been legal under the same rule; patch is the more honest reading because published .d.ts text does change. No major (version-alignment rule; Changeset Bump Policy success). ⚠️ The body of this changeset carries the ①-8 sentence and publishes it verbatim to the CHANGELOG — the level is right, the prose is not.
  • .changeset/7297-record-id-filter-token.md: frontmatter (minor / minor / patch / patch) byte-identical; +2/−0 body-only — no level change, none owed.
  • Clause-②: no — right. No accept set, type, export or runtime behaviour moves; the spec's own conversion entry reads the same family the same way ("Clause-②: no — no accept-set change").

③ Boundary flags

  • Dev deviation (R1): one token in the module docblock outside the named ElementDataSourceConfig docblock. Answered: bounded in-place fix, four conditions stated by the dev and re-checked by the seat; the claim's file surface was amended in the same round (5921413381). Accepted.
  • Dev deviation (R1): report JSON carries gates / files_changed / line_budget / deviations. Cosmetic; the dispatch asked for them. Accepted.
  • R2 (seat-requested): dated correction appended to the pending 7297 changeset. Answered: surface amended in 5921413381; append-only verified on the diff; content right (①-7). Accepted.
  • open_questions: [] in both rounds. Nothing to answer. The seat's ACCEPT named the at-tier record "a changeset-prose review face" — this is that face, and ①-8 is its finding; it is the one item this record escalates, back to the dev on the same claim.
  • Out-of-scope finding (c), the 7297 example — folded into R2, not filed. Right disposition (release-bound, PR chore: release packages #5400 open per the seat).
  • Out-of-scope finding, the 24 retired-orthography fixtures in 18 test files — the seat says it is recorded on objectui#11117 (5921421452). That comment is outside this brief's inputs and is not verified here; it does not bear on the verdict.
  • Card fences all held: the filter TYPE not narrowed; registry filter declarations (finding(plugin-map/plugin-gantt/plugin-timeline): six registrations declare neither filter nor sort while all three renderers lower both onto the query — #7712's shape, but the spec cannot be cited here #8220) untouched; fixtures (finding(types): the tolerant face refuses the documented per-element dataSource bindings — the spec waives objectName when dataSource.object is set, and objectui's zod arms still require it #11117) untouched; reader tolerance not removed; the three record-picker.tsx sites read directly, not from the paraphrase.
  • Form: draft, base main, Fixes #8945 the only closing keyword, 2 commits with the model-free trailer pair, no merge, no rebase, no model identifier in title, body, diff or changesets, no cross-file path:line in the new lines (Line Citation Gate success). The branch is 2 commits behind main; the queue rebuilds, nothing owed.
  • Check-runs on the head, read 2026-09-30T23:30Z: 42 — 33 success, 3 skipped (dependabot, the two coverage placeholders), 6 in_progress (Test shards 1, 2, 4, 5, 6, 8), 0 failure or cancelled. Recorded as in progress, not as a verdict. Success includes Changeset Declaration, Changeset Bump Policy, Changeset Claim Re-read, Changeset Fixed Group Check, Changeset Overwrite Report, Control Byte Scan, Line Citation Gate, Governed Surface Queue Guard, Spec Main Shape Gate, Type Check, Lint, Test (dist pins) and Test shards 3 and 7. No gate reads the semantics of a changeset body, so none of them could have caught ①-8.

Implemented-by: claude/issue-8945-element-datasource-filter-note
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: FAIL

Rendered 2026-09-30T23:33Z at CONTRACT_REVIEW_TIER by the isolated reviewer inside session_01VhxTqosz7wn54ahqyxgERT; ①-8 is the sole failing item, and items 1–7, ② and ③ stand once it is re-worded.

…rers still see all three filter shapes

The re-stated ElementDataSourceConfig note and its changeset said the
convergence does not rewrite metadata at rest. The migration entry they
cite says the opposite: the D2 conversion
page-component-filter-record-to-rule-array rewrites the losslessly
mappable record forms and single-level AST tuple arrays on every
ObjectStack stored-row read and under os migrate meta --stored, and
leaves combinator records, parts with no lossless rule spelling and
inline-row components' filters as stored. The reason now says that,
plus that this backend-agnostic renderer replays no conversion itself.
The conclusion, the unknown type and its cast argument are unchanged.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3584.5 KB 3607.4 KB
Main entry chunk (gzip) 150.0 KB 350 KB
Entry file index-sY3hKuzY.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.18KB 136.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 41.23KB 11.53KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.27KB 37.26KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 48.62KB 15.21KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a9dd51ca03d2dfdc90d9da4f137abd6165d344a4
Local-runs: none

Inputs, and nothing else: card #8945 (body and all 13 comments, through the seat's round-3 acceptance 5921701335), PR #11286 (body, 3-file list, the API diff against main — byte-identical, apart from index lines, to a three-dot diff against origin/main at merge base e420df310; main has since moved to 58da8aec, the API reports the head mergeable), the PR thread (the earlier record 5921608196, FAIL at 0b4445d5a), the head's three commits, the check-runs on the head (read last), objectui AGENTS.md at origin/main, the installed spec's source at the 17.5.0 tag commit 0f6dcac5e9 (ui/page.zod.ts, ui/component.zod.ts, ui/view.zod.ts, ui/filter-rule-array.ts, migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts, conversions/registry.ts, conversions/apply.ts, conversions/stored.ts), and, at objectui origin/main, record-picker.tsx, filter-converter.ts, element-data-source.ts, the pending 7297 changeset, .changeset/config.json, the two changeset gate headers, and one git grep for callers of the conversion chain. Read-only throughout: git fetch / git show / git diff / git grep on refs, no worktree, build, test or gate run. The dispatching seat's conclusions on the card thread were read as thread history and adopted nowhere; every judgment below is drawn from the diff and the sources named. This head is judged in full, not only the round-3 delta.

① Derived judgments

The diff publishes no type, export, runtime or accept-set change: ElementDataSourceConfig.filter?: unknown is untouched (both declarations), mergeFilterNodes is not edited, and the three files are one JSDoc block plus one module-doc token, one new .changeset body and one appended paragraph in a pending .changeset. Each claim the prose makes, judged against the installed spec:

  1. Author population = the ViewFilterRule array alone — right. ElementDataSourceSchema (page.zod.ts) is a strictObject whose filter is z.array(ViewFilterRuleSchema, { error: ruleArrayFilterError({ migration: 'element-data-source-and-object-block-filter-rule-array' }) }), and its describe() says the MongoDB-style record form is refused. The record form is refused by kind (invalid_type, expected array); a tuple member is refused at filter.0 because ViewFilterRuleSchema (view.zod.ts) is itself a strictObject. The docblock's two examples, { status: 'open' } and [['status', '=', 'open']], are exactly those two refusals.
  2. Spec type renamed from FilterCondition to ViewFilterRule[] — right. view.zod.ts exports ViewFilterRule as z.input of ViewFilterRuleSchema; the old sentence named a type this door no longer declares.
  3. Migration id cited — right. element-data-source-and-object-block-filter-rule-array is the id: of the entry file (the 18. is the filename prefix), and it is the id the door's own ruleArrayFilterError names.
  4. filter kept unknown, with the cast argument and the new closing sentence — right. The card's fence ("would only move the cast") holds; the triage / ruling fence (reader tolerance is a separate, evidence-backed step) holds; the added sentence "refusing the retired shapes is the spec schema's job at the authoring door, not this type's" is that fence restated. mergeFilterNodes remains the single sink (the composition seam in element-data-source.ts still reads mergeFilterNodes(viewFilter, config.filter)) and is not edited.
  5. Module-docblock example "filter": [ … ] — right. A record-form example in the same file would contradict the re-stated note; bounded in-place fix, surface amended in-round (5921413381).
  6. record-picker.tsx untouched — right. At origin/main its filter registration input says "CONVERGED this key onto z.array(ViewFilterRuleSchema) … It was FilterConditionSchema", and its description says "MEMBERS ARE RULE OBJECTS: the MongoDB-style record form … the spec now refuses it by kind, and a bare tuple … is refused as a member". ElementRecordPickerPropsSchema.filter (component.zod.ts) is z.array(ViewFilterRuleSchema, …) with the element-record-picker-filter-rule-array migration. toFilterNode's "three shapes in circulation, all legitimate" (filter-converter.ts) describes what a view hands a renderer — the renderer population — and is rightly left alone.
  7. 7297 changeset correction — right. ElementNumberPropsSchema.filter (component.zod.ts) is z.array(ViewFilterRuleSchema, …) with the element-number-filter-rule-array migration, so { "assignee": "{record_id}" } is refused at filter, expected array; equals is the first canonical member of VIEW_FILTER_OPERATORS; a string value is legal; dataSource.filter (page.zod.ts) takes the same array. Append-only re-verified on this head: origin/main's file is a byte-exact prefix of the head's (cmp over the old length), frontmatter identical, +2/−0.
  8. The renderer-population REASON — RESOLVED, right. The earlier record failed the sentence "the convergence deliberately does not rewrite metadata at rest"; that phrase has zero hits in this head's added lines, in both the docblock and the 8945 changeset, and the PR body's sub-bullet was replaced. Each clause of the replacement, traced to the installed spec:
    • "rewrites a stored filter only where the rule array spells it losslessly — a flat record, an operator object whose operators the rule vocabulary spells, several such keys, or a single-level AST tuple array — on every ObjectStack stored-row read and under os migrate meta --stored": the migration entry's reason reads "Metadata AT REST: the mappable part of the table above is a D2 conversion, page-component-filter-record-to-rule-array … so os migrate meta --stored … rewrites a stored page whose filter is a flat record, an operator object whose operators the rule vocabulary spells, several such keys, or a single-level AST tuple array, and every stored-row read replays the same rewrite until it does"; the registry entry's summary says "wherever the mapping is lossless". Right.
    • the residue — $and / $or / $not; a null value; $null / $exists or an AST like; an array or object comparand in equality position; an AST and / or group; "every filter, the binding's included, of a component whose rows are inline (data: { provider: 'value' }, a data array, or staticData)": the entry's own list, item for item and in its own words; the registry's "What is left exactly as stored" section agrees (it adds "any top-level $ key" and "all-or-nothing per filter", which the note does not contradict). Right.
    • "this renderer is backend-agnostic: it replays no conversion itself, and only ObjectStack's own data-at-rest seams do": apply.ts's includeRetired doc names three data-at-rest call sites (applyConversionsToStoredItem, where the flag is pinned and StoredConversionOptions omits the key; flow rehydration; the artifact-ingestion door in @objectstack/metadata-core), and os migrate meta calls each conversion's apply directly; stored.ts pins includeRetired: true and calls itself "the one primitive every stored-row rehydration seam calls". In objectui origin/main, git grep finds the chain called from exactly one file, a test under packages/layout, and no runtime source. Right — and the dev's wording ("data-at-rest seams", plural) is the more accurate of the two on offer, since the seat's "a stored-row seam" would have named one of three.
    • the conclusion "any of the three shapes may therefore still arrive here": follows from the residue, from rows not yet re-read, and from a page reaching a backend-agnostic renderer by a route that is not an ObjectStack data-at-rest seam. Right.
    • One precision nuance, verdict-neutral and recorded so nobody re-derives it: excludeConversionIds survives StoredConversionOptions (only includeRetired is omitted), so a stored-row seam could in principle refuse one conversion by id. The note reports the entry's own "every stored-row read" disposition and cites the entry, which is the card's ask; it claims no stronger invariant of its own.
      ⇒ The 8945 changeset now carries the same corrected reason, so nothing BORN FALSE (in check-changeset-claims.mjs's taxonomy) goes to the CHANGELOG from this head. ①-8 is resolved; items 1–7 stand unchanged on this head.

② Semver level

  • .changeset/8945-element-datasource-filter-note.md declares '@object-ui/core': patch — right level. @object-ui/core is in the one fixed group of .changeset/config.json; the diff edits packages/core/src/**, so AGENTS.md §9 (Housekeeping) and the check-changeset-presence.mjs header require a declaration (an empty frontmatter would also count). The published .d.ts changes only in JSDoc text and nothing in the API or accept set moves, so patch, not minor; patch is the more honest of the two legal spellings because published .d.ts text does change. No major (version-alignment rule; Changeset Bump Policy, Changeset Fixed Group Check and Changeset Declaration all success on this head). The changeset body's prose is now right (①-8), so the level and the prose agree.
  • .changeset/7297-record-id-filter-token.md: frontmatter (minor / minor / patch / patch) byte-identical; +2/−0 body-only — no level change, none owed.
  • Clause-②: no — right. No accept set, type, export or runtime behaviour moves; the spec's own registry comment for this very conversion reads the family the same way ("The ruling is Clause-②: no — no accept-set change").

③ Boundary flags

  • Dev deviation (R1): one token in the module docblock outside the named ElementDataSourceConfig docblock. Answered: bounded in-place fix, four conditions stated by the dev and re-checked by the seat; surface amended in the same round (5921413381). Accepted.
  • Dev deviation (R1): report JSON carries gates / files_changed / line_budget / deviations. Cosmetic; the dispatch asked for them. Accepted.
  • R2 (seat-requested): dated correction appended to the pending 7297 changeset. Answered: surface amended in 5921413381; append-only verified on this head; content right (①-7). Accepted.
  • R3 wording deviation: "only ObjectStack's own data-at-rest seams" rather than the seat's "an ObjectStack stored-row seam". Answered in ①-8: apply.ts names three such seams, so the dev's wording is the accurate one. Accepted.
  • R3 out-of-scope finding: the card body's own premise sentence is false on 17.5.0. The seat owned it as the correction of record (5921701335); it is not in the diff and the diff no longer repeats it. Nothing owed on this PR.
  • open_questions: [] in all three rounds. Nothing to answer, nothing to escalate. The one item the earlier record escalated (①-8) is resolved on this head.
  • Card fences all held: the filter TYPE not narrowed; registry filter declarations (finding(plugin-map/plugin-gantt/plugin-timeline): six registrations declare neither filter nor sort while all three renderers lower both onto the query — #7712's shape, but the spec cannot be cited here #8220) untouched; fixtures (finding(types): the tolerant face refuses the documented per-element dataSource bindings — the spec waives objectName when dataSource.object is set, and objectui's zod arms still require it #11117) untouched; reader tolerance not removed; the three record-picker.tsx sites read directly, not from the paraphrase.
  • Out-of-scope finding, the 24 retired-orthography fixtures in 18 test files — said to be recorded on objectui#11117; outside this brief's inputs, not verified here, does not bear on the verdict.
  • Form: draft, base main, Fixes #8945 the only closing keyword in the body, 3 commits each with a single parent (no merge, no rebase) and each ending with the model-free trailer pair; no model identifier in title, body, diff or commit messages; no cross-file path:line in the added lines; no angle brackets or control bytes in the added lines. The branch is behind main; the queue rebuilds, nothing owed.
  • Check-runs on the head, read 2026-09-30T23:45Z: 42 — 29 success, 3 skipped (dependabot, the two coverage placeholders), 10 in_progress (Type Check, Spec Main Shape Gate, Test shards 1–8), 0 failure or cancelled. The ten are recorded as in progress, not as a verdict. Success includes Changeset Declaration, Changeset Bump Policy, Changeset Claim Re-read, Changeset Fixed Group Check, Changeset Overwrite Report, Control Byte Scan, Line Citation Gate, Governed Surface Queue Guard, Lint, Test (dist pins), Build & E2E and Bundle Analysis. No gate reads the semantics of a changeset body, so ①-8's resolution is this record's finding, not theirs.

Implemented-by: claude/issue-8945-element-datasource-filter-note
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

Rendered 2026-09-30T23:49Z at CONTRACT_REVIEW_TIER by the isolated reviewer inside session_01VhxTqosz7wn54ahqyxgERT; this record supersedes 5921608196 for this PR, ①-8 is resolved on this head, and the ten in-progress check-runs are the seat's to read before enqueue.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 23:52
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 969d4f2 Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-8945-element-datasource-filter-note branch October 1, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants