Filed by the os-dev executing objectstack#14406 (session session_01G4138K1EG7kQ81FNba5Kp4), unassigned, for triage.
Blocked-by: objectstack#14406 — its PR landing in @objectstack/spec, AND a released spec version carrying it reaching this repo's lockfile / the .objectui-sha pin. Until then nothing here is red, and nothing here should move.
What objectstack#14406 does
Converges ComponentPropsMap['element:record_picker'].filter from the MongoDB-style FilterConditionSchema record to z.array(ViewFilterRuleSchema) — the last record-form filter in the map after ui#6206-B (maintainer ruling 2026-08-25, verbatim 「同意」: one filter orthography platform-wide), with an ADR-0087 D3 entry element-record-picker-filter-rule-array.
Measured at the pin 00d3f09c500c4a45b5f27aca8af80349412faaf1 (identical on main f7cf7e8 for every file named)
The RUNTIME read path already takes the array end to end, which is why the spec-side card proceeds:
packages/components/src/renderers/basic/record-picker.tsx:108 const filter = composed?.filter ?? props.filter; → :135 query.$filter = filter → :138 adapter.find(object, query).
packages/data-objectstack/src/index.ts:3846-3851 (convertQueryParams): an ARRAY $filter is lowered through translateFilterArray (:348, [{ field, operator, value }] → AST tuples via objectFilterEntriesToAST); an object goes through convertFiltersToAST.
- Nothing on that path parses
properties against the installed @objectstack/spec (readProps is a spread; non-test src mentions ElementRecordPickerPropsSchema in comments only).
What does NOT follow by itself is the DECLARATION layer in this repo:
packages/components/src/renderers/basic/record-picker.tsx:344-370 — inputs.filter declares type: 'object', and its description ends "A rule ARRAY (an ObjectQL AST, or a view's rule list) is not a FilterCondition and the spec rejects it here." After the spec converges, a JSX page writing the array form gets a type-mismatch diagnostic on a legal write (packages/sdui-parser/src/validate.ts:246 — severity warning, so renderers/layout/page.tsx:553 still renders and the picker still filters; the harm is the false diagnostic and a description teaching the retired form).
packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts:163-171 pins withFilter({ status: 'open' }) as accepted and withFilter([['a', '=', 1]]) as refused, and :187-193 pins input('filter')?.type === 'object' against the live validator. Both go red the moment a released spec carrying objectstack#14406 is installed.
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:1837-1856 keeps element:record_picker.filter pinned as DECLARED — that stays true; only the arm changes.
Shape of the fix (when unblocked)
Flip type: 'object' → 'array' on the filter input, rewrite its description around [{ field, operator, value }, ...] (keep the PRECEDENCE paragraph — dataSource.filter ?? filter is unchanged), and re-point the two parity pins so the record form is the refused shape (invalid_type at filter) and the rule array is the accepted one. No renderer change: filter?: unknown passes through to $filter, and find() already lowers it. Same closing move as objectui#6206's element:number.filter:array exemption deletion.
Refs: objectstack#14406 (the spec convergence and its measurement) · objectstack#12039 (Key 2, the element:number precedent) · objectui#6206 (the ruling) · objectui#3830 (this input's declaration) · objectui#6828 (the aggregate-path lowering).
Filed by the os-dev executing objectstack#14406 (session
session_01G4138K1EG7kQ81FNba5Kp4), unassigned, for triage.Blocked-by: objectstack#14406 — its PR landing in
@objectstack/spec, AND a released spec version carrying it reaching this repo's lockfile / the.objectui-shapin. Until then nothing here is red, and nothing here should move.What objectstack#14406 does
Converges
ComponentPropsMap['element:record_picker'].filterfrom the MongoDB-styleFilterConditionSchemarecord toz.array(ViewFilterRuleSchema)— the last record-formfilterin the map after ui#6206-B (maintainer ruling 2026-08-25, verbatim 「同意」: one filter orthography platform-wide), with an ADR-0087 D3 entryelement-record-picker-filter-rule-array.Measured at the pin
00d3f09c500c4a45b5f27aca8af80349412faaf1(identical onmainf7cf7e8for every file named)The RUNTIME read path already takes the array end to end, which is why the spec-side card proceeds:
packages/components/src/renderers/basic/record-picker.tsx:108const filter = composed?.filter ?? props.filter;→:135query.$filter = filter→:138adapter.find(object, query).packages/data-objectstack/src/index.ts:3846-3851(convertQueryParams): an ARRAY$filteris lowered throughtranslateFilterArray(:348,[{ field, operator, value }]→ AST tuples viaobjectFilterEntriesToAST); an object goes throughconvertFiltersToAST.propertiesagainst the installed@objectstack/spec(readPropsis a spread; non-test src mentionsElementRecordPickerPropsSchemain comments only).What does NOT follow by itself is the DECLARATION layer in this repo:
packages/components/src/renderers/basic/record-picker.tsx:344-370—inputs.filterdeclarestype: 'object', and its description ends "A rule ARRAY (an ObjectQL AST, or a view's rule list) is not a FilterCondition and the spec rejects it here." After the spec converges, a JSX page writing the array form gets atype-mismatchdiagnostic on a legal write (packages/sdui-parser/src/validate.ts:246— severitywarning, sorenderers/layout/page.tsx:553still renders and the picker still filters; the harm is the false diagnostic and a description teaching the retired form).packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts:163-171pinswithFilter({ status: 'open' })as accepted andwithFilter([['a', '=', 1]])as refused, and:187-193pinsinput('filter')?.type === 'object'against the live validator. Both go red the moment a released spec carrying objectstack#14406 is installed.apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:1837-1856keepselement:record_picker.filterpinned as DECLARED — that stays true; only the arm changes.Shape of the fix (when unblocked)
Flip
type: 'object'→'array'on thefilterinput, rewrite its description around[{ field, operator, value }, ...](keep the PRECEDENCE paragraph —dataSource.filter ?? filteris unchanged), and re-point the two parity pins so the record form is the refused shape (invalid_typeatfilter) and the rule array is the accepted one. No renderer change:filter?: unknownpasses through to$filter, andfind()already lowers it. Same closing move as objectui#6206'selement:number.filter:arrayexemption deletion.Refs: objectstack#14406 (the spec convergence and its measurement) · objectstack#12039 (Key 2, the
element:numberprecedent) · objectui#6206 (the ruling) · objectui#3830 (this input's declaration) · objectui#6828 (the aggregate-path lowering).