Skip to content

dataSource.filter's "three shapes legitimately reach a renderer" note is now half true — upstream converged the authoring door on the ViewFilterRule array #8945

Description

@os-bill

Filed from objectstack by the domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T06:46Z, out of the at-ACCEPT residue of objectstack PR #17257 (#15442 / #15449) and PR #17267. ⛔ Unclaimed. No domain:* label and no pm:* state: ⛔ both are the triage seat's to produce.

The shipped migration entry that caused this card names it as owed work, in its own words: "objectui cards filed by the seat, not blocked on here." This is that card.

What changed upstream

Seven filter doors in @objectstack/spec converged on z.array(ViewFilterRuleSchema) — the rule array [{ field, operator, value }, ...] — and the MongoDB-style record form and the ObjectQL AST tuple array are now refused at all seven (objectui#6206, maintainer batch adjudication 2026-08-25, verbatim 「同意」, Option B; family-wide convergence, decision batch #55, 2026-09-06, verbatim 「同意」, option A). The one that reaches this repo hardest is the binding: ElementDataSourceSchema.filter.

Prescription (record → rule array, operator objects, several-keys-AND, tuple arrays, legacy shorthands) is in the objectstack semantic entries 18.element-data-source-and-object-block-filter-rule-array, 18.element-number-filter-rule-array, 18.element-record-picker-filter-rule-array.

What this repo says today, measured at the pin 53ded82bf7a494f54e344e19099dbf00854b8694

packages/core/src/data-scope/element-data-source.ts — the ElementDataSourceConfig docblock, verbatim:

filter is typed unknown rather than the spec's FilterCondition because three shapes legitimately reach a renderer here (a MongoDB-style condition object, an ObjectQL AST node array, a spec ViewFilterRule[]) and {@link mergeFilterNodes} is the single sink that lowers all three. Narrowing the type here would only move the cast, not remove it.

⚠️ After the convergence only ONE of those three is legal at the authoring door. The other two remain legal at the renderer — the upstream disposition deliberately does not rewrite metadata at rest, so a stored page carrying the record form keeps loading and keeps arriving here. ⇒ The note is not simply wrong; it now conflates two populations (what an author may write vs. what a renderer may receive) that upstream has just split apart. That is what needs re-stating.

packages/components/src/renderers/basic/record-picker.tsx — carries spec-facing filter commentary at three sites (measured at the pin: :93, :332, :348). ⚠️ Do not trust a second-hand summary of what they say. A review handed to the objectstack seat described these as "documents dataSource.filter as a FilterCondition and says the spec rejects the array", and that wording did not reproduce on inspection; the same review also gave the path as packages/plugin-list/src/record-picker.tsx, which is wrong. Read the three sites; do not carry the paraphrase forward.

Registry declarations — objectui#7712 (closed) recorded that plugin-kanban and plugin-calendar registrations declare no filter input at all while both renderers read schema.filter; objectui#8220 (open, pm:queue) is the live successor for plugin-map / plugin-gantt / plugin-timeline. Both are about a missing declaration; this card is about the shape a present one should declare. Whoever takes #8220 should land the array arm rather than re-declaring the old shape.

⚠️ The author population — a number this card deliberately does NOT assert

The upstream entry's own acceptance text says: "the seventeen dataSource.filter test authors at the pin (fifteen tuple arrays, two records) become off-spec fixtures". That is the shipped claim and it is the one to start from.

⛔ The objectstack seat could not reproduce it and is not asserting a rival number. What it did, so the next person does not repeat it:

  • A git grep --multiline -P 'dataSource\s*:\s*\{[^}]{0,400}?filter\s*:' at the pin returned 0 files — a pattern failure, not a reading (a control git grep -l dataSource returns 765 files). [^}] cannot cross the nested object literals these bindings contain.
  • A line-proximity heuristic (a dataSource mention with a filter: within 8 lines) returned 103 hits across 62 files — and inspection of the sample shows it is measuring the wrong population: most hits are runtime $filter query objects (sharedUserFeeds.ts, RecordAttachmentsPanel.tsx, RecordDetailView.tsx) and re-reads of somebody else's dataSource.filter (ReportView.tsx:284), not authored binding metadata.
  • An earlier, broader pattern used in the objectstack lane returned 27 files. It is not comparable to the seventeen either, for the same reason.

⇒ Re-measure with a stated method and publish the method with the number. A proximity or regex probe over .ts / .tsx will not separate authored binding metadata from runtime query construction; the population is defined by what parses against ElementDataSourceSchema, so the reliable instrument is parsing the candidates, not grepping them.

Restart condition — this is not workable today

objectui consumes @objectstack/spec as a published package ("@objectstack/spec": "^17.0.0"), and the convergence is not released yet. Measured on objectstack origin/main 501959b72: EvaluatedExpressionInputSchema appears 0× in packages/spec/CHANGELOG.md (lit control: EvaluatedExpressionSchema appears 4× there) and the change sits unconsumed in .changeset/flow-edge-condition-evaluated-slot.md; spec package.json reads 17.4.0.

Restart-when: node --input-type=module -e "const m=await import('@objectstack/spec/ui');const S=m.ElementDataSourceSchema;if(!S)process.exit(9);const r=v=>S.safeParse(v).success;if(!(r({object:'a'})&&!r({object:'a',nope:1})&&!r({filter:{}})))process.exit(9);process.exit(r({object:'a',filter:{status:'open'}})?1:0)"

⇒ run from this repo's root after pnpm install. Exit 0 fires the hold (the record-form filter is refused); exit 1 keeps it; ⛔ exit 9 means the instrument is dead and the run says nothing — it is ⛔ never a fire. Still the install-face probe, ⛔ not "the upstream PR merged".
Restart-touch: packages/core/src/data-scope/element-data-source.ts, packages/components/src/renderers/basic/record-picker.tsx

⚠️ ⛔ Until that probe passes, nothing here is red and nothing here should move. The state label (pm:blocked vs pm:on-hold — the cross-repo rule says an unreleased upstream is pm:on-hold with an install-face Restart-when:, not pm:queue) is the triage seat's to set.

Dedup

Complete enumeration read 2026-09-10T06:46Z: objectui open domain:ui = 280 (3 pages, 100 + 100 + 80 — the third page under the cap, so the enumeration is complete, not truncated). Grepped for dataSource / filter / FilterCondition / rule array / record-picker.

Nearest neighbours, each read and judged not a duplicate:

Source

objectstack #15442 · #15449 · PR #17257 · #15807 · PR #17267 · the three 18.*-filter-rule-array semantic entries on objectstack origin/main 501959b72


⚠️ Restart-when: REPAIRED — it was prose, and prose is not an exit (2026-09-19T03:02Z)

Half-state patrol row H9 named this card: 「pm:on-hold with its Restart-when: is prose naming no issue, no tracked path and no runnable command — a manual in disguise; nothing schedules the actor it waits on」. It was right. The condition was well-chosen and correct; it had no spelling any actor could run, and 〈可执行判据由分诊席每日一个低频子轮批量执行〉 is the actor that needs one.

⭐ The condition is UNCHANGED — only its spelling moved, from English into something with an exit code. ⛔ No hold was loosened, tightened or re-decided.

⭐ The controls are INSIDE the predicate, and that is the point

The old prose could only be run by a human who would notice a broken import. A command cannot notice anything — so the three controls run before the subject, and a failure exits 9 rather than falling through to a verdict. ⛔ A dead instrument can no longer be misread as a fire. That is the failure mode this hold was most exposed to: the probe imports a package that may be re-exported, renamed or moved by any upstream release, and 「⛔ 同仪器的控制词双零是仪器坏,不读作缺席」.

Measured before it was written — against the pinned @objectstack/spec@17.4.0

probe reading required
SUBJECT record-form filter ACCEPTED ⇒ ⛔ the hold stands, exit 1
CONTROL minimal valid doc {object} ACCEPTED must be ACCEPTED
CONTROL unknown key on a $strict object REFUSED must be REFUSED
CONTROL missing required object REFUSED must be REFUSED

⇒ instrument lit: true, one-liner exit 1. The hold is correct today and the exit now fires by itself when it stops being.

⭐⭐ A correction this probe forced, recorded because the card's own wording invites it

The first cut of the control asserted an array-form filter would be ACCEPTED. It read REFUSED, and ⚠️ a control failing is not a result — it means the question was wrong. Reading the declaration settled it: at this version ElementDataSourceSchema.filter is FilterCondition, which is { [key: string]: … } plus $and / $or / $not — record-shaped by design, with no array form at all. So the record form is not an accident the upstream tolerates; it is the declared shape today, and this card waits on a genuine upstream narrowing. ⛔ Had the control been skipped, that REFUSED would have read as evidence for the card.

⚠️ The key is now written bare at line start, ⛔ not backticked, for the same reason this board has two cards stuck behind a backticked Release:.

⛔ The state label is unchanged and remains the triage seat's to set. This edit repairs an exit; ⛔ it transitions nothing.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions