Skip to content

Commit 317cddd

Browse files
feat(spec)!: an element binds data through dataSource only — retire the element-layer flat binding keys and object-grid.defaultFilters (#11509) (#22421)
Fixes #11509 Clause-②: no (narrowing: the ten element-layer flat data-binding keys and `object-grid.defaultFilters` leave the accept set, and the component-props gate's `dataSource.object` waiver becomes a refusal) ADR-0087 disposition, as the changeset's marker carries it: `registered element-flat-data-binding-retired, object-grid-default-filters-retired` (`.changeset/11509-element-flat-binding-retired.md`). **Consumer pin rule (triage `6071460039`):** a consumer that pins both repositories moves its objectui pin past objectui#11880 no later than its objectstack pin moves past this retirement. The converted shape is one only objectui after objectui#11880 reads. **Landing gate: met.** The gate is the claim's (`6072058120`, ruling sub-question 2), moved by the seat (`6080485027`) to #22476: `.objectui-sha` must cover the objectui merge that repairs the designer row named in objectstack-ai/objectui#12056. - #22476 landed (PR #22592, `ba956070b1`), and `.objectui-sha` on `main` is `20c6d351ad74`. This branch merged that `main` (`76bc1e03a`), and does not move `.objectui-sha` itself. - objectui `2a48bd408dab` (the merge of objectui PR #12064, the repair for objectstack-ai/objectui#12056) is an ancestor of `20c6d351ad74`: `git merge-base --is-ancestor` exits 0, and `rev-list --count` reads 7 ahead / 0 behind, agreeing with the `domain:devx` seat's REST compare (`6093574958`). The first gate, objectui `5bc55c0c5a1e`, is an ancestor too (exit 0). - The gate's reason, re-measured at `20c6d351ad74` by `git show` (the objectui checkout's HEAD did not move). Studio's page-block inspector row for `element:repeater` is `packages/app-shell/src/views/metadata-admin/previews/block-config.ts:344-351`. Its Object picker (`:345`) and its Limit box (`:348`) both carry `at: 'dataSource'`. `PageBlockInspector.tsx:800` hands every `at: 'dataSource'` field to `patchBinding`, and `patchBinding` (`:483-484`) writes `dataSource: { ...binding, [name]: value }`. No designer row writes a retired flat key: the `element:number` row (`:357-358`) writes `dataSource.object` as before, and `element:record_picker` has no page-block row (the palette excludes it as page content, `block-types.ts:208`). ## What this does Ruling `6051498447` (A-narrow, all four sub-questions at their recommendation), carried out as the claim states it: 1. **Tombstones.** `element:record_picker` `object` / `filter` / `sort` / `limit`, `element:number` `object` / `filter`, `element:repeater` `object` / `filter` / `sort` / `limit`, and `object-grid.defaultFilters` are `retiredKey()` tombstones in `packages/spec/src/ui/component.zod.ts`. Each is refused at its key with the prescription (use `dataSource.KEY` on the node; what to do where the binding already sets it, by that element's old rule), and its input type is `never`. The repeater's other spellings (`objectName`, `filters`, `where`, `orderBy`, `sortBy`, `top`, `pageSize`) were aliases of the flat keys and now point at the binding. The `object-*` block layer and the relationship-scoped blocks are unchanged. 2. **Conversions (step 18, both retired from the load path, both ordered at 35.5, before `page-component-filter-record-to-rule-array` at 36).** - `element-flat-data-binding-to-data-source` follows each element's old rule: a key the binding lacks moves there; a key the binding already set is deleted where the binding always won (the picker, and `element:number`'s `object`); `element:number`'s flat filter is appended to the binding's (the two always AND-combined). It reports a TODO and leaves the key in three cases: a picker key beside a `dataSource.view` that sets no such key of its own, a repeater key the binding sets to a different value or beside a `view` (the repeater read neither until the console put its binding first, so what it applied depends on the console version), and an `element:number` filter pair that is not two rule arrays. - `object-grid-default-filters-removed` is the shape `defaultSort`'s retirement took. When `filter` is empty (absent, `null`, `[]` or `{}`), the rules move into it. When `filter` has content, the key is deleted. When `filter` is a value no lowering reads (a bare string), it reports a TODO rather than overwrite it. - A record-form value either entry moves reaches the record-form conversion at its new door. So the absorbed arms (the element flat `filter` and `defaultFilters` doors of `page-component-filter-record-to-rule-array`) are dropped from that entry's reach. 3. **Lint.** In `packages/lint/src/validate-component-props.ts` the type-blind `suppliedByDataSource` waiver is gone. One of the three elements with no `dataSource.object` is the rule's existing `component-props-invalid` finding (warning tier, at `…dataSource.object`); this is not a new rule. The docs gate's twin waiver (`packages/spec/scripts/check-yaml-examples.ts`) turns with it. 4. **The repeater trap** is closed in this PR from both sides. A repeater bound only through `dataSource` is the clean shape: zero findings, and objectui reads it since objectui#11880. A repeater aimed by a flat `object` is refused twice: the tombstone at the key and the missing binding. Pinned in `validate-component-props.test.ts` and in the docs gate's self-test. 5. **Registry and generated outputs.** There are eleven `RETIRED_KEYS_BY_MAJOR[18]` rows. There are two D3 entries (`element-flat-data-binding-retired`, `object-grid-default-filters-retired`), each with a step-18 rationale fragment. `authorable-surface/ui.json` (11 rows `[RETIRED]`) and `content/docs/references/ui/component.mdx` are regenerated by the repo's generators. The hand-edited `dropped-refinements.baseline.json` loses three rows whose only dropped refinement was a retired `filter`. The committed `packages/spec/spec-changes.json` and `docs/protocol-upgrade-guide.md` are regenerated from the merged registries (`gen:spec-changes`, `gen:upgrade-guide`); see "This round" below. 6. **The `object-tree` `titleField` rationale, corrected (offered by the `domain:devx` seat, `6093574958` item 2).** At objectui `20c6d351a` nothing reads a tree `titleField`: the renderer's `getTreeConfig` rung went on objectui#8841, and the `case 'tree'` arm's flatten rung went on objectui#6152 round 14 (objectui `3fd862510`), so `ListView.tsx:3985` reads `labelField: treeCfg.labelField || 'name'` and emits no `titleField`. The `OBJECT_TREE_FLAT_CONFIG_GUIDANCE` docblock and the last sentence of its prescription said the key is "only ever read as that key's last fallback". Both now say the key stays in the set for its prescription, not for a read, and the prescription still names `tree.labelField`. The flat key set and every schema shape are unchanged, and the in-test note beside the key-set pin (`component.test.ts`) gains the same dated reading. Item 1 of that offer (`kernel/functional-completeness.ts`) is outside this PR's files and is not taken. ## Premises, verified on `origin/main` `bb4f5cc00` before the first edit - P1 holds. The flat `object` keys were at `component.zod.ts:2602` (`element:number`), `:3015` (`element:record_picker`) and `:4134` (`element:repeater`). The repeater docblock's "NOT read at the pin" is at `:4117`. - P2 holds. `suppliedByDataSource` (`validate-component-props.ts:169-185`) checks the issue path and `dataSource.object`, never the component type. - P3 holds. `defaultFilters` is `z.array(ViewFilterRuleSchema)` (`:4563`), and the `defaultSort` precedent is `objectGridDefaultSortRemoved` (`conversions/registry.ts:9264`). - P4 holds. There are zero authors of the eleven keys in `examples/**`, `skills/**` and the hand-written `content/docs/**`. The one element-layer author, the showcase picker (`page-variables.page.ts:59`), already writes `dataSource`. The spec, lint and metadata-protocol tests that authored the flat keys as fixtures are re-bound here, and the tree-scoped absence pin found two of them that grep had missed (`page.test.ts`, `i18n-resolver.test.ts`). `skills/objectstack-ui/rules/pages.md` names `element:number` / `element:repeater` only as printable blocks and teaches no key, so no `skills/**` edit is needed and no hand-written docs page needed an edit. ## Decisions the reviewer should check - **Three step-18 narrowings are absorbed, not one.** The ruling names `object-grid-default-filters-rule-array`. `element-number-filter-rule-array` and `element-record-picker-filter-rule-array` narrowed the two element flat `filter` keys this PR retires in the same unreleased major, and their acceptance criteria assert parses this PR refuses (`safeParse({ object, aggregate, filter })` succeeds). That is the same-major bookkeeping of the `spec-property-retirement` playbook (§0) and the measurement's "same-major bookkeeping against … its semantic entries". The record-form guidance they carried is the binding's own entry, `element-data-source-and-object-block-filter-rule-array`. The CLI's migrate-meta guidance pin drops the three absorbed ids from its `REWRITTEN` floor. The absorbing entries are not added there, because the list admits only entries rewritten off a tracker id and these two were born without one; the printed-block case holds them with the whole directory. - **No new public export.** The three-element set lives privately in `component.zod.ts`. The conversion registry and the lint rule each keep a copy, and each copy is pinned against the tombstones by probing `ComponentPropsMap`. Publishing it would have widened a narrowing-only retirement. - **Tombstones name `@objectstack/spec 17`, not 18.** `check:future-spec-major` (ADR-0087, amended 2026-09-13) refuses a tombstone that names the next major. The migrate sentence is `--from 17`, as with `defaultSort`. - The `XParsed` aliases of the three element props stay, although their two shapes now coincide. Deleting a published type name would be an export removal of its own. ## Acceptance notes (not filed) - `packages/lint/src/page-walk.ts:135-136` (`:137` before the merge of `main` `76bc1e03a`) still resolves a component's object as `dataSource.object ?? properties.object ?? page.object` for the field-binding rules, and `content/docs/deployment/validating-metadata.mdx:157` describes that chain. After this PR the flat `object` on the three elements is refused by its tombstone, so the fallback only ever reads a refused key (and `element:metadata_viewer`'s metadata owner). This PR does not change it: the file is outside the claim. Carrier: none named; the next card that touches `page-walk.ts`. - The docs gate judges only nodes that carry a `properties` mapping, so a bare `type: element:repeater` fence with neither `properties` nor `dataSource` is not reported there. The lint rule judges it. This is pre-existing dispatch behaviour. - The object-tree read-point record that `main` re-measured at `20c6d351a` (the `OBJECT_TREE_FLAT_CONFIG_GUIDANCE` docblock header, from #22592) is kept verbatim, as ordered, with two slips in it. It cites the arm's `labelField` read as `:3986`, but at `20c6d351a` that read is `:3985`, and `:3986` is `fields: treeCfg.fields || effectiveFields,`. Its opening sentence still cites `ListView.tsx:3913-3932`, the range its own re-read moved to `3965-3989`. Neither is an asserting quote, so no gate reads them. The corrected `titleField` paragraph cites `:3985`. Carrier: the next re-read of that record at a pin bump. - `os validate` refuses the clean repeater on an html-tier page and accepts the retired one. Measured through `node packages/cli/bin/run.js validate` at this head, on a probe app carrying this repo's `sdui.manifest.json`: - A page whose source is one `element:repeater` tag carrying only `dataSource={{ "object": "probe_task", "limit": 5 }}` gets exit 1 with `jsx-missing-required-prop` (the repeater "is missing required prop \"object\""). - The same tag carrying a flat `object="probe_task"` instead gets no finding. - The cause is objectui's registration at `20c6d351a` (`components/src/renderers/basic/data-list.tsx:320-326`, `object` `required: true`), which `sdui.manifest.json` carries. The manifest also still lists the flat `filter` / `sort` / `limit` inputs, and `element:number`'s flat `object` / `filter`. - This predates this PR (the binding-only node was legal before it), and the fix site is in objectui. It is reported to the seat as a finding, not fixed here. - objectui's own tests at `20c6d351a` parse flat `object` against the spec's element schemas (`record-picker-inputs-spec-parity.test.ts`, `record-picker-empty-text-i18n.test.tsx`, `types/src/__tests__/element-repeater-data-source-11880.test.ts`). They resolve `@objectstack/spec` `^17` from npm, so nothing reds now. They will need their fixtures re-bound when objectui moves to spec 18. Carrier: objectui's spec-18 upgrade. - The committed `spec-changes.json` and upgrade guide on `main` lag `main`'s own registries. Since #22533, `check:spec-changes` and `check:upgrade-guide` generate in memory and no longer compare the committed copies. `check-adr-0087-registration` still reads the committed `spec-changes.json` at HEAD as its parser witness, so a PR that removes a registry entry reads "ledger parser drift … widen extractIds()". The real remedy is `gen:spec-changes`. That is the red this round fixed (below). Regenerating also carried text `main` had changed in other entries' registries (the objectui pin readings at `20c6d351a`, the storage-scope and flow-slot guidance). Carrier: #22485, which deletes the committed copies. ## This round: `main` merged, the gate re-measured, the Check Changeset red fixed - **Merge.** `origin/main` `76bc1e03a` was merged by `scripts/pm/os-regen-merge.sh` (merge commit `1eb154c6f`, no rebase, no force-push). The pre-merge tip was `d8e0996f4`, and the merge base `35ef501e1`. - **One text conflict:** `packages/spec/dropped-refinements.baseline.json`, only its two `measured` totals. That ledger has no generator by design (its header and `scripts/lib/dropped-refinements.ts` say why), so the order's "regenerate it" has nothing to run. It was resolved by hand: - the branch's 220/680 → 217/677 (three rows retired) and `main`'s 220/680 → 226/688 (six schemas, eight sites) stack at **223 entries / 685 sites**; - the merged `entries` list counts exactly that; - the spec build's ledger check, which fails on any mismatch, reads "685 refinement site(s) across 223 published schema(s) … all declared". - **Auto-merged:** `component.zod.ts`, `component.test.ts`, `migrations/registry.ts`, `i18n-resolver.test.ts` and `protocol.stored-migration.test.ts`. Every line `main` added to those files between `35ef501e1` and `76bc1e03a` is present at this head, with one exception: the object-tree `titleField` paragraph's `ListView.tsx:3927` line, which item 6 replaces on purpose. So `main`'s re-measured pin readings stand verbatim, and the retirement sits on top. One example is the repeater record, which keeps `main`'s `data-objectstack/src/index.ts:5048-5059` with this PR's tense change and REMOVED paragraph. `gen:migration-registry` rewrote `registry.ts` byte-identically. - **Step 2** kept the branch's bytes of `content/docs/references/ui/component.mdx` and `authorable-surface/ui.json` (branch-only edits). `check:generated` reads 15/15 current. - **Regeneration, `675b121bb`.** `Check Changeset` was red at `1eb154c6f` (check-run `114130272368`, "Require an ADR-0087 disposition on a declared-breaking changeset"). - Reproduced locally: `node scripts/check-adr-0087-registration.mjs --base origin/main` exit 1, "ledger parser drift: 3 id(s) present in the generated packages/spec/spec-changes.json are NOT found by extractIds()". The three were `element-number-filter-rule-array`, `element-record-picker-filter-rule-array` and `object-grid-default-filters-rule-array`, the entries this PR absorbs. `main`'s committed copy still listed them. - `gen:spec-changes` and `gen:upgrade-guide` regenerated the committed copies from the merged registries. At `675b121bb` the gate exits 0: "1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered element-flat-data-binding-retired, object-grid-default-filters-retired". - `check-changeset-no-major.mjs --base origin/main` exits 0 at both heads (pre mode, tag `next`). No label and no level was changed. - **Prose, `5faf12295`.** Item 6 above. - **Pin citations at `20c6d351a`.** - `pnpm --filter @objectstack/spec check:objectui-pin-citations` exit 0: 55 asserting citations match `.objectui-sha` (`20c6d351a`), and 145 historical ones are recorded and not checked. - `--verify-anchors` (`OBJECTUI_ROOT=../objectui`, read by `git show` at the pin) exit 0: 13 anchor content assertions verified against objectui at `20c6d351a`. - This PR adds or changes no asserting `.objectui-sha` citation. The objectui anchors it adds are two: - `data-list.tsx:320-326` (the repeater registration), byte-identical across `f0268ad78…20c6d351a`, so it holds; - `ListView.tsx:3985`, read at `20c6d351a`. - No record was re-pointed. The two slips reported above are `main`'s and stay verbatim. - **Size:** 43 files (the 41, plus `spec-changes.json` and `docs/protocol-upgrade-guide.md`), +2789 / -1521 = 4310 changed lines. That is over the 3000-line human-merge line (#22490). ## Tests and gates All readings are at head `5faf12295`, and every exit code was captured before any pipe. Heavy runs went through `scripts/pm/os-verify-lock.sh` on a shared box. - `@objectstack/spec`, both tiers in full (`vitest run --project local --project repo --maxWorkers=4`): 694 files, 20015 passed, 1 todo, 0 failed. Lock verdict: command-exit 0, held 16m44s. `pnpm --filter @objectstack/spec typecheck`: exit 0. - `@objectstack/lint`, full (`vitest run --maxWorkers=4`): 134 files, 6128 passed. `typecheck`: exit 0. - `@objectstack/cli`: - the unit tier in full (`vitest run --project unit`): 277 files, 4105 passed; - `test/migrate-meta-engine-guidance.test.ts`, an integration-tier file this PR edits (`--project integration`): 3 passed; - `typecheck`: exit 0; - the rest of the integration tier is left to CI. - `@objectstack/metadata-protocol` `src/protocol.stored-migration.test.ts`: 40 passed. `examples/app-showcase` `test/my-work-visibility.test.ts`: 7 passed. - The builds behind these: the spec build (exit 0), `turbo run build --filter='@objectstack/cli^...'` (58/58 tasks), and the CLI build. A locked full build followed (72/72 tasks). - **Gates.** `node scripts/pm/dispatch-gates.mjs --commands` derived 120 at `5faf12295`. - All 120 were run, and all 120 exit 0. - On the first pass, `check:skill-examples` and `check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET: no `dist/` for `client-react` and eight others). After the full build both exit 0. - `--ran`: "120 run, 0 NOT-MEASURED". - `check:pm-dispatch-gates`: 1825 cases pass (fast tier; 6 slow sections deferred). - CI at `5faf12295`, read once when the report was written (not awaited): all 35 check runs completed, 33 success and 2 skipped (Console Pin Gate, Packed-tarball smoke opt-in), none red. `Check Changeset` is green again. ## 维护者速读(草稿) ### 改了什么 - 页面里的三个元素组件,不再接受直接写在 `properties` 里的数据绑定键: - 记录选择器 `element:record_picker`:`object` / `filter` / `sort` / `limit`; - 数字 `element:number`:`object` / `filter`; - 重复列表 `element:repeater`:`object` / `filter` / `sort` / `limit`。 - 作者改为写在组件节点的 `dataSource` 上,即 `dataSource.object`、`dataSource.filter`、`dataSource.sort`、`dataSource.limit`。repeater 原来的别名(`objectName`、`filters`、`where`、`orderBy`、`sortBy`、`top`、`pageSize`)也一并指向 `dataSource`。 - 对象表格 `object-grid` 的 `defaultFilters` 不再接受,规则改写到 `filter`。 - 旧写法在校验和保存时会被拒绝,错误信息里直接给出新写法。`os validate` 对缺少 `dataSource.object` 的这三个元素给出警告。 - 已有元数据可以用 `os migrate meta --from 17` 自动改写。改写不了的情况会留一条 TODO 提示作者手工处理:两种写法取值不同,或者 `dataSource` 指向了一个已保存的视图。 - Studio 的页面设计器在新的控制台 pin(`.objectui-sha` = `20c6d351a`)上,已经按新形状写这三个元素。repeater 的“对象”和“条数”两栏写入 `dataSource`,作者在 Studio 里拖出来的组件不会被拒绝。 ### 为什么改 - 同一个查询原来有两个入口(`properties` 和 `dataSource`),三个组件各自用不同规则合并两边,作者写下的值不一定生效。 - repeater 还有一个陷阱:只写 `dataSource` 时,`os validate` 显示通过,页面却显示“No records”。 - 裁决 A-narrow(`6051498447`)定为:只保留 `dataSource` 一个入口,先让 objectui 读写新形状,再在 spec 里收回旧键。这一步现在已经具备条件。 ### 风险与代价(含回滚) - **破坏性变更。** 属于 v18 大版本:`@objectstack/spec` 与 `@objectstack/lint` 带 major changeset。没有跑迁移的旧元数据会在校验时报错。 - **宿主 pin 规则。** 同时 pin 两个仓库的宿主,要先移动 objectui 的 pin,越过 objectui#11880,才能读迁移后的形状;之后才能把 objectstack 的 pin 移过本次退役。Studio 设计器按新形状写 repeater,要从 objectui#12056 起;当前控制台 pin `20c6d351a` 两者都已包含。 - **已知遗留(不在本 PR 内)。** - html 类型页面里,`os validate` 仍要求 `element:repeater` 标签写平铺的 `object`,并拒绝只写 `dataSource` 的写法。原因在 objectui 的组件注册清单,已作为发现交给席位处理。 - objectui 自己的部分测试在升级到 spec 18 时,需要改用新写法。 - **体量。** 4310 行变更,超过 3000 行人工合并线。 - **回滚。** 直接 revert 本 PR 的合并提交即可。已经迁移成 `dataSource` / `filter` 的元数据,在旧版本里本来就是合法写法,回滚后不需要再迁移回去。 ### 席位意见 ### 你要做的(一个动作) 审阅并亲手合并 PR #22421。 --- _Generated by [Claude Code](https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a800912 commit 317cddd

41 files changed

Lines changed: 2694 additions & 1435 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
'@objectstack/spec': major
3+
'@objectstack/lint': major
4+
---
5+
6+
feat(spec)!: an element binds data through the node-level `dataSource` only — the flat `object` / `filter` / `sort` / `limit` of `element:record_picker`, `element:number` and `element:repeater`, and `object-grid.defaultFilters`, are retired (#11509)
7+
8+
Clause-②: no (narrowing: the ten element-layer flat data-binding keys and `object-grid.defaultFilters` leave the accept set, and the component-props gate's `dataSource.object` waiver becomes a refusal)
9+
10+
<!-- adr-0087: registered element-flat-data-binding-retired, object-grid-default-filters-retired -->
11+
12+
**BREAKING**: an accept-set narrowing on a published authoring surface, shipped as `major` on the v18 line (`.changeset/pre.json` is open on `main` in `next` pre mode, so the release is `18.0.0-next.*`). `@objectstack/lint` is `major` with it: its component-props rule refuses what it used to waive.
13+
14+
**Why.** One page element carried two doors onto one query. Each of these flat keys was the same query as a key of the node-level `dataSource` binding (`ElementDataSourceSchema`), and the three renderers resolved them by three contradictory rules: the record picker let the binding win, `element:number` AND-combined the two filters, and the repeater read the flat keys alone and ignored the binding — while the component-props rule waived a missing flat `object` whenever `dataSource.object` was present, so a repeater bound only through `dataSource` passed `os validate` and drew "No records". The console moved all three elements onto the binding first (objectui#11880); from this release the binding is the one door. `object-grid.defaultFilters` was the legacy second spelling of `filter`, read only when `filter` lowered to nothing.
15+
16+
**What changes.**
17+
18+
- **`@objectstack/spec`.** `ElementRecordPickerPropsSchema` `object` / `filter` / `sort` / `limit`, `ElementNumberPropsSchema` `object` / `filter`, `ElementRepeaterPropsSchema` `object` / `filter` / `sort` / `limit` and `ObjectGridPropsSchema` `defaultFilters` are `retiredKey()` tombstones: each is refused at its key with the prescription, and its input type is `never`. The repeater's other spellings of its query keys (`objectName`, `where`, `orderBy`, `top`, …) now point at the binding. The `object-*` blocks keep their own keys, and the relationship-scoped blocks are unchanged.
19+
- **`@objectstack/lint`.** `validate-component-props` (`component-props-invalid`, warning) reports an `element:record_picker`, `element:number` or `element:repeater` node with no `dataSource.object` at that path, instead of waiving the flat `object` for any type whose binding names one. A flat key is reported by its tombstone.
20+
- **Conversions (stored rows, artifacts, `os migrate meta --from 17`).** `element-flat-data-binding-to-data-source` moves a flat key the binding lacks onto it, deletes one the binding already set where the binding won, and appends `element:number`'s flat filter to the binding's (they always AND-combined). It leaves for the author, as a TODO: a record-picker key beside a `dataSource.view` that sets no such key of its own, a repeater key the binding sets to a different value or beside a `view`, and an `element:number` filter pair that is not two rule arrays. `object-grid-default-filters-removed` moves `defaultFilters` onto an empty `filter` (absent, `null`, `[]` or `{}`) and deletes it beside a `filter` that has rules. Both run before `page-component-filter-record-to-rule-array`, which then converts a moved record-form filter at its new door. Both are retired from the load path: an author is refused at the parse.
21+
22+
## FROM → TO
23+
24+
| you wrote | write instead |
25+
|:--|:--|
26+
| `{ type: 'element:record_picker', properties: { object: 'deal', limit: 20, labelField: 'name' } }` | `{ type: 'element:record_picker', dataSource: { object: 'deal', limit: 20 }, properties: { labelField: 'name' } }` |
27+
| `{ type: 'element:number', properties: { object: 'deal', aggregate: 'count', filter: [...] } }` | `{ type: 'element:number', dataSource: { object: 'deal', filter: [...] }, properties: { aggregate: 'count' } }` |
28+
| `{ type: 'element:repeater', properties: { object: 'deal_note', sort: [...], titleField: 'subject' } }` | `{ type: 'element:repeater', dataSource: { object: 'deal_note', sort: [...] }, properties: { titleField: 'subject' } }` |
29+
| `object-grid` `properties: { defaultFilters: [...] }` (no `filter`) | `properties: { filter: [...] }` |
30+
| `object-grid` `properties: { filter: [...], defaultFilters: [...] }` | `properties: { filter: [...] }` — the fallback was never read beside a `filter` with rules |
31+
32+
**The one-line fix: move each key from `properties` to the node's `dataSource` (a sibling of `type`), unchanged; rename `defaultFilters` to `filter` where `filter` is empty, and delete it where it is not.**
33+
34+
**For a consumer that pins both repositories:** its objectui pin moves past objectui#11880 no later than its objectstack pin moves past this release — the converted shape is one only that objectui reads.
35+
36+
**Who is affected, measured.** This repository authors none of the eleven keys: its one element-layer author (the showcase record picker) already binds through `dataSource`, and a tree-scoped absence pin (`packages/spec/src/ui/element-flat-binding-retirement.test.ts`) keeps it that way. Other repositories and deployed metadata were not measured here.
37+
38+
### The retirement kit
39+
40+
- `RETIRED_KEYS_BY_MAJOR[18]`: `ui/ElementRecordPickerProps:object|filter|sort|limit`, `ui/ElementNumberProps:object|filter`, `ui/ElementRepeaterProps:object|filter|sort|limit`, `ui/ObjectGridProps:defaultFilters`. D3 entries `element-flat-data-binding-retired` and `object-grid-default-filters-retired`, each with a step-18 rationale fragment. They absorb the three protocol-18 narrowings of the same keys to the rule array (`element-number-filter-rule-array`, `element-record-picker-filter-rule-array`, `object-grid-default-filters-rule-array`), whose keys are gone in the same major; `page-component-filter-record-to-rule-array` drops the retired doors from its reach.
41+
- Generated: `authorable-surface/ui.json` (11 rows `[RETIRED]`) and the `ui/component` reference page. Hand-edited ledger: `dropped-refinements.baseline.json` loses the three rows whose only dropped refinement was a retired `filter`.
42+
- Pins: the tombstones, the binding, both conversions and the tree-scoped absence walk in `element-flat-binding-retirement.test.ts`; the missing-binding refusal and the repeater trap in `validate-component-props.test.ts`; the docs gate's twin in `check-yaml-examples.ts --self-test`.

0 commit comments

Comments
 (0)