Skip to content

Commit ce991bd

Browse files
fix(components): element:repeater and element:number publish the dataSource binding as their only query input, and objectui compiles against spec main again (objectui#12085, objectui#12093) (#12100)
Fixes #12085 Fixes #12093 Clause-②: no Session: `https://claude.ai/code/session_01B1gHb9baeX7oioD5sHVm7z` (`domain:ui` seat 3, claim `6097673277` on objectui#12085, fold claim `6097776869` on objectui#12093). ## objectui#12085: the element registrations publish the binding as the only query door - **`element:repeater`** (`packages/components/src/renderers/basic/data-list.tsx`): the registration drops its flat `object` (which was `required: true`), `filter`, `sort` and `limit` inputs. The injected `dataSource` input (the `elementDataSourceBlock` seam) is unchanged, so the repeater now requires nothing. - **`element:number`** (`packages/components/src/renderers/basic/elements.tsx`): the registration drops its flat `object` and `filter` inputs. The metric has not read them since objectui#11880. - **Mirrored from the spec, not recalled.** On objectstack `main` (read at `243dd3c6`, still so at `d8830c28`), `ElementRepeaterPropsSchema` declares `object`, `filter`, `sort` and `limit` as `retiredKey(elementFlatBindingRetired('element:repeater', KEY))`. `ElementNumberPropsSchema` does the same for `object` and `filter`. Both landed with objectstack-ai/objectstack#22421 (`317cddd40d`). - **The renderer's fallback read of the repeater's flat keys is kept, on purpose.** Evidence: - The ruling on objectstack#11509 (comment `6051498447`, sub-question 2) has `record_picker` and `number` lose their fallbacks, and has the repeater "take `dataSource`". It does not retire the repeater's read. - The spec row that landed says, in the `ElementRepeaterPropsSchema` docblock: "The renderer's fallback read of them, recorded above, is the console's tolerance for metadata written before this release, not a door this row keeps". The repeater's tombstone prescription says the same ("keeping this key only as a fallback for metadata written before it"). - objectui still pins `@objectstack/spec` 17.7.0. There, `ComponentPropsMap['element:repeater']` requires a flat `object`, and `@object-ui/types`' repeater arm requires `properties.object` wherever no `dataSource.object` names one. Dropping the read now would make objectui's own validator accept a node whose list the renderer leaves empty. - So the read stays, and it is not published: a back-compat read is not an authoring surface. Its retirement belongs with objectui's spec-18 pin bump (see Acceptance notes). - **The pin, at the manifest level.** These are the same `manifestFromConfigs` + `validateTree` pair that feed `sdui.manifest.json`: - a repeater bound only through `dataSource` draws no diagnostic (it drew `missing-required-prop`); - each flat `object` / `filter` / `sort` / `limit` beside the binding draws `unknown-prop`, with the bound node as the control; - the same holds for `element:number`'s flat `object` / `filter`. - **Pins flipped, load-bearing:** - `data-list.elementDataSource-11880.test.tsx` and `elementNumber.dataSourceBinding-10909.test.tsx` carry the registration pins. - `element-list-inputs-11168.test.tsx` mounts the repeater's query rows through `dataSource`, and its required-input control moves to `element:number.aggregate`. - `apps/console` `registry-inputs-spec-parity.test.ts`: the three member pins on keys no longer published are struck, and the six keys installed 17.7.0 still declares get reasoned `UNPUBLISHED_EXEMPTIONS`. Each entry says it goes stale at the pin that carries objectstack#11509's tombstones. - `objectname-binding-required-11605.test.ts`: the `element:*` blocks land the binding on no key of theirs. - **Docs:** `content/docs/guide/data-source.md` says the repeater and the metric offer the binding alone. No code fence changed. ### Pin-window order 1. objectstack's spec retirement is already on its `main` (objectstack-ai/objectstack#22421). 2. This objectui change lands. 3. objectstack's next console pin bump regenerates `sdui.manifest.json` from these registrations, through `gen-sdui-manifest-node.mjs`. That bump is objectstack's own card. This PR does not touch that file. Until the bump, objectstack's manifest keeps the old registrations. ## objectui#12093: objectui compiles against spec `main` again Every fix compiles against BOTH specs: the installed 17.7.0 that the pull-request type-check uses, and the Spec Main Shape Gate's build of objectstack `main`. No behaviour changes on the installed spec. - **The nine named sites:** - Fixtures that only carried a flat `object` now name it through `dataSource`: `record-picker-label-association`, `page-variables`, `authoring-nodes-11364` `pickerStyled`, and `element-repeater-data-source-11880` `bound` / `aliased`. - Three pins asserted that a retired key compiles. Each now asserts that **the face's verdict IS the installed row's verdict**: - `authoring-nodes-11364`, the record picker's bag `object`; - `element-repeater-data-source-11880`, the repeater's bag `object`; - `grid-default-filters-...-round10-6152`, `defaultFilters` on the grid and on the `table` slot, plus two by-reference `Equal`s. - Each pin holds on both specs, and asserts REFUSED on `main`. - **Further signatures found by re-running the gate in full** (the annotations are capped): - **`overlayScope`.** objectstack-ai/objectstack#22628 narrows it to `env`. The alias pin in `data-objectstack` now reads the expected set off the schema VALUE in both directions, plus a label-table ceiling. `LayeredDiff.overlayScope`'s en-US case renders `env`. `app-shell`'s `LAYER_SCOPE_ZH` keys `org` beside the spec union. The type is internal and the behaviour is unchanged. - **`object-grid.defaultFilters`.** `plugin-view`'s `ObjectView` hands its legacy `table.defaultFilters` rung to the grid as `filter`. On `main` the by-reference type is the tombstone, so `plugin-view`'s build failed, and that cascaded TS7016 into `app-shell`, `console` and `examples/byo-backend-console`. The read now goes through `legacyTableDefaults`, which carries ONE explicit `unknown`-to-rule-array assertion with its reason. The value read and the rung are unchanged. The `plugin-grid` and `plugin-view` tests that pin that legacy read build their fixtures the same way. This assertion is a review point; see open question 2 in the report. ## Measured Heads cited are this branch's commits. Turbo's cache is shared across worktrees in this container, so every type-check below ran with `TURBO_FORCE=true`. **Spec Main Shape Gate reproduction.** objectstack `main` was archived read-only and built from source. The tarball was injected with `scripts/spec-main-shape-gate.mjs inject` into a separate worktree, and both installs were checked by markers: | head | objectstack `main` | scope | result | |---|---|---|---| | base `de302c73` | `243dd3c6` | types + components | RED: the ten diagnostics on the card | | `c70f848db` | `96469912` | full | RED: 8 of 81 tasks; the `overlayScope` and `defaultFilters` signatures above | | **`c4e01fb39`** | **`d8830c28`** | **full** (`pnpm type-check --continue`, 81 tasks force-executed) | **GREEN**: `TYPECHECK_EXIT=0`; the gate's own `report` prints "objectui type-checks against `@objectstack/spec` at that commit" | **Installed spec, `c4e01fb39`.** `TURBO_FORCE=true pnpm type-check --continue`: 81 of 81 tasks, force-executed 81, cache hits 0, exit 0. **vitest, `c4e01fb39`, from the repo root:** - 55 files and 1245 tests pass: the components registration and element pins, the console parity / 11605 / html-tier / public-contract set, the three types files, and `check-doc-links`. - 82 files and 744 tests pass: all of `plugin-view`, four `plugin-grid` filter files, the `data-objectstack` overlay pin, and three `app-shell` metadata-admin files. **Reverse verification.** The two registration files were checked out from base with the fix committed. That gives 13 failed tests in 5 files, among them the binding-only "no diagnostic" and the flat-key `unknown-prop` rows. Restored from `HEAD`: blob equals `HEAD`, and `git diff HEAD` is empty. **Ablations.** All five ran through `ablation-replace.mjs`; each mutation was proven on disk and each restore was proven by blob hash. - On `main`, the record picker's bag `object` re-forked to a string turns the new `authoring-nodes` pin red. - On `main`, `ObjectGridSchema.defaultFilters` re-forked to the rule array turns red both round-10 differentials and both by-reference `Equal`s. - On the installed spec, the record picker's bag losing its `object` turns the same pin red. - On the installed spec, the overlay alias widened to `string | null` turns `_noWider`, `_labelled` and the `@ts-expect-error` red. - On `main`, the alias restated as `'org' | 'env' | null` turns `_noWider` red. **Gates, at `c4e01fb39` unless noted.** All exit 0: - `check:new-line-citations`, `check:control-bytes`, `check:element-data-source-declaration`; - `check:changeset-claims`, `check:pending-changeset-literals`, `check:installed-pin-claims`; - `check:spec-symbols`, `check:test-path-roots`, `check:component-surface-parity`, `check:doc-types`, `check:doc-fences`; - `check-changeset-presence` (2 changesets), `check-changeset-no-major`; - `check:sdui-registration-pins` at `c70f848db`, after a console build there; no registration moved after that commit. **eslint, `--no-inline-config`, on the 19 touched TS files:** - The population is read from `eslint.config.js`, and the file count from `--format json`. - 0 errors. The warnings are identical per file to base (97 and 97). - The config enables no type-aware linting, so this diff cannot move a verdict on an untouched file. **NOT MEASURED:** - `check:doc-snippets` / `check:doc-examples`: prerequisite not met (packages outside the type-check closure are unbuilt). The doc diff changes no fenced block. - Repo-wide `pnpm lint`: CI. ## Clause-② (both claims say `no`) - Dropping `required: true` removes a refusal that the published row on objectstack `main` denies itself. Dropping the six inputs narrows the registrations. - #12093's part is tests, plus two internal, type-only source edits (`legacyTableDefaults`, the label table's key type). - No export, input, locale key or published accept set is added or widened. Changesets: `@object-ui/components` minor, and an empty-frontmatter no-release declaration for the #12093 edits. ## Acceptance notes - `element:record_picker`'s registration still publishes its four flat binding inputs, each marked NOT READ. objectstack `main` tombstones them too. They are the same family as this card, outside its ruling, and left untouched; reported to the seat. - Four things move together at objectui's `@objectstack/spec` 18 pin bump: - the renderer's back-compat reads of retired keys: the repeater's flat four, `ObjectGrid`'s legacy `defaultFilters`, and `ObjectView`'s `legacyTableDefaults` rung; - the runtime pins that read the installed spec literally: the overlay enum, the round-10 precondition legs, and the 11168 spec-row probes; - the six new parity exemptions; - `LAYER_SCOPE_ZH`'s `org` entry. --- _Generated by [Claude Code](https://claude.ai/code/session_01B1gHb9baeX7oioD5sHVm7z)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent de302c7 commit ce991bd

22 files changed

Lines changed: 382 additions & 158 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@object-ui/components': minor
3+
---
4+
5+
`element:repeater` and `element:number` publish the node-level `dataSource` binding as their only query input (objectui#12085). `@objectstack/spec` retires the element layer's flat binding keys as tombstones (objectstack#11509, objectstack PR #22421), and the repeater's registration still required a flat `object`, so the html-page validator refused a repeater bound through `dataSource` alone and accepted the spelling the spec retires.
6+
7+
**Narrowing, no widening.** The `element:repeater` registration drops its `object` (which was `required: true`), `filter`, `sort` and `limit` inputs. The `element:number` registration drops its `object` and `filter` inputs, which the metric has not read since objectui#11880. The injected `dataSource` input is unchanged on both. In the manifest these registrations feed, a repeater bound only through `dataSource` now draws no diagnostic (it drew `missing-required-prop`), and each of those six flat keys now draws `unknown-prop`. No export is added or removed, and no input is added.
8+
9+
**Rendering is unchanged.** The repeater still reads its flat `properties.object` / `filter` / `sort` / `limit` as the binding's fallback, for metadata written before the retirement. That read is not published as an authoring surface. The metric reads its query from `dataSource` only, as before.
10+
11+
objectstack's `sdui.manifest.json` is regenerated from these registrations at objectstack's next console pin bump; this release does not change that file.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
---
3+
4+
No release. objectui#12093 makes objectui compile against `@objectstack/spec` built from objectstack `main` (the Spec Main Shape Gate) while it keeps compiling, unchanged, against the installed release. The edits are tests, plus two type-only source edits with no runtime effect: `@object-ui/plugin-view`'s `ObjectView` reads the deprecated `table.defaultFilters` through a typed legacy read (the same value, read the same way, now typed by the rule-array shape legacy metadata carries rather than by the row's declaration, a retired-key tombstone on objectstack `main`), and `@object-ui/app-shell`'s zh-CN overlay-scope label table keys `org` beside the spec's union (objectstack `main` narrows that union to `env`).

‎apps/console/src/__tests__/objectname-binding-required-11605.test.ts‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,18 @@ const DOC_ROWS = bindingDocRows();
119119
/**
120120
* The key the binding's `object` lands on. The doc: "it lands on the block's
121121
* own object key, which is `objectName` everywhere except `record:line_items`"
122-
* (its row spells that key in the `object` column), and "The two `element:*`
123-
* rows keep their configuration in the node's `properties` bag ...
124-
* `dataSource.object` wins over `properties.object`".
122+
* (its row spells that key in the `object` column).
123+
*
124+
* `undefined` for the `element:*` blocks: each reads `dataSource.object`
125+
* itself, so the binding lands on no key of theirs (objectui#11880), and since
126+
* objectui#12085 neither `element:number` nor `element:repeater` publishes a
127+
* flat `object` for it to land on. Row 3 asserts that exception is exactly the
128+
* `element:*` tags, so it cannot quietly cover an `object-*` block.
125129
*/
126-
function objectKeyOf(tag: string): string {
130+
function objectKeyOf(tag: string): string | undefined {
127131
const named = /\(`([^`]+)`\)/.exec(DOC_ROWS.get(tag)?.object ?? '');
128132
if (named) return named[1];
129-
if (tag.startsWith('element:')) return 'object';
133+
if (tag.startsWith('element:')) return undefined;
130134
return 'objectName';
131135
}
132136

@@ -137,7 +141,8 @@ function objectKeyOf(tag: string): string {
137141
* are supplied too, but no registration requires one, so they are not read.
138142
*/
139143
function bindingSupplies(tag: string): Set<string> {
140-
const supplied = new Set([objectKeyOf(tag)]);
144+
const key = objectKeyOf(tag);
145+
const supplied = new Set<string>(key === undefined ? [] : [key]);
141146
const view = DOC_ROWS.get(tag)?.view ?? '';
142147
if (view === '✅' || view.includes('columns')) supplied.add('columns');
143148
return supplied;
@@ -234,11 +239,21 @@ describe('objectui#11605 — a bound registration requires no input its binding
234239
expect(undocumented, `${BINDING_DOC} must say what the binding supplies on each bound block`).toEqual([]);
235240
for (const tag of BOUND) {
236241
const key = objectKeyOf(tag);
242+
if (key === undefined) continue;
237243
expect(
238244
inputsOf(tag).map((input) => input.name),
239245
`<${tag}>: the binding's object lands on "${key}", which the entry does not declare`,
240246
).toContain(key);
241247
}
248+
// The exception is the `element:*` tags and nothing else, and it is not
249+
// vacuous: the two whose flat `object` objectui#12085 stopped publishing
250+
// are bound entries of the shipped manifest that declare no `object`.
251+
const landsNowhere = BOUND.filter((tag) => objectKeyOf(tag) === undefined);
252+
expect(landsNowhere.every((tag) => tag.startsWith('element:'))).toBe(true);
253+
for (const tag of ['element:number', 'element:repeater']) {
254+
expect(landsNowhere, tag).toContain(tag);
255+
expect(inputsOf(tag).map((input) => input.name), tag).not.toContain('object');
256+
}
242257
// Controls on the doc reading: the exception it names, and a full-view row.
243258
expect(objectKeyOf('record:line_items')).toBe('childObject');
244259
expect(bindingSupplies('object-grid').has('columns')).toBe(true);

0 commit comments

Comments
 (0)