Skip to content

convertFiltersToAST has no branch for $and / $or / $not, so spec-legal FilterCondition combinators never reach the wire #6948

Description

@claude

Split out of #4664 (PR #6946). Pre-existing on every consumer of the shared
filter sink; that card newly makes it reachable by an author following the
spec's own type, which is why it is being written down now.

What happens

convertFiltersToAST (packages/core/src/utils/filter-converter.ts) — the
repo's ONE lowering from the MongoDB-style filter object to the ObjectQL AST,
reached through toFilterNode / mergeFilterNodes — has no branch for the
logical combinators. Measured against packages/core/dist at
40c479af2, composing each condition with a parent scope:

$or    ->  ["and",["task_version","=","tv-1"],["$or","=",[{"status":"open"},{"status":"blocked"}]]]
$and   ->  ["and",["task_version","=","tv-1"],["$and","=",[{"status":"open"},{"is_active":true}]]]
$not   ->  THROWS FilterOperatorError: Unknown filter operator 'status' for field '$not'.
plain  ->  ["and",["task_version","=","tv-1"],["status","!=","archived"]]   (correct)

Two distinct failures, neither of them the right answer:

  • $and / $or fall through to the simple-equality branch (their value is
    an array, so the operator loop is skipped) and produce a leaf naming a field
    literally called $and / $or. That is a well-formed AST node carrying a
    nonsense field, so the server refuses it (400 INVALID_FILTER) — loud, but
    the diagnostic points at the author's field list rather than at the missing
    lowering.
  • $not enters the operator loop with its OWN nested object's keys read as
    operators, so the thrown message names a nonsense operator
    (Unknown filter operator 'status' for field '$not') and lists the supported
    operators — none of which is what the author needs to hear.

Why it matters now

Field.relatedListFilter (@objectstack/spec 17.1.0, objectstack#8704 /
PR #8955) is typed FilterConditionSchema, and FilterCondition explicitly
declares all three:

$and?: FilterCondition[];
$or?: FilterCondition[];
$not?: FilterCondition;   // NULL-safe per #5146

So the spec tells a metadata author these are legal in the very key #4664 wired
up, while this repo's lowering cannot carry any of them to the wire. The same
gap applies to every other producer that reaches this sink — the component-level
record:related_list.filter (objectstack#7118), ListView.filter /
ViewTab.filter via buildEffectiveFilter, and plugin-view's ObjectView —
so this is a sink-level bug, not a related-list one.

Also worth deciding as part of the fix: $not is NULL-safe by ruling
(objectstack#5146, maintainer 2026-08-04 — NOT (…) OR col IS NULL, with the
guard on each LEAF inside the negation, not hoisted). Whatever lowering is
chosen has to preserve that or hand the negation to the server intact.

Why it was not fixed in #4664

Changing the shared sink changes what every one of those callers puts on the
wire, so it needs its own card, its own fixture set and its own reverse
verification. Working around it renderer-side — accepting the combinators in
one consumer and lowering them there — is precisely the second de-facto
contract AGENTS.md #0.1 forbids, so #4664 carried the value verbatim to the
existing sink and named this boundary in its PR body instead.

Where

  • packages/core/src/utils/filter-converter.tsconvertFiltersToAST
    (the missing combinator branch), toFilterNode, mergeFilterNodes

Filed unassigned by the #4664 execution seat. No dedupe hit: a targeted issue
search for the combinator lowering returned only objectui#4744 (a different
function, ListView.convertFilterGroupToAST, closed), with a control query in
the same session returning #4664 itself.


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:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatchedpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions