Skip to content

finding(types): ObjectGallerySchema.filter is typed unknown while its own docblock says it is forwarded verbatim as $filter — the one view schema of six that is not any[] #9309

Description

@os-tesla

Measured while implementing objectui#7912 (typing the renderer seam's dataSource). Filed
unassigned and NOT fixed there: repairing it is a published type change on a second package
(@object-ui/types) with its own accept set, and #7912's ruling is scoped to the renderer
seam. The narrowing is what surfaced it, because it is the one place in that work where a
verbatim forward had to be asserted rather than declared.

Measured on origin/main at 243fc8317.

The site

packages/types/src/objectql.ts, in ObjectGallerySchema:

/** Query filter, forwarded verbatim as `$filter` */
filter?: unknown;

Its own docblock states where the value goes: forwarded verbatim as $filter, which is
QueryParams['$filter'] in packages/types/src/data.ts — declared as the MongoDB-style
field-keyed record or a FilterArray, and documented there as such.

unknown is wider than that. So the declaration promises a destination it does not type, and
every consumer that performs the documented forward has to assert the value first. There is
exactly one such consumer in this repository (plugin-list's ObjectGallery), and on the
objectui#7912 branch it now carries that assertion, with a comment naming this card.

Why it reads as a defect rather than a style choice: it is the only one of six

Every sibling view schema in the same file declares the same key as any[], with the same
"forwarded verbatim as $filter" language — six of them (grep -c 'filter?: any\[\];'
returns 6, against this one filter?: unknown). Two of those docblocks go further and say
the spelling is deliberately identical across views ("Spelled exactly as
ObjectGanttSchema.filter, so the two views' filters are one vocabulary").

So the outlier is not a stricter contract, it is an untyped one: any[] at least tells an
author the shape is an array of rules, and it is assignable to the destination. unknown
tells the author nothing and tells the compiler to refuse the forward the docblock mandates.

Why it matters for an AI author specifically

This is the shape that teaches casting. An author (or an agent) writing a gallery reads
"forwarded verbatim as $filter", writes the forward, gets a type error on a key the docblock
just told them to forward, and reaches for as any — at which point the destination's real
type (Record or FilterArray) stops being checked at that call site too. The repository's
own rule is the opposite direction: fix the declaration, do not widen the consumer
(AGENTS.md #0.1).

Not decided here

Whether the repair is any[] (match the six siblings, one vocabulary) or
QueryParams['$filter'] (match the destination exactly, which is strictly better typing but
narrows the published accept set further) is a small published-surface choice with a
changeset, and it belongs to whoever takes this card. Both are narrowings of unknown and so
both are breaking for a TypeScript consumer that assigned something else — which nothing in
this repository does, because nothing could read the key without asserting it first.

Related: objectui#7912 (where this was measured; the assertion it forced is commented there),
objectui#7804 (a different @object-ui/types declaration gap on the same schema family).

Filed by an ObjectUI development agent seat (Claude Code) while implementing objectui#7912;
the measurement commands and their readings are quoted above rather than attached.

Held, not stalled — the work is done and the PR is stuck behind an external gate

Blocked-by: objectstack-ai/objectstack#18234

PR objectui#9540 implements this card at head 3e60765fb4a80c3ee5c098b8133a26533de39842.
Landing gates (1) and (3) are MET on that head; gate (2) -- check-clause2-carriers --pair 9540
-- exits 4 on a C5 false positive filed as objectstack#18234. The two mechanical clears both
amount to recording a widening that did not happen, so neither is taken. The card stays claimed
and the PR stays open; the unlock scan puts this back when the upstream closes.


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

bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepm:blockedpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions