Skip to content

The seven converged rule-array filter doors refuse the record form with a bare invalid_type — this package's own guidance convention is not applied at any of them #17320

Description

@os-bill

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

Measured on origin/main 501959b72.

The reading

Seven filter doors converged on z.array(ViewFilterRuleSchema) in the objectui#6206 family. On the record form an author used to write — { status: 'active' } — each one produces exactly one zod issue: invalid_type at path filter, expected array, received object. Nothing else.

The seven doors, by declaring symbol (packages/spec/src):

door symbol file
the binding every data-bound element carries ElementDataSourceSchema.filter ui/page.zod.ts
object-grid ComponentPropsMap['object-grid'].filter ui/component.zod.ts
object-metric ComponentPropsMap['object-metric'].filter ui/component.zod.ts
object-kanban ComponentPropsMap['object-kanban'].filter ui/component.zod.ts
object-calendar ComponentPropsMap['object-calendar'].filter ui/component.zod.ts
element:number ComponentPropsMap['element:number'].filter ui/component.zod.ts
element:record_picker ComponentPropsMap['element:record_picker'].filter ui/component.zod.ts

What is already there, and what is not — the narrowing that matters

⚠️ A first draft of this card said the prescription "ships only in the migration ledger". That is false and is corrected here before filing. All seven .describe() strings already end with The MongoDB-style record form is refused — see migration \element-…-filter-rule-array`, and the three SemanticMigrationentries carry the full conversion table (record → rule array, operator objects →operator`, several keys AND together, legacy shorthands normalized on parse):

  • migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts
  • migrations/entries/semantic/18.element-number-filter-rule-array.ts
  • migrations/entries/semantic/18.element-record-picker-filter-rule-array.ts

The gap is the runtime refusal, not the documentation. Nothing bridges .describe() into a zod issue: there is no global error map in this package (setErrorMap / z.config → 0 hits under packages/spec/src, lit control: strictObject's own error callback at shared/strict-object.ts strictObject() is present and does exactly this bridging for the unknown-key case). So an author — or an AI agent authoring metadata, which is the population these doors exist for — reads expected array, received object and has no pointer to the entry that would tell it what to write instead.

Each of the three migration entries states the bare refusal as its own acceptance criterion, in the entry's own words: "a record-form filter: { status: 'active' } is refused at the filter path (invalid_type, expected array)". So this is not a regression against what was ruled — it is the residue the ruling did not cover.

Why this package already treats a bare invalid_type as insufficient

Three precedents in packages/spec/src, all zod-v4 { error } params, all measured on 501959b72:

  • shared/expression.zod.tsEvaluatedExpressionSchema.source is z.string({ error: () => EVALUATED_EXPRESSION_SOURCE_REQUIRED }) plus a .refine(..., { message: … }), so a missing or blank source names itself instead of emitting invalid_type.
  • ui/view.zod.tsflattenedViewOverlayFields()'s object and viewKind both use error: (issue) => (issue.input === undefined ? INLINE_VIEW_… : undefined), i.e. a targeted message for the one shape an author gets wrong, falling through to the default otherwise.
  • shared/strict-object.tsstrictObject() passes error: strictObjectError(options, shape), which names the surface, echoes the offending key and suggests the closest declared key with the candidate list read from shape.

The third one is the closest model: guidance derived from the schema rather than transcribed beside it, so it cannot drift.

Why it matters

The population at these doors is metadata authors, and the failure mode the convergence was ruled to end (objectui#6206, maintainer batch adjudication 2026-08-25, verbatim 「同意」, Option B) was two orthographies, silent success on both. The convergence replaced silence with a refusal, which is the right direction. But the refusal an author actually receives is the one sentence that does not say what the other orthography is — so the author's next action is a guess, and the second-guess (an ObjectQL AST tuple array, which the z.unknown() block doors used to take) fails at filter.0 with a second bare invalid_type.

Suggested shape for whoever takes it

One shared guidance constant per family, wired through the zod-v4 { error } param at all seven doors, naming the rule-array form and the migration entry id. ⚠️ The error callback must fall through (return undefined) for anything other than the record shape, as flattenedViewOverlayFields() does — a blanket message would swallow the filter.0 element-level issues an array author needs.

⚠️ Whoever takes it should check whether widening the message counts as clause ② (needs:contract-review): the accept set does not move, but the emitted issue text is read by the objectui parity fixtures and by packages/lint, so the blast radius is larger than the schema diff.

Dedup

Complete enumerations on origin/main-era GitHub, read 2026-09-10T06:40Z: objectstack open domain:spec = 111 (2 pages, 100 + 11), open finding = 160 (100 + 60), open domain:services = 94 (1 page). Union 295 cards grepped for filter / invalid_type / rule array / guidance / refusal.

Nearest neighbours, each read and judged not a duplicate:

Source

PR #17257 (#15442 anchor, #15449 member) · PR #17267 (#15807) · the three 18.*-filter-rule-array semantic entries on origin/main


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

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions